diff --git a/.github/workflows/build-codeql.yaml b/.github/workflows/build-codeql.yaml index 8c3760aa..cdc638ea 100644 --- a/.github/workflows/build-codeql.yaml +++ b/.github/workflows/build-codeql.yaml @@ -13,9 +13,13 @@ on: # Allow manual scheduling workflow_dispatch: +env: + CODEQL_VERSION: 2.21.2 + jobs: - build-publish: + build: runs-on: windows-latest + permissions: contents: read packages: write @@ -30,22 +34,62 @@ jobs: path: . fetch-depth: 0 - - name: Download CodeQL CLI - uses: i3h/download-release-asset@v1.2.0 - with: - owner: "github" - repo: "codeql-cli-binaries" - tag: "v2.15.4" - file: "codeql-win64.zip" + - name: CodeQL Download + run: + Invoke-WebRequest -Uri "https://github.com/github/codeql-cli-binaries/releases/download/v${{ env.CODEQL_VERSION }}/codeql-win64.zip" -OutFile codeql-win64.zip; + Expand-Archive -Path codeql-win64.zip -DestinationPath .\codeql-zip -Force; + Move-Item -Path .\codeql-zip\codeql -Destination .\codeql-cli\ + + - name: Install CodeQL pack dependencies + shell: cmd + run: | + pushd .\src + ..\codeql-cli\codeql.cmd pack install + popd + + - name: codeql version test + run: .\codeql-cli\codeql.exe version + + - name: Build must-fix driver suite + shell: cmd + run: .\codeql-cli\codeql.cmd query compile --check-only mustfix.qls - - name: Unzip CodeQL CLI - run: Expand-Archive -Path codeql-win64.zip -DestinationPath .\codeql-zip -Force + - name: Build recommended driver suite + shell: cmd + run: .\codeql-cli\codeql.cmd query compile --check-only recommended.qls - - name: Move CodeQL CLI folder to main subdirectory + - name: Build CA ported queries shell: cmd - continue-on-error: true # Required because robocopy returns 1 on success - run: robocopy /S /move .\codeql-zip\codeql .\codeql-cli\ - + run: .\codeql-cli\codeql.cmd query compile --check-only ported_driver_ca_checks.qls + + - name: Build all Windows queries + shell: cmd + run: .\codeql-cli\codeql.cmd query compile --check-only .\src + + test-query-health: + runs-on: windows-latest + needs: build + permissions: + contents: read + packages: write + id-token: write + env: + ACCOUNT_NAME: ${{ secrets.ACCOUNT_NAME }} + SHARE_NAME: ${{ secrets.SHARE_NAME }} + steps: + - name: Enable long git paths + shell: cmd + run: git config --global core.longpaths true + - name: Clone self (windows-driver-developer-supplemental-tools) + uses: actions/checkout@v4 + with: + path: . + fetch-depth: 0 + - name: CodeQL Download + run: + Invoke-WebRequest -Uri "https://github.com/github/codeql-cli-binaries/releases/download/v${{ env.CODEQL_VERSION }}/codeql-win64.zip" -OutFile codeql-win64.zip; + Expand-Archive -Path codeql-win64.zip -DestinationPath .\codeql-zip -Force; + Move-Item -Path .\codeql-zip\codeql -Destination .\codeql-cli\ - name: Install CodeQL pack dependencies shell: cmd run: | @@ -54,80 +98,254 @@ jobs: popd - name: codeql version test run: .\codeql-cli\codeql.exe version - - name: Setup Python uses: actions/setup-python@v5 with: python-version: 3.11 - - name: Install Python Packages run: | python -m pip install --upgrade pip pip install -r .\src\drivers\test\requirements.txt - name: Add msbuild to PATH uses: microsoft/setup-msbuild@v2 - + - name: Azure Login + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + enable-AzPSSession: true + - name: Download previous results + uses: azure/powershell@v2 + with: + azPSVersion: latest + inlineScript: | + $context = New-AzStorageContext -StorageAccountName "$env:ACCOUNT_NAME" -UseConnectedAccount -EnableFileBackupRequestIntent + $destination = "azure-detailedfunctiontestresults.xlsx" + Get-AzStorageFileContent -ShareName "$env:SHARE_NAME" -Path "detailedfunctiontestresults.xlsx" -Destination $destination -Context $context - name: Run test script shell: pwsh - continue-on-error: true # Allow script to return non-zero exit code - env: - CONNECTION_STRING: ${{ secrets.CONNECTION_STRING }} - ACCOUNT_KEY: ${{ secrets.ACCOUNT_KEY }} - SHARE_NAME: ${{ secrets.SHARE_NAME }} - CONTAINER_NAME: ${{ secrets.CONTAINER_NAME }} - ACCOUNT_NAME: ${{ secrets.ACCOUNT_NAME }} - - run: python src\drivers\test\build_create_analyze_test.py --codeql_path .\codeql-cli\codeql.exe --no_build --compare_results --connection_string "$env:CONNECTION_STRING" --share_name "$env:SHARE_NAME" --container_name "$env:CONTAINER_NAME" --storage_account_key "$env:ACCOUNT_KEY" --storage_account_name "$env:ACCOUNT_NAME" + run: python src\drivers\test\build_create_analyze_test.py --codeql_path .\codeql-cli\codeql.exe --no_build --compare_results -v + - name: Upload result diff + if: ${{ hashFiles('diffdetailedfunctiontestresults.xlsx') != '' }} # Only upload if there are changes + uses: azure/powershell@v2 + with: + azPSVersion: latest + inlineScript: | + Update-AzConfig -DisplayBreakingChangeWarning $false + $context = New-AzStorageContext -StorageAccountName "$env:ACCOUNT_NAME" -UseConnectedAccount -EnableFileBackupRequestIntent + Set-AzStorageFileContent -ShareName "$env:SHARE_NAME" -Source "diffdetailedfunctiontestresults.xlsx" -Path "health-diffdetailedfunctiontestresults.xlsx" -Context $context + exit 1 + - - name: Build must-fix driver suite + test-codeql-latest-vs-current: + # Tests if the latest codeql version produces the same results as the current version. + runs-on: windows-latest + continue-on-error: true # Allow script to return non-zero exit code + needs: [build,test-query-health] + permissions: + contents: read + packages: write + id-token: write + env: + ACCOUNT_NAME: ${{ secrets.ACCOUNT_NAME }} + SHARE_NAME: ${{ secrets.SHARE_NAME }} + steps: + - name: Check Prev Job + if: ${{ needs.test-query-health.result == 'failure' }} + shell: pwsh + run: exit 1 + - name: Enable long git paths shell: cmd - run: .\codeql-cli\codeql.cmd query compile --check-only windows_mustfix_partial.qls - - - name: Build recommended driver suite + run: git config --global core.longpaths true + - name: Clone self (windows-driver-developer-supplemental-tools) + uses: actions/checkout@v4 + with: + path: . + fetch-depth: 0 + - name: CodeQL Download + run: | + $latest=(((Invoke-WebRequest -Uri https://github.com/github/codeql-cli-binaries/releases/latest).links.href | Where-Object{$_ -like "/github/codeql-cli-binaries/releases/tag/v*"}[0]) -split "/")[-1] + Invoke-WebRequest -Uri "https://github.com/github/codeql-cli-binaries/releases/download/$latest/codeql-win64.zip" -OutFile codeql-win64.zip + Expand-Archive -Path codeql-win64.zip -DestinationPath .\codeql-zip -Force + Move-Item -Path .\codeql-zip\codeql -Destination .\codeql-cli\ + New-Item LatestVersion -ItemType "Directory" + New-Item LatestVersion/$latest + - name: Install CodeQL pack dependencies shell: cmd - run: .\codeql-cli\codeql.cmd query compile --check-only windows_recommended_partial.qls - - - name: Build CA ported queries + run: | + pushd .\src + ..\codeql-cli\codeql.cmd pack install + popd + - name: codeql version test + run: .\codeql-cli\codeql.exe version + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: 3.11 + - name: Install Python Packages + run: | + python -m pip install --upgrade pip + pip install -r .\src\drivers\test\requirements.txt + - name: Add msbuild to PATH + uses: microsoft/setup-msbuild@v2 + - name: Azure Login + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + enable-AzPSSession: true + - name: Download previous results + uses: azure/powershell@v2 + with: + azPSVersion: latest + inlineScript: | + $context = New-AzStorageContext -StorageAccountName "$env:ACCOUNT_NAME" -UseConnectedAccount -EnableFileBackupRequestIntent + $destination = "azure-detailedfunctiontestresults.xlsx" + Get-AzStorageFileContent -ShareName "$env:SHARE_NAME" -Path "detailedfunctiontestresults.xlsx" -Destination $destination -Context $context + - name: Run test script + shell: pwsh + run: python src\drivers\test\build_create_analyze_test.py --codeql_path .\codeql-cli\codeql.exe --no_build --compare_results -v + - name: Upload result diff + if: ${{ hashFiles('diffdetailedfunctiontestresults.xlsx') != '' }} # Only upload if there are changes + uses: azure/powershell@v2 + with: + azPSVersion: latest + inlineScript: | + $context = New-AzStorageContext -StorageAccountName "$env:ACCOUNT_NAME" -UseConnectedAccount -EnableFileBackupRequestIntent + Set-AzStorageFileContent -ShareName "$env:SHARE_NAME" -Source "diffdetailedfunctiontestresults.xlsx" -Path "version-diffdetailedfunctiontestresults.xlsx" -Context $context + exit 1 + - name: Save Latest Version + if: ${{ hashFiles('diffdetailedfunctiontestresults.xlsx') == '' }} # Only if there were no differences + uses: actions/upload-artifact@v4 + with: + name: latest-codeql-results + path: | + LatestVersion\* + + test-pack-version-update: + runs-on: windows-latest + needs: build + permissions: + contents: read + packages: write + steps: + - name: Enable long git paths shell: cmd - run: .\codeql-cli\codeql.cmd query compile --check-only ported_driver_ca_checks.qls + run: git config --global core.longpaths true - - name: Build all Windows queries - shell: cmd - run: .\codeql-cli\codeql.cmd query compile --check-only .\src - + - name: Clone self (windows-driver-developer-supplemental-tools) + uses: actions/checkout@v4 + with: + path: . + fetch-depth: 0 + - name: Check for changes to qlpack shell: pwsh run: $qlpack_diff = git diff HEAD~1:src/qlpack.yml src/qlpack.yml; - $rec_diff = git diff HEAD~1:src/windows-driver-suites/windows_recommended_partial.qls src/windows-driver-suites/windows_recommended_partial.qls; - $mf_diff = git diff HEAD~1:src/windows-driver-suites/windows_mustfix_partial.qls src/windows-driver-suites/windows_mustfix_partial.qls; - if (!$qlpack_diff -and ($rec_diff -or $mf_diff)) { "Query suite file updated without updating qlpack version"; exit 1 } + $rec_diff = git diff HEAD~1:src/windows-driver-suites/recommended.qls src/windows-driver-suites/recommended.qls; + $mf_diff = git diff HEAD~1:src/windows-driver-suites/mustfix.qls src/windows-driver-suites/mustfix.qls; + if (!$qlpack_diff -and ($rec_diff -or $mf_diff)) { "Query suite file updated without updating qlpack version"; exit 2 } $last_qlpack_commit = git log -n 1 --pretty=format:%H -- src/qlpack.yml; $qlpack_changes =git show $last_qlpack_commit -- .\src\qlpack.yml; - $last_mf_commit = git log -n 1 --pretty=format:%H -- src/windows-driver-suites/windows_mustfix_partial.qls; - $last_rec_commit = git log -n 1 --pretty=format:%H -- src/windows-driver-suites/windows_recommended_partial.qls; + $last_mf_commit = git log -n 1 --pretty=format:%H -- src/windows-driver-suites/mustfix.qls; + $last_rec_commit = git log -n 1 --pretty=format:%H -- src/windows-driver-suites/recommended.qls; $commits_since_qlpack_change = [int](git rev-list --count HEAD...$last_qlpack_commit); $commits_since_mf_change = [int](git rev-list --count HEAD...$last_mf_commit); $commits_since_rec_change = [int](git rev-list --count HEAD...$last_rec_commit); - if ($commits_since_qlpack_change -gt $commits_since_mf_change) { "Mustfix query suite file modified without updating version"; exit 1 }; - if ($commits_since_qlpack_change -gt $commits_since_rec_change) {"Recommended query suite file modified without updating version"; exit 1 }; + if ($commits_since_qlpack_change -gt $commits_since_mf_change) { "Mustfix query suite file modified without updating version"; exit 3 }; + if ($commits_since_qlpack_change -gt $commits_since_rec_change) {"Recommended query suite file modified without updating version"; exit 4 }; + + if($qlpack_changes -contains "version"){ + try{$old_qlpack_version = [version]($qlpack_changes -match "-version").Substring(10);} catch {"Changed qlpack.yml without updating version"; exit 5 } + try{$new_qlpack_version = [version]($qlpack_changes -match "\+version").Substring(10);} catch {"Changed qlpack.yml without updating version"; exit 6 } + if ($new_qlpack_version -gt $old_qlpack_version) { exit 0 } else { "qlpack.yml version not incremented. Previously updated to version $old_qlpack_version, current version $new_qlpack_version"; exit 7 } + } + test-create-dvl: + runs-on: windows-latest + needs: build + permissions: + contents: read + packages: write + steps: + - name: Enable long git paths + shell: cmd + run: git config --global core.longpaths true + + - name: Clone self (windows-driver-developer-supplemental-tools) + uses: actions/checkout@v4 + with: + path: . + fetch-depth: 0 + + - name: CodeQL Download + run: + Invoke-WebRequest -Uri "https://github.com/github/codeql-cli-binaries/releases/download/v${{ env.CODEQL_VERSION }}/codeql-win64.zip" -OutFile codeql-win64.zip; + Expand-Archive -Path codeql-win64.zip -DestinationPath .\codeql-zip -Force; + Move-Item -Path .\codeql-zip\codeql -Destination .\codeql-cli\ + + - name: Install CodeQL pack dependencies + shell: cmd + run: | + pushd .\src + ..\codeql-cli\codeql.cmd pack install + popd + - name: Add msbuild to PATH + uses: microsoft/setup-msbuild@v2 - try{$old_qlpack_version = [version]($qlpack_changes -match "-version").Substring(10);} catch {"Changed qlpack.yml without updating version"; exit 1 } - try{$new_qlpack_version = [version]($qlpack_changes -match "\+version").Substring(10);} catch {"Changed qlpack.yml without updating version"; exit 1 } - if ($new_qlpack_version -gt $old_qlpack_version) { exit 0 } else { "qlpack.yml version not incremented"; exit 1 } + - name: Test DVL + run: src\drivers\test\dvl_tests\dvl_tests.ps1 + + - name: Archive code coverage results + uses: actions/upload-artifact@v4 + with: + name: dvl-outputs + path: | + clean_results\*.* + mustfix_results\*.* + + publish: + runs-on: windows-latest + continue-on-error: true + needs: [build, test-pack-version-update, test-query-health] + permissions: + contents: read + packages: write + steps: + - name: Enable long git paths + shell: cmd + run: git config --global core.longpaths true + + - name: Clone self (windows-driver-developer-supplemental-tools) + uses: actions/checkout@v4 + with: + path: . + fetch-depth: 0 + - name: CodeQL Download + run: + Invoke-WebRequest -Uri "https://github.com/github/codeql-cli-binaries/releases/download/v${{ env.CODEQL_VERSION }}/codeql-win64.zip" -OutFile codeql-win64.zip; + Expand-Archive -Path codeql-win64.zip -DestinationPath .\codeql-zip -Force; + Move-Item -Path .\codeql-zip\codeql -Destination .\codeql-cli\ + + - name: Install CodeQL pack dependencies + shell: cmd + run: | + pushd .\src + ..\codeql-cli\codeql.cmd pack install + popd - name: Publish New CodeQL Pack shell: pwsh env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: $build = git rev-parse --short HEAD; $version =( Select-String .\src\qlpack.yml -Pattern "version").line; $new_ver = "$version-alpha+$build"; (Get-Content .\src\qlpack.yml).Replace($version, $new_ver) | Set-Content .\src\qlpack.yml; .\codeql-cli\codeql.cmd pack publish --allow-prerelease ./src; - diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 00000000..4ee0dabf --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,44 @@ + +name: Build and Publish Windows CodeQL queries +on: + workflow_dispatch: + input: + version: + description: 'CodeQL version to use' + required: true + type: string +jobs: + publish: + runs-on: windows-latest + permissions: + contents: read + packages: write + steps: + - name: Enable long git paths + shell: cmd + run: git config --global core.longpaths true + + - name: Clone self (windows-driver-developer-supplemental-tools) + uses: actions/checkout@v4 + with: + path: . + fetch-depth: 0 + + - name: CodeQL Download + run: + Invoke-WebRequest -Uri "https://github.com/github/codeql-cli-binaries/releases/download/v${{ github.event.inputs.version }}/codeql-win64.zip" -OutFile codeql-win64.zip; + Expand-Archive -Path codeql-win64.zip -DestinationPath .\codeql-zip -Force; + Move-Item -Path .\codeql-zip\codeql -Destination .\codeql-cli\ + + - name: Install CodeQL pack dependencies + shell: cmd + run: | + pushd .\src + ..\codeql-cli\codeql.cmd pack install + popd + - name: Publish New CodeQL Pack + shell: pwsh + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: + .\codeql-cli\codeql.cmd pack publish ./src; diff --git a/.gitignore b/.gitignore index 2c0570b6..290adb6b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ # query compilation caches **/*.cache +**/*.codeql/ + # IDE folders .vscode/* **/.vs/* @@ -13,6 +15,11 @@ src/drivers/test/**/Debug/* src/drivers/test/TestDB/* src/drivers/test/working/* src/drivers/test/AnalysisFiles/* - +AnalysisFiles/* +TestDB/* +working/* #excel files -**/*.xlsx \ No newline at end of file +**/*.xlsx + +# Log files +**/*.log diff --git a/README.md b/README.md index f1576298..ca9c99db 100644 --- a/README.md +++ b/README.md @@ -2,96 +2,61 @@ This repository contains open-source components for supplemental use in developing device drivers for Windows, as well as driver specific [CodeQL](https://codeql.github.com/) query suites used for the [Windows Hardware Compatibility Program](https://learn.microsoft.com/en-us/windows-hardware/design/compatibility/). The quickstart below will get you set up to build your database and analyze your driver using CodeQL. For the full documentation, troubleshooting, and more details about the Static Tools Logo test within the WHCP Program, please visit [CodeQL and the Static Tools Logo Test](https://docs.microsoft.com/windows-hardware/drivers/devtest/static-tools-and-codeql). -### For General Use +### For General Use or Windows Hardware Compatibility Program Use -| CodeQL CLI version | microsoft/windows-drivers qlpack version | codeql/cpp-queries version |Associated Repo Branch| -|--------------------------|-------------------------------------------|------------------------|------------------------| -| 2.15.4 | latest | latest |main | +| CodeQL CLI Version | microsoft/windows-drivers CodeQL Pack Version | microsoft/cpp-queries CodeQL Pack Version | Associated Repo Branch| +|--------------------------|------------------------------------------|-------------------------------|-----------------------------| +| 2.15.4 or greater* | [Latest Stable Version](https://github.com/microsoft/Windows-Driver-Developer-Supplemental-Tools/pkgs/container/windows-drivers) | 0.0.4 | Main | -### For Windows Hardware Compatibility Program Use - -### Windows Hardware Compatibility Program Release Version Matrix -| Release | CodeQL CLI version | microsoft/windows-drivers qlpack version| codeql/cpp-queries version | Associated Repo Branch| -|--------------------------|--------------------|-----------------------------------------|----------------------|-----------------------------| -| Windows Server 2022 | [2.4.6](https://github.com/github/codeql-cli-binaries/releases/tag/v2.4.6) or [2.15.4](https://github.com/github/codeql-cli-binaries/releases/tag/v2.15.4)| 1.0.13 (If using codeql 2.15.4)| 0.9.0 (If using codeql 2.15.4) | WHCP_21H2| -| Windows 11 | [2.4.6](https://github.com/github/codeql-cli-binaries/releases/tag/v2.4.6) or [2.15.4](https://github.com/github/codeql-cli-binaries/releases/tag/v2.15.4)| 1.0.13 (If using codeql 2.15.4)| 0.9.0 (If using codeql 2.15.4)|WHCP_21H2| -| Windows 11, version 22H2 | [2.6.3](https://github.com/github/codeql-cli-binaries/releases/tag/v2.6.3) or [2.15.4](https://github.com/github/codeql-cli-binaries/releases/tag/v2.15.4)| 1.0.13 (If using codeql 2.15.4)| 0.9.0 (If using codeql 2.15.4)|WHCP_22H2| -| Windows 11, version 23H2 | [2.6.3](https://github.com/github/codeql-cli-binaries/releases/tag/v2.6.3) or [2.15.4](https://github.com/github/codeql-cli-binaries/releases/tag/v2.15.4)| 1.0.13 (If using codeql 2.15.4)| 0.9.0 (If using codeql 2.15.4)|WHCP_22H2| -| Windows 11, version 24H2 | [2.15.4](https://github.com/github/codeql-cli-binaries/releases/tag/v2.15.4) | 1.1.0 | 0.9.0 | WHCP_24H2 | +#### Validated CodeQL Versions For Use with WHCP +| CodeQL CLI Version | +|--------------------------| +| 2.21.4 | +| 2.21.2 | +| 2.20.1 | +| 2.15.4 | +*See appendix for more information +### For Testing the Latest in Development +| CodeQL CLI Version | microsoft/windows-drivers CodeQL Pack Version | microsoft/cpp-queries CodeQL Pack Version | Associated Repo Branch| +|--------------------------|------------------------------------------|-------------------------------|-----------------------------| +| [Latest](https://github.com/github/codeql-cli-binaries/releases/latest) | [Latest Beta Version](https://github.com/microsoft/Windows-Driver-Developer-Supplemental-Tools/pkgs/container/windows-drivers) | [Latest](https://github.com/orgs/microsoft/packages/container/package/cpp-queries) | Development | ## Quickstart 1. Create a directory where you can place the CodeQL CLI and the queries you want to use: ``` - D:\> mkdir codeql-home + mkdir codeql-home ``` -1. Download the CodeQL CLI zip by selecting the asset associated with your OS and architecture (codeql-win64.zip, codeql-linux64.zip, etc.), then extract it to the directory you created in the previous step. - - **NOTE** Visual Studio 17.8 broke compatibility with the older versions of CodeQL used in the WHCP_21H2 and WHCP_22H2 branches. [CodeQL CLI version 2.15.4](https://github.com/github/codeql-cli-binaries/releases/tag/v2.15.4) has been validated for use with WHCP 21H2 and WHCP 22H2 when using Visual Studio 17.8 or greater. - - For the WHCP Program, use the CodeQL CLI version in accordance with the table above and Windows release you are certifying for: [version 2.4.6](https://github.com/github/codeql-cli-binaries/releases/tag/v2.4.6), [version 2.6.3](https://github.com/github/codeql-cli-binaries/releases/tag/v2.6.3), or [version 2.15.4](https://github.com/github/codeql-cli-binaries/releases/tag/v2.15.4). - - - - For general use, use [CodeQL CLI version 2.15.4](https://github.com/github/codeql-cli-binaries/releases/tag/v2.15.4) and the latest version of the microsoft/windows-driver qlpack. - - - -1. Verify CodeQL is installed correctly by checking the version: - ``` - D:\codeql-home\codeql>codeql --version - CodeQL command-line toolchain release 2.15.4. - Copyright (C) 2019-2023 GitHub, Inc. - Unpacked in: D:\codeql-home\codeql - Analysis results depend critically on separately distributed query and - extractor modules. To list modules that are visible to the toolchain, - use 'codeql resolve qlpacks' and 'codeql resolve languages'. - ``` +1. Download the CodeQL CLI + + For the WHCP Program, use the CodeQL CLI version specified above. For special cases and more information see appendix. + 1. Navigate to the [CodeQL CLI Release Page](https://github.com/github/codeql-cli-binaries/releases) + 1. Find the release version based on the tables above and select the asset associated with your OS and architecture (codeql-win64.zip, codeql-linux64.zip, etc.), + 1. Extract the downloaded zip to the directory you created in the previous step. + 1. (Optional) Add the CodeQL install location to your PATH + 1. (Optional) Verify CodeQL is installed correctly by checking the version `codeql --version` 1. Install CodeQL Packages - For WHCP_21H2 and WHCP_22H2 branches: - - 1. If using Visual Studio 2022 17.8 or greater with WHCP_21H2 or WHCP_22H2 and CodeQL CLI version 2.15.4: - - Follow the steps for "ALL OTHER BRANCHES." **Make sure to remove the CodeQL submodule if you still have an old version of the repo cloned.** CodeQL might try to use the queries in the submodule by default which will cause errors because of mismatched versions. - - 1. If using Visual Studio version 17.7 or below **AND** either WHCP_21H2 or WHCP_22H2 **AND** CodeQL VLI version 2.4.6 or 2.6.3: - - Follow special instructions for WHCP_21H2 and WHCP_22H2 using VS17.7 at the end of this readme - - - **For ALL OTHER BRANCHES:** - - **Note:** It is no longer necessary to clone the Windows-Driver-Developer-Supplemental-Tools repo to use the queries for certification. - - Download the correct version of the CodeQL packs from the Windows Hardware Compatibility Program Release Version Matrix: + Download the correct version of the CodeQL packs. For special cases and more information see appendix. ``` codeql pack download microsoft/windows-drivers@ ``` ``` - codeql pack download codeql/cpp-queries@ + codeql pack download microsoft/cpp-queries@ ``` CodeQL will install the packs to the default directory `C:\Users\\.codeql\packages\microsoft\windows-drivers\\`. Do not change this directory or move the installed pack. - For examples, if using WHCP_24H2, run the following command to download query the microsoft/windows-drivers pack: - - ``` - codeql pack download microsoft/windows-drivers@1.1.0 - ``` - - - 1. Build your CodeQL database: ``` - D:\codeql-home\codeql>codeql database create --language=cpp --source-root= --command= + codeql database create --language=cpp --source-root= --command= ``` Single driver example: `codeql database create D:\DriverDatabase --language=cpp --source-root=D:\Drivers\SingleDriver --command="msbuild /t:rebuild D:\Drivers\SingleDriver\SingleDriver.sln"` @@ -101,36 +66,28 @@ This repository contains open-source components for supplemental use in developi 1. Analyze your CodeQL database: - CodeQL's analysis output is provided in the form of a SARIF log file. For a human readable format, drop the SARIF file into [SARIF Viewer Website](https://microsoft.github.io/sarif-web-component/). (If there are violations, they will show up. If not, the page will not update.) + CodeQL's analysis output is provided in the form of a SARIF log file. For a human readable format, drop the SARIF file into [SARIF Viewer Website](https://microsoft.github.io/sarif-web-component/) (If there are violations, they will show up. If not, the page will not update) or view using an extension in Visual Studio or Visual Studio Code. - CodeQL query suites are provided in the suites directory and contain the sets of all recommended and mustfix queries. The desired query suite file should be downloaded/copied locally. - - 1. Create a local copy of the desired query suite file: - - * windows_driver_mustfix.qls - * windows_driver_recommended.qls + CodeQL query suites are provided in the windows-driver-suites directory and contain the sets of all recommended and mustfix queries. Both the recommended and mustfix queries must be run. Once the microsoft/windows-drivers pack is downloaded, these suites can be referenced relative to the pack name, as seen below. - 2. To analyze a CodeQL database run the following command: + 1. To analyze a CodeQL database run the following command: ``` - codeql database analyze --download --format=sarifv2.1.0 --output=.sarif + codeql database analyze --format=sarifv2.1.0 --output=.sarif ``` - **NOTE** The "--download" flag tells CodeQL to download dependencies before running the queries. + Example: + ```codeql database analyze D:\DriverDatabase microsoft/windows-drivers:windows-driver-suites/recommended.qls --format=sarifv2.1.0 --output=D:\DriverAnalysis1.sarif ``` + + **NOTE** The "--download" flag can be used to tell CodeQL to download dependencies before running the queries. - Specific versions, queries, or suites can be specified using the format `codeql database analyze /@x.x.x:`. For futher information, see the [CodeQL documentation](https://docs.github.com/en/code-security/codeql-cli/using-the-advanced-functionality-of-the-codeql-cli/publishing-and-using-codeql-packs#using-a-codeql-pack-to-analyze-a-codeql-database). + **NOTE** Specific versions, queries, or suites can be specified using the format `codeql database analyze /@x.x.x:`. For futher information, see the [CodeQL documentation](https://docs.github.com/en/code-security/codeql-cli/using-the-advanced-functionality-of-the-codeql-cli/publishing-and-using-codeql-packs#using-a-codeql-pack-to-analyze-a-codeql-database). - Example: `codeql database analyze --download D:\DriverDatabase suites/windows_driver_recommended.qls --format=sarifv2.1.0 --output=D:\DriverAnalysis1.sarif ` - - _(Parameters: path to new database, query pack, format, output sarif file)_ - 1. ***For WHCP Users Only***: Prepare to Create a Driver Verification Log (DVL): - Before you can create a DVL, you must copy your SARIF log file to the parent directory of your driver project. You can also modify your output location in the `codeql database analyze` step in order to skip this additional step. Once you have finished this step, please refer to the continued instructions at [CodeQL and the Static Tools Logo Test, Driver Verification Log DVL Consumption of SARIF Output](https://learn.microsoft.com/en-us/windows-hardware/drivers/devtest/static-tools-and-codeql#driver-verification-log-dvl-consumption-of-sarif-output). - ``` - D:\codeql-home\codeql>copy - ``` - Example: `D:\codeql-home\codeql> copy D:\DriverAnalysis1.sarif D:\Drivers\SingleDriver` + To create a DVL, your SARIF log file must be in the parent directory of your driver project. You can modify your output location in the `codeql database analyze` step or copy the file manyally + + Please refer to the continued instructions at [CodeQL and the Static Tools Logo Test, Driver Verification Log DVL Consumption of SARIF Output](https://learn.microsoft.com/en-us/windows-hardware/drivers/devtest/static-tools-and-codeql#driver-verification-log-dvl-consumption-of-sarif-output). ## Navigation @@ -138,7 +95,7 @@ Windows drivers queries are in the `src/drivers` directory. Non-driver Microsoft-specific queries provided by Microsoft are in the `src/microsoft` directory. -Query suites are located in the `suites` directory and contain the Must-Fix and Recommended-Fix suites used by the WHCP Program. +Query suites are located in the `windows-driver-suites` directory and contain the Must-Fix and Recommended suites used by the WHCP Program. @@ -177,7 +134,36 @@ Use of Microsoft trademarks or logos in modified versions of this project must n Any use of third-party trademarks or logos are subject to those third-party's policies. -## Special instructions for WHCP_21H2 and WHCP_22H2 using VS17.7 or below +## Appendix + +### Windows Hardware Compatibility Program Release Version Matrix +The versions below are the minumum required versions for WHCP certification. Newer versions continue to be validated for use and are listed above. +| Release | CodeQL CLI version | microsoft/windows-drivers qlpack version| microsoft/cpp-queries version | codeql/cpp-queries version | Associated Repo Branch| +|--------------------------|--------------------|-----------------------------------------|-------------------------------|-----------------------------|----------------------| +| Windows Server 2022 | [2.4.6](https://github.com/github/codeql-cli-binaries/releases/tag/v2.4.6) or [2.15.4](https://github.com/github/codeql-cli-binaries/releases/tag/v2.15.4)| 1.0.13 (If using codeql 2.15.4)| N/A |0.9.0 (If using codeql 2.15.4) | WHCP_21H2 | +| Windows 11, version 22H2 | [2.6.3](https://github.com/github/codeql-cli-binaries/releases/tag/v2.6.3) or [2.15.4](https://github.com/github/codeql-cli-binaries/releases/tag/v2.15.4)| 1.0.13 (If using codeql 2.15.4)| N/A |0.9.0 (If using codeql 2.15.4) | WHCP_22H2 | +| Windows 11, version 23H2 | [2.6.3](https://github.com/github/codeql-cli-binaries/releases/tag/v2.6.3) or [2.15.4](https://github.com/github/codeql-cli-binaries/releases/tag/v2.15.4)| 1.0.13 (If using codeql 2.15.4)| N/A |0.9.0 (If using codeql 2.15.4) | WHCP_22H2 | +| Windows 11 | [2.4.6](https://github.com/github/codeql-cli-binaries/releases/tag/v2.4.6) or [2.15.4](https://github.com/github/codeql-cli-binaries/releases/tag/v2.15.4)| 1.0.13 (If using codeql 2.15.4)| N/A |0.9.0 (If using codeql 2.15.4) | WHCP_21H2 | +| Windows 11, version 24H2 | [2.15.4](https://github.com/github/codeql-cli-binaries/releases/tag/v2.15.4) | 1.1.0 | N/A |0.9.0 | WHCP_24H2 | +| Windows Server 2025 | [2.20.1](https://github.com/github/codeql-cli-binaries/releases/tag/v2.20.1) | 1.6.0 | 0.0.4 | N/A | | +| Windows 11, version | [2.20.1](https://github.com/github/codeql-cli-binaries/releases/tag/v2.20.1) | 1.6.0 | 0.0.4 | N/A | | + + +### Special instructions for for WHCP_21H2 and WHCP_22H2 branches: +Visual Studio 17.8 broke compatibility with the older versions of CodeQL used in the WHCP_21H2 and WHCP_22H2 branches. [CodeQL CLI version 2.15.4](https://github.com/github/codeql-cli-binaries/releases/tag/v2.15.4) has been validated for use with WHCP 21H2 and WHCP 22H2 when using Visual Studio 17.8 or greater. + + +1. If using Visual Studio 2022 17.8 or greater with WHCP_21H2 or WHCP_22H2 and CodeQL CLI version 2.15.4: + + Follow regular steps, above. **Make sure to remove the CodeQL submodule if you still have an old version of the repo cloned.** CodeQL might try to use the queries in the submodule by default which will cause errors because of mismatched versions. + +1. If using Visual Studio version 17.7 or below **AND** either WHCP_21H2 or WHCP_22H2 **AND** CodeQL VLI version 2.4.6 or 2.6.3: + + Follow special instructions for WHCP_21H2 and WHCP_22H2 using VS17.7 at the end of this readme + +### Special instructions for WHCP_21H2 and WHCP_22H2 using VS17.7 or below + + These instructions only apply when using both Visual Studio 17.7 or below along with CodeQL 2.6.3 or 2.4.6 diff --git a/config/codeql-config.yml b/config/codeql-config.yml new file mode 100644 index 00000000..2ec7940a --- /dev/null +++ b/config/codeql-config.yml @@ -0,0 +1,9 @@ +name: "CodeQL config" +disable-default-queries: true + +packs: + - microsoft/cpp-queries@0.0.2:codeql-suites/cpp-code-scanning.qls + - microsoft/windows-drivers@1.5.0-beta+5:windows-driver-suites/recommended.qls + - microsoft/windows-drivers@1.5.0-beta+5:drivers\general\queries\experimental\DriverIsolationZwViolation1\DriverIsolationZwViolation1.ql + - microsoft/windows-drivers@1.5.0-beta+5:drivers\general\queries\experimental\DriverIsolationZwViolation2\DriverIsolationZwViolation2.ql + - microsoft/windows-drivers@1.5.0-beta+5:drivers\general\queries\experimental\DriverIsolationRtlViolation\DriverIsolationRtlViolation.ql diff --git a/src/codeql-pack.lock.yml b/src/codeql-pack.lock.yml index a68640f1..be288ce1 100644 --- a/src/codeql-pack.lock.yml +++ b/src/codeql-pack.lock.yml @@ -2,17 +2,27 @@ lockVersion: 1.0.0 dependencies: codeql/cpp-all: - version: 0.12.1 + version: 4.2.0 codeql/dataflow: - version: 0.1.4 + version: 2.0.5 + codeql/mad: + version: 1.0.21 codeql/rangeanalysis: - version: 0.0.3 + version: 1.0.21 codeql/ssa: - version: 0.2.4 + version: 1.1.0 + codeql/suite-helpers: + version: 1.0.21 codeql/tutorial: - version: 0.2.4 + version: 1.0.21 + codeql/typeflow: + version: 1.0.21 codeql/typetracking: - version: 0.2.4 + version: 2.0.5 codeql/util: - version: 0.2.4 + version: 2.0.8 + codeql/xml: + version: 1.0.21 + microsoft/cpp-queries: + version: 0.0.4 compiled: false diff --git a/src/drivers/apps/queries/experimental/UnsafeCallInGlobalInit/UnsafeCallInGlobalInit.qhelp b/src/drivers/apps/queries/experimental/UnsafeCallInGlobalInit/UnsafeCallInGlobalInit.qhelp new file mode 100644 index 00000000..4c92a0b1 --- /dev/null +++ b/src/drivers/apps/queries/experimental/UnsafeCallInGlobalInit/UnsafeCallInGlobalInit.qhelp @@ -0,0 +1,66 @@ + + + +

+ When using a DLL, it is frequently the case that any static construtors are called from DllMain. There are a number of constraints that apply to calling other functions from DllMain. In particular, it is possible to create memory leaks if the DLL is loaded and unloaded dynamically. SysAllocString is an example of a function that, in this case, could cause a memory leak. +

+
+ +

+ The ideal DllMain would be just an empty stub. However, given the complexity of many applications, this is generally too restrictive. A good rule of thumb for DllMain is to postpone as much initialization as possible. Lazy initialization increases robustness of the application because this initialization is not performed while the loader lock is held. Also, lazy initialization enables you to safely use much more of the Windows API. +

+
+ +

+ DLLMain function +

+ + +
+ +

+ +

+
+ +
  • + + C28637 + +
  • +
    +
    diff --git a/src/drivers/apps/queries/experimental/UnsafeCallInGlobalInit/UnsafeCallInGlobalInit.ql b/src/drivers/apps/queries/experimental/UnsafeCallInGlobalInit/UnsafeCallInGlobalInit.ql new file mode 100644 index 00000000..44fa4bef --- /dev/null +++ b/src/drivers/apps/queries/experimental/UnsafeCallInGlobalInit/UnsafeCallInGlobalInit.ql @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/** + * @id cpp/drivers/unsafe-call-in-global-init + * @kind problem + * @name UnsafeCallInGlobalInit + * @description When using a DLL, it is frequently the case that any + * static construtors are called from DllMain. + * There are a number of constraints that apply to calling + * other functions from DllMain. In particular, it is + * possible to create memory leaks if the DLL is loaded + * and unloaded dynamically. + * @platform Desktop + * @feature.area Multiple + * @impact Insecure Coding Practice + * @repro.text + * @owner.email: sdat@microsoft.com + * @opaqueid CQLD-C28637 + * @problem.severity warning + * @precision medium + * @tags correctness + * ca_ported + * @scope domainspecific + * @query-version v1 + */ + + import cpp + import drivers.libraries.DriverIsolation + + from Function f, string msg + where + f.getName().matches("DllMain") and + exists(FunctionCall fc | + fc.getEnclosingFunction() = f + and + ( + fc.getTarget() + .getName() + .matches([ + "LoadLibrary%", "LoadLibraryEx", "GetStringTypeA", "GetStringTypeEx", "GetStringTypeW", + "CoInitializeEx", "CreateProcess%", "ExitThread%", "CreateThread%", "ShGetFolderPathW" + ]) or + fc instanceof RegistryIsolationFunctionCall + ) + and + msg = "Unsafe call in DllMain: " + fc.getTarget().getName() + ". " + ) + or + exists(Initializer i | + f.getName().matches("DllMain") and + i.getExpr().getEnclosingFunction() = f and + not i.getDeclaration().isStatic() and + i.getExpr().toString().toLowerCase().matches("null") and + msg = "Potential unsafe initialization in DllMain. " + ) + select f, msg + " Review Dynamic-Link Library Best Practices." diff --git a/src/drivers/apps/queries/experimental/UnsafeCallInGlobalInit/UnsafeCallInGlobalInit.sarif b/src/drivers/apps/queries/experimental/UnsafeCallInGlobalInit/UnsafeCallInGlobalInit.sarif new file mode 100644 index 00000000..948ba8d7 --- /dev/null +++ b/src/drivers/apps/queries/experimental/UnsafeCallInGlobalInit/UnsafeCallInGlobalInit.sarif @@ -0,0 +1,253 @@ +{ + "$schema": "https://json.schemastore.org/sarif-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "CodeQL", + "organization": "GitHub", + "semanticVersion": "2.20.4", + "notifications": [ + { + "id": "cpp/baseline/expected-extracted-files", + "name": "cpp/baseline/expected-extracted-files", + "shortDescription": { + "text": "Expected extracted files" + }, + "fullDescription": { + "text": "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration": { + "enabled": true + }, + "properties": { + "tags": [ + "expected-extracted-files", + "telemetry" + ] + } + }, + { + "id": "cpp/extractor/summary", + "name": "cpp/extractor/summary", + "shortDescription": { + "text": "C++ extractor telemetry" + }, + "fullDescription": { + "text": "C++ extractor telemetry" + }, + "defaultConfiguration": { + "enabled": true + } + } + ], + "rules": [ + { + "id": "cpp/drivers/unsafe-call-in-global-init", + "name": "cpp/drivers/unsafe-call-in-global-init", + "shortDescription": { + "text": "UnsafeCallInGlobalInit" + }, + "fullDescription": { + "text": "When using a DLL, it is frequently the case that any static construtors are called from DllMain. There are a number of constraints that apply to calling other functions from DllMain. In particular, it is possible to create memory leaks if the DLL is loaded and unloaded dynamically." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning" + }, + "properties": { + "tags": [ + "correctness" + ], + "description": "When using a DLL, it is frequently the case that any\n static construtors are called from DllMain.\n There are a number of constraints that apply to calling\n other functions from DllMain. In particular, it is\n possible to create memory leaks if the DLL is loaded\n and unloaded dynamically.", + "feature.area": "Multiple", + "id": "cpp/drivers/unsafe-call-in-global-init", + "impact": "Insecure Coding Practice", + "kind": "problem", + "name": "UnsafeCallInGlobalInit", + "opaqueid": "CQLD-C28637", + "owner.email:": "sdat@microsoft.com", + "platform": "Desktop", + "precision": "medium", + "problem.severity": "warning", + "query-version": "v1", + "repro.text": "", + "scope": "domainspecific" + } + } + ] + }, + "extensions": [ + { + "name": "microsoft/windows-drivers", + "semanticVersion": "1.3.0+b07e02f3113bb2484479302f733f94b124503172", + "locations": [ + { + "uri": "file:///C:/codeql-home/WDDST/src/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///C:/codeql-home/WDDST/src/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + }, + { + "name": "codeql/cpp-all", + "semanticVersion": "3.1.0+d42788844f7ec0a6b9832140313cc2318e513987", + "locations": [ + { + "uri": "file:///C:/Users/jronstadt/.codeql/packages/codeql/cpp-all/3.1.0/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///C:/Users/jronstadt/.codeql/packages/codeql/cpp-all/3.1.0/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + } + ] + }, + "invocations": [ + { + "toolExecutionNotifications": [ + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "Source.cpp", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "message": { + "text": "Internal telemetry for the C++ extractor.\n\nNo action needed.", + "markdown": "Internal telemetry for the C++ extractor.\n\nNo action needed." + }, + "level": "note", + "timeUtc": "2025-02-11T03:13:43.543961700Z", + "descriptor": { + "id": "cpp/extractor/summary", + "index": 1 + }, + "properties": { + "attributes": { + "cache-hits": 0, + "cache-misses": 1, + "compilers": [ + { + "program": "cl", + "version": "Microsoft (R) C/C++ Optimizing Compiler Version 19.42.34436 for x64" + } + ], + "extractor-failures": 0, + "extractor-successes": 1, + "trap-caching": "disabled" + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + } + ], + "executionSuccessful": true + } + ], + "artifacts": [ + { + "location": { + "uri": "Source.cpp", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + }, + { + "location": { + "uri": "driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + } + ], + "results": [], + "columnKind": "utf16CodeUnits", + "properties": { + "semmle.formatSpecifier": "sarifv2.1.0" + } + } + ] +} \ No newline at end of file diff --git a/src/drivers/apps/queries/experimental/UnsafeCallInGlobalInit/driver_snippet.c b/src/drivers/apps/queries/experimental/UnsafeCallInGlobalInit/driver_snippet.c new file mode 100644 index 00000000..6b63c806 --- /dev/null +++ b/src/drivers/apps/queries/experimental/UnsafeCallInGlobalInit/driver_snippet.c @@ -0,0 +1,48 @@ + +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +// Macros to enable or disable a code section that may or may not conflict with this test. +#define SET_DISPATCH 1 + +// Template function. Not used for this test. +void top_level_call() +{ +} +#include +extern "C" BOOL WINAPI DllMain ( + HINSTANCE hinstDLL, // handle to DLL module + DWORD fdwReason, // reason for calling function + LPVOID lpvReserved) // reserved +{ + UNREFERENCED_PARAMETER(hinstDLL); + + // Perform actions based on the reason for calling. + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: + LoadLibrary(L"kernel32.dll"); // Unsafe: LoadLibrary in global init + // Initialize once for each new process. + // Return FALSE to fail DLL load. + break; + + case DLL_THREAD_ATTACH: + // Do thread-specific initialization. + break; + + case DLL_THREAD_DETACH: + // Do thread-specific cleanup. + break; + + case DLL_PROCESS_DETACH: + + if (lpvReserved != nullptr) + { + break; // do not do cleanup if process termination scenario + } + + // Perform any necessary cleanup. + break; + } + return TRUE; // Successful DLL_PROCESS_ATTACH. +} diff --git a/src/drivers/general/queries/AnnotationSyntax/AnnotationSyntax.qhelp b/src/drivers/general/queries/AnnotationSyntax/AnnotationSyntax.qhelp new file mode 100644 index 00000000..b4976dba --- /dev/null +++ b/src/drivers/general/queries/AnnotationSyntax/AnnotationSyntax.qhelp @@ -0,0 +1,58 @@ + + + +

    + A syntax error in the annotations was found for the property in the function. +

    +
    + +

    + This warning indicates an error in the annotations, not in the code that is being analyzed. +

    +
    + +

    + _IRQL_saves_global_ not applied to entire function +

    + + +

    + _Kernel_clear_do_init_ not used with either "yes" or "no" +

    + + +
    + +

    +

    +
    + +
  • + + C28266 + +
  • +
    +
    diff --git a/src/drivers/general/queries/AnnotationSyntax/AnnotationSyntax.ql b/src/drivers/general/queries/AnnotationSyntax/AnnotationSyntax.ql new file mode 100644 index 00000000..0c6c4e8a --- /dev/null +++ b/src/drivers/general/queries/AnnotationSyntax/AnnotationSyntax.ql @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/** + * @id cpp/drivers/annotation-syntax + * @kind problem + * @name Annotation syntax error + * @description A syntax error in the annotations was found for the property in the function. + * @platform Desktop + * @feature.area Multiple + * @impact Annotations + * @repro.text + * @owner.email: sdat@microsoft.com + * @opaqueid CQLD-C28266 + * @problem.severity warning + * @precision medium + * @tags correctness + * ca_ported + * @scope domainspecific + * @query-version v1 + */ + +import cpp +import drivers.libraries.SAL + +from SALAnnotation sa +where + // restoreIRQLGlobal was not on the whole function + // saveIRQLGlobal was not on the whole function + ( + sa.toString().matches("%restoresIRQLGlobal%") or //restoreIRQLGlobal //__drv_restoresIRQLGlobal //_IRQL_restores_global_ + sa.toString().matches("%_IRQL_saves_global_%") or //restoreIRQLGlobal //__drv_restoresIRQLGlobal //_IRQL_restores_global_ + sa.toString().matches("%savesIRQLGlobal%") or //saveIRQLGlobal //__drv_savesIRQLGlobal //_IRQL_saves_global_ + sa.toString().matches("%_IRQL_restores_global_%") + ) and + exists(SALParameter sp | sp.getAnnotation() = sa) + or + ( + sa.toString().matches("%_When_%") or + sa.toString().matches("%drv_when%") + ) and + ( + //_Kernel_clear_do_init_ was not \"yes\" or \"no\"") + exists(int i | + sa.getUnexpandedArgument(i).toString().matches("%_Kernel_clear_do_init_%") and + not sa.getUnexpandedArgument(i).toString().matches("_Kernel_clear_do_init_(%yes%)") and + not sa.getUnexpandedArgument(i).toString().matches("_Kernel_clear_do_init_(%no%)") + ) + or + //__drv_dispatchType cannot be used with __drv_when + exists(int i | sa.getUnexpandedArgument(i).toString().matches("%__drv_dispatchType%")) + ) + or + sa.toString().matches("%_Kernel_clear_do_init_%") and + not sa.getUnexpandedArgument(0).toString().toLowerCase().matches("__yes") and + not sa.getUnexpandedArgument(0).toString().toLowerCase().matches("__no") + or + //__drv_dispatch value out of range val > 63 || val < -1 + sa.toString().matches("%drv_dispatch%") and + ( + sa.getUnexpandedArgument(0).toInt() > 63 or + sa.getUnexpandedArgument(0).toInt() < -1 + ) +select sa, "Possible annotation syntax error" diff --git a/src/drivers/general/queries/AnnotationSyntax/AnnotationSyntax.sarif b/src/drivers/general/queries/AnnotationSyntax/AnnotationSyntax.sarif new file mode 100644 index 00000000..9f831f6b --- /dev/null +++ b/src/drivers/general/queries/AnnotationSyntax/AnnotationSyntax.sarif @@ -0,0 +1,440 @@ +{ + "$schema": "https://json.schemastore.org/sarif-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "CodeQL", + "organization": "GitHub", + "semanticVersion": "2.20.3", + "notifications": [ + { + "id": "cpp/baseline/expected-extracted-files", + "name": "cpp/baseline/expected-extracted-files", + "shortDescription": { + "text": "Expected extracted files" + }, + "fullDescription": { + "text": "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration": { + "enabled": true + }, + "properties": { + "tags": [ + "expected-extracted-files", + "telemetry" + ] + } + }, + { + "id": "cpp/extractor/summary", + "name": "cpp/extractor/summary", + "shortDescription": { + "text": "C++ extractor telemetry" + }, + "fullDescription": { + "text": "C++ extractor telemetry" + }, + "defaultConfiguration": { + "enabled": true + } + } + ], + "rules": [ + { + "id": "cpp/drivers/annotation-syntax", + "name": "cpp/drivers/annotation-syntax", + "shortDescription": { + "text": "Annotation syntax error" + }, + "fullDescription": { + "text": "A syntax error in the annotations was found for the property in the function." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning" + }, + "properties": { + "tags": [ + "correctness" + ], + "description": "A syntax error in the annotations was found for the property in the function.", + "feature.area": "Multiple", + "id": "cpp/drivers/annotation-syntax", + "impact": "Annotations", + "kind": "problem", + "name": "Annotation syntax error", + "opaqueid": "CQLD-C28266", + "owner.email:": "sdat@microsoft.com", + "platform": "Desktop", + "precision": "medium", + "problem.severity": "warning", + "query-version": "v1", + "repro.text": "", + "scope": "domainspecific" + } + } + ] + }, + "extensions": [ + { + "name": "microsoft/windows-drivers", + "semanticVersion": "1.3.0+ab606f7bc01041ace595c15e9859c2c65dd81af9", + "locations": [ + { + "uri": "file:///C:/codeql-home/WDDST/src/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///C:/codeql-home/WDDST/src/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + }, + { + "name": "codeql/cpp-all", + "semanticVersion": "3.1.0+d42788844f7ec0a6b9832140313cc2318e513987", + "locations": [ + { + "uri": "file:///C:/Users/jronstadt/.codeql/packages/codeql/cpp-all/3.1.0/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///C:/Users/jronstadt/.codeql/packages/codeql/cpp-all/3.1.0/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + } + ] + }, + "invocations": [ + { + "toolExecutionNotifications": [ + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "message": { + "text": "Internal telemetry for the C++ extractor.\n\nNo action needed.", + "markdown": "Internal telemetry for the C++ extractor.\n\nNo action needed." + }, + "level": "note", + "timeUtc": "2025-01-31T04:18:40.981726200Z", + "descriptor": { + "id": "cpp/extractor/summary", + "index": 1 + }, + "properties": { + "attributes": { + "cache-hits": 0, + "cache-misses": 1, + "compilers": [ + { + "program": "cl", + "version": "Microsoft (R) C/C++ Optimizing Compiler Version 19.42.34436 for x64" + } + ], + "extractor-failures": 1, + "extractor-successes": 0, + "trap-caching": "disabled" + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + } + ], + "executionSuccessful": true + } + ], + "artifacts": [ + { + "location": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + }, + { + "location": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + }, + { + "location": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + ], + "results": [ + { + "ruleId": "cpp/drivers/annotation-syntax", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/annotation-syntax", + "index": 0 + }, + "message": { + "text": "Possible annotation syntax error" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 14, + "startColumn": 5, + "endColumn": 40 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "a8ca85c09f4ea8d5:1", + "primaryLocationStartColumnFingerprint": "0" + } + }, + { + "ruleId": "cpp/drivers/annotation-syntax", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/annotation-syntax", + "index": 0 + }, + "message": { + "text": "Possible annotation syntax error" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 71, + "endColumn": 23 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "1170a14db2f7b3fb:1", + "primaryLocationStartColumnFingerprint": "0" + } + }, + { + "ruleId": "cpp/drivers/annotation-syntax", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/annotation-syntax", + "index": 0 + }, + "message": { + "text": "Possible annotation syntax error" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 60, + "startColumn": 5, + "endColumn": 42 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "387006e1da902de8:1", + "primaryLocationStartColumnFingerprint": "0" + } + }, + { + "ruleId": "cpp/drivers/annotation-syntax", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/annotation-syntax", + "index": 0 + }, + "message": { + "text": "Possible annotation syntax error" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 32, + "startColumn": 5, + "endColumn": 63 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "86056ed81ec34e35:1", + "primaryLocationStartColumnFingerprint": "0" + } + }, + { + "ruleId": "cpp/drivers/annotation-syntax", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/annotation-syntax", + "index": 0 + }, + "message": { + "text": "Possible annotation syntax error" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 21, + "endColumn": 55 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "d87072f5fe36dbe4:1", + "primaryLocationStartColumnFingerprint": "0" + } + } + ], + "columnKind": "utf16CodeUnits", + "properties": { + "semmle.formatSpecifier": "sarifv2.1.0" + } + } + ] +} \ No newline at end of file diff --git a/src/drivers/general/queries/AnnotationSyntax/driver_snippet.c b/src/drivers/general/queries/AnnotationSyntax/driver_snippet.c new file mode 100644 index 00000000..d2ae2dc1 --- /dev/null +++ b/src/drivers/general/queries/AnnotationSyntax/driver_snippet.c @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +// Macros to enable or disable a code section that may or may not conflict with this test. +#define SET_DISPATCH 1 + +// Template function. Not used for this test. +void top_level_call() +{ +} + +// FAIL _IRQL_saves_global_ not applied to entire function +VOID test1( + _IRQL_saves_global_(OldIrql, *Irql) PKIRQL Irql) +{ + // ... + ; +} + +// FAIL using when with dispatch type +_When_(return >= 0, __drv_dispatchType(IRP_MJ_CREATE)) + VOID test2( + IN PDRIVER_OBJECT DriverObject) +{ + ; // do nothing +} + +// FAIL +_Function_class_(DRIVER_ADD_DEVICE) + _IRQL_requires_(PASSIVE_LEVEL) + _IRQL_requires_same_ + _When_(return >= 0, _Kernel_clear_do_init_(IRP_MJ_CREATE)) + NTSTATUS + test3( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PDEVICE_OBJECT PhysicalDeviceObject) + +{ + ; // do nothing +} + +// OK +_Function_class_(DRIVER_ADD_DEVICE) + _IRQL_requires_(PASSIVE_LEVEL) + _IRQL_requires_same_ + _When_(return >= 0, _Kernel_clear_do_init_(__yes)) + NTSTATUS + test3Ok( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PDEVICE_OBJECT PhysicalDeviceObject) + +{ + ; // do nothing +} + +// FAIL +_Function_class_(DRIVER_ADD_DEVICE) + _IRQL_requires_(PASSIVE_LEVEL) + _IRQL_requires_same_ + _Kernel_clear_do_init_(IRP_MJ_CREATE) +NTSTATUS +test4( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PDEVICE_OBJECT PhysicalDeviceObject) + +{ + ; // do nothing +} + +// out of range +__drv_dispatchType(65) + VOID test5( + IN PDRIVER_OBJECT DriverObject) +{ + ; // do nothing +} \ No newline at end of file diff --git a/src/drivers/general/queries/CurrentFunctionTypeNotCorrect/CurrentFunctionTypeNotCorrect.qhelp b/src/drivers/general/queries/CurrentFunctionTypeNotCorrect/CurrentFunctionTypeNotCorrect.qhelp new file mode 100644 index 00000000..a9cce681 --- /dev/null +++ b/src/drivers/general/queries/CurrentFunctionTypeNotCorrect/CurrentFunctionTypeNotCorrect.qhelp @@ -0,0 +1,21 @@ + + + +

    + This function appears to be an unannotated DriverEntry function +

    +
    + +

    + DriverEntry functions should be declared using the DRIVER_INITIALIZE function typedef. +

    +
    + + +
  • + + C28101 + +
  • +
    +
    diff --git a/src/drivers/general/queries/CurrentFunctionTypeNotCorrect/CurrentFunctionTypeNotCorrect.ql b/src/drivers/general/queries/CurrentFunctionTypeNotCorrect/CurrentFunctionTypeNotCorrect.ql new file mode 100644 index 00000000..d81f3988 --- /dev/null +++ b/src/drivers/general/queries/CurrentFunctionTypeNotCorrect/CurrentFunctionTypeNotCorrect.ql @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/** + * @id cpp/drivers/current-function-type-not-correct + * @kind problem + * @name Current function type not correct (C28101) + * @description DriverEntry functions should be declared using the DRIVER_INITIALIZE function typedef. + * @platform Desktop + * @security.severity Low + * @feature.area Multiple + * @impact Attack Surface Reduction + * @repro.text + * @owner.email sdat@microsoft.com + * @opaqueid CQLD-C28101 + * @problem.severity warning + * @precision high + * @tags correctness + * wddst + * ca_ported + * @scope domainspecific + * @query-version v1 + */ + +import cpp +import drivers.libraries.RoleTypes + +from Function f +where + f.getName().matches("DriverEntry%") and + not f.(RoleTypeFunction).getRoleTypeString().matches("DRIVER_INITIALIZE") +select f, "DriverEntry functions should be declared using the DRIVER_INITIALIZE function typedef." diff --git a/src/drivers/general/queries/CurrentFunctionTypeNotCorrect/CurrentFunctionTypeNotCorrect.sarif b/src/drivers/general/queries/CurrentFunctionTypeNotCorrect/CurrentFunctionTypeNotCorrect.sarif new file mode 100644 index 00000000..44d71fd8 --- /dev/null +++ b/src/drivers/general/queries/CurrentFunctionTypeNotCorrect/CurrentFunctionTypeNotCorrect.sarif @@ -0,0 +1,200 @@ +{ + "$schema" : "https://json.schemastore.org/sarif-2.1.0.json", + "version" : "2.1.0", + "runs" : [ { + "tool" : { + "driver" : { + "name" : "CodeQL", + "organization" : "GitHub", + "semanticVersion" : "2.15.4", + "notifications" : [ { + "id" : "cpp/baseline/expected-extracted-files", + "name" : "cpp/baseline/expected-extracted-files", + "shortDescription" : { + "text" : "Expected extracted files" + }, + "fullDescription" : { + "text" : "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration" : { + "enabled" : true + }, + "properties" : { + "tags" : [ "expected-extracted-files", "telemetry" ] + } + } ], + "rules" : [ { + "id" : "cpp/drivers/current-function-type-not-correct", + "name" : "cpp/drivers/current-function-type-not-correct", + "shortDescription" : { + "text" : "Current function type not correct (C28101)" + }, + "fullDescription" : { + "text" : "DriverEntry functions should be declared using the DRIVER_INITIALIZE function typedef." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "properties" : { + "tags" : [ "correctness", "wddst" ], + "description" : "DriverEntry functions should be declared using the DRIVER_INITIALIZE function typedef.", + "feature.area" : "Multiple", + "id" : "cpp/drivers/current-function-type-not-correct", + "impact" : "Attack Surface Reduction", + "kind" : "problem", + "name" : "Current function type not correct (C28101)", + "opaqueid" : "CQLD-C28101", + "owner.email" : "sdat@microsoft.com", + "platform" : "Desktop", + "precision" : "high", + "problem.severity" : "warning", + "query-version" : "v1", + "repro.text" : "", + "scope" : "domainspecific", + "security.severity" : "Low" + } + } ] + }, + "extensions" : [ { + "name" : "microsoft/windows-drivers", + "semanticVersion" : "1.0.13+4cf80ade609037becb8999823de45e08bd818a20", + "locations" : [ { + "uri" : "file:///C:/codeql-home/WDDST/src/", + "description" : { + "text" : "The QL pack root directory." + } + }, { + "uri" : "file:///C:/codeql-home/WDDST/src/qlpack.yml", + "description" : { + "text" : "The QL pack definition file." + } + } ] + } ] + }, + "invocations" : [ { + "toolExecutionNotifications" : [ { + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + } + } + } ], + "message" : { + "text" : "" + }, + "level" : "none", + "descriptor" : { + "id" : "cpp/baseline/expected-extracted-files", + "index" : 0 + }, + "properties" : { + "formattedMessage" : { + "text" : "" + } + } + }, { + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/fail_driver1.c", + "uriBaseId" : "%SRCROOT%", + "index" : 1 + } + } + } ], + "message" : { + "text" : "" + }, + "level" : "none", + "descriptor" : { + "id" : "cpp/baseline/expected-extracted-files", + "index" : 0 + }, + "properties" : { + "formattedMessage" : { + "text" : "" + } + } + }, { + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/fail_driver1.h", + "uriBaseId" : "%SRCROOT%", + "index" : 2 + } + } + } ], + "message" : { + "text" : "" + }, + "level" : "none", + "descriptor" : { + "id" : "cpp/baseline/expected-extracted-files", + "index" : 0 + }, + "properties" : { + "formattedMessage" : { + "text" : "" + } + } + } ], + "executionSuccessful" : true + } ], + "artifacts" : [ { + "location" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + } + }, { + "location" : { + "uri" : "driver/fail_driver1.c", + "uriBaseId" : "%SRCROOT%", + "index" : 1 + } + }, { + "location" : { + "uri" : "driver/fail_driver1.h", + "uriBaseId" : "%SRCROOT%", + "index" : 2 + } + } ], + "results" : [ { + "ruleId" : "cpp/drivers/current-function-type-not-correct", + "ruleIndex" : 0, + "rule" : { + "id" : "cpp/drivers/current-function-type-not-correct", + "index" : 0 + }, + "message" : { + "text" : "DriverEntry functions should be declared using the DRIVER_INITIALIZE function typedef." + }, + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + }, + "region" : { + "startLine" : 17, + "endColumn" : 15 + } + } + } ], + "partialFingerprints" : { + "primaryLocationLineHash" : "655dd62a2d68045f:1", + "primaryLocationStartColumnFingerprint" : "0" + } + } ], + "columnKind" : "utf16CodeUnits", + "properties" : { + "semmle.formatSpecifier" : "sarifv2.1.0" + } + } ] +} \ No newline at end of file diff --git a/src/drivers/general/queries/CurrentFunctionTypeNotCorrect/driver_snippet.c b/src/drivers/general/queries/CurrentFunctionTypeNotCorrect/driver_snippet.c new file mode 100644 index 00000000..65037598 --- /dev/null +++ b/src/drivers/general/queries/CurrentFunctionTypeNotCorrect/driver_snippet.c @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +// +// driver_snippet.c +// + +#define SET_DISPATCH 1 +const extern ULONG myTag; +char * myString = "Hello!"; +const ULONG myTag2 = '2gat'; +ULONG * myTag3; + +// Template. Not called in this test. +void top_level_call() {} + +NTSTATUS +DriverEntryBad( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) +{ + return STATUS_SUCCESS; +} + +DRIVER_INITIALIZE DriverEntryGood; +NTSTATUS +DriverEntryGood( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) +{ + return STATUS_SUCCESS; +} + diff --git a/src/drivers/general/queries/DefaultPoolTag/DefaultPoolTag.ql b/src/drivers/general/queries/DefaultPoolTag/DefaultPoolTag.ql index 503418b2..6907803d 100644 --- a/src/drivers/general/queries/DefaultPoolTag/DefaultPoolTag.ql +++ b/src/drivers/general/queries/DefaultPoolTag/DefaultPoolTag.ql @@ -14,6 +14,7 @@ * @problem.severity warning * @precision high * @tags correctness + * ca_ported * @scope domainspecific * @query-version v1 */ diff --git a/src/drivers/general/queries/DriverEntrySaveBuffer/DriverEntrySaveBuffer.qhelp b/src/drivers/general/queries/DriverEntrySaveBuffer/DriverEntrySaveBuffer.qhelp new file mode 100644 index 00000000..940d3fbc --- /dev/null +++ b/src/drivers/general/queries/DriverEntrySaveBuffer/DriverEntrySaveBuffer.qhelp @@ -0,0 +1,26 @@ + + + +

    + The DriverEntry routine should save a copy of the argument, not the pointer, because the I/O Manager frees the buffer +

    +
    + +

    + The driver's DriverEntry routine is saving a copy of the pointer to the buffer instead of saving a copy of the buffer. Because the buffer is freed when the DriverEntry routine returns, the pointer to the buffer will soon be invalid. +

    +
    + + + + +
  • + + C28131 + +
  • +
    + +

    This rule reports a false positive when the registry path pointer is saved for use in functions such as HidRegisterMinidriver

    +
    +
    diff --git a/src/drivers/general/queries/DriverEntrySaveBuffer/DriverEntrySaveBuffer.ql b/src/drivers/general/queries/DriverEntrySaveBuffer/DriverEntrySaveBuffer.ql new file mode 100644 index 00000000..3a838330 --- /dev/null +++ b/src/drivers/general/queries/DriverEntrySaveBuffer/DriverEntrySaveBuffer.ql @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/** + * @id cpp/drivers/driver-entry-save-buffer + * @name Driver Entry Save Buffer + * @description C28131: The DriverEntry routine should save a copy of the argument, not the pointer, because the I/O Manager frees the buffer + * @platform Desktop + * @security.severity Medium + * @feature.area Multiple + * @impact Exploitable Design + * @repro.text + * @owner.email sdat@microsoft.com + * @opaqueid CQLD-C28131 + * @kind problem + * @problem.severity warning + * @precision medium + * @tags correctness + * wddst +* ca_ported + * @scope domainspecific + * @query-version v1 + */ + +import cpp + +from VariableAccess va +where + va.getParent() instanceof AssignExpr and + exists(Parameter p | p.getAnAccess() = va and p.getFunction().getName().matches("DriverEntry%")) and + ( + exists(GlobalVariable gv | + gv = va.getParent().(AssignExpr).getLValue().(VariableAccess).getTarget() + ) + or + exists(FieldAccess fa | + fa.getTarget() = va.getParent().(AssignExpr).getLValue().(VariableAccess).getTarget() and + fa.getQualifier().(VariableAccess).getTarget() instanceof LocalVariable + ) + ) + and va.getTarget().getType().toString().matches("PUNICODE_STRING") +select va, + "The DriverEntry routine should save a copy of the argument $@, not the pointer, because the I/O Manager frees the buffer", + va, va.toString() diff --git a/src/drivers/general/queries/DriverEntrySaveBuffer/DriverEntrySaveBuffer.sarif b/src/drivers/general/queries/DriverEntrySaveBuffer/DriverEntrySaveBuffer.sarif new file mode 100644 index 00000000..0a9d1503 --- /dev/null +++ b/src/drivers/general/queries/DriverEntrySaveBuffer/DriverEntrySaveBuffer.sarif @@ -0,0 +1,347 @@ +{ + "$schema": "https://json.schemastore.org/sarif-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "CodeQL", + "organization": "GitHub", + "semanticVersion": "2.17.6", + "notifications": [ + { + "id": "cpp/baseline/expected-extracted-files", + "name": "cpp/baseline/expected-extracted-files", + "shortDescription": { + "text": "Expected extracted files" + }, + "fullDescription": { + "text": "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration": { + "enabled": true + }, + "properties": { + "tags": [ + "expected-extracted-files", + "telemetry" + ] + } + }, + { + "id": "cpp/extractor/summary", + "name": "cpp/extractor/summary", + "shortDescription": { + "text": "C++ extractor telemetry" + }, + "fullDescription": { + "text": "C++ extractor telemetry" + }, + "defaultConfiguration": { + "enabled": true + } + } + ], + "rules": [ + { + "id": "cpp/drivers/driver-entry-save-buffer", + "name": "cpp/drivers/driver-entry-save-buffer", + "shortDescription": { + "text": "Driver Entry Save Buffer" + }, + "fullDescription": { + "text": "C28131: The DriverEntry routine should save a copy of the argument, not the pointer, because the I/O Manager frees the buffer" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning" + }, + "properties": { + "tags": [ + "correctness", + "wddst" + ], + "description": "C28131: The DriverEntry routine should save a copy of the argument, not the pointer, because the I/O Manager frees the buffer", + "feature.area": "Multiple", + "id": "cpp/drivers/driver-entry-save-buffer", + "impact": "Exploitable Design", + "kind": "problem", + "name": "Driver Entry Save Buffer", + "opaqueid": "CQLD-C28131", + "owner.email": "sdat@microsoft.com", + "platform": "Desktop", + "precision": "medium", + "problem.severity": "warning", + "query-version": "v1", + "repro.text": "", + "scope": "domainspecific", + "security.severity": "Medium" + } + } + ] + }, + "extensions": [ + { + "name": "microsoft/windows-drivers", + "semanticVersion": "1.1.0+51a61366e45774d4f272190ede40570b925b7a47", + "locations": [ + { + "uri": "file:///C:/codeql-home/WDDST/src/", + "description": { + "text": "The QL pack root directory." + } + }, + { + "uri": "file:///C:/codeql-home/WDDST/src/qlpack.yml", + "description": { + "text": "The QL pack definition file." + } + } + ] + } + ] + }, + "invocations": [ + { + "toolExecutionNotifications": [ + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "message": { + "text": "Internal telemetry for the C++ extractor.\n\nNo action needed.", + "markdown": "Internal telemetry for the C++ extractor.\n\nNo action needed." + }, + "level": "note", + "timeUtc": "2024-08-24T05:00:01.909+00:00", + "descriptor": { + "id": "cpp/extractor/summary", + "index": 1 + }, + "properties": { + "attributes": { + "cache-hits": 0, + "cache-misses": 1, + "extractor-failures": 1, + "extractor-successes": 0, + "trap-caching": "disabled" + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + } + ], + "executionSuccessful": true + } + ], + "artifacts": [ + { + "location": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + }, + { + "location": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + }, + { + "location": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + ], + "results": [ + { + "ruleId": "cpp/drivers/driver-entry-save-buffer", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/driver-entry-save-buffer", + "index": 0 + }, + "message": { + "text": "The DriverEntry routine should save a copy of the argument [RegistryPath](1), not the pointer, because the I/O Manager frees the buffer" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 64, + "startColumn": 23, + "endColumn": 35 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "4d740d3f2799e655:1", + "primaryLocationStartColumnFingerprint": "18" + }, + "relatedLocations": [ + { + "id": 1, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 64, + "startColumn": 23, + "endColumn": 35 + } + }, + "message": { + "text": "RegistryPath" + } + } + ] + }, + { + "ruleId": "cpp/drivers/driver-entry-save-buffer", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/driver-entry-save-buffer", + "index": 0 + }, + "message": { + "text": "The DriverEntry routine should save a copy of the argument [RegistryPath](1), not the pointer, because the I/O Manager frees the buffer" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 20, + "startColumn": 13, + "endColumn": 25 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "ea30678974f0f370:1", + "primaryLocationStartColumnFingerprint": "8" + }, + "relatedLocations": [ + { + "id": 1, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 20, + "startColumn": 13, + "endColumn": 25 + } + }, + "message": { + "text": "RegistryPath" + } + } + ] + } + ], + "columnKind": "utf16CodeUnits", + "properties": { + "semmle.formatSpecifier": "sarifv2.1.0" + } + } + ] +} \ No newline at end of file diff --git a/src/drivers/general/queries/DriverEntrySaveBuffer/driver_snippet.c b/src/drivers/general/queries/DriverEntrySaveBuffer/driver_snippet.c new file mode 100644 index 00000000..fef0c8ea --- /dev/null +++ b/src/drivers/general/queries/DriverEntrySaveBuffer/driver_snippet.c @@ -0,0 +1,66 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +// +// driver_snippet.c +// +#include "ntstrsafe.h" + +#define SET_DISPATCH 1 +// Template. Not called in this test. +void top_level_call() {} + +PUNICODE_STRING g_RP1; + +NTSTATUS +DriverEntryBad( + PDRIVER_OBJECT DriverObject, + PUNICODE_STRING RegistryPath + ) +{ + g_RP1 = RegistryPath; + return 0; +} + + +UNICODE_STRING g_RP2; + +NTSTATUS +DriverEntryGood( + PDRIVER_OBJECT DriverObject, + PUNICODE_STRING RegistryPath + ) +{ + return RtlUnicodeStringCopy(&g_RP2,RegistryPath); +} + + +UNICODE_STRING g_RP3; + +NTSTATUS +DriverEntryGood2( + PDRIVER_OBJECT DriverObject, + PUNICODE_STRING RegistryPath + ) +{ + g_RP3 = *RegistryPath; + return 0; +} + +typedef struct _test_struct { + int a; + PUNICODE_STRING g_RP4; + char b; +} test_struct; + +test_struct g_test_struct; + +NTSTATUS +DriverEntryBad2( + PDRIVER_OBJECT DriverObject, + PUNICODE_STRING RegistryPath + ) +{ + test_struct* localPtr = &g_test_struct; + localPtr->g_RP4 = RegistryPath; + return 0; +} \ No newline at end of file diff --git a/src/drivers/general/queries/ExaminedValue/ExaminedValue.ql b/src/drivers/general/queries/ExaminedValue/ExaminedValue.ql index 37074eaf..211a9525 100644 --- a/src/drivers/general/queries/ExaminedValue/ExaminedValue.ql +++ b/src/drivers/general/queries/ExaminedValue/ExaminedValue.ql @@ -16,6 +16,7 @@ * @precision high * @tags correctness * wddst + * ca_ported * @scope general * @query-version v1 */ diff --git a/src/drivers/general/queries/ExtendedDeprecatedApis/ExtendedDeprecatedApis.ql b/src/drivers/general/queries/ExtendedDeprecatedApis/ExtendedDeprecatedApis.ql index 8b73d8e5..63fd8fac 100644 --- a/src/drivers/general/queries/ExtendedDeprecatedApis/ExtendedDeprecatedApis.ql +++ b/src/drivers/general/queries/ExtendedDeprecatedApis/ExtendedDeprecatedApis.ql @@ -16,8 +16,9 @@ * @precision high * @tags correctness * security +* ca_ported * @scope generic - * @query-version v1 + * @query-version v2 */ import cpp @@ -72,6 +73,9 @@ predicate matchesBannedApi(string input) { or // Functions marked deprecated in C28750 input = any(["lstrlen", "lstrlenA", "lstrlenW"]) + or + // Functions marked deprecated in C28727 + input = any(["_itow", "_ultow", "strtok", "swscanf", "wcstok"]) } /** A deprecated API. */ @@ -319,7 +323,7 @@ class ExtendedDeprecatedCall extends Element { or name.matches("swprintf") and replacement = - "swprintf_s StringCbPrintf, StringCbPrintf_l, StringCbPrintf_lEx, StringCbPrintf, StringCbPrintfEx" + "swprintf_s, StringCbPrintf, StringCbPrintf_l, StringCbPrintf_lEx, StringCbPrintf, StringCbPrintfEx" or name.matches("ualstrcpyW") and replacement = "None" or @@ -443,11 +447,25 @@ class ExtendedDeprecatedCall extends Element { or // Functions marked deprecated in C28750 ( - name.matches("lstrlen") and replacement = "_tcslen" + name.matches("lstrlen") and replacement = "_tcslen if the data is trusted, or _tcsnlen or StringCchLength if the data is untrusted" + or + name.matches("lstrlenA") and replacement = "strlen if the data is trusted, or strnlen or StringCchLengthA if the data is untrusted" + or + name.matches("lstrlenW") and replacement = "wcslen if the data is trusted, or wcsnlen or StringCchLengthW if the data is untrusted" + ) + + or + // Functions marked deprecated in C28727 + ( + name.matches("_itow") and replacement = "None" + or + name.matches("_ultow") and replacement = "None" + or + name.matches("strtok") and replacement = "None" or - name.matches("lstrlenA") and replacement = "strlen" + name.matches("swscanf") and replacement = "None" or - name.matches("lstrlenW") and replacement = "wcslen" + name.matches("wcstok") and replacement = "None" ) ) } diff --git a/src/drivers/general/queries/FloatHardwareStateProtection/FloatHardwareStateProtection.qhelp b/src/drivers/general/queries/FloatHardwareStateProtection/FloatHardwareStateProtection.qhelp new file mode 100644 index 00000000..3366e055 --- /dev/null +++ b/src/drivers/general/queries/FloatHardwareStateProtection/FloatHardwareStateProtection.qhelp @@ -0,0 +1,52 @@ + + + +

    + Drivers must protect floating-point hardware state. +

    +
    + +

    + This warning is only applicable in kernel mode. The driver is attempting to use a variable or constant of a float type when the code is not protected by KeSaveFloatingPointState and KeRestoreFloatingPointState, or EngSaveFloatingPointState and EngRestoreFloatingPointState. Display drivers should use EngSaveFloatingPointState and EngRestoreFloatingPointState. +

    +
    + +

    + Function that uses float without protecting floating-point hardware state +

    + + +

    + Function that uses float with protected floating-point hardware state +

    + + +
    + +

    +

    +
    + +
  • + + Warning C28110 + +
  • +
    +
    diff --git a/src/drivers/general/queries/FloatHardwareStateProtection/FloatHardwareStateProtection.ql b/src/drivers/general/queries/FloatHardwareStateProtection/FloatHardwareStateProtection.ql new file mode 100644 index 00000000..f8506655 --- /dev/null +++ b/src/drivers/general/queries/FloatHardwareStateProtection/FloatHardwareStateProtection.ql @@ -0,0 +1,112 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/** + * @id cpp/drivers/float-hardware-state-protection + * @kind problem + * @name Float Hardware State Protection + * @description Drivers must protect floating-point hardware state. + * @platform Desktop + * @feature.area Multiple + * @impact Insecure Coding Practice + * @repro.text This warning is only applicable in kernel mode. The driver is attempting to use a variable or constant of a float type when the code is not protected by KeSaveFloatingPointState and KeRestoreFloatingPointState, or EngSaveFloatingPointState and EngRestoreFloatingPointState. + * @owner.email: sdat@microsoft.com + * @opaqueid CQLD-C28110 + * @problem.severity warning + * @precision medium + * @tags correctness + * ca_ported + * @scope domainspecific + * @query-version v1 + */ + +import cpp +import semmle.code.cpp.dataflow.new.DataFlow +import drivers.libraries.SAL +import drivers.kmdf.libraries.KmdfDrivers +import semmle.code.cpp.Specifier + +class KernelFloatFunctionAnnotation extends SALAnnotation { + KernelFloatFunctionAnnotation() { this.getMacroName().matches(["_Kernel_float_used_"]) } +} + +class KernelFloatAnnotatedTypedef extends TypedefType { + KernelFloatFunctionAnnotation kernelFloatAnnotation; + + KernelFloatAnnotatedTypedef() { kernelFloatAnnotation.getTypedefDeclarations() = this } + + KernelFloatFunctionAnnotation getKernelFloatAnnotation() { result = kernelFloatAnnotation } +} + +class KernelFloatAnnotatedFunction extends Function { + KernelFloatFunctionAnnotation kernelFloatAnnotation; + + + KernelFloatAnnotatedFunction() { + ( + // this.hasCLinkage() and + exists( + FunctionDeclarationEntry fde // actual function declarations + | + fde = this.getADeclarationEntry() and + fde.getFile().getAnIncludedFile().getBaseName().matches("%wdf.h") and + kernelFloatAnnotation.getDeclarationEntry() = fde + ) + or + exists( + FunctionDeclarationEntry fde // typedefs + | + fde.getFunction() = this and + fde.getTypedefType().(KernelFloatAnnotatedTypedef).getKernelFloatAnnotation() = + kernelFloatAnnotation + ) + ) + } +} + +class FuncWithSafeFloatAccess extends Function { + FuncWithSafeFloatAccess() { + exists(FunctionCall funcCallThatUsesFloat, FunctionCall saveFloat, ControlFlowNode saveFloatBB, Function funcThatUsesFloat, VariableAccess floatAccess| + ( + saveFloat.getTarget().getName() = ("KeSaveFloatingPointState") or + saveFloat.getTarget().getName() = ("EngSaveFloatingPointState") + ) + and saveFloatBB = saveFloat.getBasicBlock() + and floatAccess.getTarget().getType() instanceof FloatingPointType + and funcThatUsesFloat = floatAccess.getEnclosingFunction() + and funcCallThatUsesFloat.getTarget() = funcThatUsesFloat + and funcCallThatUsesFloat.getBasicBlock().getAPredecessor*() = saveFloatBB + and this.calls*(funcThatUsesFloat) + // this function can call a function that uses float + ) + } +} + +class SafeFloatAccess extends VariableAccess { + SafeFloatAccess() { + this.getType() instanceof FloatingPointType and + exists(FunctionCall saveFloat, ControlFlowNode saveFloatBB | + ( + saveFloat.getTarget().getName() = ("KeSaveFloatingPointState") or + saveFloat.getTarget().getName() = ("EngSaveFloatingPointState") + ) and + saveFloatBB = saveFloat.getBasicBlock() and + ( + this.getBasicBlock().getAPredecessor*() = saveFloatBB or + saveFloatBB.getASuccessor*() = this.getBasicBlock() or + this.getBasicBlock() = saveFloatBB + ) + ) + or + this.getTarget().getEnclosingElement() instanceof KernelFloatAnnotatedFunction + } +} + +from VariableAccess floatAccess +where + floatAccess.getTarget().getType() instanceof FloatingPointType and + not floatAccess instanceof SafeFloatAccess and + not exists(FuncWithSafeFloatAccess safeFunc | + safeFunc = floatAccess.getEnclosingFunction()) + +select floatAccess, + "Use of float detected without protecting floating-point hardware state" diff --git a/src/drivers/general/queries/FloatHardwareStateProtection/FloatHardwareStateProtection.sarif b/src/drivers/general/queries/FloatHardwareStateProtection/FloatHardwareStateProtection.sarif new file mode 100644 index 00000000..ef48dc90 --- /dev/null +++ b/src/drivers/general/queries/FloatHardwareStateProtection/FloatHardwareStateProtection.sarif @@ -0,0 +1,367 @@ +{ + "$schema": "https://json.schemastore.org/sarif-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "CodeQL", + "organization": "GitHub", + "semanticVersion": "2.17.6", + "notifications": [ + { + "id": "cpp/baseline/expected-extracted-files", + "name": "cpp/baseline/expected-extracted-files", + "shortDescription": { + "text": "Expected extracted files" + }, + "fullDescription": { + "text": "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration": { + "enabled": true + }, + "properties": { + "tags": [ + "expected-extracted-files", + "telemetry" + ] + } + }, + { + "id": "cpp/extractor/summary", + "name": "cpp/extractor/summary", + "shortDescription": { + "text": "C++ extractor telemetry" + }, + "fullDescription": { + "text": "C++ extractor telemetry" + }, + "defaultConfiguration": { + "enabled": true + } + } + ], + "rules": [ + { + "id": "cpp/drivers/float-hardware-state-protection", + "name": "cpp/drivers/float-hardware-state-protection", + "shortDescription": { + "text": "Float Hardware State Protection" + }, + "fullDescription": { + "text": "Drivers must protect floating-point hardware state." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning" + }, + "properties": { + "tags": [ + "correctness" + ], + "description": "Drivers must protect floating-point hardware state.", + "feature.area": "Multiple", + "id": "cpp/drivers/float-hardware-state-protection", + "impact": "Insecure Coding Practice", + "kind": "problem", + "name": "Float Hardware State Protection", + "opaqueid": "CQLD-C28110", + "owner.email:": "sdat@microsoft.com", + "platform": "Desktop", + "precision": "medium", + "problem.severity": "warning", + "query-version": "v1", + "repro.text": "This warning is only applicable in kernel mode. The driver is attempting to use a variable or constant of a float type when the code is not protected by KeSaveFloatingPointState and KeRestoreFloatingPointState, or EngSaveFloatingPointState and EngRestoreFloatingPointState.", + "scope": "domainspecific" + } + } + ] + }, + "extensions": [ + { + "name": "microsoft/windows-drivers", + "semanticVersion": "1.1.0+ed45a0947090dd9c23386d5d318ddc2662469c34", + "locations": [ + { + "uri": "file:///C:/codeql-home/WDDST/src/", + "description": { + "text": "The QL pack root directory." + } + }, + { + "uri": "file:///C:/codeql-home/WDDST/src/qlpack.yml", + "description": { + "text": "The QL pack definition file." + } + } + ] + } + ] + }, + "invocations": [ + { + "toolExecutionNotifications": [ + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "message": { + "text": "Internal telemetry for the C++ extractor.\n\nNo action needed.", + "markdown": "Internal telemetry for the C++ extractor.\n\nNo action needed." + }, + "level": "note", + "timeUtc": "2024-08-24T03:52:35.107+00:00", + "descriptor": { + "id": "cpp/extractor/summary", + "index": 1 + }, + "properties": { + "attributes": { + "cache-hits": 0, + "cache-misses": 1, + "extractor-failures": 1, + "extractor-successes": 0, + "trap-caching": "disabled" + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + } + ], + "executionSuccessful": true + } + ], + "artifacts": [ + { + "location": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + }, + { + "location": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + }, + { + "location": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + ], + "results": [ + { + "ruleId": "cpp/drivers/float-hardware-state-protection", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/float-hardware-state-protection", + "index": 0 + }, + "message": { + "text": "Use of float detected without protecting floating-point hardware state" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 35, + "startColumn": 5, + "endColumn": 6 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "26ede89e4ff516da:1", + "primaryLocationStartColumnFingerprint": "0" + } + }, + { + "ruleId": "cpp/drivers/float-hardware-state-protection", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/float-hardware-state-protection", + "index": 0 + }, + "message": { + "text": "Use of float detected without protecting floating-point hardware state" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 35, + "startColumn": 9, + "endColumn": 10 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "26ede89e4ff516da:1", + "primaryLocationStartColumnFingerprint": "4" + } + }, + { + "ruleId": "cpp/drivers/float-hardware-state-protection", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/float-hardware-state-protection", + "index": 0 + }, + "message": { + "text": "Use of float detected without protecting floating-point hardware state" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 17, + "startColumn": 5, + "endColumn": 6 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "9c6e5f7e3e1f2dc2:1", + "primaryLocationStartColumnFingerprint": "0" + } + }, + { + "ruleId": "cpp/drivers/float-hardware-state-protection", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/float-hardware-state-protection", + "index": 0 + }, + "message": { + "text": "Use of float detected without protecting floating-point hardware state" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 17, + "startColumn": 9, + "endColumn": 10 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "9c6e5f7e3e1f2dc2:1", + "primaryLocationStartColumnFingerprint": "4" + } + } + ], + "columnKind": "utf16CodeUnits", + "properties": { + "semmle.formatSpecifier": "sarifv2.1.0" + } + } + ] +} \ No newline at end of file diff --git a/src/drivers/general/queries/FloatHardwareStateProtection/driver_snippet.c b/src/drivers/general/queries/FloatHardwareStateProtection/driver_snippet.c new file mode 100644 index 00000000..96f92d43 --- /dev/null +++ b/src/drivers/general/queries/FloatHardwareStateProtection/driver_snippet.c @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +// Macros to enable or disable a code section that may or may not conflict with this test. +#define SET_DISPATCH 1 + +// Template function. Not used for this test. +void top_level_call() +{ +} +int _fltused; + +_Kernel_float_used_ +void float_used_good() +{ + float f = 0.0f; + f = f + 1.0f; +} + +void float_used_good2() +{ + KFLOATING_SAVE saveData; + NTSTATUS status; + float f = 0.0f; + status = KeSaveFloatingPointState(&saveData); + for (int i = 0; i < 100; i++) + { + f = f + 1.0f; + } + KeRestoreFloatingPointState(&saveData); +} +void float_used_bad() +{ + float f = 0.0f; + f = f + 1.0f; +} + diff --git a/src/drivers/general/queries/IRPStackEntryCopy/IRPStackEntryCopy.qhelp b/src/drivers/general/queries/IRPStackEntryCopy/IRPStackEntryCopy.qhelp new file mode 100644 index 00000000..ebd10110 --- /dev/null +++ b/src/drivers/general/queries/IRPStackEntryCopy/IRPStackEntryCopy.qhelp @@ -0,0 +1,23 @@ + + + +

    + Copying a whole IRP stack entry leaves certain fields initialized that should be cleared or updated +

    +
    + +

    + The driver is copying an IRP improperly. Improperly copying an IRP can cause serious problems with a driver, including loss of data and system crashes. If an IRP must be copied and IoCopyCurrentIrpStackLocationToNext does not suffice, then certain members of the IRP should not be copied or should be zeroed after copying. +

    +
    + + + + +
  • + + C28114 + +
  • +
    +
    diff --git a/src/drivers/general/queries/IRPStackEntryCopy/IRPStackEntryCopy.ql b/src/drivers/general/queries/IRPStackEntryCopy/IRPStackEntryCopy.ql new file mode 100644 index 00000000..1ec3c72f --- /dev/null +++ b/src/drivers/general/queries/IRPStackEntryCopy/IRPStackEntryCopy.ql @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/** + * @id cpp/drivers/irp-stack-entry-copy + * @name Irp stack entry copy + * @description C28114: Copying a whole IRP stack entry leaves certain fields initialized that should be cleared or updated. + * @platform Desktop + * @security.severity Medium + * @feature.area Multiple + * @impact Exploitable Design + * @repro.text + * @owner.email sdat@microsoft.com + * @opaqueid CQLD-C28114 + * @kind problem + * @problem.severity warning + * @precision medium + * @tags correctness + * ca_ported + * wddst + * @scope domainspecific + * @query-version v1 + */ + +import cpp + +class PIO_STACK_LOCATION extends TypedefType { + PIO_STACK_LOCATION() { + this instanceof TypedefType and + this.getName() = "PIO_STACK_LOCATION" + } +} + +from FunctionCall mem_copy_func, Expr io_stack_param, string size +where + ( + mem_copy_func.getTarget().getName() = "RtlMoveMemory" or + mem_copy_func.getTarget().getName() = "RtlCopyMemory" or + mem_copy_func.getTarget().getName() = "RtlCopyBytes" or + mem_copy_func.getTarget().getName() = "RtlCopyMemory32" or + mem_copy_func.getTarget().getName() = "memmove" or + mem_copy_func.getTarget().getName() = "memcpy" + ) and + // All of these memory copy functions have source as the second parameter and size as the third parameter + io_stack_param = mem_copy_func.getArgument(1) and + io_stack_param.getType() instanceof PIO_STACK_LOCATION and + size = mem_copy_func.getArgument(2).getValue() and + size = "36" // Size of the IRP stack entry in bytes in decimal form. (0x24 hex) +select mem_copy_func, "Possible copy of a whole IRP stack entry $@ at $@", io_stack_param, + io_stack_param.toString(), mem_copy_func, mem_copy_func.toString() diff --git a/src/drivers/general/queries/IRPStackEntryCopy/IRPStackEntryCopy.sarif b/src/drivers/general/queries/IRPStackEntryCopy/IRPStackEntryCopy.sarif new file mode 100644 index 00000000..aa2cde90 --- /dev/null +++ b/src/drivers/general/queries/IRPStackEntryCopy/IRPStackEntryCopy.sarif @@ -0,0 +1,383 @@ +{ + "$schema": "https://json.schemastore.org/sarif-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "CodeQL", + "organization": "GitHub", + "semanticVersion": "2.17.6", + "notifications": [ + { + "id": "cpp/baseline/expected-extracted-files", + "name": "cpp/baseline/expected-extracted-files", + "shortDescription": { + "text": "Expected extracted files" + }, + "fullDescription": { + "text": "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration": { + "enabled": true + }, + "properties": { + "tags": [ + "expected-extracted-files", + "telemetry" + ] + } + }, + { + "id": "cpp/extractor/summary", + "name": "cpp/extractor/summary", + "shortDescription": { + "text": "C++ extractor telemetry" + }, + "fullDescription": { + "text": "C++ extractor telemetry" + }, + "defaultConfiguration": { + "enabled": true + } + } + ], + "rules": [ + { + "id": "cpp/drivers/irp-stack-entry-copy", + "name": "cpp/drivers/irp-stack-entry-copy", + "shortDescription": { + "text": "Irp stack entry copy" + }, + "fullDescription": { + "text": "C28114: Copying a whole IRP stack entry leaves certain fields initialized that should be cleared or updated." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning" + }, + "properties": { + "tags": [ + "correctness", + "wddst" + ], + "description": "C28114: Copying a whole IRP stack entry leaves certain fields initialized that should be cleared or updated.", + "feature.area": "Multiple", + "id": "cpp/drivers/irp-stack-entry-copy", + "impact": "Exploitable Design", + "kind": "problem", + "name": "Irp stack entry copy", + "opaqueid": "CQLD-C28114", + "owner.email": "sdat@microsoft.com", + "platform": "Desktop", + "precision": "medium", + "problem.severity": "warning", + "query-version": "v1", + "repro.text": "", + "scope": "domainspecific", + "security.severity": "Medium" + } + } + ] + }, + "extensions": [ + { + "name": "microsoft/windows-drivers", + "semanticVersion": "1.1.0+090220833fe7953b20e1287167c438367e68d1d1", + "locations": [ + { + "uri": "file:///C:/codeql-home/WDDST/src/", + "description": { + "text": "The QL pack root directory." + } + }, + { + "uri": "file:///C:/codeql-home/WDDST/src/qlpack.yml", + "description": { + "text": "The QL pack definition file." + } + } + ] + } + ] + }, + "invocations": [ + { + "toolExecutionNotifications": [ + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "message": { + "text": "Internal telemetry for the C++ extractor.\n\nNo action needed.", + "markdown": "Internal telemetry for the C++ extractor.\n\nNo action needed." + }, + "level": "note", + "timeUtc": "2024-08-22T00:11:46.713+00:00", + "descriptor": { + "id": "cpp/extractor/summary", + "index": 1 + }, + "properties": { + "attributes": { + "cache-hits": 0, + "cache-misses": 1, + "extractor-failures": 1, + "extractor-successes": 0, + "trap-caching": "disabled" + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + } + ], + "executionSuccessful": true + } + ], + "artifacts": [ + { + "location": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + }, + { + "location": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + }, + { + "location": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + ], + "results": [ + { + "ruleId": "cpp/drivers/irp-stack-entry-copy", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irp-stack-entry-copy", + "index": 0 + }, + "message": { + "text": "Possible copy of a whole IRP stack entry [... + ...](1) at [call to memcpy](2)" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 28, + "startColumn": 5, + "endColumn": 89 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "4553eaa36b9212be:1", + "primaryLocationStartColumnFingerprint": "0" + }, + "relatedLocations": [ + { + "id": 1, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 28, + "startColumn": 32, + "endColumn": 39 + } + }, + "message": { + "text": "... + ..." + } + }, + { + "id": 2, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 28, + "startColumn": 5, + "endColumn": 89 + } + }, + "message": { + "text": "call to memcpy" + } + } + ] + }, + { + "ruleId": "cpp/drivers/irp-stack-entry-copy", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irp-stack-entry-copy", + "index": 0 + }, + "message": { + "text": "Possible copy of a whole IRP stack entry [irpSp](1) at [call to memcpy](2)" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 18, + "startColumn": 5, + "endColumn": 42 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "529201672625ca13:1", + "primaryLocationStartColumnFingerprint": "0" + }, + "relatedLocations": [ + { + "id": 1, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 18, + "startColumn": 30, + "endColumn": 35 + } + }, + "message": { + "text": "irpSp" + } + }, + { + "id": 2, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 18, + "startColumn": 5, + "endColumn": 42 + } + }, + "message": { + "text": "call to memcpy" + } + } + ] + } + ], + "columnKind": "utf16CodeUnits", + "properties": { + "semmle.formatSpecifier": "sarifv2.1.0" + } + } + ] +} \ No newline at end of file diff --git a/src/drivers/general/queries/IRPStackEntryCopy/driver_snippet.c b/src/drivers/general/queries/IRPStackEntryCopy/driver_snippet.c new file mode 100644 index 00000000..905ec3f8 --- /dev/null +++ b/src/drivers/general/queries/IRPStackEntryCopy/driver_snippet.c @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +// +// driver_snippet.c +// + +#define SET_DISPATCH 1 +// Template. Not called in this test. +void top_level_call() {} + +void bad_irp_copy( + PIRP irp) +{ + PIO_STACK_LOCATION irpSp; + PIO_STACK_LOCATION nextIrpSp; + irpSp = IoGetCurrentIrpStackLocation(irp); + nextIrpSp = IoGetNextIrpStackLocation(irp); + RtlCopyMemory(nextIrpSp, irpSp, 0x24); + nextIrpSp->Control = 0; +} +void bad_irp_copy2( + PIRP irp) +{ + PIO_STACK_LOCATION irpSp; + PIO_STACK_LOCATION nextIrpSp; + irpSp = IoGetCurrentIrpStackLocation(irp); + nextIrpSp = IoGetNextIrpStackLocation(irp); + RtlCopyMemory(nextIrpSp+4, irpSp+4, FIELD_OFFSET(IO_STACK_LOCATION, DeviceObject)-4); + nextIrpSp->Control = 0; +} +void good_irp_copy( + PIRP irp) +{ + IoCopyCurrentIrpStackLocationToNext(irp); +} diff --git a/src/drivers/general/queries/ImportantFunctionCallOptimizedOut/ImportantFunctionCallOptimizedOut.qhelp b/src/drivers/general/queries/ImportantFunctionCallOptimizedOut/ImportantFunctionCallOptimizedOut.qhelp new file mode 100644 index 00000000..6de83cb5 --- /dev/null +++ b/src/drivers/general/queries/ImportantFunctionCallOptimizedOut/ImportantFunctionCallOptimizedOut.qhelp @@ -0,0 +1,58 @@ + + + +

    + Function call used to clear sensitive data will be optimized away +

    +
    + +

    + This Function call may be optimized away during compile, + resulting in sensitive data lingering in memory. + Use SecureZeroMemory or RtlSecureZeroMemory instead. + A heuristic looks for identifier names that contain items such as "key" or "pass" to trigger this warning. +

    +
    + +

    + Example of instance where function call may be optimized away +

    + + +

    + Using SecureZeroMemory or RtlSecureZeroMemory will prevent the compiler from optimizing away the function call. +

    + + +
    + +

    +

    +
    + +
  • + + C28625 + +
  • +
    +
    diff --git a/src/drivers/general/queries/ImportantFunctionCallOptimizedOut/ImportantFunctionCallOptimizedOut.ql b/src/drivers/general/queries/ImportantFunctionCallOptimizedOut/ImportantFunctionCallOptimizedOut.ql new file mode 100644 index 00000000..86821038 --- /dev/null +++ b/src/drivers/general/queries/ImportantFunctionCallOptimizedOut/ImportantFunctionCallOptimizedOut.ql @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/** + * @id cpp/drivers/important-function-call-optimized-out + * @kind problem + * @name Important function call optimized out + * @description Function call used to clear sensitive data will be optimized away + * @platform Desktop + * @feature.area Multiple + * @impact Insecure Coding Practice + * @repro.text The current function call might be optimized during compilation, which could make sensitive data stay in memory. Use the SecureZeroMemory or RtlSecureZeroMemory functions instead. A heuristic looks for identifier names that contain items such as "key" or "pass" to trigger this warning. + * @owner.email: sdat@microsoft.com + * @opaqueid CQLD-C28625 + * @problem.severity warning + * @precision medium + * @tags correctness + * ca_ported + * @scope domainspecific + * @query-version v1 + */ + +import cpp +import semmle.code.cpp.dataflow.EscapesTree +import semmle.code.cpp.commons.Exclusions +import semmle.code.cpp.models.interfaces.Alias + +class NotSecureZeroMemFunc extends Function { + NotSecureZeroMemFunc() { + this.getName().matches(["ZeroMemory", "%memset", "RtlZeroMemory"]) or + this.hasGlobalOrStdOrBslName(["ZeroMemory", "%memset", "RtlZeroMemory"]) + } +} + +predicate isNonEscapingArgument(Expr escaped) { + exists(Call call, AliasFunction aliasFunction, int i | + aliasFunction = call.getTarget() and + call.getArgument(i) = escaped.getUnconverted() and + ( + aliasFunction.parameterNeverEscapes(i) + or + aliasFunction.parameterEscapesOnlyViaReturn(i) and + (call instanceof ExprInVoidContext or call.getConversion*() instanceof BoolConversion) + ) + ) +} + +from FunctionCall call, LocalVariable v, VariableAccess acc, NotSecureZeroMemFunc memFunc +where + not v.isStatic() and + not v.getUnspecifiedType() instanceof ReferenceType and + call.getTarget() = memFunc and + acc = v.getAnAccess() and + variableAddressEscapesTree(acc, call.getArgument(0).getFullyConverted()) and + // `v` doesn't escape anywhere else. + forall(Expr escape | variableAddressEscapesTree(v.getAnAccess(), escape) | + isNonEscapingArgument(escape) + ) and + // There is no later use of `v`. + not v.getAnAccess() = call.getASuccessor*() and + v.getName().toString().toLowerCase().matches(["%pass%", "%key%"]) +select call, + "Call to " + memFunc.getName() + + " for variable $@ may be deleted by the compiler. Consider using a secure function instead, such as RtlSecureZeroMemory.", + acc, acc.getTarget().toString() diff --git a/src/drivers/general/queries/ImportantFunctionCallOptimizedOut/ImportantFunctionCallOptimizedOut.sarif b/src/drivers/general/queries/ImportantFunctionCallOptimizedOut/ImportantFunctionCallOptimizedOut.sarif new file mode 100644 index 00000000..5e51ad69 --- /dev/null +++ b/src/drivers/general/queries/ImportantFunctionCallOptimizedOut/ImportantFunctionCallOptimizedOut.sarif @@ -0,0 +1,389 @@ +{ + "$schema": "https://json.schemastore.org/sarif-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "CodeQL", + "organization": "GitHub", + "semanticVersion": "2.20.3", + "notifications": [ + { + "id": "cpp/baseline/expected-extracted-files", + "name": "cpp/baseline/expected-extracted-files", + "shortDescription": { + "text": "Expected extracted files" + }, + "fullDescription": { + "text": "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration": { + "enabled": true + }, + "properties": { + "tags": [ + "expected-extracted-files", + "telemetry" + ] + } + }, + { + "id": "cpp/extractor/summary", + "name": "cpp/extractor/summary", + "shortDescription": { + "text": "C++ extractor telemetry" + }, + "fullDescription": { + "text": "C++ extractor telemetry" + }, + "defaultConfiguration": { + "enabled": true + } + } + ], + "rules": [ + { + "id": "cpp/drivers/important-function-call-optimized-out", + "name": "cpp/drivers/important-function-call-optimized-out", + "shortDescription": { + "text": "Important function call optimized out" + }, + "fullDescription": { + "text": "Function call used to clear sensitive data will be optimized away" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning" + }, + "properties": { + "tags": [ + "correctness" + ], + "description": "Function call used to clear sensitive data will be optimized away", + "feature.area": "Multiple", + "id": "cpp/drivers/important-function-call-optimized-out", + "impact": "Insecure Coding Practice", + "kind": "problem", + "name": "Important function call optimized out", + "opaqueid": "CQLD-C28625", + "owner.email:": "sdat@microsoft.com", + "platform": "Desktop", + "precision": "medium", + "problem.severity": "warning", + "query-version": "v1", + "repro.text": "The current function call might be optimized during compilation, which could make sensitive data stay in memory. Use the SecureZeroMemory or RtlSecureZeroMemory functions instead. A heuristic looks for identifier names that contain items such as \"key\" or \"pass\" to trigger this warning.", + "scope": "domainspecific" + } + } + ] + }, + "extensions": [ + { + "name": "microsoft/windows-drivers", + "semanticVersion": "1.3.0+8ce22c1c7f0cb84b37fd703ad218688b4cede456", + "locations": [ + { + "uri": "file:///C:/codeql-home/WDDST/src/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///C:/codeql-home/WDDST/src/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + }, + { + "name": "codeql/cpp-all", + "semanticVersion": "3.1.0+d42788844f7ec0a6b9832140313cc2318e513987", + "locations": [ + { + "uri": "file:///C:/Users/jronstadt/.codeql/packages/codeql/cpp-all/3.1.0/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///C:/Users/jronstadt/.codeql/packages/codeql/cpp-all/3.1.0/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + } + ] + }, + "invocations": [ + { + "toolExecutionNotifications": [ + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "message": { + "text": "Internal telemetry for the C++ extractor.\n\nNo action needed.", + "markdown": "Internal telemetry for the C++ extractor.\n\nNo action needed." + }, + "level": "note", + "timeUtc": "2025-02-06T09:35:25.225411600Z", + "descriptor": { + "id": "cpp/extractor/summary", + "index": 1 + }, + "properties": { + "attributes": { + "cache-hits": 0, + "cache-misses": 1, + "compilers": [ + { + "program": "cl", + "version": "Microsoft (R) C/C++ Optimizing Compiler Version 19.42.34436 for x64" + } + ], + "extractor-failures": 1, + "extractor-successes": 0, + "trap-caching": "disabled" + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + } + ], + "executionSuccessful": true + } + ], + "artifacts": [ + { + "location": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + }, + { + "location": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + }, + { + "location": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + ], + "results": [ + { + "ruleId": "cpp/drivers/important-function-call-optimized-out", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/important-function-call-optimized-out", + "index": 0 + }, + "message": { + "text": "Call to memset for variable [Password](1) may be deleted by the compiler. Consider using a secure function instead, such as RtlSecureZeroMemory." + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 33, + "startColumn": 5, + "endColumn": 46 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "aa993e4d80ec68f:1", + "primaryLocationStartColumnFingerprint": "0" + }, + "relatedLocations": [ + { + "id": 1, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 33, + "startColumn": 19, + "endColumn": 27 + } + }, + "message": { + "text": "Password" + } + } + ] + }, + { + "ruleId": "cpp/drivers/important-function-call-optimized-out", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/important-function-call-optimized-out", + "index": 0 + }, + "message": { + "text": "Call to memset for variable [Password](1) may be deleted by the compiler. Consider using a secure function instead, such as RtlSecureZeroMemory." + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 22, + "startColumn": 5, + "endColumn": 43 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "1bdea489840076d:1", + "primaryLocationStartColumnFingerprint": "0" + }, + "relatedLocations": [ + { + "id": 1, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 22, + "startColumn": 16, + "endColumn": 24 + } + }, + "message": { + "text": "Password" + } + } + ] + } + ], + "columnKind": "utf16CodeUnits", + "properties": { + "semmle.formatSpecifier": "sarifv2.1.0" + } + } + ] +} \ No newline at end of file diff --git a/src/drivers/general/queries/ImportantFunctionCallOptimizedOut/driver_snippet.c b/src/drivers/general/queries/ImportantFunctionCallOptimizedOut/driver_snippet.c new file mode 100644 index 00000000..cba6ae97 --- /dev/null +++ b/src/drivers/general/queries/ImportantFunctionCallOptimizedOut/driver_snippet.c @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +// Macros to enable or disable a code section that may or may not conflict with this test. +#define SET_DISPATCH 1 + +// Template function. Not used for this test. +void top_level_call() +{ +} +#define ZeroMemory(a, b) memset(a, 0, b); + +void bad_func() +{ + char Password[100]; + + /* + * The Buffer will be going out of scope + * anyway so the compiler optimises away + * the following + */ + ZeroMemory(Password, sizeof(Password)); +} +void bad_func2() +{ + char Password[100]; + + /* + * The Buffer will be going out of scope + * anyway so the compiler optimises away + * the following + */ + RtlZeroMemory(Password, sizeof(Password)); +} +void good_func() +{ + char Password[100]; + + RtlSecureZeroMemory(Password, sizeof(Password)); +} +char globalPassword[100]; +void good_func2() +{ + char * Password; + Password = globalPassword; + // ... do something with Password + RtlZeroMemory(Password, sizeof(globalPassword)); +} \ No newline at end of file diff --git a/src/drivers/general/queries/ImproperNotOperatorOnZero/ImproperNotOperatorOnZero.qhelp b/src/drivers/general/queries/ImproperNotOperatorOnZero/ImproperNotOperatorOnZero.qhelp new file mode 100644 index 00000000..aa9abeba --- /dev/null +++ b/src/drivers/general/queries/ImproperNotOperatorOnZero/ImproperNotOperatorOnZero.qhelp @@ -0,0 +1,37 @@ + + + +

    + The type for which !0 is being used does not treat it as failure case. Returning a status value such as !TRUE is not the same as returning a status value that indicates failure. +

    +
    + +

    + Certain data types such as NTSTATUS and HRESULT have associated macros that classify values of these types into SUCCESS or FAILURE. These macros check the most significant bit of the returned value or values to determine this. Thus, 0 and 1 are both classified as SUCCESS values. + The proper way to fix this warning is to return a proper error code instead of a generic value such as -1. +

    +
    + +

    + Returning !0 instead of a proper error code. +

    + + +
    + +

    +

    +
    + +
  • + + C28650 + +
  • +
    +
    diff --git a/src/drivers/general/queries/ImproperNotOperatorOnZero/ImproperNotOperatorOnZero.ql b/src/drivers/general/queries/ImproperNotOperatorOnZero/ImproperNotOperatorOnZero.ql new file mode 100644 index 00000000..cef38e09 --- /dev/null +++ b/src/drivers/general/queries/ImproperNotOperatorOnZero/ImproperNotOperatorOnZero.ql @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/** + * @id cpp/drivers/improper-not-operator-on-zero + * @kind problem + * @name Improper Not Operator On Zero + * @description The type for which !0 is being used does not treat it as failure case. + * @platform Desktop + * @feature.area Multiple + * @impact Insecure Coding Practice + * @repro.text Returning a status value such as !TRUE is not the same as returning a status value that indicates failure. + * @owner.email: sdat@microsoft.com + * @opaqueid CQLD-C28650 + * @problem.severity warning + * @precision medium + * @tags correctness + * ca_ported + * @scope domainspecific + * @query-version v1 + */ + +import cpp +import semmle.code.cpp.ir.IR + +from NotExpr ne, Conversion c, ReturnStmt rs +where + c.getType().toString().matches(["NTSTATUS", "HRESULT"]) and + c.getUnconverted().getType() instanceof IntegralType and + ne.getConversion() = c and + ne.getOperand().getValue() = ["0", "1", "TRUE", "FALSE", "-1"] and + rs.getAChild*() = ne +select c, "Improper use of the not operator on return value" diff --git a/src/drivers/general/queries/ImproperNotOperatorOnZero/ImproperNotOperatorOnZero.sarif b/src/drivers/general/queries/ImproperNotOperatorOnZero/ImproperNotOperatorOnZero.sarif new file mode 100644 index 00000000..2207f312 --- /dev/null +++ b/src/drivers/general/queries/ImproperNotOperatorOnZero/ImproperNotOperatorOnZero.sarif @@ -0,0 +1,349 @@ +{ + "$schema": "https://json.schemastore.org/sarif-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "CodeQL", + "organization": "GitHub", + "semanticVersion": "2.20.3", + "notifications": [ + { + "id": "cpp/baseline/expected-extracted-files", + "name": "cpp/baseline/expected-extracted-files", + "shortDescription": { + "text": "Expected extracted files" + }, + "fullDescription": { + "text": "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration": { + "enabled": true + }, + "properties": { + "tags": [ + "expected-extracted-files", + "telemetry" + ] + } + }, + { + "id": "cpp/extractor/summary", + "name": "cpp/extractor/summary", + "shortDescription": { + "text": "C++ extractor telemetry" + }, + "fullDescription": { + "text": "C++ extractor telemetry" + }, + "defaultConfiguration": { + "enabled": true + } + } + ], + "rules": [ + { + "id": "cpp/drivers/improper-not-operator-on-zero", + "name": "cpp/drivers/improper-not-operator-on-zero", + "shortDescription": { + "text": "Improper Not Operator On Zero" + }, + "fullDescription": { + "text": "The type for which !0 is being used does not treat it as failure case." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning" + }, + "properties": { + "tags": [ + "correctness" + ], + "description": "The type for which !0 is being used does not treat it as failure case.", + "feature.area": "Multiple", + "id": "cpp/drivers/improper-not-operator-on-zero", + "impact": "Insecure Coding Practice", + "kind": "problem", + "name": "Improper Not Operator On Zero", + "opaqueid": "CQLD-TODO", + "owner.email:": "sdat@microsoft.com", + "platform": "Desktop", + "precision": "medium", + "problem.severity": "warning", + "query-version": "v1", + "repro.text": "Returning a status value such as !TRUE is not the same as returning a status value that indicates failure.", + "scope": "domainspecific" + } + } + ] + }, + "extensions": [ + { + "name": "microsoft/windows-drivers", + "semanticVersion": "1.3.0+8ce22c1c7f0cb84b37fd703ad218688b4cede456", + "locations": [ + { + "uri": "file:///C:/codeql-home/WDDST/src/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///C:/codeql-home/WDDST/src/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + }, + { + "name": "codeql/cpp-all", + "semanticVersion": "3.1.0+d42788844f7ec0a6b9832140313cc2318e513987", + "locations": [ + { + "uri": "file:///C:/Users/jronstadt/.codeql/packages/codeql/cpp-all/3.1.0/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///C:/Users/jronstadt/.codeql/packages/codeql/cpp-all/3.1.0/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + } + ] + }, + "invocations": [ + { + "toolExecutionNotifications": [ + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "message": { + "text": "Internal telemetry for the C++ extractor.\n\nNo action needed.", + "markdown": "Internal telemetry for the C++ extractor.\n\nNo action needed." + }, + "level": "note", + "timeUtc": "2025-02-07T08:00:42.156347100Z", + "descriptor": { + "id": "cpp/extractor/summary", + "index": 1 + }, + "properties": { + "attributes": { + "cache-hits": 0, + "cache-misses": 1, + "compilers": [ + { + "program": "cl", + "version": "Microsoft (R) C/C++ Optimizing Compiler Version 19.42.34436 for x64" + } + ], + "extractor-failures": 1, + "extractor-successes": 0, + "trap-caching": "disabled" + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + } + ], + "executionSuccessful": true + } + ], + "artifacts": [ + { + "location": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + }, + { + "location": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + }, + { + "location": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + ], + "results": [ + { + "ruleId": "cpp/drivers/improper-not-operator-on-zero", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/improper-not-operator-on-zero", + "index": 0 + }, + "message": { + "text": "Improper use of the not operator on return value" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 19, + "startColumn": 12, + "endColumn": 14 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "f164a707d45e254f:1", + "primaryLocationStartColumnFingerprint": "7" + } + }, + { + "ruleId": "cpp/drivers/improper-not-operator-on-zero", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/improper-not-operator-on-zero", + "index": 0 + }, + "message": { + "text": "Improper use of the not operator on return value" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 14, + "startColumn": 12, + "endColumn": 14 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "c5d9d8fa4715192e:1", + "primaryLocationStartColumnFingerprint": "7" + } + } + ], + "columnKind": "utf16CodeUnits", + "properties": { + "semmle.formatSpecifier": "sarifv2.1.0" + } + } + ] +} \ No newline at end of file diff --git a/src/drivers/general/queries/ImproperNotOperatorOnZero/driver_snippet.c b/src/drivers/general/queries/ImproperNotOperatorOnZero/driver_snippet.c new file mode 100644 index 00000000..e381651e --- /dev/null +++ b/src/drivers/general/queries/ImproperNotOperatorOnZero/driver_snippet.c @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +// Macros to enable or disable a code section that may or may not conflict with this test. +#define SET_DISPATCH 1 + +// Template function. Not used for this test. +void top_level_call() +{ +} + +NTSTATUS test_func() +{ + return !0; +} + +HRESULT test_func2() +{ + return !0; +} + +void test_test_func() +{ + if(test_func()){ + ; + }else if(test_func2()){ + ; + } + else{ + ; + } +} +// TODO add tests for query \ No newline at end of file diff --git a/src/drivers/general/queries/InvalidFunctionClassTypedef/InvalidFunctionClassTypedef.qhelp b/src/drivers/general/queries/InvalidFunctionClassTypedef/InvalidFunctionClassTypedef.qhelp new file mode 100644 index 00000000..8dc627dc --- /dev/null +++ b/src/drivers/general/queries/InvalidFunctionClassTypedef/InvalidFunctionClassTypedef.qhelp @@ -0,0 +1,49 @@ + + + +

    + The function class on the function does not match the function class on the typedef used here +

    +
    + +

    + This warning indicates that an annotation on a function class does not match the function type, as specified by the type declaration. This warning indicates an error in the annotations, not in the code that is being analyzed. +

    +
    + +

    + Example where typedef of one type is used to declare the function but typedef of a different type is used in the function definition inside __drv_functionClass +

    + + + +
    + +

    +

    +
    + +
  • + + C28268 + +
  • +
    +
    diff --git a/src/drivers/general/queries/InvalidFunctionClassTypedef/InvalidFunctionClassTypedef.ql b/src/drivers/general/queries/InvalidFunctionClassTypedef/InvalidFunctionClassTypedef.ql new file mode 100644 index 00000000..74e85da9 --- /dev/null +++ b/src/drivers/general/queries/InvalidFunctionClassTypedef/InvalidFunctionClassTypedef.ql @@ -0,0 +1,78 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/** + * @id cpp/drivers/invalid-function-class-typedef + * @kind problem + * @name Invalid Function Class Typedef + * @description The function class on the function does not match the function class on the typedef used here + * @platform Desktop + * @feature.area Multiple + * @impact Insecure Coding Practice + * @repro.text + * @owner.email: sdat@microsoft.com + * @opaqueid CQLD-C28268 + * @problem.severity warning + * @precision medium + * @tags correctness + * ca_ported + * @scope domainspecific + * @query-version v1 + */ + +import cpp +import drivers.libraries.SAL + +class FunctionClassAnnotatedTypedef extends TypedefType { + FunctionClassAnnotation funcAnnotation; + + FunctionClassAnnotatedTypedef() { funcAnnotation.getTypedefDeclarations() = this } + + FunctionClassAnnotation getFuncClassAnnotation() { result = funcAnnotation } +} + +class FunctionClassAnnotation extends SALAnnotation { + string annotationName; + + FunctionClassAnnotation() { + this.getMacroName() = ["__drv_functionClass", "_Function_class_"] and + annotationName = this.getMacroName() + } +} + +class AnnotatedFunction extends Function { + FunctionClassAnnotation funcClassAnnotation; + + AnnotatedFunction() { + funcClassAnnotation.getMacroName() = ["__drv_functionClass", "_Function_class_"] and + exists(FunctionDeclarationEntry fde | + fde = this.getADeclarationEntry() and + funcClassAnnotation.getDeclarationEntry() = fde + ) + or + exists(FunctionDeclarationEntry fde | + fde.getFunction() = this and + fde.getTypedefType().(FunctionClassAnnotatedTypedef).getFuncClassAnnotation() = + funcClassAnnotation + ) + } + + FunctionClassAnnotation getFuncClassAnnotation() { result = funcClassAnnotation } +} + +from AnnotatedFunction af, string declTypedef, string funcClass +where + declTypedef = af.getADeclarationEntry().getTypedefType().toString() and + funcClass = af.getFuncClassAnnotation().getUnexpandedArgument(0) and + funcClass.replaceAll("*", "").trim() != declTypedef.replaceAll("*", "").trim() and // pointer to type OK + not exists(TypedefType t, string baseType | + t.toString() = funcClass and + baseType = t.getBaseType().toString().replaceAll("*", "").trim() and + baseType = declTypedef.replaceAll("*", "").trim() + ) and + // Allow for EVT_WDF_DEVICE_ARM/DISARM_WAKE_FROM_S0/X + not ( + funcClass.matches("EVT_WDF_DEVICE_%ARM_WAKE_FROM_S%") and + declTypedef.matches("EVT_WDF_DEVICE_%ARM_WAKE_FROM_S%") + ) +select af, "The function class " + funcClass + " on the function does not match the function class " + + declTypedef + " on the typedef used here" diff --git a/src/drivers/general/queries/InvalidFunctionClassTypedef/InvalidFunctionClassTypedef.sarif b/src/drivers/general/queries/InvalidFunctionClassTypedef/InvalidFunctionClassTypedef.sarif new file mode 100644 index 00000000..726d8bf8 --- /dev/null +++ b/src/drivers/general/queries/InvalidFunctionClassTypedef/InvalidFunctionClassTypedef.sarif @@ -0,0 +1,318 @@ +{ + "$schema": "https://json.schemastore.org/sarif-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "CodeQL", + "organization": "GitHub", + "semanticVersion": "2.20.3", + "notifications": [ + { + "id": "cpp/baseline/expected-extracted-files", + "name": "cpp/baseline/expected-extracted-files", + "shortDescription": { + "text": "Expected extracted files" + }, + "fullDescription": { + "text": "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration": { + "enabled": true + }, + "properties": { + "tags": [ + "expected-extracted-files", + "telemetry" + ] + } + }, + { + "id": "cpp/extractor/summary", + "name": "cpp/extractor/summary", + "shortDescription": { + "text": "C++ extractor telemetry" + }, + "fullDescription": { + "text": "C++ extractor telemetry" + }, + "defaultConfiguration": { + "enabled": true + } + } + ], + "rules": [ + { + "id": "cpp/drivers/invalid-function-class-typedef", + "name": "cpp/drivers/invalid-function-class-typedef", + "shortDescription": { + "text": "Invalid Function Class Typedef" + }, + "fullDescription": { + "text": "The function class on the function does not match the function class on the typedef used here" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning" + }, + "properties": { + "tags": [ + "correctness" + ], + "description": "The function class on the function does not match the function class on the typedef used here", + "feature.area": "Multiple", + "id": "cpp/drivers/invalid-function-class-typedef", + "impact": "Insecure Coding Practice", + "kind": "problem", + "name": "Invalid Function Class Typedef", + "opaqueid": "CQLD-C28268", + "owner.email:": "sdat@microsoft.com", + "platform": "Desktop", + "precision": "medium", + "problem.severity": "warning", + "query-version": "v1", + "repro.text": "", + "scope": "domainspecific" + } + } + ] + }, + "extensions": [ + { + "name": "microsoft/windows-drivers", + "semanticVersion": "1.3.0+aa5a9fcd1a748aa2ee27f86f4178133865ecbf34", + "locations": [ + { + "uri": "file:///C:/codeql-home/WDDST/src/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///C:/codeql-home/WDDST/src/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + }, + { + "name": "codeql/cpp-all", + "semanticVersion": "3.1.0+d42788844f7ec0a6b9832140313cc2318e513987", + "locations": [ + { + "uri": "file:///C:/Users/jronstadt/.codeql/packages/codeql/cpp-all/3.1.0/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///C:/Users/jronstadt/.codeql/packages/codeql/cpp-all/3.1.0/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + } + ] + }, + "invocations": [ + { + "toolExecutionNotifications": [ + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "message": { + "text": "Internal telemetry for the C++ extractor.\n\nNo action needed.", + "markdown": "Internal telemetry for the C++ extractor.\n\nNo action needed." + }, + "level": "note", + "timeUtc": "2025-02-07T08:51:20.768551900Z", + "descriptor": { + "id": "cpp/extractor/summary", + "index": 1 + }, + "properties": { + "attributes": { + "cache-hits": 0, + "cache-misses": 1, + "compilers": [ + { + "program": "cl", + "version": "Microsoft (R) C/C++ Optimizing Compiler Version 19.42.34436 for x64" + } + ], + "extractor-failures": 1, + "extractor-successes": 0, + "trap-caching": "disabled" + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + } + ], + "executionSuccessful": true + } + ], + "artifacts": [ + { + "location": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + }, + { + "location": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + }, + { + "location": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + ], + "results": [ + { + "ruleId": "cpp/drivers/invalid-function-class-typedef", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/invalid-function-class-typedef", + "index": 0 + }, + "message": { + "text": "The function class TEST_ROUTINE2 on the function does not match the function class TEST_ROUTINE on the typedef used here" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 37, + "startColumn": 10, + "endColumn": 15 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "b8db234dee1fec96:1", + "primaryLocationStartColumnFingerprint": "5" + } + } + ], + "columnKind": "utf16CodeUnits", + "properties": { + "semmle.formatSpecifier": "sarifv2.1.0" + } + } + ] +} \ No newline at end of file diff --git a/src/drivers/general/queries/InvalidFunctionClassTypedef/driver_snippet.c b/src/drivers/general/queries/InvalidFunctionClassTypedef/driver_snippet.c new file mode 100644 index 00000000..ef627a8a --- /dev/null +++ b/src/drivers/general/queries/InvalidFunctionClassTypedef/driver_snippet.c @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +// Macros to enable or disable a code section that may or may not conflict with this test. +#define SET_DISPATCH 1 + +// Template function. Not used for this test. +void top_level_call() +{ +} + +typedef __drv_functionClass(TEST_ROUTINE) + VOID + TEST_ROUTINE( + VOID); + +typedef TEST_ROUTINE *PTEST_ROUTINE; + +typedef __drv_functionClass(TEST_ROUTINE2) + VOID + TEST_ROUTINE2( + VOID); + +typedef TEST_ROUTINE2 *PTEST_ROUTINE2; + +TEST_ROUTINE func1; +TEST_ROUTINE func2; + +__drv_functionClass(TEST_ROUTINE) + VOID func1( + VOID) +{ + ; // Don't need to do anything heres +} + +__drv_functionClass(TEST_ROUTINE2) + VOID func2( + VOID) +{ + ; // Don't need to do anything heres +} \ No newline at end of file diff --git a/src/drivers/general/queries/InvalidFunctionPointerAnnotation/InvalidFunctionPointerAnnotation.qhelp b/src/drivers/general/queries/InvalidFunctionPointerAnnotation/InvalidFunctionPointerAnnotation.qhelp new file mode 100644 index 00000000..66196a30 --- /dev/null +++ b/src/drivers/general/queries/InvalidFunctionPointerAnnotation/InvalidFunctionPointerAnnotation.qhelp @@ -0,0 +1,34 @@ + + + +

    + The function pointer annotation class does not match the function class +

    +
    + +

    + A function pointer has a __drv_functionClass annotation that specifies that only functions of a particular functional class should be assigned to it. In an assignment or implied assignment in a function call, the source and target must be of the same function class, but the function classes do not match. +

    +
    + +

    + A call to IoQueueWorkItem is expecting a function pointer annotated with __drv_functionClass(IO_WORKITEM_ROUTINE) but in this example, badAnnotationFunc1 is annotated with __drv_functionClass(IO_TIMER_ROUTINE). +

    + + + +
    + +

    +

    +
    + +
  • + + Warning C28165 + +
  • +
    +
    diff --git a/src/drivers/general/queries/InvalidFunctionPointerAnnotation/InvalidFunctionPointerAnnotation.ql b/src/drivers/general/queries/InvalidFunctionPointerAnnotation/InvalidFunctionPointerAnnotation.ql new file mode 100644 index 00000000..41982962 --- /dev/null +++ b/src/drivers/general/queries/InvalidFunctionPointerAnnotation/InvalidFunctionPointerAnnotation.ql @@ -0,0 +1,83 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/** + * @id cpp/drivers/invalid-function-pointer-annotation + * @kind problem + * @name Invalid Function Pointer Annotation + * @description The function pointer annotation class does not match the function class + * @platform Desktop + * @feature.area Multiple + * @impact Insecure Coding Practice + * @repro.text + * @owner.email: sdat@microsoft.com + * @opaqueid CQLD-C28165 + * @problem.severity warning + * @precision medium + * @tags correctness + * ca_ported + * @scope domainspecific + * @query-version v1 + */ + +import cpp +import drivers.libraries.SAL + +class FunctionClassAnnotatedTypedef extends TypedefType { + FunctionClassAnnotation funcAnnotation; + + FunctionClassAnnotatedTypedef() { funcAnnotation.getTypedefDeclarations() = this } + + FunctionClassAnnotation getFuncClassAnnotation() { result = funcAnnotation } +} + +class FunctionClassAnnotation extends SALAnnotation { + string annotationName; + + FunctionClassAnnotation() { + this.getMacroName() = ["__drv_functionClass", "_Function_class_"] and + annotationName = this.getMacroName() + } +} + +class AnnotatedFunction extends Function { + FunctionClassAnnotation funcClassAnnotation; + + AnnotatedFunction() { + funcClassAnnotation.getMacroName() = ["__drv_functionClass", "_Function_class_"] and + exists(FunctionDeclarationEntry fde | + fde = this.getADeclarationEntry() and + funcClassAnnotation.getDeclarationEntry() = fde + ) + or + exists(FunctionDeclarationEntry fde | + fde.getFunction() = this and + fde.getTypedefType().(FunctionClassAnnotatedTypedef).getFuncClassAnnotation() = + funcClassAnnotation + ) + } + + FunctionClassAnnotation getFuncClassAnnotation() { result = funcClassAnnotation } +} + +from + AnnotatedFunction af, FunctionAccess access, FunctionCall callingFunc, int n, string funcClass, + string expectedFuncClass +where +af.getAnAccess() = access and + callingFunc.getArgument(n) = access and + funcClass = af.getFuncClassAnnotation().getUnexpandedArgument(0).toString() and + expectedFuncClass = + callingFunc + .getTarget() + .getADeclarationEntry() + .getParameterDeclarationEntry(n) + .getType() + .toString() and + funcClass.replaceAll("*", "").trim() != expectedFuncClass.replaceAll("*", "").trim() and // pointer to type OK + not exists(TypedefType t, string baseType | + t = callingFunc.getTarget().getADeclarationEntry().getParameterDeclarationEntry(n).getType() and + baseType = t.getBaseType().toString().replaceAll("*", "").trim() and + baseType = funcClass.replaceAll("*", "").trim() + ) +select callingFunc.getArgument(n), "Function pointer annotation mismatch. Function pointer type: "+expectedFuncClass+". Function annotation: "+funcClass + diff --git a/src/drivers/general/queries/InvalidFunctionPointerAnnotation/InvalidFunctionPointerAnnotation.sarif b/src/drivers/general/queries/InvalidFunctionPointerAnnotation/InvalidFunctionPointerAnnotation.sarif new file mode 100644 index 00000000..40819746 --- /dev/null +++ b/src/drivers/general/queries/InvalidFunctionPointerAnnotation/InvalidFunctionPointerAnnotation.sarif @@ -0,0 +1,305 @@ +{ + "$schema": "https://json.schemastore.org/sarif-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "CodeQL", + "organization": "GitHub", + "semanticVersion": "2.17.6", + "notifications": [ + { + "id": "cpp/baseline/expected-extracted-files", + "name": "cpp/baseline/expected-extracted-files", + "shortDescription": { + "text": "Expected extracted files" + }, + "fullDescription": { + "text": "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration": { + "enabled": true + }, + "properties": { + "tags": [ + "expected-extracted-files", + "telemetry" + ] + } + }, + { + "id": "cpp/extractor/summary", + "name": "cpp/extractor/summary", + "shortDescription": { + "text": "C++ extractor telemetry" + }, + "fullDescription": { + "text": "C++ extractor telemetry" + }, + "defaultConfiguration": { + "enabled": true + } + } + ], + "rules": [ + { + "id": "cpp/drivers/invalid-function-pointer-annotation", + "name": "cpp/drivers/invalid-function-pointer-annotation", + "shortDescription": { + "text": "Invalid Function Pointer Annotation" + }, + "fullDescription": { + "text": "The function pointer annotation class does not match the function class" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning" + }, + "properties": { + "tags": [ + "correctness" + ], + "description": "The function pointer annotation class does not match the function class", + "feature.area": "Multiple", + "id": "cpp/drivers/invalid-function-pointer-annotation", + "impact": "Insecure Coding Practice", + "kind": "problem", + "name": "Invalid Function Pointer Annotation", + "opaqueid": "CQLD-C28165", + "owner.email:": "sdat@microsoft.com", + "platform": "Desktop", + "precision": "medium", + "problem.severity": "warning", + "query-version": "v1", + "repro.text": "", + "scope": "domainspecific" + } + } + ] + }, + "extensions": [ + { + "name": "microsoft/windows-drivers", + "semanticVersion": "1.1.0+57263c4bfacdfd36208d9a3be4842cc73545538d", + "locations": [ + { + "uri": "file:///C:/codeql-home/WDDST/src/", + "description": { + "text": "The QL pack root directory." + } + }, + { + "uri": "file:///C:/codeql-home/WDDST/src/qlpack.yml", + "description": { + "text": "The QL pack definition file." + } + } + ] + } + ] + }, + "invocations": [ + { + "toolExecutionNotifications": [ + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "message": { + "text": "Internal telemetry for the C++ extractor.\n\nNo action needed.", + "markdown": "Internal telemetry for the C++ extractor.\n\nNo action needed." + }, + "level": "note", + "timeUtc": "2024-08-24T03:43:22.057+00:00", + "descriptor": { + "id": "cpp/extractor/summary", + "index": 1 + }, + "properties": { + "attributes": { + "cache-hits": 0, + "cache-misses": 1, + "extractor-failures": 1, + "extractor-successes": 0, + "trap-caching": "disabled" + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + } + ], + "executionSuccessful": true + } + ], + "artifacts": [ + { + "location": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + }, + { + "location": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + }, + { + "location": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + ], + "results": [ + { + "ruleId": "cpp/drivers/invalid-function-pointer-annotation", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/invalid-function-pointer-annotation", + "index": 0 + }, + "message": { + "text": "Function pointer annotation mismatch. Function pointer type: PIO_WORKITEM_ROUTINE. Function annotation: IO_TIMER_ROUTINE" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 67, + "startColumn": 33, + "endColumn": 44 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "e4fdf327b8fc5c9d:1", + "primaryLocationStartColumnFingerprint": "28" + } + }, + { + "ruleId": "cpp/drivers/invalid-function-pointer-annotation", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/invalid-function-pointer-annotation", + "index": 0 + }, + "message": { + "text": "Function pointer annotation mismatch. Function pointer type: PIO_WORKITEM_ROUTINE. Function annotation: IO_TIMER_ROUTINE" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 68, + "startColumn": 33, + "endColumn": 44 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "72692f5b879eb465:1", + "primaryLocationStartColumnFingerprint": "28" + } + } + ], + "columnKind": "utf16CodeUnits", + "properties": { + "semmle.formatSpecifier": "sarifv2.1.0" + } + } + ] +} \ No newline at end of file diff --git a/src/drivers/general/queries/InvalidFunctionPointerAnnotation/driver_snippet.c b/src/drivers/general/queries/InvalidFunctionPointerAnnotation/driver_snippet.c new file mode 100644 index 00000000..f1401aca --- /dev/null +++ b/src/drivers/general/queries/InvalidFunctionPointerAnnotation/driver_snippet.c @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +// Macros to enable or disable a code section that may or may not conflict with this test. +#define SET_DISPATCH 1 +// Template function. Not used for this test. +void top_level_call() +{ +} + +IO_WORKITEM_ROUTINE workerFunc4; + +_Use_decl_annotations_ + VOID + workerFunc4( + _In_ PDEVICE_OBJECT DeviceObject, + _In_opt_ PVOID Context) +{ + ; // Don't actually need to do anything here. +} + +IO_TIMER_ROUTINE workerFunc5; + +_Use_decl_annotations_ + VOID + workerFunc5( + _In_ PDEVICE_OBJECT DeviceObject, + _In_opt_ PVOID Context) +{ + ; // Don't actually need to do anything here. +} + +__drv_functionClass(IO_WORKITEM_ROUTINE) + __drv_requiresIRQL(PASSIVE_LEVEL) + __drv_sameIRQL + VOID + workerFunc1( + _In_ PDEVICE_OBJECT DeviceObject, + _In_opt_ PVOID Context) +{ + ; // Don't actually need to do anything here. +} + +__drv_functionClass(IO_TIMER_ROUTINE) + __drv_requiresIRQL(PASSIVE_LEVEL) + __drv_sameIRQL + VOID + workerFunc2( + _In_ PDEVICE_OBJECT DeviceObject, + _In_opt_ PVOID Context) +{ + ; // Don't actually need to do anything here. +} + +void test_good() +{ + __drv_aliasesMem PIO_WORKITEM IoWorkItem = NULL; + __drv_aliasesMem PVOID Context = NULL; + IoQueueWorkItem(IoWorkItem, workerFunc1, DelayedWorkQueue, Context); + IoQueueWorkItem(IoWorkItem, workerFunc4, DelayedWorkQueue, Context); +} + +void test_bad() +{ + __drv_aliasesMem PIO_WORKITEM IoWorkItem = NULL; + __drv_aliasesMem PVOID Context = NULL; + IoQueueWorkItem(IoWorkItem, workerFunc2, DelayedWorkQueue, Context); + IoQueueWorkItem(IoWorkItem, workerFunc5, DelayedWorkQueue, Context); +} diff --git a/src/drivers/general/queries/IoInitializeTimerCall/IoInitializeTimerCall.qhelp b/src/drivers/general/queries/IoInitializeTimerCall/IoInitializeTimerCall.qhelp new file mode 100644 index 00000000..f63b6ede --- /dev/null +++ b/src/drivers/general/queries/IoInitializeTimerCall/IoInitializeTimerCall.qhelp @@ -0,0 +1,20 @@ + + + +

    + IoInitializeTimer is best called from AddDevice +

    +
    + +

    + IoInitializeTimer can only be called once per device object. Calling it from the AddDevice routine helps assure that it is not unexpectedly called more than once. +

    +
    + +
  • + + C28133 + +
  • +
    +
    diff --git a/src/drivers/general/queries/IoInitializeTimerCall/IoInitializeTimerCall.ql b/src/drivers/general/queries/IoInitializeTimerCall/IoInitializeTimerCall.ql new file mode 100644 index 00000000..37254252 --- /dev/null +++ b/src/drivers/general/queries/IoInitializeTimerCall/IoInitializeTimerCall.ql @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/** + * @id cpp/drivers/io-initialize-timer-call + * @kind problem + * @name IoInitializeTimer is best called from AddDevice + * @description IoInitializeTimer can only be called once per device object. Calling it from the AddDevice routine helps assure that it is not unexpectedly called more than once. + * @platform Desktop + * @security.severity Low + * @feature.area Multiple + * @impact + * @repro.text + * @owner.email sdat@microsoft.com + * @opaqueid CQLD-C28133 + * @problem.severity warning + * @precision high + * @tags correctness + * ca_ported + * wddst + * @scope domainspecific + * @query-version v1 + */ + +import cpp +import drivers.wdm.libraries.WdmDrivers + +from FunctionCall fc, WdmAddDevice wad +where + fc.getTarget().getName() = "IoInitializeTimer" and + not fc.getEnclosingFunction() = wad +select fc, "IoInitializeTimer should be called from AddDevice" \ No newline at end of file diff --git a/src/drivers/general/queries/IoInitializeTimerCall/IoInitializeTimerCall.sarif b/src/drivers/general/queries/IoInitializeTimerCall/IoInitializeTimerCall.sarif new file mode 100644 index 00000000..19611796 --- /dev/null +++ b/src/drivers/general/queries/IoInitializeTimerCall/IoInitializeTimerCall.sarif @@ -0,0 +1,201 @@ +{ + "$schema" : "https://json.schemastore.org/sarif-2.1.0.json", + "version" : "2.1.0", + "runs" : [ { + "tool" : { + "driver" : { + "name" : "CodeQL", + "organization" : "GitHub", + "semanticVersion" : "2.15.4", + "notifications" : [ { + "id" : "cpp/baseline/expected-extracted-files", + "name" : "cpp/baseline/expected-extracted-files", + "shortDescription" : { + "text" : "Expected extracted files" + }, + "fullDescription" : { + "text" : "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration" : { + "enabled" : true + }, + "properties" : { + "tags" : [ "expected-extracted-files", "telemetry" ] + } + } ], + "rules" : [ { + "id" : "cpp/drivers/pool-tag-integral", + "name" : "cpp/drivers/pool-tag-integral", + "shortDescription" : { + "text" : "IoInitializeTimer is best called from AddDevice" + }, + "fullDescription" : { + "text" : "IoInitializeTimer can only be called once per device object. Calling it from the AddDevice routine helps assure that it is not unexpectedly called more than once." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "properties" : { + "tags" : [ "correctness", "wddst" ], + "description" : "IoInitializeTimer can only be called once per device object. Calling it from the AddDevice routine helps assure that it is not unexpectedly called more than once.", + "feature.area" : "Multiple", + "id" : "cpp/drivers/pool-tag-integral", + "impact" : "", + "kind" : "problem", + "name" : "IoInitializeTimer is best called from AddDevice", + "opaqueid" : "CQLD-C28133", + "owner.email" : "sdat@microsoft.com", + "platform" : "Desktop", + "precision" : "high", + "problem.severity" : "warning", + "query-version" : "v1", + "repro.text" : "", + "scope" : "domainspecific", + "security.severity" : "Low" + } + } ] + }, + "extensions" : [ { + "name" : "microsoft/windows-drivers", + "semanticVersion" : "1.0.13+4cf80ade609037becb8999823de45e08bd818a20", + "locations" : [ { + "uri" : "file:///C:/codeql-home/WDDST/src/", + "description" : { + "text" : "The QL pack root directory." + } + }, { + "uri" : "file:///C:/codeql-home/WDDST/src/qlpack.yml", + "description" : { + "text" : "The QL pack definition file." + } + } ] + } ] + }, + "invocations" : [ { + "toolExecutionNotifications" : [ { + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + } + } + } ], + "message" : { + "text" : "" + }, + "level" : "none", + "descriptor" : { + "id" : "cpp/baseline/expected-extracted-files", + "index" : 0 + }, + "properties" : { + "formattedMessage" : { + "text" : "" + } + } + }, { + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/fail_driver1.c", + "uriBaseId" : "%SRCROOT%", + "index" : 1 + } + } + } ], + "message" : { + "text" : "" + }, + "level" : "none", + "descriptor" : { + "id" : "cpp/baseline/expected-extracted-files", + "index" : 0 + }, + "properties" : { + "formattedMessage" : { + "text" : "" + } + } + }, { + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/fail_driver1.h", + "uriBaseId" : "%SRCROOT%", + "index" : 2 + } + } + } ], + "message" : { + "text" : "" + }, + "level" : "none", + "descriptor" : { + "id" : "cpp/baseline/expected-extracted-files", + "index" : 0 + }, + "properties" : { + "formattedMessage" : { + "text" : "" + } + } + } ], + "executionSuccessful" : true + } ], + "artifacts" : [ { + "location" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + } + }, { + "location" : { + "uri" : "driver/fail_driver1.c", + "uriBaseId" : "%SRCROOT%", + "index" : 1 + } + }, { + "location" : { + "uri" : "driver/fail_driver1.h", + "uriBaseId" : "%SRCROOT%", + "index" : 2 + } + } ], + "results" : [ { + "ruleId" : "cpp/drivers/pool-tag-integral", + "ruleIndex" : 0, + "rule" : { + "id" : "cpp/drivers/pool-tag-integral", + "index" : 0 + }, + "message" : { + "text" : "IoInitializeTimer should be called from AddDevice" + }, + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + }, + "region" : { + "startLine" : 16, + "startColumn" : 5, + "endColumn" : 22 + } + } + } ], + "partialFingerprints" : { + "primaryLocationLineHash" : "dc91db8b202a7996:1", + "primaryLocationStartColumnFingerprint" : "0" + } + } ], + "columnKind" : "utf16CodeUnits", + "properties" : { + "semmle.formatSpecifier" : "sarifv2.1.0" + } + } ] +} \ No newline at end of file diff --git a/src/drivers/general/queries/IoInitializeTimerCall/driver_snippet.c b/src/drivers/general/queries/IoInitializeTimerCall/driver_snippet.c new file mode 100644 index 00000000..217bd3e9 --- /dev/null +++ b/src/drivers/general/queries/IoInitializeTimerCall/driver_snippet.c @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +// +// driver_snippet.c +// +#define SET_DISPATCH 1 + +// Template. Not called in this test. +void top_level_call() {} + +VOID functionThatsNotAddDevice() +{ + PDEVICE_OBJECT DeviceObject = NULL; + PIO_TIMER_ROUTINE TimerRoutine= NULL; + PVOID Context= NULL; + IoInitializeTimer( + DeviceObject, + TimerRoutine, + Context); +} + diff --git a/src/drivers/general/queries/IrqlAnnotationIssue/IrqlAnnotationIssue.qhelp b/src/drivers/general/queries/IrqlAnnotationIssue/IrqlAnnotationIssue.qhelp new file mode 100644 index 00000000..0216e52e --- /dev/null +++ b/src/drivers/general/queries/IrqlAnnotationIssue/IrqlAnnotationIssue.qhelp @@ -0,0 +1,42 @@ + + + +

    + The value for an IRQL from annotation could not be evaluated in this context. +

    +
    + +

    + This warning indicates that the Code Analysis tool cannot interpret the function annotation because the annotation is not + coded correctly. As a result, the Code Analysis tool cannot determine the specified IRQL value. This warning can occur with any of + the driver-specific annotations that mention an IRQL when the Code Analysis tool cannot evaluate the expression for the IRQL. +

    +
    + +

    + Incorrect IRQL annotation +

    + + +

    + Incorrect IRQL annotation +

    + + +
    + +

    +

    +
    + +
  • + + C28153 + +
  • +
    +
    diff --git a/src/drivers/general/queries/IrqlAnnotationIssue/IrqlAnnotationIssue.ql b/src/drivers/general/queries/IrqlAnnotationIssue/IrqlAnnotationIssue.ql new file mode 100644 index 00000000..d2693963 --- /dev/null +++ b/src/drivers/general/queries/IrqlAnnotationIssue/IrqlAnnotationIssue.ql @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/** + * @id cpp/drivers/irql-annotation-issue + * @kind problem + * @name Irql Annotation Issue + * @description The value for an IRQL from annotation could not be evaluated in this context. + * @platform Desktop + * @feature.area Multiple + * @impact Insecure Coding Practice + * @repro.text This warning indicates that the Code Analysis tool cannot interpret the function annotation because the annotation is not + * coded correctly. As a result, the Code Analysis tool cannot determine the specified IRQL value. This warning can occur with any of + * the driver-specific annotations that mention an IRQL when the Code Analysis tool cannot evaluate the expression for the IRQL. + * @owner.email: sdat@microsoft.com + * @opaqueid CQLD-C28153 + * @problem.severity warning + * @precision medium + * @tags correctness + * ca_ported + * @scope domainspecific + * @query-version v1 + */ + +import cpp +import drivers.libraries.Irql + +from IrqlFunctionAnnotation ifa +where not ifa.getIrqlLevel() instanceof IrqlValue +select ifa, "Invalid IRQL annotation: " + ifa.getIrqlLevel() diff --git a/src/drivers/general/queries/IrqlAnnotationIssue/IrqlAnnotationIssue.sarif b/src/drivers/general/queries/IrqlAnnotationIssue/IrqlAnnotationIssue.sarif new file mode 100644 index 00000000..cc8b2c5f --- /dev/null +++ b/src/drivers/general/queries/IrqlAnnotationIssue/IrqlAnnotationIssue.sarif @@ -0,0 +1,377 @@ +{ + "$schema": "https://json.schemastore.org/sarif-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "CodeQL", + "organization": "GitHub", + "semanticVersion": "2.20.3", + "notifications": [ + { + "id": "cpp/baseline/expected-extracted-files", + "name": "cpp/baseline/expected-extracted-files", + "shortDescription": { + "text": "Expected extracted files" + }, + "fullDescription": { + "text": "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration": { + "enabled": true + }, + "properties": { + "tags": [ + "expected-extracted-files", + "telemetry" + ] + } + }, + { + "id": "cpp/extractor/summary", + "name": "cpp/extractor/summary", + "shortDescription": { + "text": "C++ extractor telemetry" + }, + "fullDescription": { + "text": "C++ extractor telemetry" + }, + "defaultConfiguration": { + "enabled": true + } + } + ], + "rules": [ + { + "id": "cpp/drivers/irql-annotation-issue", + "name": "cpp/drivers/irql-annotation-issue", + "shortDescription": { + "text": "Irql Annotation Issue" + }, + "fullDescription": { + "text": "The value for an IRQL from annotation could not be evaluated in this context." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning" + }, + "properties": { + "tags": [ + "correctness" + ], + "description": "The value for an IRQL from annotation could not be evaluated in this context.", + "feature.area": "Multiple", + "id": "cpp/drivers/irql-annotation-issue", + "impact": "Insecure Coding Practice", + "kind": "problem", + "name": "Irql Annotation Issue", + "opaqueid": "CQLD-C28153", + "owner.email:": "sdat@microsoft.com", + "platform": "Desktop", + "precision": "medium", + "problem.severity": "warning", + "query-version": "v1", + "repro.text": "This warning indicates that the Code Analysis tool cannot interpret the function annotation because the annotation is not\n coded correctly. As a result, the Code Analysis tool cannot determine the specified IRQL value. This warning can occur with any of\n the driver-specific annotations that mention an IRQL when the Code Analysis tool cannot evaluate the expression for the IRQL.", + "scope": "domainspecific" + } + } + ] + }, + "extensions": [ + { + "name": "microsoft/windows-drivers", + "semanticVersion": "1.3.0+4f62dc40ad4c6e0c3b6d23bddcf5bf13fbdcdc33", + "locations": [ + { + "uri": "file:///C:/codeql-home/WDDST/src/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///C:/codeql-home/WDDST/src/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + }, + { + "name": "codeql/cpp-all", + "semanticVersion": "3.1.0+d42788844f7ec0a6b9832140313cc2318e513987", + "locations": [ + { + "uri": "file:///C:/Users/jronstadt/.codeql/packages/codeql/cpp-all/3.1.0/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///C:/Users/jronstadt/.codeql/packages/codeql/cpp-all/3.1.0/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + } + ] + }, + "invocations": [ + { + "toolExecutionNotifications": [ + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "message": { + "text": "Internal telemetry for the C++ extractor.\n\nNo action needed.", + "markdown": "Internal telemetry for the C++ extractor.\n\nNo action needed." + }, + "level": "note", + "timeUtc": "2025-01-31T03:05:35.226520Z", + "descriptor": { + "id": "cpp/extractor/summary", + "index": 1 + }, + "properties": { + "attributes": { + "cache-hits": 0, + "cache-misses": 1, + "compilers": [ + { + "program": "cl", + "version": "Microsoft (R) C/C++ Optimizing Compiler Version 19.42.34436 for x64" + } + ], + "extractor-failures": 1, + "extractor-successes": 0, + "trap-caching": "disabled" + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + } + ], + "executionSuccessful": true + } + ], + "artifacts": [ + { + "location": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + }, + { + "location": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + }, + { + "location": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + ], + "results": [ + { + "ruleId": "cpp/drivers/irql-annotation-issue", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-annotation-issue", + "index": 0 + }, + "message": { + "text": "Invalid IRQL annotation: -1" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 50, + "endColumn": 33 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "c485bcb41025b395:1", + "primaryLocationStartColumnFingerprint": "0" + } + }, + { + "ruleId": "cpp/drivers/irql-annotation-issue", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-annotation-issue", + "index": 0 + }, + "message": { + "text": "Invalid IRQL annotation: -1" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 42, + "endColumn": 18 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "df94724e38a83f68:1", + "primaryLocationStartColumnFingerprint": "0" + } + }, + { + "ruleId": "cpp/drivers/irql-annotation-issue", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-annotation-issue", + "index": 0 + }, + "message": { + "text": "Invalid IRQL annotation: 65" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 37, + "endColumn": 20 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "d2e3355b345c7d56:1", + "primaryLocationStartColumnFingerprint": "0" + } + } + ], + "columnKind": "utf16CodeUnits", + "properties": { + "semmle.formatSpecifier": "sarifv2.1.0" + } + } + ] +} \ No newline at end of file diff --git a/src/drivers/general/queries/IrqlAnnotationIssue/driver_snippet.c b/src/drivers/general/queries/IrqlAnnotationIssue/driver_snippet.c new file mode 100644 index 00000000..b7034ef6 --- /dev/null +++ b/src/drivers/general/queries/IrqlAnnotationIssue/driver_snippet.c @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +// Macros to enable or disable a code section that may or may not conflict with this test. +#define SET_DISPATCH 1 +#include + +// Template function. Not used for this test. +void top_level_call() +{ +} + +_IRQL_requires_(DISPATCH_LEVEL) + VOID DoNothing_RequiresDispatch(void) +{ + __noop; +} +_IRQL_raises_(DISPATCH_LEVEL) + VOID IrqlRaiseLevelExplicit_pass(void) +{ + KIRQL oldIRQL; + KeRaiseIrql(APC_LEVEL, &oldIRQL); // Raise level + KeRaiseIrql(DISPATCH_LEVEL, &oldIRQL); // Raise level again + // Function Exits at DISPATCH_LEVEL +} +_IRQL_always_function_max_(PASSIVE_LEVEL) + VOID IrqlRaiseLevelExplicit_pass2(void) +{ + __noop; +} + + +// Mistakes in IRQL annotations + +int irql = 1; + +_IRQL_requires_(65) + VOID DoNothing_RequiresDispatch2(void) +{ + __noop; +} +_IRQL_raises_(-1) + VOID IrqlRaiseLevelExplicit_fail(void) +{ + KIRQL oldIRQL; + KeRaiseIrql(APC_LEVEL, &oldIRQL); // Raise level + KeRaiseIrql(DISPATCH_LEVEL, &oldIRQL); // Raise level again + // Function Exits at DISPATCH_LEVEL +} +_IRQL_always_function_max_(irql) + VOID IrqlRaiseLevelExplicit_fail2(void) +{ + KIRQL oldIRQL; + KeRaiseIrql(DISPATCH_LEVEL, &oldIRQL); +} \ No newline at end of file diff --git a/src/drivers/general/queries/IrqlCancelRoutine/IrqlCancelRoutine.qhelp b/src/drivers/general/queries/IrqlCancelRoutine/IrqlCancelRoutine.qhelp new file mode 100644 index 00000000..bbe722d3 --- /dev/null +++ b/src/drivers/general/queries/IrqlCancelRoutine/IrqlCancelRoutine.qhelp @@ -0,0 +1,42 @@ + + + +

    + Within a cancel routine, at the point of exit, the IRQL in Irp->CancelIrql should be the current IRQL. +

    +
    + +

    + When the driver's Cancel routine exits, the value of the Irp->CancelIrql member is not the current IRQL. + Typically, this error occurs when the driver does not call IoReleaseCancelSpinLock with the IRQL that was supplied by + the most recent call to IoAcquireCancelSpinLock. +

    +
    + +

    + The following example shows an incorrect use of IoReleaseCncelSpinLock within a cancel routine +

    + + +

    + Correct use of IoReleaseCncelSpinLock within a cancel routine +

    + CancelIrql); + }]]> + +
    + +

    +

    +
    + +
  • + + C28144 + +
  • +
    +
    diff --git a/src/drivers/general/queries/IrqlCancelRoutine/IrqlCancelRoutine.ql b/src/drivers/general/queries/IrqlCancelRoutine/IrqlCancelRoutine.ql new file mode 100644 index 00000000..78cfb2b9 --- /dev/null +++ b/src/drivers/general/queries/IrqlCancelRoutine/IrqlCancelRoutine.ql @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/** + * @id cpp/drivers/irql-cancel-routine + * @kind problem + * @name Irql Cancel Routine + * @description Within a cancel routine, at the point of exit, the IRQL in Irp->CancelIrql should be the current IRQL. + * @platform Desktop + * @feature.area Multiple + * @impact Insecure Coding Practice + * @repro.text When the driver's Cancel routine exits, the value of the Irp->CancelIrql member is not the current IRQL. + * Typically, this error occurs when the driver does not call IoReleaseCancelSpinLock with the IRQL that was supplied by + * the most recent call to IoAcquireCancelSpinLock. + * @owner.email: sdat@microsoft.com + * @opaqueid CQLD-C28144 + * @problem.severity warning + * @precision medium + * @tags correctness + * ca_ported + * @scope domainspecific + * @query-version v1 + */ + +import cpp +import drivers.libraries.Irql + +from Function f, FunctionCall fc +where + ( + f.(RoleTypeFunction).getRoleTypeString().matches("DRIVER_CANCEL") or + f.(ImplicitRoleTypeFunction).getExpectedRoleTypeString().matches("DRIVER_CANCEL") + ) and + fc.getEnclosingFunction() = f and + fc.getTarget().getName() = "IoReleaseCancelSpinLock" and + ( + not fc.getArgument(0).(PointerFieldAccess).getQualifier() = f.getParameter(1).getAnAccess() or + not fc.getArgument(0).(PointerFieldAccess).getTarget().getName() = "CancelIrql" + ) +select fc, "IoReleaseCancelSpinLock inside a cancel routine needs to be called with Irp->CancelIrql" diff --git a/src/drivers/general/queries/IrqlCancelRoutine/IrqlCancelRoutine.sarif b/src/drivers/general/queries/IrqlCancelRoutine/IrqlCancelRoutine.sarif new file mode 100644 index 00000000..e8377d62 --- /dev/null +++ b/src/drivers/general/queries/IrqlCancelRoutine/IrqlCancelRoutine.sarif @@ -0,0 +1,312 @@ +{ + "$schema": "https://json.schemastore.org/sarif-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "CodeQL", + "organization": "GitHub", + "semanticVersion": "2.19.3", + "notifications": [ + { + "id": "cpp/baseline/expected-extracted-files", + "name": "cpp/baseline/expected-extracted-files", + "shortDescription": { + "text": "Expected extracted files" + }, + "fullDescription": { + "text": "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration": { + "enabled": true + }, + "properties": { + "tags": [ + "expected-extracted-files", + "telemetry" + ] + } + }, + { + "id": "cpp/extractor/summary", + "name": "cpp/extractor/summary", + "shortDescription": { + "text": "C++ extractor telemetry" + }, + "fullDescription": { + "text": "C++ extractor telemetry" + }, + "defaultConfiguration": { + "enabled": true + } + } + ], + "rules": [ + { + "id": "cpp/drivers/irql-cancel-routine", + "name": "cpp/drivers/irql-cancel-routine", + "shortDescription": { + "text": "Irql Cancel Routine" + }, + "fullDescription": { + "text": "Within a cancel routine, at the point of exit, the IRQL in Irp->CancelIrql should be the current IRQL." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning" + }, + "properties": { + "tags": [ + "correctness" + ], + "description": "Within a cancel routine, at the point of exit, the IRQL in Irp->CancelIrql should be the current IRQL.", + "feature.area": "Multiple", + "id": "cpp/drivers/irql-cancel-routine", + "impact": "Insecure Coding Practice", + "kind": "problem", + "name": "Irql Cancel Routine", + "opaqueid": "CQLD-C28144", + "owner.email:": "sdat@microsoft.com", + "platform": "Desktop", + "precision": "medium", + "problem.severity": "warning", + "query-version": "v1", + "repro.text": "When the driver's Cancel routine exits, the value of the Irp->CancelIrql member is not the current IRQL. \n Typically, this error occurs when the driver does not call IoReleaseCancelSpinLock with the IRQL that was supplied by \n the most recent call to IoAcquireCancelSpinLock.", + "scope": "domainspecific" + } + } + ] + }, + "extensions": [ + { + "name": "microsoft/windows-drivers", + "semanticVersion": "1.3.0+2a7c167ba9555b452f626258191b4709647a936f", + "locations": [ + { + "uri": "file:///C:/codeql-home/WDDST/src/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///C:/codeql-home/WDDST/src/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + }, + { + "name": "codeql/cpp-all", + "semanticVersion": "3.1.0+d42788844f7ec0a6b9832140313cc2318e513987", + "locations": [ + { + "uri": "file:///C:/Users/jronstadt/.codeql/packages/codeql/cpp-all/3.1.0/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///C:/Users/jronstadt/.codeql/packages/codeql/cpp-all/3.1.0/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + } + ] + }, + "invocations": [ + { + "toolExecutionNotifications": [ + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "message": { + "text": "Internal telemetry for the C++ extractor.\n\nNo action needed.", + "markdown": "Internal telemetry for the C++ extractor.\n\nNo action needed." + }, + "level": "note", + "timeUtc": "2025-01-17T07:55:40.432830800Z", + "descriptor": { + "id": "cpp/extractor/summary", + "index": 1 + }, + "properties": { + "attributes": { + "cache-hits": 0, + "cache-misses": 1, + "extractor-failures": 1, + "extractor-successes": 0, + "trap-caching": "disabled" + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + } + ], + "executionSuccessful": true + } + ], + "artifacts": [ + { + "location": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + }, + { + "location": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + }, + { + "location": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + ], + "results": [ + { + "ruleId": "cpp/drivers/irql-cancel-routine", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-cancel-routine", + "index": 0 + }, + "message": { + "text": "IoReleaseCancelSpinLock inside a cancel routine needs to be called with Irp->CancelIrql" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 207, + "startColumn": 5, + "endColumn": 28 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "cb9584c3c973d221:1", + "primaryLocationStartColumnFingerprint": "0" + } + } + ], + "columnKind": "utf16CodeUnits", + "properties": { + "semmle.formatSpecifier": "sarifv2.1.0" + } + } + ] +} \ No newline at end of file diff --git a/src/drivers/general/queries/IrqlCancelRoutine/driver_snippet.c b/src/drivers/general/queries/IrqlCancelRoutine/driver_snippet.c new file mode 100644 index 00000000..8ed3ccc7 --- /dev/null +++ b/src/drivers/general/queries/IrqlCancelRoutine/driver_snippet.c @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +// Macros to enable or disable a code section that may or may not conflict with this test. +#define SET_DISPATCH 1 +// Template function. Not used for this test. +void top_level_call() +{ +} + +#define IRQL_CANCEL_CHECK diff --git a/src/drivers/general/queries/IrqlFloatStateMismatch/IrqlFloatStateMismatch.qhelp b/src/drivers/general/queries/IrqlFloatStateMismatch/IrqlFloatStateMismatch.qhelp new file mode 100644 index 00000000..a41dac90 --- /dev/null +++ b/src/drivers/general/queries/IrqlFloatStateMismatch/IrqlFloatStateMismatch.qhelp @@ -0,0 +1,71 @@ + + + +

    + The IRQL where the floating-point state was saved does not match the current IRQL (for this restore operation). +

    +
    + +

    + The IRQL at which the driver is executing when it restores a floating-point state is different than the IRQL at which it was executing when it saved the floating-point state. + Because the IRQL at which the driver runs determines how the floating-point state is saved, the driver must be executing at the same IRQL when it calls the functions to save and to restore the floating-point state. +

    +
    + +

    + Example of incorrect code. Floating point state was saved at APC_LEVEL but restored at PASSIVE_LEVEL +

    + + +

    + Correct example +

    + + +
    + +

    + TODO notes +

    +
    + +
  • + + Example link + +
  • +
    +
    diff --git a/src/drivers/general/queries/IrqlFloatStateMismatch/IrqlFloatStateMismatch.ql b/src/drivers/general/queries/IrqlFloatStateMismatch/IrqlFloatStateMismatch.ql new file mode 100644 index 00000000..8a40366a --- /dev/null +++ b/src/drivers/general/queries/IrqlFloatStateMismatch/IrqlFloatStateMismatch.ql @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/** + * @id cpp/drivers/irql-float-state-mismatch + * @kind problem + * @name Irql Float State Mismatch + * @description The IRQL where the floating-point state was saved does not match the current IRQL (for this restore operation). + * @platform Desktop + * @feature.area Multiple + * @impact Insecure Coding Practice + * @repro.text The IRQL at which the driver is executing when it restores a floating-point state is different than the IRQL at which it was executing when it saved the floating-point state. + * Because the IRQL at which the driver runs determines how the floating-point state is saved, the driver must be executing at the same IRQL when it calls the functions to save and to restore the floating-point state. + * @owner.email: sdat@microsoft.com + * @opaqueid CQLD-C28111 + * @problem.severity warning + * @precision medium + * @tags correctness + * ca_ported + * @scope domainspecific + * @query-version v1 + */ + +import cpp +import drivers.libraries.Irql +import semmle.code.cpp.dataflow.new.DataFlow + +module FloatStateFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + exists(FunctionCall fc | + fc.getTarget().getName().matches("KeSaveFloatingPointState") and + source.asIndirectExpr() = fc.getArgument(0) + ) + } + + predicate isSink(DataFlow::Node sink) { + exists(FunctionCall fc | + fc.getTarget().getName().matches("KeRestoreFloatingPointState") and + sink.asIndirectExpr() = fc.getArgument(0) + ) + } +} + +module FloatStateFlow = DataFlow::Global; + +from DataFlow::Node source, DataFlow::Node sink, int irqlSink, int irqlSource +where + FloatStateFlow::flow(source, sink) and + irqlSource = getPotentialExitIrqlAtCfn(source.asIndirectExpr()) and + irqlSink = getPotentialExitIrqlAtCfn(sink.asIndirectExpr()) and + irqlSink != irqlSource +select sink.asIndirectExpr(), + "The irql level where the floating-point state was saved (" + irqlSource + + ") does not match the irql level for the restore operation (" + irqlSink + ")." diff --git a/src/drivers/general/queries/IrqlFloatStateMismatch/IrqlFloatStateMismatch.sarif b/src/drivers/general/queries/IrqlFloatStateMismatch/IrqlFloatStateMismatch.sarif new file mode 100644 index 00000000..9379bb24 --- /dev/null +++ b/src/drivers/general/queries/IrqlFloatStateMismatch/IrqlFloatStateMismatch.sarif @@ -0,0 +1,343 @@ +{ + "$schema": "https://json.schemastore.org/sarif-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "CodeQL", + "organization": "GitHub", + "semanticVersion": "2.19.3", + "notifications": [ + { + "id": "cpp/baseline/expected-extracted-files", + "name": "cpp/baseline/expected-extracted-files", + "shortDescription": { + "text": "Expected extracted files" + }, + "fullDescription": { + "text": "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration": { + "enabled": true + }, + "properties": { + "tags": [ + "expected-extracted-files", + "telemetry" + ] + } + }, + { + "id": "cpp/extractor/summary", + "name": "cpp/extractor/summary", + "shortDescription": { + "text": "C++ extractor telemetry" + }, + "fullDescription": { + "text": "C++ extractor telemetry" + }, + "defaultConfiguration": { + "enabled": true + } + } + ], + "rules": [ + { + "id": "cpp/drivers/irql-float-state-mismatch", + "name": "cpp/drivers/irql-float-state-mismatch", + "shortDescription": { + "text": "Irql Float State Mismatch" + }, + "fullDescription": { + "text": "The IRQL where the floating-point state was saved does not match the current IRQL (for this restore operation)." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning" + }, + "properties": { + "tags": [ + "correctness" + ], + "description": "The IRQL where the floating-point state was saved does not match the current IRQL (for this restore operation).", + "feature.area": "Multiple", + "id": "cpp/drivers/irql-float-state-mismatch", + "impact": "Insecure Coding Practice", + "kind": "problem", + "name": "Irql Float State Mismatch", + "opaqueid": "CQLD-C28111", + "owner.email:": "sdat@microsoft.com", + "platform": "Desktop", + "precision": "medium", + "problem.severity": "warning", + "query-version": "v1", + "repro.text": "The IRQL at which the driver is executing when it restores a floating-point state is different than the IRQL at which it was executing when it saved the floating-point state.\n Because the IRQL at which the driver runs determines how the floating-point state is saved, the driver must be executing at the same IRQL when it calls the functions to save and to restore the floating-point state.", + "scope": "domainspecific" + } + } + ] + }, + "extensions": [ + { + "name": "microsoft/windows-drivers", + "semanticVersion": "1.3.0+ffa7244da2c2fe57cdf6260be5d8b90e7c335336", + "locations": [ + { + "uri": "file:///C:/codeql-home/WDDST/src/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///C:/codeql-home/WDDST/src/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + }, + { + "name": "codeql/cpp-all", + "semanticVersion": "3.1.0+d42788844f7ec0a6b9832140313cc2318e513987", + "locations": [ + { + "uri": "file:///C:/Users/jronstadt/.codeql/packages/codeql/cpp-all/3.1.0/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///C:/Users/jronstadt/.codeql/packages/codeql/cpp-all/3.1.0/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + } + ] + }, + "invocations": [ + { + "toolExecutionNotifications": [ + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "message": { + "text": "Internal telemetry for the C++ extractor.\n\nNo action needed.", + "markdown": "Internal telemetry for the C++ extractor.\n\nNo action needed." + }, + "level": "note", + "timeUtc": "2025-01-17T05:02:26.230274500Z", + "descriptor": { + "id": "cpp/extractor/summary", + "index": 1 + }, + "properties": { + "attributes": { + "cache-hits": 0, + "cache-misses": 1, + "extractor-failures": 1, + "extractor-successes": 0, + "trap-caching": "disabled" + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + } + ], + "executionSuccessful": true + } + ], + "artifacts": [ + { + "location": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + }, + { + "location": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + }, + { + "location": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + ], + "results": [ + { + "ruleId": "cpp/drivers/irql-float-state-mismatch", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-float-state-mismatch", + "index": 0 + }, + "message": { + "text": "The irql level where the floating-point state was saved (1) does not match the irql level for the restore operation (0)." + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 23, + "startColumn": 38, + "endColumn": 46 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "b3909ff165022b51:1", + "primaryLocationStartColumnFingerprint": "29" + } + }, + { + "ruleId": "cpp/drivers/irql-float-state-mismatch", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-float-state-mismatch", + "index": 0 + }, + "message": { + "text": "The irql level where the floating-point state was saved (1) does not match the irql level for the restore operation (0)." + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 23, + "startColumn": 37, + "endColumn": 46 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "b3909ff165022b51:1", + "primaryLocationStartColumnFingerprint": "28" + } + } + ], + "columnKind": "utf16CodeUnits", + "properties": { + "semmle.formatSpecifier": "sarifv2.1.0" + } + } + ] +} \ No newline at end of file diff --git a/src/drivers/general/queries/IrqlFloatStateMismatch/driver_snippet.c b/src/drivers/general/queries/IrqlFloatStateMismatch/driver_snippet.c new file mode 100644 index 00000000..c16d0ce1 --- /dev/null +++ b/src/drivers/general/queries/IrqlFloatStateMismatch/driver_snippet.c @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +// Macros to enable or disable a code section that may or may not conflict with this test. +#define SET_DISPATCH 1 + +// Template function. Not used for this test. +void top_level_call() +{ +} + +_IRQL_requires_(PASSIVE_LEVEL) +void driver_utility_bad(void) +{ + KIRQL oldIRQL; + KeRaiseIrql(APC_LEVEL, &oldIRQL); + // running at APC level + KFLOATING_SAVE FloatBuf; + if (KeSaveFloatingPointState(&FloatBuf)) + { + KeLowerIrql(oldIRQL); // lower back to PASSIVE_LEVEL + // ... + KeRestoreFloatingPointState(&FloatBuf); + } +} + +_IRQL_requires_(PASSIVE_LEVEL) +void driver_utility_good(void) +{ + // running at APC level + KFLOATING_SAVE FloatBuf; + KIRQL oldIRQL; + KeRaiseIrql(APC_LEVEL, &oldIRQL); + + if (KeSaveFloatingPointState(&FloatBuf)) + { + KeLowerIrql(oldIRQL); + // ... + KeRaiseIrql(APC_LEVEL, &oldIRQL); + KeRestoreFloatingPointState(&FloatBuf); + } +} diff --git a/src/drivers/general/queries/IrqlFunctionNotAnnotated/IrqlFunctionNotAnnotated.qhelp b/src/drivers/general/queries/IrqlFunctionNotAnnotated/IrqlFunctionNotAnnotated.qhelp new file mode 100644 index 00000000..4bca85d4 --- /dev/null +++ b/src/drivers/general/queries/IrqlFunctionNotAnnotated/IrqlFunctionNotAnnotated.qhelp @@ -0,0 +1,46 @@ + + + +

    + The function changes the IRQL and does not restore the IRQL before it exits. It should be annotated to reflect the change or the IRQL should be restored. +

    +
    + +

    + This warning indicates that the following conditions are true: + 1. The function changes the IRQL at which the driver is running. + 2. There is at least one path through a function that does not, by function exit, restore the IRQL to the original IRQL that the driver was running at function entry. +

    +
    + +

    + Function which potentially raises the IRQL level but is not annotated to reflect the change. +

    + + +
    + +

    +

    +
    + +
  • + + C28167 + +
  • +
    +
    diff --git a/src/drivers/general/queries/IrqlFunctionNotAnnotated/IrqlFunctionNotAnnotated.ql b/src/drivers/general/queries/IrqlFunctionNotAnnotated/IrqlFunctionNotAnnotated.ql new file mode 100644 index 00000000..2664b366 --- /dev/null +++ b/src/drivers/general/queries/IrqlFunctionNotAnnotated/IrqlFunctionNotAnnotated.ql @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/** + * @id cpp/drivers/irql-function-not-annotated + * @kind problem + * @name Irql Function Not Annotated + * @description The function changes the IRQL and does not restore the IRQL before it exits. It should be annotated to reflect the change or the IRQL should be restored. + * @platform Desktop + * @feature.area Multiple + * @impact Insecure Coding Practice + * @repro.text This warning occurs when an IRQL annotation on a function is required, but one doesn't exist. + * @owner.email: sdat@microsoft.com + * @opaqueid CQLD-C28167 + * @problem.severity warning + * @precision medium + * @tags correctness + * ca_ported + * @scope domainspecific + * @query-version v1 + */ + +import cpp +import drivers.libraries.Irql + +from + Function f, int irqlLevelEntry, int irqlLevelExit, ControlFlowNode exitCfn, + ControlFlowNode entryCfn +where + not f instanceof IrqlChangesFunction and + exists(FunctionCall fc | + fc.getTarget() instanceof IrqlChangesFunction and fc.getEnclosingFunction() = f + ) and + exitCfn = f.getControlFlowScope() and + entryCfn = f.getBlock() and + irqlLevelEntry = getPotentialExitIrqlAtCfn(entryCfn) and + irqlLevelExit = getPotentialExitIrqlAtCfn(exitCfn) and + irqlLevelEntry != irqlLevelExit +select f, "Function potentially changes the IRQL without restoring it to the original level, however, the function is not annotated to reflect such a change." diff --git a/src/drivers/general/queries/IrqlFunctionNotAnnotated/IrqlFunctionNotAnnotated.sarif b/src/drivers/general/queries/IrqlFunctionNotAnnotated/IrqlFunctionNotAnnotated.sarif new file mode 100644 index 00000000..d43c6190 --- /dev/null +++ b/src/drivers/general/queries/IrqlFunctionNotAnnotated/IrqlFunctionNotAnnotated.sarif @@ -0,0 +1,432 @@ +{ + "$schema": "https://json.schemastore.org/sarif-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "CodeQL", + "organization": "GitHub", + "semanticVersion": "2.19.3", + "notifications": [ + { + "id": "cpp/baseline/expected-extracted-files", + "name": "cpp/baseline/expected-extracted-files", + "shortDescription": { + "text": "Expected extracted files" + }, + "fullDescription": { + "text": "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration": { + "enabled": true + }, + "properties": { + "tags": [ + "expected-extracted-files", + "telemetry" + ] + } + }, + { + "id": "cpp/extractor/summary", + "name": "cpp/extractor/summary", + "shortDescription": { + "text": "C++ extractor telemetry" + }, + "fullDescription": { + "text": "C++ extractor telemetry" + }, + "defaultConfiguration": { + "enabled": true + } + } + ], + "rules": [ + { + "id": "cpp/drivers/irql-function-not-annotated", + "name": "cpp/drivers/irql-function-not-annotated", + "shortDescription": { + "text": "Irql Function Not Annotated" + }, + "fullDescription": { + "text": "The function changes the IRQL and does not restore the IRQL before it exits. It should be annotated to reflect the change or the IRQL should be restored." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning" + }, + "properties": { + "tags": [ + "correctness" + ], + "description": "The function changes the IRQL and does not restore the IRQL before it exits. It should be annotated to reflect the change or the IRQL should be restored.", + "feature.area": "Multiple", + "id": "cpp/drivers/irql-function-not-annotated", + "impact": "Insecure Coding Practice", + "kind": "problem", + "name": "Irql Function Not Annotated", + "opaqueid": "CQLD-TODO", + "owner.email:": "sdat@microsoft.com", + "platform": "Desktop", + "precision": "medium", + "problem.severity": "warning", + "query-version": "v1", + "repro.text": "", + "scope": "domainspecific" + } + } + ] + }, + "extensions": [ + { + "name": "microsoft/windows-drivers", + "semanticVersion": "1.3.0+2a7c167ba9555b452f626258191b4709647a936f", + "locations": [ + { + "uri": "file:///C:/codeql-home/WDDST/src/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///C:/codeql-home/WDDST/src/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + }, + { + "name": "codeql/cpp-all", + "semanticVersion": "3.1.0+d42788844f7ec0a6b9832140313cc2318e513987", + "locations": [ + { + "uri": "file:///C:/Users/jronstadt/.codeql/packages/codeql/cpp-all/3.1.0/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///C:/Users/jronstadt/.codeql/packages/codeql/cpp-all/3.1.0/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + } + ] + }, + "invocations": [ + { + "toolExecutionNotifications": [ + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "message": { + "text": "Internal telemetry for the C++ extractor.\n\nNo action needed.", + "markdown": "Internal telemetry for the C++ extractor.\n\nNo action needed." + }, + "level": "note", + "timeUtc": "2025-01-16T06:28:21.752771Z", + "descriptor": { + "id": "cpp/extractor/summary", + "index": 1 + }, + "properties": { + "attributes": { + "cache-hits": 0, + "cache-misses": 1, + "extractor-failures": 1, + "extractor-successes": 0, + "trap-caching": "disabled" + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + } + ], + "executionSuccessful": true + } + ], + "artifacts": [ + { + "location": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + }, + { + "location": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + }, + { + "location": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + ], + "results": [ + { + "ruleId": "cpp/drivers/irql-function-not-annotated", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-function-not-annotated", + "index": 0 + }, + "message": { + "text": "Function potentially changes the IRQL without restoring it to the original level, however, the function is not annotated to reflect such a change." + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 303, + "endColumn": 18 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "9f98ff813d1498bc:1", + "primaryLocationStartColumnFingerprint": "0" + } + }, + { + "ruleId": "cpp/drivers/irql-function-not-annotated", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-function-not-annotated", + "index": 0 + }, + "message": { + "text": "Function potentially changes the IRQL without restoring it to the original level, however, the function is not annotated to reflect such a change." + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 246, + "endColumn": 22 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "8eddd6a4558a2051:1", + "primaryLocationStartColumnFingerprint": "0" + } + }, + { + "ruleId": "cpp/drivers/irql-function-not-annotated", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-function-not-annotated", + "index": 0 + }, + "message": { + "text": "Function potentially changes the IRQL without restoring it to the original level, however, the function is not annotated to reflect such a change." + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 1 + }, + "region": { + "startLine": 13, + "startColumn": 6, + "endColumn": 11 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "9a255999974eed8:1", + "primaryLocationStartColumnFingerprint": "5" + } + }, + { + "ruleId": "cpp/drivers/irql-function-not-annotated", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-function-not-annotated", + "index": 0 + }, + "message": { + "text": "Function potentially changes the IRQL without restoring it to the original level, however, the function is not annotated to reflect such a change." + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 285, + "endColumn": 12 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "b6acb5a643d7209f:1", + "primaryLocationStartColumnFingerprint": "0" + } + }, + { + "ruleId": "cpp/drivers/irql-function-not-annotated", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-function-not-annotated", + "index": 0 + }, + "message": { + "text": "Function potentially changes the IRQL without restoring it to the original level, however, the function is not annotated to reflect such a change." + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 202, + "endColumn": 13 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "85d63309bc40267f:1", + "primaryLocationStartColumnFingerprint": "0" + } + } + ], + "columnKind": "utf16CodeUnits", + "properties": { + "semmle.formatSpecifier": "sarifv2.1.0" + } + } + ] +} \ No newline at end of file diff --git a/src/drivers/general/queries/IrqlFunctionNotAnnotated/driver_snippet.c b/src/drivers/general/queries/IrqlFunctionNotAnnotated/driver_snippet.c new file mode 100644 index 00000000..b0449856 --- /dev/null +++ b/src/drivers/general/queries/IrqlFunctionNotAnnotated/driver_snippet.c @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +// Macros to enable or disable a code section that may or may not conflict with this test. +#define SET_DISPATCH 1 + +// Template function. Not used for this test. +void top_level_call() +{ +} + + +void fail1(PKIRQL oldIrql) +{ + + if (oldIrql == PASSIVE_LEVEL) + { + KeLowerIrql(*oldIrql); + } + else + { + KeRaiseIrql(DISPATCH_LEVEL, oldIrql); // Function exits at DISPATCH_LEVEL + } +} + +_IRQL_raises_(DISPATCH_LEVEL) +void pass(PKIRQL oldIrql) +{ + + if (oldIrql == PASSIVE_LEVEL) + { + KeLowerIrql(*oldIrql); + } + KeRaiseIrql(DISPATCH_LEVEL, oldIrql); // Function exits at DISPATCH_LEVEL +} \ No newline at end of file diff --git a/src/drivers/general/queries/IrqlIllegalValue/IrqlIllegalValue.qhelp b/src/drivers/general/queries/IrqlIllegalValue/IrqlIllegalValue.qhelp new file mode 100644 index 00000000..62e75eef --- /dev/null +++ b/src/drivers/general/queries/IrqlIllegalValue/IrqlIllegalValue.qhelp @@ -0,0 +1,53 @@ + + + +

    + The value is not a legal value for an IRQL +

    +
    + +

    + The IRQL should be within the range of valid values for an IRQL (0-31). +

    +
    + +

    + Example of a function with an OK IRQL requirement. +

    + + +

    + Exmaple of a function with an IRQL requirement that is too high. +

    + + +
    + +

    + +

    +
    + +
  • + + Warning C28151 + +
  • +
    +
    diff --git a/src/drivers/general/queries/IrqlIllegalValue/IrqlIllegalValue.ql b/src/drivers/general/queries/IrqlIllegalValue/IrqlIllegalValue.ql new file mode 100644 index 00000000..5048295b --- /dev/null +++ b/src/drivers/general/queries/IrqlIllegalValue/IrqlIllegalValue.ql @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/** + * @id cpp/drivers/irql-illegal-value + * @kind problem + * @name Irql Illegal Value + * @description The value is not a legal value for an IRQL (port of C28151) + * @platform Desktop + * @feature.area Multiple + * @impact Insecure Coding Practice + * @repro.text + * @owner.email: sdat@microsoft.com + * @opaqueid CQLD-C28151 + * @problem.severity warning + * @precision medium + * @tags correctness + * ca_ported + * @scope domainspecific + * @query-version v1 + */ + +import cpp +import drivers.libraries.Irql + +from IrqlFunctionAnnotation a +where a.getIrqlLevel() > 31 +select a, "The value is not a legal value for an IRQL" diff --git a/src/drivers/general/queries/IrqlIllegalValue/IrqlIllegalValue.sarif b/src/drivers/general/queries/IrqlIllegalValue/IrqlIllegalValue.sarif new file mode 100644 index 00000000..39e585a6 --- /dev/null +++ b/src/drivers/general/queries/IrqlIllegalValue/IrqlIllegalValue.sarif @@ -0,0 +1,273 @@ +{ + "$schema": "https://json.schemastore.org/sarif-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "CodeQL", + "organization": "GitHub", + "semanticVersion": "2.17.6", + "notifications": [ + { + "id": "cpp/baseline/expected-extracted-files", + "name": "cpp/baseline/expected-extracted-files", + "shortDescription": { + "text": "Expected extracted files" + }, + "fullDescription": { + "text": "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration": { + "enabled": true + }, + "properties": { + "tags": [ + "expected-extracted-files", + "telemetry" + ] + } + }, + { + "id": "cpp/extractor/summary", + "name": "cpp/extractor/summary", + "shortDescription": { + "text": "C++ extractor telemetry" + }, + "fullDescription": { + "text": "C++ extractor telemetry" + }, + "defaultConfiguration": { + "enabled": true + } + } + ], + "rules": [ + { + "id": "cpp/drivers/irql-illegal-value", + "name": "cpp/drivers/irql-illegal-value", + "shortDescription": { + "text": "Irql Illegal Value" + }, + "fullDescription": { + "text": "The value is not a legal value for an IRQL (port of C28151)" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning" + }, + "properties": { + "tags": [ + "correctness" + ], + "description": "The value is not a legal value for an IRQL (port of C28151)", + "feature.area": "Multiple", + "id": "cpp/drivers/irql-illegal-value", + "impact": "Insecure Coding Practice", + "kind": "problem", + "name": "Irql Illegal Value", + "opaqueid": "CQLD-C28151", + "owner.email:": "sdat@microsoft.com", + "platform": "Desktop", + "precision": "medium", + "problem.severity": "warning", + "query-version": "v1", + "repro.text": "", + "scope": "domainspecific" + } + } + ] + }, + "extensions": [ + { + "name": "microsoft/windows-drivers", + "semanticVersion": "1.1.0+2034189b0eace12539d3b51ced8d39b9cc9717f0", + "locations": [ + { + "uri": "file:///C:/codeql-home/WDDST/src/", + "description": { + "text": "The QL pack root directory." + } + }, + { + "uri": "file:///C:/codeql-home/WDDST/src/qlpack.yml", + "description": { + "text": "The QL pack definition file." + } + } + ] + } + ] + }, + "invocations": [ + { + "toolExecutionNotifications": [ + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "message": { + "text": "Internal telemetry for the C++ extractor.\n\nNo action needed.", + "markdown": "Internal telemetry for the C++ extractor.\n\nNo action needed." + }, + "level": "note", + "timeUtc": "2024-08-16T06:02:00.862+00:00", + "descriptor": { + "id": "cpp/extractor/summary", + "index": 1 + }, + "properties": { + "attributes": { + "cache-hits": 0, + "cache-misses": 1, + "extractor-failures": 1, + "extractor-successes": 0, + "trap-caching": "disabled" + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + } + ], + "executionSuccessful": true + } + ], + "artifacts": [ + { + "location": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + }, + { + "location": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + }, + { + "location": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + ], + "results": [ + { + "ruleId": "cpp/drivers/irql-illegal-value", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-illegal-value", + "index": 0 + }, + "message": { + "text": "The value is not a legal value for an IRQL" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 19, + "endColumn": 20 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "e23fd8d8633c67eb:1", + "primaryLocationStartColumnFingerprint": "0" + } + } + ], + "columnKind": "utf16CodeUnits", + "properties": { + "semmle.formatSpecifier": "sarifv2.1.0" + } + } + ] +} \ No newline at end of file diff --git a/src/drivers/general/queries/IrqlIllegalValue/driver_snippet.c b/src/drivers/general/queries/IrqlIllegalValue/driver_snippet.c new file mode 100644 index 00000000..520e7358 --- /dev/null +++ b/src/drivers/general/queries/IrqlIllegalValue/driver_snippet.c @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +// Macros to enable or disable a code section that may or may not conflict with this test. +#define SET_DISPATCH 1 + +// Template function. Not used for this test. +void top_level_call() +{ +} + +_IRQL_requires_(PASSIVE_LEVEL) + VOID DoNothing_RequiresPassive(void) +{ + __noop; +} + +// This function has an IRQL requirement that is too high. +_IRQL_requires_(42) + VOID DoNothing_bad(void) +{ + __noop; +} diff --git a/src/drivers/general/queries/IrqlInconsistentWithRequired/IrqlInconsistentWithRequired.qhelp b/src/drivers/general/queries/IrqlInconsistentWithRequired/IrqlInconsistentWithRequired.qhelp new file mode 100644 index 00000000..7742155a --- /dev/null +++ b/src/drivers/general/queries/IrqlInconsistentWithRequired/IrqlInconsistentWithRequired.qhelp @@ -0,0 +1,44 @@ + + + +

    + The actual IRQL is inconsistent with the required IRQL +

    +
    + +

    + An _IRQL_requires_same_ annotation specifies that the driver should be executing at a particular IRQL when the function completes, but there is at least one path in which the driver is executing at a different IRQL when the function completes. +

    +
    + +

    + Function annotated with _IRQL_requires_same_ but can possibly exit at a different IRQL level. +

    + + +
    + +

    +

    +
    + +
  • + + C28166 + +
  • +
    +
    diff --git a/src/drivers/general/queries/IrqlInconsistentWithRequired/IrqlInconsistentWithRequired.ql b/src/drivers/general/queries/IrqlInconsistentWithRequired/IrqlInconsistentWithRequired.ql new file mode 100644 index 00000000..bf11a6ba --- /dev/null +++ b/src/drivers/general/queries/IrqlInconsistentWithRequired/IrqlInconsistentWithRequired.ql @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/** + * @id cpp/drivers/irql-inconsistent-with-required + * @kind problem + * @name Irql Inconsistent With Required + * @description The actual IRQL is inconsistent with the required IRQL + * @platform Desktop + * @feature.area Multiple + * @impact Insecure Coding Practice + * @repro.text An _IRQL_requires_same_ annotation specifies that the driver should be executing at a particular IRQL when the function completes, but there is at least one path in which the driver is executing at a different IRQL when the function completes. + * @owner.email: sdat@microsoft.com + * @opaqueid CQLD-C28166 + * @problem.severity warning + * @precision medium + * @tags correctness + * ca_ported + * @scope domainspecific + * @query-version v1 + */ + +import cpp +import drivers.libraries.Irql + +from + IrqlRequiresSameAnnotatedFunction f, int irqlLevelEntry, int irqlLevelExit, + ControlFlowNode exitCfn, ControlFlowNode entryCfn +where + exitCfn = f.getControlFlowScope() and + entryCfn = f.getBlock() and + irqlLevelEntry = getPotentialExitIrqlAtCfn(entryCfn) and + irqlLevelExit = getPotentialExitIrqlAtCfn(exitCfn) and + irqlLevelEntry != irqlLevelExit +select f, + "Possible IRQL level at function completion inconsistent with the required IRQL level for some path. Irql level expected: " + + irqlLevelEntry + ". Irql level found: " + irqlLevelExit + + ". Review the IRQL level of the function." \ No newline at end of file diff --git a/src/drivers/general/queries/IrqlInconsistentWithRequired/IrqlInconsistentWithRequired.sarif b/src/drivers/general/queries/IrqlInconsistentWithRequired/IrqlInconsistentWithRequired.sarif new file mode 100644 index 00000000..22ffbaa0 --- /dev/null +++ b/src/drivers/general/queries/IrqlInconsistentWithRequired/IrqlInconsistentWithRequired.sarif @@ -0,0 +1,318 @@ +{ + "$schema": "https://json.schemastore.org/sarif-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "CodeQL", + "organization": "GitHub", + "semanticVersion": "2.20.3", + "notifications": [ + { + "id": "cpp/baseline/expected-extracted-files", + "name": "cpp/baseline/expected-extracted-files", + "shortDescription": { + "text": "Expected extracted files" + }, + "fullDescription": { + "text": "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration": { + "enabled": true + }, + "properties": { + "tags": [ + "expected-extracted-files", + "telemetry" + ] + } + }, + { + "id": "cpp/extractor/summary", + "name": "cpp/extractor/summary", + "shortDescription": { + "text": "C++ extractor telemetry" + }, + "fullDescription": { + "text": "C++ extractor telemetry" + }, + "defaultConfiguration": { + "enabled": true + } + } + ], + "rules": [ + { + "id": "cpp/drivers/irql-inconsistent-with-required", + "name": "cpp/drivers/irql-inconsistent-with-required", + "shortDescription": { + "text": "Irql Inconsistent With Required" + }, + "fullDescription": { + "text": "The actual IRQL is inconsistent with the required IRQL" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning" + }, + "properties": { + "tags": [ + "correctness" + ], + "description": "The actual IRQL is inconsistent with the required IRQL", + "feature.area": "Multiple", + "id": "cpp/drivers/irql-inconsistent-with-required", + "impact": "Insecure Coding Practice", + "kind": "problem", + "name": "Irql Inconsistent With Required", + "opaqueid": "CQLD-C28166", + "owner.email:": "sdat@microsoft.com", + "platform": "Desktop", + "precision": "medium", + "problem.severity": "warning", + "query-version": "v1", + "repro.text": "An _IRQL_requires_same_ annotation specifies that the driver should be executing at a particular IRQL when the function completes, but there is at least one path in which the driver is executing at a different IRQL when the function completes.", + "scope": "domainspecific" + } + } + ] + }, + "extensions": [ + { + "name": "microsoft/windows-drivers", + "semanticVersion": "1.3.0+3d5524ebd603638cbf8a5a8990968f05c797a9cf", + "locations": [ + { + "uri": "file:///C:/codeql-home/WDDST/src/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///C:/codeql-home/WDDST/src/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + }, + { + "name": "codeql/cpp-all", + "semanticVersion": "3.1.0+d42788844f7ec0a6b9832140313cc2318e513987", + "locations": [ + { + "uri": "file:///C:/Users/jronstadt/.codeql/packages/codeql/cpp-all/3.1.0/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///C:/Users/jronstadt/.codeql/packages/codeql/cpp-all/3.1.0/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + } + ] + }, + "invocations": [ + { + "toolExecutionNotifications": [ + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "message": { + "text": "Internal telemetry for the C++ extractor.\n\nNo action needed.", + "markdown": "Internal telemetry for the C++ extractor.\n\nNo action needed." + }, + "level": "note", + "timeUtc": "2025-01-31T03:10:53.741898200Z", + "descriptor": { + "id": "cpp/extractor/summary", + "index": 1 + }, + "properties": { + "attributes": { + "cache-hits": 0, + "cache-misses": 1, + "compilers": [ + { + "program": "cl", + "version": "Microsoft (R) C/C++ Optimizing Compiler Version 19.42.34436 for x64" + } + ], + "extractor-failures": 1, + "extractor-successes": 0, + "trap-caching": "disabled" + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + } + ], + "executionSuccessful": true + } + ], + "artifacts": [ + { + "location": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + }, + { + "location": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + }, + { + "location": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + ], + "results": [ + { + "ruleId": "cpp/drivers/irql-inconsistent-with-required", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-inconsistent-with-required", + "index": 0 + }, + "message": { + "text": "Possible IRQL level at function completion inconsistent with the required IRQL level for some path. Irql level expected: 0. Irql level found: 2. Review the IRQL level of the function." + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 11, + "startColumn": 27, + "endColumn": 32 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "e8222ed04cfffe33:1", + "primaryLocationStartColumnFingerprint": "26" + } + } + ], + "columnKind": "utf16CodeUnits", + "properties": { + "semmle.formatSpecifier": "sarifv2.1.0" + } + } + ] +} \ No newline at end of file diff --git a/src/drivers/general/queries/IrqlInconsistentWithRequired/driver_snippet.c b/src/drivers/general/queries/IrqlInconsistentWithRequired/driver_snippet.c new file mode 100644 index 00000000..163e7b99 --- /dev/null +++ b/src/drivers/general/queries/IrqlInconsistentWithRequired/driver_snippet.c @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +// Macros to enable or disable a code section that may or maynot conflict with this test. +#define SET_DISPATCH 1 + +void top_level_call() +{ +} + +_IRQL_requires_same_ void fail1(PKIRQL oldIrql) +{ + + if (oldIrql == PASSIVE_LEVEL) + { + KeLowerIrql(*oldIrql); + } + else + { + KeRaiseIrql(DISPATCH_LEVEL, oldIrql); // Function exits at DISPATCH_LEVEL + } +} + +_IRQL_requires_same_ + NTSTATUS + pass1(PKIRQL oldIrql) +{ + KeRaiseIrql(DISPATCH_LEVEL, oldIrql); + KeLowerIrql(*oldIrql); + return STATUS_SUCCESS; +} \ No newline at end of file diff --git a/src/drivers/general/queries/IrqlLoweredImproperly/IrqlLoweredImproperly.qhelp b/src/drivers/general/queries/IrqlLoweredImproperly/IrqlLoweredImproperly.qhelp new file mode 100644 index 00000000..ea06606d --- /dev/null +++ b/src/drivers/general/queries/IrqlLoweredImproperly/IrqlLoweredImproperly.qhelp @@ -0,0 +1,42 @@ + + + +

    + The argument causes the IRQ Level to be set below the current IRQL, and this function cannot be used for that purpose +

    +
    + +

    + A function call that lowers the IRQL at which a caller is executing is being used inappropriately. Typically, the function call lowers the IRQL as part of a more general routine or is intended to raise the caller's IRQL. +

    +
    + +

    + The following code example elicits this warning. +

    + + +

    + The following code example avoids this warning. +

    + + +
    + +

    +

    +
    + +
  • + + C28141 + +
  • +
    +
    diff --git a/src/drivers/general/queries/IrqlLoweredImproperly/IrqlLoweredImproperly.ql b/src/drivers/general/queries/IrqlLoweredImproperly/IrqlLoweredImproperly.ql new file mode 100644 index 00000000..41bc12e5 --- /dev/null +++ b/src/drivers/general/queries/IrqlLoweredImproperly/IrqlLoweredImproperly.ql @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/** + * @id cpp/drivers/irql-lowered-improperly + * @kind problem + * @name IRQL Lowered Improperly + * @description A function being called changes the IRQL to below the current IRQL, and the function is not intended for that purpose. + * @platform Desktop + * @feature.area Multiple + * @impact Insecure Coding Practice + * @repro.text + * @owner.email: sdat@microsoft.com + * @opaqueid CQLD-C28141 + * @problem.severity warning + * @precision medium + * @tags correctness + * ca_ported + * @scope domainspecific + * @query-version v1 + */ + +import cpp +import drivers.libraries.Irql + +from KeRaiseIrqlCall call, ControlFlowNode prior, int irqlRequirement +where + prior = call.getAPredecessor() and + irqlRequirement = call.getIrqlLevel() and + irqlRequirement != -1 and + irqlRequirement < min(getPotentialExitIrqlAtCfn(prior)) +select call,"$@: The function being called changes the IRQL to below the current IRQL, and the function is not intended for that purpose.", + call, call.toString() \ No newline at end of file diff --git a/src/drivers/general/queries/IrqlLoweredImproperly/IrqlLoweredImproperly.sarif b/src/drivers/general/queries/IrqlLoweredImproperly/IrqlLoweredImproperly.sarif new file mode 100644 index 00000000..cd08ab99 --- /dev/null +++ b/src/drivers/general/queries/IrqlLoweredImproperly/IrqlLoweredImproperly.sarif @@ -0,0 +1,338 @@ +{ + "$schema": "https://json.schemastore.org/sarif-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "CodeQL", + "organization": "GitHub", + "semanticVersion": "2.20.3", + "notifications": [ + { + "id": "cpp/baseline/expected-extracted-files", + "name": "cpp/baseline/expected-extracted-files", + "shortDescription": { + "text": "Expected extracted files" + }, + "fullDescription": { + "text": "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration": { + "enabled": true + }, + "properties": { + "tags": [ + "expected-extracted-files", + "telemetry" + ] + } + }, + { + "id": "cpp/extractor/summary", + "name": "cpp/extractor/summary", + "shortDescription": { + "text": "C++ extractor telemetry" + }, + "fullDescription": { + "text": "C++ extractor telemetry" + }, + "defaultConfiguration": { + "enabled": true + } + } + ], + "rules": [ + { + "id": "cpp/drivers/irql-lowered-improperly", + "name": "cpp/drivers/irql-lowered-improperly", + "shortDescription": { + "text": "IRQL Lowered Improperly" + }, + "fullDescription": { + "text": "A function being called changes the IRQL to below the current IRQL, and the function is not intended for that purpose." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning" + }, + "properties": { + "tags": [ + "correctness" + ], + "description": "A function being called changes the IRQL to below the current IRQL, and the function is not intended for that purpose.", + "feature.area": "Multiple", + "id": "cpp/drivers/irql-lowered-improperly", + "impact": "Insecure Coding Practice", + "kind": "problem", + "name": "IRQL Lowered Improperly", + "opaqueid": "CQLD-C28141", + "owner.email:": "sdat@microsoft.com", + "platform": "Desktop", + "precision": "medium", + "problem.severity": "warning", + "query-version": "v1", + "repro.text": "", + "scope": "domainspecific" + } + } + ] + }, + "extensions": [ + { + "name": "microsoft/windows-drivers", + "semanticVersion": "1.3.0+a9c818955ea6a723328a7e157691b69535d9b4d0", + "locations": [ + { + "uri": "file:///C:/codeql-home/WDDST/src/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///C:/codeql-home/WDDST/src/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + }, + { + "name": "codeql/cpp-all", + "semanticVersion": "3.1.0+d42788844f7ec0a6b9832140313cc2318e513987", + "locations": [ + { + "uri": "file:///C:/Users/jronstadt/.codeql/packages/codeql/cpp-all/3.1.0/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///C:/Users/jronstadt/.codeql/packages/codeql/cpp-all/3.1.0/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + } + ] + }, + "invocations": [ + { + "toolExecutionNotifications": [ + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "message": { + "text": "Internal telemetry for the C++ extractor.\n\nNo action needed.", + "markdown": "Internal telemetry for the C++ extractor.\n\nNo action needed." + }, + "level": "note", + "timeUtc": "2025-01-31T02:39:27.052464300Z", + "descriptor": { + "id": "cpp/extractor/summary", + "index": 1 + }, + "properties": { + "attributes": { + "cache-hits": 0, + "cache-misses": 1, + "compilers": [ + { + "program": "cl", + "version": "Microsoft (R) C/C++ Optimizing Compiler Version 19.42.34436 for x64" + } + ], + "extractor-failures": 1, + "extractor-successes": 0, + "trap-caching": "disabled" + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + } + ], + "executionSuccessful": true + } + ], + "artifacts": [ + { + "location": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + }, + { + "location": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + }, + { + "location": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + ], + "results": [ + { + "ruleId": "cpp/drivers/irql-lowered-improperly", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-lowered-improperly", + "index": 0 + }, + "message": { + "text": "[call to KfRaiseIrql](1): The function being called changes the IRQL to below the current IRQL, and the function is not intended for that purpose." + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 21, + "startColumn": 5, + "endColumn": 41 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "8458b7bb75b39595:1", + "primaryLocationStartColumnFingerprint": "0" + }, + "relatedLocations": [ + { + "id": 1, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 21, + "startColumn": 5, + "endColumn": 41 + } + }, + "message": { + "text": "call to KfRaiseIrql" + } + } + ] + } + ], + "columnKind": "utf16CodeUnits", + "properties": { + "semmle.formatSpecifier": "sarifv2.1.0" + } + } + ] +} \ No newline at end of file diff --git a/src/drivers/general/queries/IrqlLoweredImproperly/driver_snippet.c b/src/drivers/general/queries/IrqlLoweredImproperly/driver_snippet.c new file mode 100644 index 00000000..125c1eef --- /dev/null +++ b/src/drivers/general/queries/IrqlLoweredImproperly/driver_snippet.c @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +// +// driver_snippet.c +// + +#define SET_DISPATCH 1 + +// Template. Not called in this test. +void top_level_call() {} + +#include + +/* + */ +_IRQL_raises_(DISPATCH_LEVEL) + VOID IrqlRaiseLevelExplicit_fail(void) +{ + KIRQL oldIRQL; + KeRaiseIrql(DISPATCH_LEVEL, &oldIRQL); + KeRaiseIrql(PASSIVE_LEVEL, &oldIRQL); // raise the IRQL to PASSIVE_LEVEL, which is lower than DISPATCH_LEVEL +} +/* +Function can be called to raise the IRQL but needs to exit at DISPATCH_LEVEL. +*/ +_IRQL_raises_(DISPATCH_LEVEL) + VOID IrqlRaiseLevelExplicit_pass(void) +{ + KIRQL oldIRQL; + KeRaiseIrql(DISPATCH_LEVEL, &oldIRQL); + KeLowerIrql(oldIRQL); +} diff --git a/src/drivers/general/queries/IrqlNotSaved/IrqlNotSaved.ql b/src/drivers/general/queries/IrqlNotSaved/IrqlNotSaved.ql index 709faec2..43e2a076 100644 --- a/src/drivers/general/queries/IrqlNotSaved/IrqlNotSaved.ql +++ b/src/drivers/general/queries/IrqlNotSaved/IrqlNotSaved.ql @@ -15,6 +15,7 @@ * @problem.severity warning * @precision medium * @tags correctness + * ca_ported * wddst * @scope domainspecific * @query-version v1 @@ -23,21 +24,19 @@ import cpp import drivers.libraries.Irql import semmle.code.cpp.dataflow.new.DataFlow -import semmle.code.cpp.dataflow.new.DataFlow2 /** * A data-flow configuration describing flow from an * \_IRQL\_saves\_-annotated parameter to an OS function that restores * the IRQL. */ -class IrqlFlowConfiguration extends DataFlow::Configuration { - IrqlFlowConfiguration() { this = "IrqlFlowConfiguration" } +module IrqlFlowConfigurationConfig implements DataFlow::ConfigSig { - override predicate isSource(DataFlow::Node source) { + predicate isSource(DataFlow::Node source) { source.asParameter() instanceof IrqlSaveParameter } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { exists(FunctionCall fc, FundamentalIrqlSaveFunction fisf | fc.getTarget() = fisf and ( @@ -50,6 +49,8 @@ class IrqlFlowConfiguration extends DataFlow::Configuration { } } +module IrqlFlowConfiguration = DataFlow::Global; + /** * A function that we know will restore the IRQL, i.e. one defined * by the Windows OS itself. This is in general in a Windows Kits header. For @@ -75,38 +76,38 @@ class FundamentalIrqlSaveFunction extends IrqlSavesFunction { /** * A simple data flow from any IrqlSaveParameter. */ -class IrqlSaveParameterFlowConfiguration extends DataFlow2::Configuration { - IrqlSaveParameterFlowConfiguration() { this = "IrqlSaveParameterFlowConfiguration" } +module IrqlSaveParameterFlowConfigurationConfig implements DataFlow::ConfigSig { - override predicate isSource(DataFlow::Node source) { + predicate isSource(DataFlow::Node source) { source.asParameter() instanceof IrqlSaveParameter } - override predicate isSink(DataFlow::Node sink) { sink instanceof DataFlow::Node } + predicate isSink(DataFlow::Node sink) { sink instanceof DataFlow::Node } } +module IrqlSaveParameterFlowConfiguration = DataFlow::Global; + /** * A data-flow configuration representing flow from an * OS function that returns an IRQL to be saved to a parameter marked * \_IRQL\_saves\_ (or a variable aliasing that parameter.) */ -class IrqlAssignmentFlowConfiguration extends DataFlow::Configuration { - IrqlAssignmentFlowConfiguration() { this = "IrqlAssignmentFlowConfiguration" } +module IrqlAssignmentFlowConfigurationConfig implements DataFlow::ConfigSig { - override predicate isSource(DataFlow::Node source) { + predicate isSource(DataFlow::Node source) { source.asExpr() instanceof FunctionCall and source.asExpr().(FunctionCall).getTarget() instanceof FundamentalIrqlSaveFunction and source.asExpr().(FunctionCall).getTarget() instanceof IrqlSavesViaReturnFunction } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { exists(Assignment a | a.getLValue().getAChild*().(VariableAccess).getTarget() instanceof IrqlSaveVariableFlowedTo and a.getRValue() = sink.asExpr() ) } } - +module IrqlAssignmentFlowConfiguration = DataFlow::Global; /** * A variable that is either a parameter annotated \_IRQL\_saves\_ * or a variable which contains the value from a parameter annotated as such. @@ -116,14 +117,14 @@ class IrqlSaveVariableFlowedTo extends Variable { IrqlSaveVariableFlowedTo() { exists( - IrqlSaveParameterFlowConfiguration ispfc, DataFlow::Node parameter, DataFlow::Node assignment + DataFlow::Node parameter, DataFlow::Node assignment | ( this.getAnAssignedValue() = assignment.asExpr() or this = assignment.asParameter() ) and parameter.asParameter() = isp and - ispfc.hasFlow(parameter, assignment) + IrqlSaveParameterFlowConfiguration::flow(parameter, assignment) ) or this = isp @@ -142,19 +143,19 @@ where */ not exists( - DataFlow::Node node, IrqlSaveVariableFlowedTo isvft, IrqlAssignmentFlowConfiguration iafc + DataFlow::Node node, IrqlSaveVariableFlowedTo isvft | isvft.getSaveParameter() = isp and exists(Assignment a | a.getLValue().getAChild*().(VariableAccess).getTarget() = isvft and a.getRValue() = node.asExpr() ) and - iafc.hasFlow(_, node) + IrqlAssignmentFlowConfiguration::flow(_, node) ) and // Case two: is the IrqlSaveParameter passed into an OS function that will save a value to it? - not exists(DataFlow::Node node, IrqlFlowConfiguration ifc | + not exists(DataFlow::Node node | node.asParameter() = isp and - ifc.hasFlow(node, _) + IrqlFlowConfiguration::flow(node, _) ) select isp, "The parameter $@ is annotated \"_IRQL_saves_\" but never has the IRQL saved to it.", isp, isp.getName() diff --git a/src/drivers/general/queries/IrqlNotUsed/IrqlNotUsed.ql b/src/drivers/general/queries/IrqlNotUsed/IrqlNotUsed.ql index da32c0bf..e3c3a1d4 100644 --- a/src/drivers/general/queries/IrqlNotUsed/IrqlNotUsed.ql +++ b/src/drivers/general/queries/IrqlNotUsed/IrqlNotUsed.ql @@ -15,6 +15,7 @@ * @problem.severity warning * @precision medium * @tags correctness + * ca_ported * wddst * @scope domainspecific * @query-version v1 @@ -23,7 +24,6 @@ import cpp import drivers.libraries.Irql import semmle.code.cpp.dataflow.new.DataFlow -import semmle.code.cpp.dataflow.new.DataFlow2 /** * A function that has at least one parameter annotated with "\_IRQL\_restores\_". @@ -61,14 +61,12 @@ class FundamentalIrqlRestoreFunction extends IrqlRestoreFunction { * _IRQL_restores_-annotated parameter to an OS function that restores * the IRQL. */ -class IrqlFlowConfiguration extends DataFlow::Configuration { - IrqlFlowConfiguration() { this = "IrqlFlowConfiguration" } - - override predicate isSource(DataFlow::Node source) { +module IrqlFlowConfigurationConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source.asParameter() instanceof IrqlRestoreParameter } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { exists(FunctionCall fc, FundamentalIrqlRestoreFunction firf | fc.getTarget() = firf and ( @@ -79,21 +77,22 @@ class IrqlFlowConfiguration extends DataFlow::Configuration { ) } } +module IrqlFlowConfiguration = DataFlow::Global; -from IrqlRestoreFunction irf, IrqlFlowConfiguration ifc +from IrqlRestoreFunction irf where // Exclude OS functions not irf instanceof FundamentalIrqlRestoreFunction and ( // Account for case where parameter is touched but has no path to restore the IRQL - exists(DataFlow::PathNode source | - source.getNode().asParameter() = irf.getRestoreParameter() and - not ifc.hasFlowPath(source, _) + exists(DataFlow::Node source | + source.asParameter() = irf.getRestoreParameter() and + not IrqlFlowConfiguration::flow(source, _) ) or // Account for case where parameter is totally untouched - not exists(DataFlow::PathNode source | - source.getNode().asParameter() = irf.getRestoreParameter() + not exists(DataFlow::Node source | + source.asParameter() = irf.getRestoreParameter() ) ) select irf, diff --git a/src/drivers/general/queries/IrqlSetTooHigh/IrqlSetTooHigh.ql b/src/drivers/general/queries/IrqlSetTooHigh/IrqlSetTooHigh.ql index 11cbc3b0..7bd54b8d 100644 --- a/src/drivers/general/queries/IrqlSetTooHigh/IrqlSetTooHigh.ql +++ b/src/drivers/general/queries/IrqlSetTooHigh/IrqlSetTooHigh.ql @@ -15,6 +15,7 @@ * @problem.severity warning * @precision medium * @tags correctness + * ca_ported * wddst * @scope domainspecific * @query-version v1 diff --git a/src/drivers/general/queries/IrqlSetTooLow/IrqlSetTooLow.ql b/src/drivers/general/queries/IrqlSetTooLow/IrqlSetTooLow.ql index 1a894fa4..e1936c7c 100644 --- a/src/drivers/general/queries/IrqlSetTooLow/IrqlSetTooLow.ql +++ b/src/drivers/general/queries/IrqlSetTooLow/IrqlSetTooLow.ql @@ -15,6 +15,7 @@ * @problem.severity warning * @precision medium * @tags correctness + * ca_ported * wddst * @scope domainspecific * @query-version v1 diff --git a/src/drivers/general/queries/IrqlTooHigh/IrqlTooHigh.ql b/src/drivers/general/queries/IrqlTooHigh/IrqlTooHigh.ql index d89c1966..c678aa66 100644 --- a/src/drivers/general/queries/IrqlTooHigh/IrqlTooHigh.ql +++ b/src/drivers/general/queries/IrqlTooHigh/IrqlTooHigh.ql @@ -15,6 +15,7 @@ * @problem.severity warning * @precision medium * @tags correctness + * ca_ported * wddst * @scope domainspecific * @query-version v2 diff --git a/src/drivers/general/queries/IrqlTooHigh/IrqlTooHigh.sarif b/src/drivers/general/queries/IrqlTooHigh/IrqlTooHigh.sarif index 8e663932..6cdc3298 100644 --- a/src/drivers/general/queries/IrqlTooHigh/IrqlTooHigh.sarif +++ b/src/drivers/general/queries/IrqlTooHigh/IrqlTooHigh.sarif @@ -1,361 +1,529 @@ { - "$schema" : "https://json.schemastore.org/sarif-2.1.0.json", - "version" : "2.1.0", - "runs" : [ { - "tool" : { - "driver" : { - "name" : "CodeQL", - "organization" : "GitHub", - "semanticVersion" : "2.15.4", - "notifications" : [ { - "id" : "cpp/baseline/expected-extracted-files", - "name" : "cpp/baseline/expected-extracted-files", - "shortDescription" : { - "text" : "Expected extracted files" + "$schema": "https://json.schemastore.org/sarif-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "CodeQL", + "organization": "GitHub", + "semanticVersion": "2.18.4", + "notifications": [ + { + "id": "cpp/baseline/expected-extracted-files", + "name": "cpp/baseline/expected-extracted-files", + "shortDescription": { + "text": "Expected extracted files" + }, + "fullDescription": { + "text": "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration": { + "enabled": true + }, + "properties": { + "tags": [ + "expected-extracted-files", + "telemetry" + ] + } + }, + { + "id": "cpp/extractor/summary", + "name": "cpp/extractor/summary", + "shortDescription": { + "text": "C++ extractor telemetry" + }, + "fullDescription": { + "text": "C++ extractor telemetry" + }, + "defaultConfiguration": { + "enabled": true + } + } + ], + "rules": [ + { + "id": "cpp/drivers/irql-too-high", + "name": "cpp/drivers/irql-too-high", + "shortDescription": { + "text": "IRQL too high (C28121)" + }, + "fullDescription": { + "text": "A function annotated with IRQL requirements was called at an IRQL too high for the requirements." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning" + }, + "properties": { + "tags": [ + "correctness", + "wddst" + ], + "description": "A function annotated with IRQL requirements was called at an IRQL too high for the requirements.", + "feature.area": "Multiple", + "id": "cpp/drivers/irql-too-high", + "impact": "Exploitable Design", + "kind": "problem", + "name": "IRQL too high (C28121)", + "opaqueid": "CQLD-C28121", + "owner.email": "sdat@microsoft.com", + "platform": "Desktop", + "precision": "medium", + "problem.severity": "warning", + "query-version": "v2", + "repro.text": "The following function call is taking place at an IRQL too high for what the call target is annotated as.", + "scope": "domainspecific", + "security.severity": "Low" + } + } + ] + }, + "extensions": [ + { + "name": "microsoft/windows-drivers", + "semanticVersion": "1.2.0+ebba6989b75fe7ac336c358d0838781e7b17e5c2", + "locations": [ + { + "uri": "file:///C:/codeql-home/WDDST/src/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///C:/codeql-home/WDDST/src/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + } + ] }, - "fullDescription" : { - "text" : "Files appearing in the source archive that are expected to be extracted." - }, - "defaultConfiguration" : { - "enabled" : true - }, - "properties" : { - "tags" : [ "expected-extracted-files", "telemetry" ] - } - } ], - "rules" : [ { - "id" : "cpp/drivers/irql-too-high", - "name" : "cpp/drivers/irql-too-high", - "shortDescription" : { - "text" : "IRQL too high (C28121)" - }, - "fullDescription" : { - "text" : "A function annotated with IRQL requirements was called at an IRQL too high for the requirements." - }, - "defaultConfiguration" : { - "enabled" : true, - "level" : "warning" - }, - "properties" : { - "tags" : [ "correctness", "wddst" ], - "description" : "A function annotated with IRQL requirements was called at an IRQL too high for the requirements.", - "feature.area" : "Multiple", - "id" : "cpp/drivers/irql-too-high", - "impact" : "Exploitable Design", - "kind" : "problem", - "name" : "IRQL too high (C28121)", - "opaqueid" : "CQLD-C28121", - "owner.email" : "sdat@microsoft.com", - "platform" : "Desktop", - "precision" : "medium", - "problem.severity" : "warning", - "query-version" : "v2", - "repro.text" : "The following function call is taking place at an IRQL too high for what the call target is annotated as.", - "scope" : "domainspecific", - "security.severity" : "Low" - } - } ] - }, - "extensions" : [ { - "name" : "microsoft/windows-drivers", - "semanticVersion" : "1.1.0+2affc3c634804dac7504a483a378cc9ba22a0f0b", - "locations" : [ { - "uri" : "file:///C:/codeql-home/Windows-Driver-Developer-Supplemental-Tools/src/", - "description" : { - "text" : "The QL pack root directory." - } - }, { - "uri" : "file:///C:/codeql-home/Windows-Driver-Developer-Supplemental-Tools/src/qlpack.yml", - "description" : { - "text" : "The QL pack definition file." - } - } ] - } ] - }, - "invocations" : [ { - "toolExecutionNotifications" : [ { - "locations" : [ { - "physicalLocation" : { - "artifactLocation" : { - "uri" : "driver/driver_snippet.c", - "uriBaseId" : "%SRCROOT%", - "index" : 0 - } + "invocations": [ + { + "toolExecutionNotifications": [ + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "message": { + "text": "Internal telemetry for the C++ extractor.\n\nNo action needed.", + "markdown": "Internal telemetry for the C++ extractor.\n\nNo action needed." + }, + "level": "note", + "timeUtc": "2024-10-16T03:27:35.793+00:00", + "descriptor": { + "id": "cpp/extractor/summary", + "index": 1 + }, + "properties": { + "attributes": { + "cache-hits": 0, + "cache-misses": 1, + "extractor-failures": 1, + "extractor-successes": 0, + "trap-caching": "disabled" + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + } + ], + "executionSuccessful": true + } + ], + "artifacts": [ + { + "location": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + }, + { + "location": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + }, + { + "location": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + ], + "results": [ + { + "ruleId": "cpp/drivers/irql-too-high", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-too-high", + "index": 0 + }, + "message": { + "text": "[TestInner1](1): IRQL potentially too high at call to [TestInner2](2). Maximum IRQL for this call: 1, IRQL at preceding node: 2" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 42, + "startColumn": 12, + "endColumn": 22 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "1defbc9e59f0310b:1", + "primaryLocationStartColumnFingerprint": "7" + }, + "relatedLocations": [ + { + "id": 1, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 41, + "startColumn": 10, + "endColumn": 20 + } + }, + "message": { + "text": "TestInner1" + } + }, + { + "id": 2, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 42, + "startColumn": 12, + "endColumn": 22 + } + }, + "message": { + "text": "TestInner2" + } + } + ] + }, + { + "ruleId": "cpp/drivers/irql-too-high", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-too-high", + "index": 0 + }, + "message": { + "text": "[TestInner2](1): IRQL potentially too high at call to [TestInner4](2). Maximum IRQL for this call: 0, IRQL at preceding node: 2" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 36, + "startColumn": 14, + "endColumn": 24 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "7ae2af586e0dd70a:1", + "primaryLocationStartColumnFingerprint": "9" + }, + "relatedLocations": [ + { + "id": 1, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 26, + "startColumn": 10, + "endColumn": 20 + } + }, + "message": { + "text": "TestInner2" + } + }, + { + "id": 2, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 36, + "startColumn": 14, + "endColumn": 24 + } + }, + "message": { + "text": "TestInner4" + } + } + ] + }, + { + "ruleId": "cpp/drivers/irql-too-high", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-too-high", + "index": 0 + }, + "message": { + "text": "[DpcForIsrRoutine](1): IRQL potentially too high at call to [IoGetInitialStack](2). Maximum IRQL for this call: 1, IRQL at preceding node: 2" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 1 + }, + "region": { + "startLine": 366, + "startColumn": 5, + "endColumn": 22 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "48e9dbeaff18e9e7:1", + "primaryLocationStartColumnFingerprint": "0" + }, + "relatedLocations": [ + { + "id": 1, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 1 + }, + "region": { + "startLine": 347, + "endColumn": 17 + } + }, + "message": { + "text": "DpcForIsrRoutine" + } + }, + { + "id": 2, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 1 + }, + "region": { + "startLine": 366, + "startColumn": 5, + "endColumn": 22 + } + }, + "message": { + "text": "IoGetInitialStack" + } + } + ] + }, + { + "ruleId": "cpp/drivers/irql-too-high", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-too-high", + "index": 0 + }, + "message": { + "text": "[CompletionRoutine](1): IRQL potentially too high at call to [KeSetEvent](2). Maximum IRQL for this call: 1, IRQL at preceding node: 2" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 1 + }, + "region": { + "startLine": 324, + "startColumn": 5, + "endColumn": 15 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "779dfb1bf8eb10c3:1", + "primaryLocationStartColumnFingerprint": "0" + }, + "relatedLocations": [ + { + "id": 1, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 1 + }, + "region": { + "startLine": 303, + "endColumn": 18 + } + }, + "message": { + "text": "CompletionRoutine" + } + }, + { + "id": 2, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 1 + }, + "region": { + "startLine": 324, + "startColumn": 5, + "endColumn": 15 + } + }, + "message": { + "text": "KeSetEvent" + } + } + ] + } + ], + "columnKind": "utf16CodeUnits", + "properties": { + "semmle.formatSpecifier": "sarifv2.1.0" } - } ], - "message" : { - "text" : "" - }, - "level" : "none", - "descriptor" : { - "id" : "cpp/baseline/expected-extracted-files", - "index" : 0 - }, - "properties" : { - "formattedMessage" : { - "text" : "" - } - } - }, { - "locations" : [ { - "physicalLocation" : { - "artifactLocation" : { - "uri" : "driver/fail_driver1.c", - "uriBaseId" : "%SRCROOT%", - "index" : 1 - } - } - } ], - "message" : { - "text" : "" - }, - "level" : "none", - "descriptor" : { - "id" : "cpp/baseline/expected-extracted-files", - "index" : 0 - }, - "properties" : { - "formattedMessage" : { - "text" : "" - } - } - }, { - "locations" : [ { - "physicalLocation" : { - "artifactLocation" : { - "uri" : "driver/fail_driver1.h", - "uriBaseId" : "%SRCROOT%", - "index" : 2 - } - } - } ], - "message" : { - "text" : "" - }, - "level" : "none", - "descriptor" : { - "id" : "cpp/baseline/expected-extracted-files", - "index" : 0 - }, - "properties" : { - "formattedMessage" : { - "text" : "" - } - } - } ], - "executionSuccessful" : true - } ], - "artifacts" : [ { - "location" : { - "uri" : "driver/driver_snippet.c", - "uriBaseId" : "%SRCROOT%", - "index" : 0 - } - }, { - "location" : { - "uri" : "driver/fail_driver1.c", - "uriBaseId" : "%SRCROOT%", - "index" : 1 } - }, { - "location" : { - "uri" : "driver/fail_driver1.h", - "uriBaseId" : "%SRCROOT%", - "index" : 2 - } - } ], - "results" : [ { - "ruleId" : "cpp/drivers/irql-too-high", - "ruleIndex" : 0, - "rule" : { - "id" : "cpp/drivers/irql-too-high", - "index" : 0 - }, - "message" : { - "text" : "[TestInner1](1): IRQL potentially too high at call to [TestInner2](2). Maximum IRQL for this call: 1, IRQL at preceding node: 2" - }, - "locations" : [ { - "physicalLocation" : { - "artifactLocation" : { - "uri" : "driver/driver_snippet.c", - "uriBaseId" : "%SRCROOT%", - "index" : 0 - }, - "region" : { - "startLine" : 42, - "startColumn" : 12, - "endColumn" : 22 - } - } - } ], - "partialFingerprints" : { - "primaryLocationLineHash" : "1defbc9e59f0310b:1", - "primaryLocationStartColumnFingerprint" : "7" - }, - "relatedLocations" : [ { - "id" : 1, - "physicalLocation" : { - "artifactLocation" : { - "uri" : "driver/driver_snippet.c", - "uriBaseId" : "%SRCROOT%", - "index" : 0 - }, - "region" : { - "startLine" : 41, - "startColumn" : 10, - "endColumn" : 20 - } - }, - "message" : { - "text" : "TestInner1" - } - }, { - "id" : 2, - "physicalLocation" : { - "artifactLocation" : { - "uri" : "driver/driver_snippet.c", - "uriBaseId" : "%SRCROOT%", - "index" : 0 - }, - "region" : { - "startLine" : 42, - "startColumn" : 12, - "endColumn" : 22 - } - }, - "message" : { - "text" : "TestInner2" - } - } ] - }, { - "ruleId" : "cpp/drivers/irql-too-high", - "ruleIndex" : 0, - "rule" : { - "id" : "cpp/drivers/irql-too-high", - "index" : 0 - }, - "message" : { - "text" : "[TestInner2](1): IRQL potentially too high at call to [TestInner4](2). Maximum IRQL for this call: 0, IRQL at preceding node: 2" - }, - "locations" : [ { - "physicalLocation" : { - "artifactLocation" : { - "uri" : "driver/driver_snippet.c", - "uriBaseId" : "%SRCROOT%", - "index" : 0 - }, - "region" : { - "startLine" : 36, - "startColumn" : 14, - "endColumn" : 24 - } - } - } ], - "partialFingerprints" : { - "primaryLocationLineHash" : "7ae2af586e0dd70a:1", - "primaryLocationStartColumnFingerprint" : "9" - }, - "relatedLocations" : [ { - "id" : 1, - "physicalLocation" : { - "artifactLocation" : { - "uri" : "driver/driver_snippet.c", - "uriBaseId" : "%SRCROOT%", - "index" : 0 - }, - "region" : { - "startLine" : 26, - "startColumn" : 10, - "endColumn" : 20 - } - }, - "message" : { - "text" : "TestInner2" - } - }, { - "id" : 2, - "physicalLocation" : { - "artifactLocation" : { - "uri" : "driver/driver_snippet.c", - "uriBaseId" : "%SRCROOT%", - "index" : 0 - }, - "region" : { - "startLine" : 36, - "startColumn" : 14, - "endColumn" : 24 - } - }, - "message" : { - "text" : "TestInner4" - } - } ] - }, { - "ruleId" : "cpp/drivers/irql-too-high", - "ruleIndex" : 0, - "rule" : { - "id" : "cpp/drivers/irql-too-high", - "index" : 0 - }, - "message" : { - "text" : "[DpcForIsrRoutine](1): IRQL potentially too high at call to [IoGetInitialStack](2). Maximum IRQL for this call: 1, IRQL at preceding node: 2" - }, - "locations" : [ { - "physicalLocation" : { - "artifactLocation" : { - "uri" : "driver/fail_driver1.c", - "uriBaseId" : "%SRCROOT%", - "index" : 1 - }, - "region" : { - "startLine" : 366, - "startColumn" : 5, - "endColumn" : 22 - } - } - } ], - "partialFingerprints" : { - "primaryLocationLineHash" : "48e9dbeaff18e9e7:1", - "primaryLocationStartColumnFingerprint" : "0" - }, - "relatedLocations" : [ { - "id" : 1, - "physicalLocation" : { - "artifactLocation" : { - "uri" : "driver/fail_driver1.c", - "uriBaseId" : "%SRCROOT%", - "index" : 1 - }, - "region" : { - "startLine" : 347, - "endColumn" : 17 - } - }, - "message" : { - "text" : "DpcForIsrRoutine" - } - }, { - "id" : 2, - "physicalLocation" : { - "artifactLocation" : { - "uri" : "driver/fail_driver1.c", - "uriBaseId" : "%SRCROOT%", - "index" : 1 - }, - "region" : { - "startLine" : 366, - "startColumn" : 5, - "endColumn" : 22 - } - }, - "message" : { - "text" : "IoGetInitialStack" - } - } ] - } ], - "columnKind" : "utf16CodeUnits", - "properties" : { - "semmle.formatSpecifier" : "sarifv2.1.0" - } - } ] + ] } \ No newline at end of file diff --git a/src/drivers/general/queries/IrqlTooLow/IrqlTooLow.ql b/src/drivers/general/queries/IrqlTooLow/IrqlTooLow.ql index 24f31d1a..d6f6db68 100644 --- a/src/drivers/general/queries/IrqlTooLow/IrqlTooLow.ql +++ b/src/drivers/general/queries/IrqlTooLow/IrqlTooLow.ql @@ -15,6 +15,7 @@ * @problem.severity warning * @precision medium * @tags correctness + * ca_ported * wddst * @scope domainspecific * @query-version v2 @@ -33,7 +34,7 @@ where irqlFunc.(IrqlRequiresAnnotatedFunction).getIrqlLevel() = irqlRequirement ) and irqlRequirement != -1 and - irqlRequirement > max(getPotentialExitIrqlAtCfn(prior)) + irqlRequirement > getPotentialExitIrqlAtCfn(prior) select call, "$@: IRQL potentially too low at call to $@. Minimum IRQL for this call: " + irqlRequirement + ", IRQL at preceding node: " + max(getPotentialExitIrqlAtCfn(prior)), call.getControlFlowScope(), diff --git a/src/drivers/general/queries/IrqlTooLow/IrqlTooLow.sarif b/src/drivers/general/queries/IrqlTooLow/IrqlTooLow.sarif index a1783990..d5b95a21 100644 --- a/src/drivers/general/queries/IrqlTooLow/IrqlTooLow.sarif +++ b/src/drivers/general/queries/IrqlTooLow/IrqlTooLow.sarif @@ -1,299 +1,345 @@ { - "$schema" : "https://json.schemastore.org/sarif-2.1.0.json", - "version" : "2.1.0", - "runs" : [ { - "tool" : { - "driver" : { - "name" : "CodeQL", - "organization" : "GitHub", - "semanticVersion" : "2.15.4", - "notifications" : [ { - "id" : "cpp/baseline/expected-extracted-files", - "name" : "cpp/baseline/expected-extracted-files", - "shortDescription" : { - "text" : "Expected extracted files" - }, - "fullDescription" : { - "text" : "Files appearing in the source archive that are expected to be extracted." - }, - "defaultConfiguration" : { - "enabled" : true - }, - "properties" : { - "tags" : [ "expected-extracted-files", "telemetry" ] - } - } ], - "rules" : [ { - "id" : "cpp/drivers/irql-too-low", - "name" : "cpp/drivers/irql-too-low", - "shortDescription" : { - "text" : "IRQL too low (C28120)" - }, - "fullDescription" : { - "text" : "A function annotated with IRQL requirements was called at an IRQL too low for the requirements." - }, - "defaultConfiguration" : { - "enabled" : true, - "level" : "warning" - }, - "properties" : { - "tags" : [ "correctness", "wddst" ], - "description" : "A function annotated with IRQL requirements was called at an IRQL too low for the requirements.", - "feature.area" : "Multiple", - "id" : "cpp/drivers/irql-too-low", - "impact" : "Exploitable Design", - "kind" : "problem", - "name" : "IRQL too low (C28120)", - "opaqueid" : "CQLD-C28120", - "owner.email" : "sdat@microsoft.com", - "platform" : "Desktop", - "precision" : "medium", - "problem.severity" : "warning", - "query-version" : "v2", - "repro.text" : "The following function call is taking place at an IRQL too low for what the call target is annotated as.", - "scope" : "domainspecific", - "security.severity" : "Low" - } - } ] - }, - "extensions" : [ { - "name" : "microsoft/windows-drivers", - "semanticVersion" : "1.1.0+2affc3c634804dac7504a483a378cc9ba22a0f0b", - "locations" : [ { - "uri" : "file:///C:/codeql-home/Windows-Driver-Developer-Supplemental-Tools/src/", - "description" : { - "text" : "The QL pack root directory." - } - }, { - "uri" : "file:///C:/codeql-home/Windows-Driver-Developer-Supplemental-Tools/src/qlpack.yml", - "description" : { - "text" : "The QL pack definition file." - } - } ] - } ] - }, - "invocations" : [ { - "toolExecutionNotifications" : [ { - "locations" : [ { - "physicalLocation" : { - "artifactLocation" : { - "uri" : "driver/fail_driver1.c", - "uriBaseId" : "%SRCROOT%", - "index" : 1 + "$schema": "https://json.schemastore.org/sarif-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "CodeQL", + "organization": "GitHub", + "semanticVersion": "2.15.4", + "notifications": [ + { + "id": "cpp/baseline/expected-extracted-files", + "name": "cpp/baseline/expected-extracted-files", + "shortDescription": { + "text": "Expected extracted files" + }, + "fullDescription": { + "text": "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration": { + "enabled": true + }, + "properties": { + "tags": [ + "expected-extracted-files", + "telemetry" + ] + } } - } - } ], - "message" : { - "text" : "" - }, - "level" : "none", - "descriptor" : { - "id" : "cpp/baseline/expected-extracted-files", - "index" : 0 - }, - "properties" : { - "formattedMessage" : { - "text" : "" - } - } - }, { - "locations" : [ { - "physicalLocation" : { - "artifactLocation" : { - "uri" : "driver/driver_snippet.c", - "uriBaseId" : "%SRCROOT%", - "index" : 0 + ], + "rules": [ + { + "id": "cpp/drivers/irql-too-low", + "name": "cpp/drivers/irql-too-low", + "shortDescription": { + "text": "IRQL too low (C28120)" + }, + "fullDescription": { + "text": "A function annotated with IRQL requirements was called at an IRQL too low for the requirements." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning" + }, + "properties": { + "tags": [ + "correctness", + "wddst" + ], + "description": "A function annotated with IRQL requirements was called at an IRQL too low for the requirements.", + "feature.area": "Multiple", + "id": "cpp/drivers/irql-too-low", + "impact": "Exploitable Design", + "kind": "problem", + "name": "IRQL too low (C28120)", + "opaqueid": "CQLD-C28120", + "owner.email": "sdat@microsoft.com", + "platform": "Desktop", + "precision": "medium", + "problem.severity": "warning", + "query-version": "v2", + "repro.text": "The following function call is taking place at an IRQL too low for what the call target is annotated as.", + "scope": "domainspecific", + "security.severity": "Low" + } } - } - } ], - "message" : { - "text" : "" - }, - "level" : "none", - "descriptor" : { - "id" : "cpp/baseline/expected-extracted-files", - "index" : 0 + ] }, - "properties" : { - "formattedMessage" : { - "text" : "" + "extensions": [ + { + "name": "microsoft/windows-drivers", + "semanticVersion": "1.1.0+2affc3c634804dac7504a483a378cc9ba22a0f0b", + "locations": [ + { + "uri": "file:///C:/codeql-home/Windows-Driver-Developer-Supplemental-Tools/src/", + "description": { + "text": "The QL pack root directory." + } + }, + { + "uri": "file:///C:/codeql-home/Windows-Driver-Developer-Supplemental-Tools/src/qlpack.yml", + "description": { + "text": "The QL pack definition file." + } + } + ] } - } - }, { - "locations" : [ { - "physicalLocation" : { - "artifactLocation" : { - "uri" : "driver/fail_driver1.h", - "uriBaseId" : "%SRCROOT%", - "index" : 2 + ] + }, + "invocations": [ + { + "toolExecutionNotifications": [ + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } } + ], + "executionSuccessful": true + } + ], + "artifacts": [ + { + "location": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 } - } ], - "message" : { - "text" : "" }, - "level" : "none", - "descriptor" : { - "id" : "cpp/baseline/expected-extracted-files", - "index" : 0 + { + "location": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 1 + } }, - "properties" : { - "formattedMessage" : { - "text" : "" + { + "location": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 2 } } - } ], - "executionSuccessful" : true - } ], - "artifacts" : [ { - "location" : { - "uri" : "driver/driver_snippet.c", - "uriBaseId" : "%SRCROOT%", - "index" : 0 - } - }, { - "location" : { - "uri" : "driver/fail_driver1.c", - "uriBaseId" : "%SRCROOT%", - "index" : 1 - } - }, { - "location" : { - "uri" : "driver/fail_driver1.h", - "uriBaseId" : "%SRCROOT%", - "index" : 2 - } - } ], - "results" : [ { - "ruleId" : "cpp/drivers/irql-too-low", - "ruleIndex" : 0, - "rule" : { - "id" : "cpp/drivers/irql-too-low", - "index" : 0 - }, - "message" : { - "text" : "[TestInner1](1): IRQL potentially too low at call to [TestInner2](2). Minimum IRQL for this call: 1, IRQL at preceding node: 0" - }, - "locations" : [ { - "physicalLocation" : { - "artifactLocation" : { - "uri" : "driver/driver_snippet.c", - "uriBaseId" : "%SRCROOT%", - "index" : 0 + ], + "results": [ + { + "ruleId": "cpp/drivers/irql-too-low", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-too-low", + "index": 0 }, - "region" : { - "startLine" : 41, - "startColumn" : 12, - "endColumn" : 22 - } - } - } ], - "partialFingerprints" : { - "primaryLocationLineHash" : "1defbc9e59f0310b:1", - "primaryLocationStartColumnFingerprint" : "7" - }, - "relatedLocations" : [ { - "id" : 1, - "physicalLocation" : { - "artifactLocation" : { - "uri" : "driver/driver_snippet.c", - "uriBaseId" : "%SRCROOT%", - "index" : 0 + "message": { + "text": "[TestInner1](1): IRQL potentially too low at call to [TestInner2](2). Minimum IRQL for this call: 1, IRQL at preceding node: 0" }, - "region" : { - "startLine" : 40, - "startColumn" : 10, - "endColumn" : 20 - } - }, - "message" : { - "text" : "TestInner1" - } - }, { - "id" : 2, - "physicalLocation" : { - "artifactLocation" : { - "uri" : "driver/driver_snippet.c", - "uriBaseId" : "%SRCROOT%", - "index" : 0 + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 41, + "startColumn": 12, + "endColumn": 22 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "1defbc9e59f0310b:1", + "primaryLocationStartColumnFingerprint": "7" }, - "region" : { - "startLine" : 41, - "startColumn" : 12, - "endColumn" : 22 - } + "relatedLocations": [ + { + "id": 1, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 40, + "startColumn": 10, + "endColumn": 20 + } + }, + "message": { + "text": "TestInner1" + } + }, + { + "id": 2, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 41, + "startColumn": 12, + "endColumn": 22 + } + }, + "message": { + "text": "TestInner2" + } + } + ] }, - "message" : { - "text" : "TestInner2" - } - } ] - }, { - "ruleId" : "cpp/drivers/irql-too-low", - "ruleIndex" : 0, - "rule" : { - "id" : "cpp/drivers/irql-too-low", - "index" : 0 - }, - "message" : { - "text" : "[someFunc](1): IRQL potentially too low at call to [TestInner3](2). Minimum IRQL for this call: 2, IRQL at preceding node: 0" - }, - "locations" : [ { - "physicalLocation" : { - "artifactLocation" : { - "uri" : "driver/driver_snippet.c", - "uriBaseId" : "%SRCROOT%", - "index" : 0 + { + "ruleId": "cpp/drivers/irql-too-low", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/irql-too-low", + "index": 0 }, - "region" : { - "startLine" : 21, - "startColumn" : 12, - "endColumn" : 22 - } - } - } ], - "partialFingerprints" : { - "primaryLocationLineHash" : "bf32240018f4d9fb:1", - "primaryLocationStartColumnFingerprint" : "7" - }, - "relatedLocations" : [ { - "id" : 1, - "physicalLocation" : { - "artifactLocation" : { - "uri" : "driver/driver_snippet.c", - "uriBaseId" : "%SRCROOT%", - "index" : 0 + "message": { + "text": "[someFunc](1): IRQL potentially too low at call to [TestInner3](2). Minimum IRQL for this call: 2, IRQL at preceding node: 0" }, - "region" : { - "startLine" : 20, - "startColumn" : 10, - "endColumn" : 18 - } - }, - "message" : { - "text" : "someFunc" - } - }, { - "id" : 2, - "physicalLocation" : { - "artifactLocation" : { - "uri" : "driver/driver_snippet.c", - "uriBaseId" : "%SRCROOT%", - "index" : 0 + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 21, + "startColumn": 12, + "endColumn": 22 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "bf32240018f4d9fb:1", + "primaryLocationStartColumnFingerprint": "7" }, - "region" : { - "startLine" : 21, - "startColumn" : 12, - "endColumn" : 22 - } - }, - "message" : { - "text" : "TestInner3" + "relatedLocations": [ + { + "id": 1, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 20, + "startColumn": 10, + "endColumn": 18 + } + }, + "message": { + "text": "someFunc" + } + }, + { + "id": 2, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 21, + "startColumn": 12, + "endColumn": 22 + } + }, + "message": { + "text": "TestInner3" + } + } + ] } - } ] - } ], - "columnKind" : "utf16CodeUnits", - "properties" : { - "semmle.formatSpecifier" : "sarifv2.1.0" + ], + "columnKind": "utf16CodeUnits", + "properties": { + "semmle.formatSpecifier": "sarifv2.1.0" + } } - } ] + ] } \ No newline at end of file diff --git a/src/drivers/general/queries/MultipleFunctionClassAnnotations/MultipleFunctionClassAnnotations.qhelp b/src/drivers/general/queries/MultipleFunctionClassAnnotations/MultipleFunctionClassAnnotations.qhelp new file mode 100644 index 00000000..9ea87c95 --- /dev/null +++ b/src/drivers/general/queries/MultipleFunctionClassAnnotations/MultipleFunctionClassAnnotations.qhelp @@ -0,0 +1,56 @@ + + + +

    + Function is annotated with more than one function class. All but one will be ignored. +

    +
    + +

    + This warning can be generated when there is a chain of typedefs. Only use one function class annotation. +

    +
    + +

    + Example function with multiple __drv_functionClass annotations +

    + = 0, __drv_clearDoInit(yes)) typedef NTSTATUS + FAKE_DRIVER_ADD_DEVICE( + __in struct _DRIVER_OBJECT *DriverObject, + __in struct _DEVICE_OBJECT *PhysicalDeviceObject); + + typedef FAKE_DRIVER_ADD_DEVICE *PDRIVER_ADD_DEVICE; + + FAKE_DRIVER_ADD_DEVICE FakeDriverAddDevice; + + _Use_decl_annotations_ + NTSTATUS + FakeDriverAddDevice( + __in struct _DRIVER_OBJECT *DriverObject, + __in struct _DEVICE_OBJECT *PhysicalDeviceObject) + { + return STATUS_SUCCESS; + } + }]]> + + +
    + +

    + +

    +
    + +
  • + + C28177 + +
  • +
    +
    diff --git a/src/drivers/general/queries/MultipleFunctionClassAnnotations/MultipleFunctionClassAnnotations.ql b/src/drivers/general/queries/MultipleFunctionClassAnnotations/MultipleFunctionClassAnnotations.ql new file mode 100644 index 00000000..964f6650 --- /dev/null +++ b/src/drivers/general/queries/MultipleFunctionClassAnnotations/MultipleFunctionClassAnnotations.ql @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/** + * @id cpp/drivers/multiple-function-class-annotations + * @kind problem + * @name Multiple Function Class Annotations + * @description Function is annotated with more than one function class. All but one will be ignored. + * @platform Desktop + * @feature.area Multiple + * @impact Insecure Coding Practice + * @repro.text This warning can be generated when there is a chain of typedefs. + * @owner.email: sdat@microsoft.com + * @opaqueid CQLD-c28177 + * @problem.severity warning + * @precision medium + * @tags correctness + * ca_ported + * @scope domainspecific + * @query-version v1 + */ + +import cpp +import drivers.libraries.SAL + +class FunctionClassAnnotatedTypedef extends TypedefType { + FunctionClassAnnotation funcAnnotation; + + FunctionClassAnnotatedTypedef() { funcAnnotation.getTypedefDeclarations() = this } + + FunctionClassAnnotation getFuncClassAnnotation() { result = funcAnnotation } +} + +class FunctionClassAnnotation extends SALAnnotation { + string annotationName; + + FunctionClassAnnotation() { + this.getMacroName() = ["__drv_functionClass", "_Function_class_"] and + annotationName = this.getMacroName() + } +} + +class AnnotatedFunction extends Function { + FunctionClassAnnotation funcClassAnnotation; + + AnnotatedFunction() { + funcClassAnnotation.getMacroName() = ["__drv_functionClass", "_Function_class_"] and + exists(FunctionDeclarationEntry fde | + fde = this.getADeclarationEntry() and + funcClassAnnotation.getDeclarationEntry() = fde + ) + or + exists(FunctionDeclarationEntry fde | + fde.getFunction() = this and + fde.getTypedefType().(FunctionClassAnnotatedTypedef).getFuncClassAnnotation() = + funcClassAnnotation + ) + } + + FunctionClassAnnotation getFuncClassAnnotation() { result = funcClassAnnotation } +} + +from AnnotatedFunction f +where +count(f.getFuncClassAnnotation() ) > 1 +select f, "Function is annotated with more than one function class. All but one will be ignored." \ No newline at end of file diff --git a/src/drivers/general/queries/MultipleFunctionClassAnnotations/MultipleFunctionClassAnnotations.sarif b/src/drivers/general/queries/MultipleFunctionClassAnnotations/MultipleFunctionClassAnnotations.sarif new file mode 100644 index 00000000..3695c604 --- /dev/null +++ b/src/drivers/general/queries/MultipleFunctionClassAnnotations/MultipleFunctionClassAnnotations.sarif @@ -0,0 +1,318 @@ +{ + "$schema": "https://json.schemastore.org/sarif-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "CodeQL", + "organization": "GitHub", + "semanticVersion": "2.20.3", + "notifications": [ + { + "id": "cpp/baseline/expected-extracted-files", + "name": "cpp/baseline/expected-extracted-files", + "shortDescription": { + "text": "Expected extracted files" + }, + "fullDescription": { + "text": "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration": { + "enabled": true + }, + "properties": { + "tags": [ + "expected-extracted-files", + "telemetry" + ] + } + }, + { + "id": "cpp/extractor/summary", + "name": "cpp/extractor/summary", + "shortDescription": { + "text": "C++ extractor telemetry" + }, + "fullDescription": { + "text": "C++ extractor telemetry" + }, + "defaultConfiguration": { + "enabled": true + } + } + ], + "rules": [ + { + "id": "cpp/drivers/multiple-function-class-annotations", + "name": "cpp/drivers/multiple-function-class-annotations", + "shortDescription": { + "text": "Multiple Function Class Annotations" + }, + "fullDescription": { + "text": "Function is annotated with more than one function class. All but one will be ignored." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning" + }, + "properties": { + "tags": [ + "correctness" + ], + "description": "Function is annotated with more than one function class. All but one will be ignored.", + "feature.area": "Multiple", + "id": "cpp/drivers/multiple-function-class-annotations", + "impact": "Insecure Coding Practice", + "kind": "problem", + "name": "Multiple Function Class Annotations", + "opaqueid": "CQLD-c28177", + "owner.email:": "sdat@microsoft.com", + "platform": "Desktop", + "precision": "medium", + "problem.severity": "warning", + "query-version": "v1", + "repro.text": "This warning can be generated when there is a chain of typedefs.", + "scope": "domainspecific" + } + } + ] + }, + "extensions": [ + { + "name": "microsoft/windows-drivers", + "semanticVersion": "1.3.0+8ce22c1c7f0cb84b37fd703ad218688b4cede456", + "locations": [ + { + "uri": "file:///C:/codeql-home/WDDST/src/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///C:/codeql-home/WDDST/src/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + }, + { + "name": "codeql/cpp-all", + "semanticVersion": "3.1.0+d42788844f7ec0a6b9832140313cc2318e513987", + "locations": [ + { + "uri": "file:///C:/Users/jronstadt/.codeql/packages/codeql/cpp-all/3.1.0/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///C:/Users/jronstadt/.codeql/packages/codeql/cpp-all/3.1.0/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + } + ] + }, + "invocations": [ + { + "toolExecutionNotifications": [ + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "message": { + "text": "Internal telemetry for the C++ extractor.\n\nNo action needed.", + "markdown": "Internal telemetry for the C++ extractor.\n\nNo action needed." + }, + "level": "note", + "timeUtc": "2025-02-06T06:50:14.324692600Z", + "descriptor": { + "id": "cpp/extractor/summary", + "index": 1 + }, + "properties": { + "attributes": { + "cache-hits": 0, + "cache-misses": 1, + "compilers": [ + { + "program": "cl", + "version": "Microsoft (R) C/C++ Optimizing Compiler Version 19.42.34436 for x64" + } + ], + "extractor-failures": 1, + "extractor-successes": 0, + "trap-caching": "disabled" + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + } + ], + "executionSuccessful": true + } + ], + "artifacts": [ + { + "location": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + }, + { + "location": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + }, + { + "location": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + ], + "results": [ + { + "ruleId": "cpp/drivers/multiple-function-class-annotations", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/multiple-function-class-annotations", + "index": 0 + }, + "message": { + "text": "Function is annotated with more than one function class. All but one will be ignored." + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 28, + "startColumn": 5, + "endColumn": 24 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "ba884625d89dd5e9:1", + "primaryLocationStartColumnFingerprint": "0" + } + } + ], + "columnKind": "utf16CodeUnits", + "properties": { + "semmle.formatSpecifier": "sarifv2.1.0" + } + } + ] +} \ No newline at end of file diff --git a/src/drivers/general/queries/MultipleFunctionClassAnnotations/driver_snippet.c b/src/drivers/general/queries/MultipleFunctionClassAnnotations/driver_snippet.c new file mode 100644 index 00000000..dbc414da --- /dev/null +++ b/src/drivers/general/queries/MultipleFunctionClassAnnotations/driver_snippet.c @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +// Macros to enable or disable a code section that may or may not conflict with this test. +#define SET_DISPATCH 1 + +// Template function. Not used for this test. +void top_level_call() +{ +} + +__drv_functionClass(FAKE_DRIVER_ADD_DEVICE) +__drv_functionClass(FAKE_DRIVER_ADD_DEVICE2) +__drv_maxFunctionIRQL(PASSIVE_LEVEL) +__drv_requiresIRQL(PASSIVE_LEVEL) +__drv_sameIRQL +__drv_when(return >= 0, __drv_clearDoInit(yes)) typedef NTSTATUS +FAKE_DRIVER_ADD_DEVICE( + __in struct _DRIVER_OBJECT *DriverObject, + __in struct _DEVICE_OBJECT *PhysicalDeviceObject); + +typedef FAKE_DRIVER_ADD_DEVICE *PDRIVER_ADD_DEVICE; + +FAKE_DRIVER_ADD_DEVICE FakeDriverAddDevice; + +_Use_decl_annotations_ + NTSTATUS + FakeDriverAddDevice( + __in struct _DRIVER_OBJECT *DriverObject, + __in struct _DEVICE_OBJECT *PhysicalDeviceObject) +{ + return STATUS_SUCCESS; +} diff --git a/src/drivers/general/queries/MultithreadedAVCondition/MultithreadedAVCondition.ql b/src/drivers/general/queries/MultithreadedAVCondition/MultithreadedAVCondition.ql index eae80e29..d6f0a261 100644 --- a/src/drivers/general/queries/MultithreadedAVCondition/MultithreadedAVCondition.ql +++ b/src/drivers/general/queries/MultithreadedAVCondition/MultithreadedAVCondition.ql @@ -15,6 +15,7 @@ * @problem.severity warning * @precision medium * @tags correctness + * ca_ported * wddst * @scope domainspecific * @query-version v1 diff --git a/src/drivers/general/queries/NtstatusExplicitCast/NtstatusExplicitCast.qhelp b/src/drivers/general/queries/NtstatusExplicitCast/NtstatusExplicitCast.qhelp new file mode 100644 index 00000000..6e162f62 --- /dev/null +++ b/src/drivers/general/queries/NtstatusExplicitCast/NtstatusExplicitCast.qhelp @@ -0,0 +1,52 @@ + + + +

    + Cast between semantically different integer types. This warning indicates that an NTSTATUS value is being explicitly cast to a Boolean type. This is likely to give undesirable results. For example, the typical success value for NTSTATUS, STATUS_SUCCESS, is false when tested as a Boolean. +

    +
    + +

    + In most cases, the NT_SUCCESS macro should be used to test the value of an NTSTATUS. This macro returns true if the returned status value is neither a warning nor an error code. If a function returns a Boolean to indicate its failure/success, it should explicitly return the appropriate Boolean type rather than depend on casting of NTSTATUS to a Boolean type. + Also, occasionally a program may attempt to reuse a Boolean local variable to store NTSTATUS values. This practice is often error-prone; it is much safer (and likely more efficient) to use a separate NTSTATUS variable. +

    +
    + +

    + If statement with explicit cast from NTSTATUS to Boolean +

    + + +

    + Use of NT_SUCCESS macro +

    + + +
    + +

    +

    +
    + +
  • + + Warning C28714 + +
  • +
    +
    diff --git a/src/drivers/general/queries/NtstatusExplicitCast/NtstatusExplicitCast.ql b/src/drivers/general/queries/NtstatusExplicitCast/NtstatusExplicitCast.ql new file mode 100644 index 00000000..3592b79c --- /dev/null +++ b/src/drivers/general/queries/NtstatusExplicitCast/NtstatusExplicitCast.ql @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/** + * @id cpp/drivers/ntstatus-explicit-cast + * @kind problem + * @name NTSTATUS Explicit Cast + * @description Cast between semantically different integer types (NTSTATUS to Boolean). + * @platform Desktop + * @feature.area Multiple + * @impact Insecure Coding Practice + * @repro.text This warning indicates that an NTSTATUS value is being explicitly cast to a Boolean type. This is likely to give undesirable results. For example, the typical success value for NTSTATUS, STATUS_SUCCESS, is false when tested as a Boolean. + * @owner.email: sdat@microsoft.com + * @opaqueid CQLD-C28714 + * @problem.severity warning + * @precision medium + * @tags correctness + * ca_ported + * @scope domainspecific + * @query-version v1 + */ + +import cpp + +from Conversion c +where + c.getUnconverted().getType().toString().matches("NTSTATUS") and + ( + c.getType().toString().toLowerCase().matches("boolean") or + c.getType().toString().toLowerCase().matches("bool") or + c.getType().toString().matches("VARIANT_BOOL") + ) +select c.getUnconverted(), "Cast between semantically different integer types: NTSTATUS to Boolean" diff --git a/src/drivers/general/queries/NtstatusExplicitCast/NtstatusExplicitCast.sarif b/src/drivers/general/queries/NtstatusExplicitCast/NtstatusExplicitCast.sarif new file mode 100644 index 00000000..e23a4278 --- /dev/null +++ b/src/drivers/general/queries/NtstatusExplicitCast/NtstatusExplicitCast.sarif @@ -0,0 +1,274 @@ +{ + "$schema": "https://json.schemastore.org/sarif-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "CodeQL", + "organization": "GitHub", + "semanticVersion": "2.17.6", + "notifications": [ + { + "id": "cpp/baseline/expected-extracted-files", + "name": "cpp/baseline/expected-extracted-files", + "shortDescription": { + "text": "Expected extracted files" + }, + "fullDescription": { + "text": "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration": { + "enabled": true + }, + "properties": { + "tags": [ + "expected-extracted-files", + "telemetry" + ] + } + }, + { + "id": "cpp/extractor/summary", + "name": "cpp/extractor/summary", + "shortDescription": { + "text": "C++ extractor telemetry" + }, + "fullDescription": { + "text": "C++ extractor telemetry" + }, + "defaultConfiguration": { + "enabled": true + } + } + ], + "rules": [ + { + "id": "cpp/drivers/ntstatus-explicit-cast", + "name": "cpp/drivers/ntstatus-explicit-cast", + "shortDescription": { + "text": "Ntstatus Explicit Cast" + }, + "fullDescription": { + "text": "Cast between semantically different integer types (NTSTATUS to Boolean)." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning" + }, + "properties": { + "tags": [ + "correctness" + ], + "description": "Cast between semantically different integer types (NTSTATUS to Boolean).", + "feature.area": "Multiple", + "id": "cpp/drivers/ntstatus-explicit-cast", + "impact": "Insecure Coding Practice", + "kind": "problem", + "name": "Ntstatus Explicit Cast", + "opaqueid": "CQLD-TODO", + "owner.email:": "sdat@microsoft.com", + "platform": "Desktop", + "precision": "medium", + "problem.severity": "warning", + "query-version": "v1", + "repro.text": "This warning indicates that an NTSTATUS value is being explicitly cast to a Boolean type. This is likely to give undesirable results. For example, the typical success value for NTSTATUS, STATUS_SUCCESS, is false when tested as a Boolean.", + "scope": "domainspecific" + } + } + ] + }, + "extensions": [ + { + "name": "microsoft/windows-drivers", + "semanticVersion": "1.1.0+ce7d70c32c8e0908d7c329389aa84ac3a89e7feb", + "locations": [ + { + "uri": "file:///C:/codeql-home/WDDST/src/", + "description": { + "text": "The QL pack root directory." + } + }, + { + "uri": "file:///C:/codeql-home/WDDST/src/qlpack.yml", + "description": { + "text": "The QL pack definition file." + } + } + ] + } + ] + }, + "invocations": [ + { + "toolExecutionNotifications": [ + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "message": { + "text": "Internal telemetry for the C++ extractor.\n\nNo action needed.", + "markdown": "Internal telemetry for the C++ extractor.\n\nNo action needed." + }, + "level": "note", + "timeUtc": "2024-08-21T05:39:20.983+00:00", + "descriptor": { + "id": "cpp/extractor/summary", + "index": 1 + }, + "properties": { + "attributes": { + "cache-hits": 0, + "cache-misses": 1, + "extractor-failures": 1, + "extractor-successes": 0, + "trap-caching": "disabled" + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + } + ], + "executionSuccessful": true + } + ], + "artifacts": [ + { + "location": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + }, + { + "location": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + }, + { + "location": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + ], + "results": [ + { + "ruleId": "cpp/drivers/ntstatus-explicit-cast", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/ntstatus-explicit-cast", + "index": 0 + }, + "message": { + "text": "Cast between semantically different integer types: NTSTATUS to Boolean" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 50, + "startColumn": 20, + "endColumn": 26 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "f7801528cffbc69a:1", + "primaryLocationStartColumnFingerprint": "15" + } + } + ], + "columnKind": "utf16CodeUnits", + "properties": { + "semmle.formatSpecifier": "sarifv2.1.0" + } + } + ] +} \ No newline at end of file diff --git a/src/drivers/general/queries/NtstatusExplicitCast/driver_snippet.c b/src/drivers/general/queries/NtstatusExplicitCast/driver_snippet.c new file mode 100644 index 00000000..cf4507b7 --- /dev/null +++ b/src/drivers/general/queries/NtstatusExplicitCast/driver_snippet.c @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +// Macros to enable or disable a code section that may or may not conflict with this test. +#define SET_DISPATCH 1 + +// Template function. Not used for this test. +void top_level_call() +{ +} +int _fltused; +void test_good1(){ + KFLOATING_SAVE saveData; + NTSTATUS status; + float f = 0.0f; + status = KeSaveFloatingPointState(&saveData); + if (status != STATUS_SUCCESS) + { + status = KeRestoreFloatingPointState(&saveData); + return; + } + for (int i = 0; i < 100; i++) + { + f = f + 1.0f; + } + status = KeRestoreFloatingPointState(&saveData); + if (!NT_SUCCESS(status)) + { + ; + // handle error + } +} + +void test_bad1() +{ + KFLOATING_SAVE saveData; + NTSTATUS status; + float f = 0.0f; + status = KeSaveFloatingPointState(&saveData); + if (!status) + { + status = KeRestoreFloatingPointState(&saveData); + return; + } + for (int i = 0; i < 100; i++) + { + f = f + 1.0f; + } + status = KeRestoreFloatingPointState(&saveData); + if (!((BOOLEAN)status)) + { + ; + // handle error + } +} diff --git a/src/drivers/general/queries/NtstatusExplicitCast2/NtstatusExplicitCast2.qhelp b/src/drivers/general/queries/NtstatusExplicitCast2/NtstatusExplicitCast2.qhelp new file mode 100644 index 00000000..fe033d83 --- /dev/null +++ b/src/drivers/general/queries/NtstatusExplicitCast2/NtstatusExplicitCast2.qhelp @@ -0,0 +1,55 @@ + + + +

    + Cast between semantically different integer types. This warning indicates that a Boolean is being cast to NTSTATUS. This is likely to give undesirable results. For example, the typical failure value for functions that return a Boolean (FALSE) is a success status when tested as an NTSTATUS. +

    +
    + +

    + Typically, a function that returns Boolean returns either 1 (for TRUE) or 0 (for FALSE). Both these values are treated as success codes by the NT_SUCCESS macro. Thus, the failure case will never be detected. +

    + +
    + +

    + Bad cast from Boolean to NTSTATUS +

    + + +

    + Correct use of Boolean +

    + + +
    + +

    +

    +
    + +
  • + + Warning C28715 + +
  • +
    +
    diff --git a/src/drivers/general/queries/NtstatusExplicitCast2/NtstatusExplicitCast2.ql b/src/drivers/general/queries/NtstatusExplicitCast2/NtstatusExplicitCast2.ql new file mode 100644 index 00000000..f36019c4 --- /dev/null +++ b/src/drivers/general/queries/NtstatusExplicitCast2/NtstatusExplicitCast2.ql @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/** + * @id cpp/drivers/ntstatus-explicit-cast2 + * @kind problem + * @name Ntstatus Explicit Cast 2 + * @description Cast between semantically different integer types (Boolean to NTSTATUS). + * @platform Desktop + * @feature.area Multiple + * @impact Insecure Coding Practice + * @repro.text This warning indicates that a Boolean is being cast to NTSTATUS. This is likely to give undesirable results. For example, the typical failure value for functions that return a Boolean (FALSE) is a success status when tested as an NTSTATUS. + * @opaqueid CQLD-C28715 + * @problem.severity warning + * @precision medium + * @tags correctness + * ca_ported + * @scope domainspecific + * @query-version v1 + */ + +import cpp + +from Conversion c +where + ( + c.getType().toString().toLowerCase().matches("boolean") or + c.getType().toString().toLowerCase().matches("bool") or + c.getType().toString().matches("VARIANT_BOOL") + ) and + c.getConversion().getType().toString().matches("NTSTATUS") +select c, "Cast between semantically different integer types: Boolean to NTSTATUS" diff --git a/src/drivers/general/queries/NtstatusExplicitCast2/NtstatusExplicitCast2.sarif b/src/drivers/general/queries/NtstatusExplicitCast2/NtstatusExplicitCast2.sarif new file mode 100644 index 00000000..6705cc6b --- /dev/null +++ b/src/drivers/general/queries/NtstatusExplicitCast2/NtstatusExplicitCast2.sarif @@ -0,0 +1,273 @@ +{ + "$schema": "https://json.schemastore.org/sarif-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "CodeQL", + "organization": "GitHub", + "semanticVersion": "2.17.6", + "notifications": [ + { + "id": "cpp/baseline/expected-extracted-files", + "name": "cpp/baseline/expected-extracted-files", + "shortDescription": { + "text": "Expected extracted files" + }, + "fullDescription": { + "text": "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration": { + "enabled": true + }, + "properties": { + "tags": [ + "expected-extracted-files", + "telemetry" + ] + } + }, + { + "id": "cpp/extractor/summary", + "name": "cpp/extractor/summary", + "shortDescription": { + "text": "C++ extractor telemetry" + }, + "fullDescription": { + "text": "C++ extractor telemetry" + }, + "defaultConfiguration": { + "enabled": true + } + } + ], + "rules": [ + { + "id": "cpp/drivers/ntstatus-explicit-cast2", + "name": "cpp/drivers/ntstatus-explicit-cast2", + "shortDescription": { + "text": "Ntstatus Explicit Cast 2" + }, + "fullDescription": { + "text": "Cast between semantically different integer types (Boolean to NTSTATUS)." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning" + }, + "properties": { + "tags": [ + "correctness" + ], + "description": "Cast between semantically different integer types (Boolean to NTSTATUS).", + "feature.area": "Multiple", + "id": "cpp/drivers/ntstatus-explicit-cast2", + "impact": "Insecure Coding Practice", + "kind": "problem", + "name": "Ntstatus Explicit Cast 2", + "opaqueid": "CQLD-C28715", + "platform": "Desktop", + "precision": "medium", + "problem.severity": "warning", + "query-version": "v1", + "repro.text": "This warning indicates that a Boolean is being cast to NTSTATUS. This is likely to give undesirable results. For example, the typical failure value for functions that return a Boolean (FALSE) is a success status when tested as an NTSTATUS.", + "scope": "domainspecific" + } + } + ] + }, + "extensions": [ + { + "name": "microsoft/windows-drivers", + "semanticVersion": "1.1.0+ce7d70c32c8e0908d7c329389aa84ac3a89e7feb", + "locations": [ + { + "uri": "file:///C:/codeql-home/WDDST/src/", + "description": { + "text": "The QL pack root directory." + } + }, + { + "uri": "file:///C:/codeql-home/WDDST/src/qlpack.yml", + "description": { + "text": "The QL pack definition file." + } + } + ] + } + ] + }, + "invocations": [ + { + "toolExecutionNotifications": [ + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "message": { + "text": "Internal telemetry for the C++ extractor.\n\nNo action needed.", + "markdown": "Internal telemetry for the C++ extractor.\n\nNo action needed." + }, + "level": "note", + "timeUtc": "2024-08-21T06:06:26.075+00:00", + "descriptor": { + "id": "cpp/extractor/summary", + "index": 1 + }, + "properties": { + "attributes": { + "cache-hits": 0, + "cache-misses": 1, + "extractor-failures": 1, + "extractor-successes": 0, + "trap-caching": "disabled" + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + } + ], + "executionSuccessful": true + } + ], + "artifacts": [ + { + "location": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + }, + { + "location": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + }, + { + "location": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + ], + "results": [ + { + "ruleId": "cpp/drivers/ntstatus-explicit-cast2", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/ntstatus-explicit-cast2", + "index": 0 + }, + "message": { + "text": "Cast between semantically different integer types: Boolean to NTSTATUS" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 30, + "startColumn": 9, + "endColumn": 35 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "bbf24f16ac513f29:1", + "primaryLocationStartColumnFingerprint": "4" + } + } + ], + "columnKind": "utf16CodeUnits", + "properties": { + "semmle.formatSpecifier": "sarifv2.1.0" + } + } + ] +} \ No newline at end of file diff --git a/src/drivers/general/queries/NtstatusExplicitCast2/driver_snippet.c b/src/drivers/general/queries/NtstatusExplicitCast2/driver_snippet.c new file mode 100644 index 00000000..9dce2224 --- /dev/null +++ b/src/drivers/general/queries/NtstatusExplicitCast2/driver_snippet.c @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +// Macros to enable or disable a code section that may or may not conflict with this test. +#define SET_DISPATCH 1 + +// Template function. Not used for this test. +void top_level_call() +{ +} + +BOOLEAN SomeFunction() +{ + return TRUE; +} +void test_good1() +{ + if (SomeFunction() == TRUE) + { + return 0; + } + else + { + return -1; + } +} + +void test_bad1() +{ + if (NT_SUCCESS(SomeFunction())) + { + return 0; + } + else + { + return -1; + } +} diff --git a/src/drivers/general/queries/NtstatusExplicitCast3/NtstatusExplicitCast3.qhelp b/src/drivers/general/queries/NtstatusExplicitCast3/NtstatusExplicitCast3.qhelp new file mode 100644 index 00000000..400fdba8 --- /dev/null +++ b/src/drivers/general/queries/NtstatusExplicitCast3/NtstatusExplicitCast3.qhelp @@ -0,0 +1,52 @@ + + + +

    + Compiler-inserted cast between semantically different integral types (Boolean to NTSTATUS). This warning indicates that a Boolean is being used as an NTSTATUS without being explicitly cast. +

    +
    + +

    + This warning indicates that a Boolean is being used as an NTSTATUS without being explicitly cast. This is likely to give undesirable results. For instance, the typical failure value for functions that return a Boolean (false) indicates a success status when tested as an NTSTATUS. +

    +
    + +

    + Example of SomeMemAllocFunction that has a Boolean return value but is being returned in a function with NTSTATUS return type. +

    + + +

    + This example avoids the warning +

    + + +
    + +

    +

    +
    + +
  • + + Warning C28716 + +
  • +
    +
    diff --git a/src/drivers/general/queries/NtstatusExplicitCast3/NtstatusExplicitCast3.ql b/src/drivers/general/queries/NtstatusExplicitCast3/NtstatusExplicitCast3.ql new file mode 100644 index 00000000..256f6d77 --- /dev/null +++ b/src/drivers/general/queries/NtstatusExplicitCast3/NtstatusExplicitCast3.ql @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/** + * @id cpp/drivers/ntstatus-explicit-cast3 + * @kind problem + * @name Ntstatus Explicit Cast 3 + * @description Compiler-inserted cast between semantically different integral types + * @platform Desktop + * @feature.area Multiple + * @impact Insecure Coding Practice + * @repro.text + * @owner.email: sdat@microsoft.com + * @opaqueid CQLD-C28716 + * @problem.severity warning + * @precision medium + * @tags correctness + * ca_ported + * @scope domainspecific + * @query-version v1 + */ + +import cpp + +from Conversion c +where + c.isImplicit() and + c.getType().toString().matches("NTSTATUS") and + ( + c.getUnconverted().getType().toString().toLowerCase().matches("boolean") or + c.getUnconverted().getType().toString().toLowerCase().matches("bool") or + c.getUnconverted().getType().toString().matches("VARIANT_BOOL") + ) +select c.getUnconverted(), + "Implicit cast between semantically different integer types: Boolean to NTSTATUS" diff --git a/src/drivers/general/queries/NtstatusExplicitCast3/NtstatusExplicitCast3.sarif b/src/drivers/general/queries/NtstatusExplicitCast3/NtstatusExplicitCast3.sarif new file mode 100644 index 00000000..4712a0a2 --- /dev/null +++ b/src/drivers/general/queries/NtstatusExplicitCast3/NtstatusExplicitCast3.sarif @@ -0,0 +1,274 @@ +{ + "$schema": "https://json.schemastore.org/sarif-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "CodeQL", + "organization": "GitHub", + "semanticVersion": "2.17.6", + "notifications": [ + { + "id": "cpp/baseline/expected-extracted-files", + "name": "cpp/baseline/expected-extracted-files", + "shortDescription": { + "text": "Expected extracted files" + }, + "fullDescription": { + "text": "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration": { + "enabled": true + }, + "properties": { + "tags": [ + "expected-extracted-files", + "telemetry" + ] + } + }, + { + "id": "cpp/extractor/summary", + "name": "cpp/extractor/summary", + "shortDescription": { + "text": "C++ extractor telemetry" + }, + "fullDescription": { + "text": "C++ extractor telemetry" + }, + "defaultConfiguration": { + "enabled": true + } + } + ], + "rules": [ + { + "id": "cpp/drivers/ntstatus-explicit-cast3", + "name": "cpp/drivers/ntstatus-explicit-cast3", + "shortDescription": { + "text": "Ntstatus Explicit Cast 3" + }, + "fullDescription": { + "text": "Compiler-inserted cast between semantically different integral types" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning" + }, + "properties": { + "tags": [ + "correctness" + ], + "description": "Compiler-inserted cast between semantically different integral types", + "feature.area": "Multiple", + "id": "cpp/drivers/ntstatus-explicit-cast3", + "impact": "Insecure Coding Practice", + "kind": "problem", + "name": "Ntstatus Explicit Cast 3", + "opaqueid": "CQLD-C28716", + "owner.email:": "sdat@microsoft.com", + "platform": "Desktop", + "precision": "medium", + "problem.severity": "warning", + "query-version": "v1", + "repro.text": "", + "scope": "domainspecific" + } + } + ] + }, + "extensions": [ + { + "name": "microsoft/windows-drivers", + "semanticVersion": "1.1.0+ce7d70c32c8e0908d7c329389aa84ac3a89e7feb", + "locations": [ + { + "uri": "file:///C:/codeql-home/WDDST/src/", + "description": { + "text": "The QL pack root directory." + } + }, + { + "uri": "file:///C:/codeql-home/WDDST/src/qlpack.yml", + "description": { + "text": "The QL pack definition file." + } + } + ] + } + ] + }, + "invocations": [ + { + "toolExecutionNotifications": [ + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "message": { + "text": "Internal telemetry for the C++ extractor.\n\nNo action needed.", + "markdown": "Internal telemetry for the C++ extractor.\n\nNo action needed." + }, + "level": "note", + "timeUtc": "2024-08-21T06:43:54.207+00:00", + "descriptor": { + "id": "cpp/extractor/summary", + "index": 1 + }, + "properties": { + "attributes": { + "cache-hits": 0, + "cache-misses": 1, + "extractor-failures": 1, + "extractor-successes": 0, + "trap-caching": "disabled" + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + } + ], + "executionSuccessful": true + } + ], + "artifacts": [ + { + "location": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + }, + { + "location": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + }, + { + "location": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + ], + "results": [ + { + "ruleId": "cpp/drivers/ntstatus-explicit-cast3", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/ntstatus-explicit-cast3", + "index": 0 + }, + "message": { + "text": "Implicit cast between semantically different integer types: Boolean to NTSTATUS" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 36, + "startColumn": 12, + "endColumn": 32 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "4f26d0813db0114f:1", + "primaryLocationStartColumnFingerprint": "7" + } + } + ], + "columnKind": "utf16CodeUnits", + "properties": { + "semmle.formatSpecifier": "sarifv2.1.0" + } + } + ] +} \ No newline at end of file diff --git a/src/drivers/general/queries/NtstatusExplicitCast3/driver_snippet.c b/src/drivers/general/queries/NtstatusExplicitCast3/driver_snippet.c new file mode 100644 index 00000000..8dce4118 --- /dev/null +++ b/src/drivers/general/queries/NtstatusExplicitCast3/driver_snippet.c @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +// Macros to enable or disable a code section that may or may not conflict with this test. +#define SET_DISPATCH 1 + +// Template function. Not used for this test. +void top_level_call() +{ +} +BOOLEAN SomeMemAllocFunction(void *p) +{ + if (p == NULL) + { + return FALSE; + } + return TRUE; +} + +NTSTATUS test_good() +{ + void *MyPtr; + if (SomeMemAllocFunction(&MyPtr) == TRUE) + { + return STATUS_SUCCESS; + } + else + { + return STATUS_NO_MEMORY; + } +} + +NTSTATUS test_bad() +{ + void *MyPtr; + return SomeMemAllocFunction(&MyPtr); +} +// TODO add tests for query \ No newline at end of file diff --git a/src/drivers/general/queries/NullCharacterPointerAssignment/NullCharacterPointerAssignment.qhelp b/src/drivers/general/queries/NullCharacterPointerAssignment/NullCharacterPointerAssignment.qhelp new file mode 100644 index 00000000..0922c480 --- /dev/null +++ b/src/drivers/general/queries/NullCharacterPointerAssignment/NullCharacterPointerAssignment.qhelp @@ -0,0 +1,46 @@ + + + +

    + Possible assignment of '\\0' directly to a pointer +

    +
    + +

    + This warning indicates a probable typographical error: a null character is being assigned to a pointer; it is probably the case that the character is intended as a string terminator and should be assigned to the memory where the pointer is pointing. +

    +
    + +

    + Example of incorrect assignment of '\0' to a pointer +

    + + +

    + Example of correct assignment of '\0' to where the pointer is pointing +

    + + +
    + +

    +

    +
    + +
  • + + Warning C28730 + +
  • +
    +
    diff --git a/src/drivers/general/queries/NullCharacterPointerAssignment/NullCharacterPointerAssignment.ql b/src/drivers/general/queries/NullCharacterPointerAssignment/NullCharacterPointerAssignment.ql new file mode 100644 index 00000000..29ae5cef --- /dev/null +++ b/src/drivers/general/queries/NullCharacterPointerAssignment/NullCharacterPointerAssignment.ql @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/** + * @id cpp/drivers/null-character-pointer-assignment + * @kind problem + * @name Null Character Pointer Assignment + * @description Possible assignment of '\\0' directly to a pointer + * @platform Desktop + * @feature.area Multiple + * @impact Insecure Coding Practice + * @repro.text + * @owner.email: sdat@microsoft.com + * @opaqueid CQLD-C28730 + * @problem.severity warning + * @precision medium + * @tags correctness + * ca_ported + * @scope domainspecific + * @query-version v1 + */ + + +import cpp + +from CharLiteral s, Assignment a +where + s.getCharacter() = "\\0" and + a.getRValue() = s and + a.getLValue().getType().getName().matches("% *") + +select a,"Possible assignment of '\\0' directly to a pointer" \ No newline at end of file diff --git a/src/drivers/general/queries/NullCharacterPointerAssignment/NullCharacterPointerAssignment.sarif b/src/drivers/general/queries/NullCharacterPointerAssignment/NullCharacterPointerAssignment.sarif new file mode 100644 index 00000000..57253045 --- /dev/null +++ b/src/drivers/general/queries/NullCharacterPointerAssignment/NullCharacterPointerAssignment.sarif @@ -0,0 +1,274 @@ +{ + "$schema": "https://json.schemastore.org/sarif-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "CodeQL", + "organization": "GitHub", + "semanticVersion": "2.17.6", + "notifications": [ + { + "id": "cpp/baseline/expected-extracted-files", + "name": "cpp/baseline/expected-extracted-files", + "shortDescription": { + "text": "Expected extracted files" + }, + "fullDescription": { + "text": "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration": { + "enabled": true + }, + "properties": { + "tags": [ + "expected-extracted-files", + "telemetry" + ] + } + }, + { + "id": "cpp/extractor/summary", + "name": "cpp/extractor/summary", + "shortDescription": { + "text": "C++ extractor telemetry" + }, + "fullDescription": { + "text": "C++ extractor telemetry" + }, + "defaultConfiguration": { + "enabled": true + } + } + ], + "rules": [ + { + "id": "cpp/drivers/null-character-pointer-assignment", + "name": "cpp/drivers/null-character-pointer-assignment", + "shortDescription": { + "text": "Null Character Pointer Assignment" + }, + "fullDescription": { + "text": "Possible assignment of '\\\\0' directly to a pointer" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning" + }, + "properties": { + "tags": [ + "correctness" + ], + "description": "Possible assignment of '\\\\0' directly to a pointer", + "feature.area": "Multiple", + "id": "cpp/drivers/null-character-pointer-assignment", + "impact": "Insecure Coding Practice", + "kind": "problem", + "name": "Null Character Pointer Assignment", + "opaqueid": "CQLD-C28730", + "owner.email:": "sdat@microsoft.com", + "platform": "Desktop", + "precision": "medium", + "problem.severity": "warning", + "query-version": "v1", + "repro.text": "", + "scope": "domainspecific" + } + } + ] + }, + "extensions": [ + { + "name": "microsoft/windows-drivers", + "semanticVersion": "1.1.0+2034189b0eace12539d3b51ced8d39b9cc9717f0", + "locations": [ + { + "uri": "file:///C:/codeql-home/WDDST/src/", + "description": { + "text": "The QL pack root directory." + } + }, + { + "uri": "file:///C:/codeql-home/WDDST/src/qlpack.yml", + "description": { + "text": "The QL pack definition file." + } + } + ] + } + ] + }, + "invocations": [ + { + "toolExecutionNotifications": [ + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "message": { + "text": "Internal telemetry for the C++ extractor.\n\nNo action needed.", + "markdown": "Internal telemetry for the C++ extractor.\n\nNo action needed." + }, + "level": "note", + "timeUtc": "2024-08-17T05:27:20.110+00:00", + "descriptor": { + "id": "cpp/extractor/summary", + "index": 1 + }, + "properties": { + "attributes": { + "cache-hits": 0, + "cache-misses": 1, + "extractor-failures": 1, + "extractor-successes": 0, + "trap-caching": "disabled" + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + } + ], + "executionSuccessful": true + } + ], + "artifacts": [ + { + "location": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + }, + { + "location": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + }, + { + "location": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + ], + "results": [ + { + "ruleId": "cpp/drivers/null-character-pointer-assignment", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/null-character-pointer-assignment", + "index": 0 + }, + "message": { + "text": "Possible assignment of '\\0' directly to a pointer" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 18, + "startColumn": 5, + "endColumn": 13 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "3c26f4ab5be28a11:1", + "primaryLocationStartColumnFingerprint": "0" + } + } + ], + "columnKind": "utf16CodeUnits", + "properties": { + "semmle.formatSpecifier": "sarifv2.1.0" + } + } + ] +} \ No newline at end of file diff --git a/src/drivers/general/queries/NullCharacterPointerAssignment/driver_snippet.c b/src/drivers/general/queries/NullCharacterPointerAssignment/driver_snippet.c new file mode 100644 index 00000000..5cb8dde1 --- /dev/null +++ b/src/drivers/general/queries/NullCharacterPointerAssignment/driver_snippet.c @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +// Macros to enable or disable a code section that may or may not conflict with this test. +#define SET_DISPATCH 1 + +// Template function. Not used for this test. +void top_level_call() +{ +} +void test_bad() +{ + char a[8]; + char *p = a; + char x = 0; + char y = '0'; + + p = '\0'; // should be *p = '\0'; +} + +void test_good() +{ + char a[8]; + char *p = a; + *p = '\0'; // correct! +} +// TODO add tests for query \ No newline at end of file diff --git a/src/drivers/general/queries/OperandAssignment/OperandAssignment.qhelp b/src/drivers/general/queries/OperandAssignment/OperandAssignment.qhelp new file mode 100644 index 00000000..c427d291 --- /dev/null +++ b/src/drivers/general/queries/OperandAssignment/OperandAssignment.qhelp @@ -0,0 +1,23 @@ + + + +

    + An assignment has been made to an operand, which should only be modified using bit sets and clears +

    +
    + +

    + The driver is using an assignment to modify an operand. Assigning a value might unintentionally change the values of bits other than those that it needs to change, resulting in unexpected consequences. +

    +
    + + + + +
  • + + C28129 + +
  • +
    +
    diff --git a/src/drivers/general/queries/OperandAssignment/OperandAssignment.ql b/src/drivers/general/queries/OperandAssignment/OperandAssignment.ql new file mode 100644 index 00000000..4c78ccd3 --- /dev/null +++ b/src/drivers/general/queries/OperandAssignment/OperandAssignment.ql @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/** + * @id cpp/drivers/operand-assignment + * @name Operand Assignment + * @description C28129: An assignment has been made to an operand, which should only be modified using bit sets and clears + * @platform Desktop + * @security.severity Medium + * @feature.area Multiple + * @impact Exploitable Design + * @repro.text + * @owner.email sdat@microsoft.com + * @opaqueid CQLD-C28129 + * @kind problem + * @problem.severity warning + * @precision medium + * @tags correctness + * ca_ported + * wddst + * @scope domainspecific + * @query-version v1 + */ + +import cpp + +from FieldAccess fa, Field f +where + f = fa.getTarget() and + exists(Struct v | fa.getTarget() = v.getAMember() | v.getName() = "_DEVICE_OBJECT") and + f.getName() = "Flags" and + fa.getParent() instanceof AssignExpr + +select fa, "An assignment has been made to an operand $@, which should only be modified using bit sets and clears.", fa, fa.toString() diff --git a/src/drivers/general/queries/OperandAssignment/OperandAssignment.sarif b/src/drivers/general/queries/OperandAssignment/OperandAssignment.sarif new file mode 100644 index 00000000..2254b34c --- /dev/null +++ b/src/drivers/general/queries/OperandAssignment/OperandAssignment.sarif @@ -0,0 +1,219 @@ +{ + "$schema" : "https://json.schemastore.org/sarif-2.1.0.json", + "version" : "2.1.0", + "runs" : [ { + "tool" : { + "driver" : { + "name" : "CodeQL", + "organization" : "GitHub", + "semanticVersion" : "2.15.4", + "notifications" : [ { + "id" : "cpp/baseline/expected-extracted-files", + "name" : "cpp/baseline/expected-extracted-files", + "shortDescription" : { + "text" : "Expected extracted files" + }, + "fullDescription" : { + "text" : "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration" : { + "enabled" : true + }, + "properties" : { + "tags" : [ "expected-extracted-files", "telemetry" ] + } + } ], + "rules" : [ { + "id" : "cpp/drivers/operand-assignment", + "name" : "cpp/drivers/operand-assignment", + "shortDescription" : { + "text" : "Operand Assignment" + }, + "fullDescription" : { + "text" : "C28129: An assignment has been made to an operand, which should only be modified using bit sets and clears" + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "properties" : { + "tags" : [ "correctness", "wddst" ], + "description" : "C28129: An assignment has been made to an operand, which should only be modified using bit sets and clears", + "feature.area" : "Multiple", + "id" : "cpp/drivers/operand-assignment", + "impact" : "Exploitable Design", + "kind" : "problem", + "name" : "Operand Assignment", + "opaqueid" : "CQLD-D0006", + "owner.email" : "sdat@microsoft.com", + "platform" : "Desktop", + "precision" : "medium", + "problem.severity" : "warning", + "query-version" : "v1", + "repro.text" : "", + "scope" : "domainspecific", + "security.severity" : "Medium" + } + } ] + }, + "extensions" : [ { + "name" : "microsoft/windows-drivers", + "semanticVersion" : "1.0.13+98d928b522fe299c4ef49ba9d67e17d43f15870f", + "locations" : [ { + "uri" : "file:///C:/codeql-home/WDDST/src/", + "description" : { + "text" : "The QL pack root directory." + } + }, { + "uri" : "file:///C:/codeql-home/WDDST/src/qlpack.yml", + "description" : { + "text" : "The QL pack definition file." + } + } ] + } ] + }, + "invocations" : [ { + "toolExecutionNotifications" : [ { + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + } + } + } ], + "message" : { + "text" : "" + }, + "level" : "none", + "descriptor" : { + "id" : "cpp/baseline/expected-extracted-files", + "index" : 0 + }, + "properties" : { + "formattedMessage" : { + "text" : "" + } + } + }, { + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/fail_driver1.c", + "uriBaseId" : "%SRCROOT%", + "index" : 1 + } + } + } ], + "message" : { + "text" : "" + }, + "level" : "none", + "descriptor" : { + "id" : "cpp/baseline/expected-extracted-files", + "index" : 0 + }, + "properties" : { + "formattedMessage" : { + "text" : "" + } + } + }, { + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/fail_driver1.h", + "uriBaseId" : "%SRCROOT%", + "index" : 2 + } + } + } ], + "message" : { + "text" : "" + }, + "level" : "none", + "descriptor" : { + "id" : "cpp/baseline/expected-extracted-files", + "index" : 0 + }, + "properties" : { + "formattedMessage" : { + "text" : "" + } + } + } ], + "executionSuccessful" : true + } ], + "artifacts" : [ { + "location" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + } + }, { + "location" : { + "uri" : "driver/fail_driver1.c", + "uriBaseId" : "%SRCROOT%", + "index" : 1 + } + }, { + "location" : { + "uri" : "driver/fail_driver1.h", + "uriBaseId" : "%SRCROOT%", + "index" : 2 + } + } ], + "results" : [ { + "ruleId" : "cpp/drivers/operand-assignment", + "ruleIndex" : 0, + "rule" : { + "id" : "cpp/drivers/operand-assignment", + "index" : 0 + }, + "message" : { + "text" : "An assignment has been made to an operand [Flags](1), which should only be modified using bit sets and clears." + }, + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + }, + "region" : { + "startLine" : 14, + "startColumn" : 10, + "endColumn" : 15 + } + } + } ], + "partialFingerprints" : { + "primaryLocationLineHash" : "62fc3a0be3fd95f4:1", + "primaryLocationStartColumnFingerprint" : "5" + }, + "relatedLocations" : [ { + "id" : 1, + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + }, + "region" : { + "startLine" : 14, + "startColumn" : 10, + "endColumn" : 15 + } + }, + "message" : { + "text" : "Flags" + } + } ] + } ], + "columnKind" : "utf16CodeUnits", + "properties" : { + "semmle.formatSpecifier" : "sarifv2.1.0" + } + } ] +} \ No newline at end of file diff --git a/src/drivers/general/queries/OperandAssignment/driver_snippet.c b/src/drivers/general/queries/OperandAssignment/driver_snippet.c new file mode 100644 index 00000000..27476709 --- /dev/null +++ b/src/drivers/general/queries/OperandAssignment/driver_snippet.c @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +// +// driver_snippet.c +// + +#define SET_DISPATCH 1 +// Template. Not called in this test. +void top_level_call() {} +PDEVICE_OBJECT fdo = NULL; + +void bad_operand_assignment() +{ + fdo->Flags = DO_BUFFERED_IO; +} +void good_operand_assignment() +{ + fdo->Flags |= DO_BUFFERED_IO; + +} diff --git a/src/drivers/general/queries/PointerVariableSize/PointerVariableSize.qhelp b/src/drivers/general/queries/PointerVariableSize/PointerVariableSize.qhelp new file mode 100644 index 00000000..5950616e --- /dev/null +++ b/src/drivers/general/queries/PointerVariableSize/PointerVariableSize.qhelp @@ -0,0 +1,43 @@ + + + +

    + The driver is taking the size of a pointer variable, not the size of the value that is pointed to +

    +
    + +

    + If the driver needs the size of the pointed-to value, change the code so that it references the value. If the driver actually needs the size of the pointer, take the size of the pointer type (for example, LPSTR, char* or even void*) to clarify that this is the intent. +

    +
    + +

    + The following code example elicits this warning. +

    + + +

    + The following code example avoids this warning. +

    + + +
    + + +
  • + + Warning C28132 + +
  • +
    +
    diff --git a/src/drivers/general/queries/PointerVariableSize/PointerVariableSize.ql b/src/drivers/general/queries/PointerVariableSize/PointerVariableSize.ql new file mode 100644 index 00000000..9310a832 --- /dev/null +++ b/src/drivers/general/queries/PointerVariableSize/PointerVariableSize.ql @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/** + * @id cpp/drivers/pointer-variable-size + * @kind problem + * @name Pointer Variable Size + * @description The driver is taking the size of a pointer variable, not the size of the value that is pointed to + * @platform Desktop + * @feature.area Multiple + * @impact Insecure Coding Practice + * @repro.text + * @owner.email: sdat@microsoft.com + * @opaqueid CQLD-C28132 + * @problem.severity warning + * @precision medium + * @tags correctness + * ca_ported + * @scope domainspecific + * @query-version v1 + */ + +import cpp + +from SizeofExprOperator e, VariableAccess va +where + va = e.getExprOperand() and + va.getTarget().getUnspecifiedType() instanceof PointerType + and not va.isAffectedByMacro() +select e, "Taking the size of a pointer variable, not the size of the value that is pointed to." diff --git a/src/drivers/general/queries/PointerVariableSize/PointerVariableSize.sarif b/src/drivers/general/queries/PointerVariableSize/PointerVariableSize.sarif new file mode 100644 index 00000000..51e78e8d --- /dev/null +++ b/src/drivers/general/queries/PointerVariableSize/PointerVariableSize.sarif @@ -0,0 +1,274 @@ +{ + "$schema": "https://json.schemastore.org/sarif-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "CodeQL", + "organization": "GitHub", + "semanticVersion": "2.17.6", + "notifications": [ + { + "id": "cpp/baseline/expected-extracted-files", + "name": "cpp/baseline/expected-extracted-files", + "shortDescription": { + "text": "Expected extracted files" + }, + "fullDescription": { + "text": "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration": { + "enabled": true + }, + "properties": { + "tags": [ + "expected-extracted-files", + "telemetry" + ] + } + }, + { + "id": "cpp/extractor/summary", + "name": "cpp/extractor/summary", + "shortDescription": { + "text": "C++ extractor telemetry" + }, + "fullDescription": { + "text": "C++ extractor telemetry" + }, + "defaultConfiguration": { + "enabled": true + } + } + ], + "rules": [ + { + "id": "cpp/drivers/pointer-variable-size", + "name": "cpp/drivers/pointer-variable-size", + "shortDescription": { + "text": "Pointer Variable Size" + }, + "fullDescription": { + "text": "The driver is taking the size of a pointer variable, not the size of the value that is pointed to" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning" + }, + "properties": { + "tags": [ + "correctness" + ], + "description": "The driver is taking the size of a pointer variable, not the size of the value that is pointed to", + "feature.area": "Multiple", + "id": "cpp/drivers/pointer-variable-size", + "impact": "Insecure Coding Practice", + "kind": "problem", + "name": "Pointer Variable Size", + "opaqueid": "CQLD-C28132", + "owner.email:": "sdat@microsoft.com", + "platform": "Desktop", + "precision": "medium", + "problem.severity": "warning", + "query-version": "v1", + "repro.text": "", + "scope": "domainspecific" + } + } + ] + }, + "extensions": [ + { + "name": "microsoft/windows-drivers", + "semanticVersion": "1.1.0+2034189b0eace12539d3b51ced8d39b9cc9717f0", + "locations": [ + { + "uri": "file:///C:/codeql-home/WDDST/src/", + "description": { + "text": "The QL pack root directory." + } + }, + { + "uri": "file:///C:/codeql-home/WDDST/src/qlpack.yml", + "description": { + "text": "The QL pack definition file." + } + } + ] + } + ] + }, + "invocations": [ + { + "toolExecutionNotifications": [ + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "message": { + "text": "Internal telemetry for the C++ extractor.\n\nNo action needed.", + "markdown": "Internal telemetry for the C++ extractor.\n\nNo action needed." + }, + "level": "note", + "timeUtc": "2024-08-16T04:11:45.824+00:00", + "descriptor": { + "id": "cpp/extractor/summary", + "index": 1 + }, + "properties": { + "attributes": { + "cache-hits": 0, + "cache-misses": 1, + "extractor-failures": 1, + "extractor-successes": 0, + "trap-caching": "disabled" + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + } + ], + "executionSuccessful": true + } + ], + "artifacts": [ + { + "location": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + }, + { + "location": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + }, + { + "location": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + ], + "results": [ + { + "ruleId": "cpp/drivers/pointer-variable-size", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/pointer-variable-size", + "index": 0 + }, + "message": { + "text": "Taking the size of a pointer variable, not the size of the value that is pointed to." + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 14, + "startColumn": 18, + "endColumn": 27 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "9f3af2349ee4b54b:1", + "primaryLocationStartColumnFingerprint": "13" + } + } + ], + "columnKind": "utf16CodeUnits", + "properties": { + "semmle.formatSpecifier": "sarifv2.1.0" + } + } + ] +} \ No newline at end of file diff --git a/src/drivers/general/queries/PointerVariableSize/driver_snippet.c b/src/drivers/general/queries/PointerVariableSize/driver_snippet.c new file mode 100644 index 00000000..08aec78f --- /dev/null +++ b/src/drivers/general/queries/PointerVariableSize/driver_snippet.c @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +// Macros to enable or disable a code section that may or may not conflict with this test. +#define SET_DISPATCH 1 + +// Template function. Not used for this test. +void top_level_call() +{ +} + +void bad(){ + char* b = 0; + memset(b, 0, sizeof(b)); +} + +void good(){ + char* b = 0; + memset(b, 0, sizeof(*b)); +} +// TODO add tests for query \ No newline at end of file diff --git a/src/drivers/general/queries/PoolTagIntegral/PoolTagIntegral.ql b/src/drivers/general/queries/PoolTagIntegral/PoolTagIntegral.ql index 4fe047b6..2cea55bb 100644 --- a/src/drivers/general/queries/PoolTagIntegral/PoolTagIntegral.ql +++ b/src/drivers/general/queries/PoolTagIntegral/PoolTagIntegral.ql @@ -15,6 +15,7 @@ * @problem.severity warning * @precision high * @tags correctness + * ca_ported * wddst * @scope domainspecific * @query-version v1 diff --git a/src/drivers/general/queries/RoutineFunctionTypeNotExpected/RoutineFunctionTypeNotExpected.ql b/src/drivers/general/queries/RoutineFunctionTypeNotExpected/RoutineFunctionTypeNotExpected.ql index c7cb21e3..4e935f97 100644 --- a/src/drivers/general/queries/RoutineFunctionTypeNotExpected/RoutineFunctionTypeNotExpected.ql +++ b/src/drivers/general/queries/RoutineFunctionTypeNotExpected/RoutineFunctionTypeNotExpected.ql @@ -15,6 +15,7 @@ * @problem.severity warning * @precision high * @tags correctness + * ca_ported * wddst * @scope domainspecific * @query-version v3 diff --git a/src/drivers/general/queries/StaticInitializer/StaticInitializer.qhelp b/src/drivers/general/queries/StaticInitializer/StaticInitializer.qhelp new file mode 100644 index 00000000..08035c9a --- /dev/null +++ b/src/drivers/general/queries/StaticInitializer/StaticInitializer.qhelp @@ -0,0 +1,71 @@ + + + +

    + Static initializers of global or static const variables can often + be fully evaluated at compile time, thus generated in RDATA. + However if any initializer is a pointer-to-member-function where + it is a non-static function, the entire initialier may be placed + in copy-on-write pages, which has a performance cost. +

    +
    + +

    + For binaries which require fast loading and minimizing copy on + write pages, consider making sure all function pointer in the + static initializer are not pointer-to-member-function. If a + pointer-to-member-function is required, write a simple static + member function that wraps a call to the actual member function. +

    +
    + +

    + Code which triggers this query: +

    + + +

    + Good code: +

    + memberFunc(); } + ... + }; + const StructType MyStruct[] = { + ... + &MyClass::memberFuncWrap, + ... + }; + ]]> + +
    + +

    + +

    +
    + +
  • + + C28651 + +
  • +
    +
    diff --git a/src/drivers/general/queries/StaticInitializer/StaticInitializer.ql b/src/drivers/general/queries/StaticInitializer/StaticInitializer.ql new file mode 100644 index 00000000..5416f454 --- /dev/null +++ b/src/drivers/general/queries/StaticInitializer/StaticInitializer.ql @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/** + * @id cpp/drivers/static-initializer + * @kind problem + * @name Static Initializer + * @description Static initializers of global or static const variables can often + * be fully evaluated at compile time, thus generated in RDATA. + * However if any initializer is a pointer-to-member-function where + * it is a non-static function, the entire initialier may be placed + * in copy-on-write pages, which has a performance cost. + * @platform Desktop + * @feature.area Multiple + * @impact Insecure Coding Practice + * @repro.text For binaries which require fast loading and minimizing copy on + * write pages, consider making sure all function pointer in the + * static initializer are not pointer-to-member-function. If a + * pointer-to-member-function is required, write a simple static + * member function that wraps a call to the actual member function. + * @owner.email: sdat@microsoft.com + * @opaqueid CQLD-C28651 + * @problem.severity warning + * @precision medium + * @tags performance + * ca_ported + * @scope domainspecific + * @query-version v1 + */ + +import cpp + +// Initalizer that is a pointer to a member function that is a non-static function +from Initializer i, FunctionAccess f +where + f.getParent*() = i.getExpr() and + f.getTarget() instanceof MemberFunction and + not f.getTarget().getADeclarationEntry().getDeclaration().isStatic() and + ( + i.getDeclaration() instanceof GlobalVariable + or + (i.getDeclaration().isStatic() and i.getDeclaration().(Variable).isConst()) + ) +//global or static const +select f, "Static initializer causes copy on write pages due to member function pointer(s): $@", f.getTarget(), f.getTarget().getName() diff --git a/src/drivers/general/queries/StaticInitializer/StaticInitializer.sarif b/src/drivers/general/queries/StaticInitializer/StaticInitializer.sarif new file mode 100644 index 00000000..f83bbb59 --- /dev/null +++ b/src/drivers/general/queries/StaticInitializer/StaticInitializer.sarif @@ -0,0 +1,536 @@ +{ + "$schema": "https://json.schemastore.org/sarif-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "CodeQL", + "organization": "GitHub", + "semanticVersion": "2.20.3", + "notifications": [ + { + "id": "cpp/baseline/expected-extracted-files", + "name": "cpp/baseline/expected-extracted-files", + "shortDescription": { + "text": "Expected extracted files" + }, + "fullDescription": { + "text": "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration": { + "enabled": true + }, + "properties": { + "tags": [ + "expected-extracted-files", + "telemetry" + ] + } + }, + { + "id": "cpp/extractor/summary", + "name": "cpp/extractor/summary", + "shortDescription": { + "text": "C++ extractor telemetry" + }, + "fullDescription": { + "text": "C++ extractor telemetry" + }, + "defaultConfiguration": { + "enabled": true + } + } + ], + "rules": [ + { + "id": "cpp/drivers/static-initializer", + "name": "cpp/drivers/static-initializer", + "shortDescription": { + "text": "Static Initializer" + }, + "fullDescription": { + "text": "Static initializers of global or static const variables can often be fully evaluated at compile time, thus generated in RDATA. However if any initializer is a pointer-to-member-function where it is a non-static function, the entire initialier may be placed in copy-on-write pages, which has a performance cost." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning" + }, + "properties": { + "tags": [ + "performance" + ], + "description": "Static initializers of global or static const variables can often\n be fully evaluated at compile time, thus generated in RDATA.\n However if any initializer is a pointer-to-member-function where\n it is a non-static function, the entire initialier may be placed\n in copy-on-write pages, which has a performance cost.", + "feature.area": "Multiple", + "id": "cpp/drivers/static-initializer", + "impact": "Insecure Coding Practice", + "kind": "problem", + "name": "Static Initializer", + "opaqueid": "CQLD-C28651", + "owner.email:": "sdat@microsoft.com", + "platform": "Desktop", + "precision": "medium", + "problem.severity": "warning", + "query-version": "v1", + "repro.text": "For binaries which require fast loading and minimizing copy on\n write pages, consider making sure all function pointer in the\n static initializer are not pointer-to-member-function. If a\n pointer-to-member-function is required, write a simple static\n member function that wraps a call to the actual member function.", + "scope": "domainspecific" + } + } + ] + }, + "extensions": [ + { + "name": "microsoft/windows-drivers", + "semanticVersion": "1.3.0+93a62f844e68510994a850352ad01ec90cadea19", + "locations": [ + { + "uri": "file:///C:/codeql-home/WDDST/src/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///C:/codeql-home/WDDST/src/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + }, + { + "name": "codeql/cpp-all", + "semanticVersion": "3.1.0+d42788844f7ec0a6b9832140313cc2318e513987", + "locations": [ + { + "uri": "file:///C:/Users/jronstadt/.codeql/packages/codeql/cpp-all/3.1.0/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///C:/Users/jronstadt/.codeql/packages/codeql/cpp-all/3.1.0/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + } + ] + }, + "invocations": [ + { + "toolExecutionNotifications": [ + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "Driver.cpp", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "Driver.h", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "Queue.c", + "uriBaseId": "%SRCROOT%", + "index": 3 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "Public.h", + "uriBaseId": "%SRCROOT%", + "index": 4 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "Trace.h", + "uriBaseId": "%SRCROOT%", + "index": 5 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "Device.h", + "uriBaseId": "%SRCROOT%", + "index": 6 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.cpp", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "Device.c", + "uriBaseId": "%SRCROOT%", + "index": 7 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "Queue.h", + "uriBaseId": "%SRCROOT%", + "index": 8 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "message": { + "text": "Internal telemetry for the C++ extractor.\n\nNo action needed.", + "markdown": "Internal telemetry for the C++ extractor.\n\nNo action needed." + }, + "level": "note", + "timeUtc": "2025-01-31T02:02:02.676882Z", + "descriptor": { + "id": "cpp/extractor/summary", + "index": 1 + }, + "properties": { + "attributes": { + "cache-hits": 0, + "cache-misses": 3, + "compilers": [ + { + "program": "cl", + "version": "Microsoft (R) C/C++ Optimizing Compiler Version 19.42.34436 for x64" + } + ], + "extractor-failures": 3, + "extractor-successes": 0, + "trap-caching": "disabled" + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + } + ], + "executionSuccessful": true + } + ], + "artifacts": [ + { + "location": { + "uri": "driver/driver_snippet.cpp", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + }, + { + "location": { + "uri": "Driver.cpp", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + }, + { + "location": { + "uri": "Driver.h", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + }, + { + "location": { + "uri": "Queue.c", + "uriBaseId": "%SRCROOT%", + "index": 3 + } + }, + { + "location": { + "uri": "Public.h", + "uriBaseId": "%SRCROOT%", + "index": 4 + } + }, + { + "location": { + "uri": "Trace.h", + "uriBaseId": "%SRCROOT%", + "index": 5 + } + }, + { + "location": { + "uri": "Device.h", + "uriBaseId": "%SRCROOT%", + "index": 6 + } + }, + { + "location": { + "uri": "Device.c", + "uriBaseId": "%SRCROOT%", + "index": 7 + } + }, + { + "location": { + "uri": "Queue.h", + "uriBaseId": "%SRCROOT%", + "index": 8 + } + } + ], + "results": [ + { + "ruleId": "cpp/drivers/static-initializer", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/static-initializer", + "index": 0 + }, + "message": { + "text": "Static initializer causes copy on write pages due to member function pointer(s): [memberFunc](1)" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.cpp", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 27, + "startColumn": 5, + "endColumn": 25 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "d09d666579d2855:1", + "primaryLocationStartColumnFingerprint": "0" + }, + "relatedLocations": [ + { + "id": 1, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.cpp", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 16, + "startColumn": 10, + "endColumn": 20 + } + }, + "message": { + "text": "memberFunc" + } + } + ] + } + ], + "columnKind": "utf16CodeUnits", + "properties": { + "semmle.formatSpecifier": "sarifv2.1.0" + } + } + ] +} \ No newline at end of file diff --git a/src/drivers/general/queries/StaticInitializer/driver_snippet.cpp b/src/drivers/general/queries/StaticInitializer/driver_snippet.cpp new file mode 100644 index 00000000..29787524 --- /dev/null +++ b/src/drivers/general/queries/StaticInitializer/driver_snippet.cpp @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +// Macros to enable or disable a code section that may or may not conflict with this test. +#define SET_DISPATCH 1 + +// Template function. Not used for this test. +void top_level_call() +{ +} + +class MyClass +{ + //... + public: + bool memberFunc(); + //... +}; + +typedef struct _MYSTRUCT +{ + bool (MyClass::*pfn)(); +} StructType; + +const StructType badStruct[1] = { + // ... + &MyClass::memberFunc + // ... +}; + + +// good code + +class MyClass2 +{ + //... + public: + bool memberFunc(); + static bool memberFuncWrap(MyClass2 *thisPtr) + { return thisPtr->memberFunc(); } + //... +}; + +typedef struct _MYSTRUCT2 +{ + bool (*pfn)(MyClass2*); +} StructType2; + +const StructType2 goodStruct[1] = { + &MyClass2::memberFuncWrap +}; + diff --git a/src/drivers/general/queries/StrSafe/StrSafe.ql b/src/drivers/general/queries/StrSafe/StrSafe.ql index e8790204..d041726e 100644 --- a/src/drivers/general/queries/StrSafe/StrSafe.ql +++ b/src/drivers/general/queries/StrSafe/StrSafe.ql @@ -17,6 +17,7 @@ * @precision high * @tags security * wddst + * ca_ported * @scope domainspecific * @query-version v1 */ diff --git a/src/drivers/general/queries/StrictTypeMatch/StrictTypeMatch.qhelp b/src/drivers/general/queries/StrictTypeMatch/StrictTypeMatch.qhelp new file mode 100644 index 00000000..5e889488 --- /dev/null +++ b/src/drivers/general/queries/StrictTypeMatch/StrictTypeMatch.qhelp @@ -0,0 +1,50 @@ + + + +

    + The argument should exactly match the type +

    +
    + +

    + An enumerated value in a function call does not match the type specified for the parameter in the function declaration. This error can occur when parameters are mis-coded, missing, or out of order. Because C permits enumerated values to be used interchangeably, and to be used interchangeably with integer constants, it is not unusual to pass the wrong enumerated value to a function without recognizing the error. +

    +
    + +

    + The following code example elicits this warning. +

    + + +

    + The following code example avoids this warning. +

    + + +
    + +

    +

    +
    + +
  • + + C28139 + +
  • +
    +
    diff --git a/src/drivers/general/queries/StrictTypeMatch/StrictTypeMatch.ql b/src/drivers/general/queries/StrictTypeMatch/StrictTypeMatch.ql new file mode 100644 index 00000000..a219cfa4 --- /dev/null +++ b/src/drivers/general/queries/StrictTypeMatch/StrictTypeMatch.ql @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/** + * @id cpp/drivers/strict-type-match + * @kind problem + * @name Strict Type Match + * @description The argument should exactly match the type + * @platform Desktop + * @feature.area Multiple + * @impact Insecure Coding Practice + * @repro.text + * @owner.email: sdat@microsoft.com + * @opaqueid CQLD-C28139 + * @problem.severity warning + * @precision medium + * @tags correctness + * ca_ported + * @scope domainspecific + * @query-version v1 + */ + +import cpp +import drivers.libraries.SAL + +from EnumConstantAccess eca, FunctionCall fc, Parameter p, int i +where + fc.getArgument(i) = eca and + not fc.getTarget().getName().matches("operator new%") and // exclude new operators + p = fc.getTarget().getParameter(i) and + // check for pattern __drv_strictType(typename, mode) + p instanceof SALParameter and + exists(string enumType1, string enumType2 | + enumType1 = eca.getTarget().getDeclaringEnum().toString() and + enumType2 = + p.(SALParameter) + .getAnnotation() + .getUnexpandedArgument(0) + .toString() + .splitAt("/", _) + .replaceAll("enum", "") + .trim() and + not enumType2.matches("__drv_%") and // exclude other SAL annotations + not exists(string allowedType | + allowedType = + p.(SALParameter) + .getAnnotation() + .getUnexpandedArgument(0) + .toString() + .splitAt("/", _) + .replaceAll("enum", "") + .trim() and + allowedType = enumType1 + ) + ) +select eca, + "Enumerated value in a function call does not match the type specified for the parameter in the function declaration" diff --git a/src/drivers/general/queries/StrictTypeMatch/StrictTypeMatch.sarif b/src/drivers/general/queries/StrictTypeMatch/StrictTypeMatch.sarif new file mode 100644 index 00000000..51126d24 --- /dev/null +++ b/src/drivers/general/queries/StrictTypeMatch/StrictTypeMatch.sarif @@ -0,0 +1,319 @@ +{ + "$schema": "https://json.schemastore.org/sarif-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "CodeQL", + "organization": "GitHub", + "semanticVersion": "2.20.4", + "notifications": [ + { + "id": "cpp/baseline/expected-extracted-files", + "name": "cpp/baseline/expected-extracted-files", + "shortDescription": { + "text": "Expected extracted files" + }, + "fullDescription": { + "text": "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration": { + "enabled": true + }, + "properties": { + "tags": [ + "expected-extracted-files", + "telemetry" + ] + } + }, + { + "id": "cpp/extractor/summary", + "name": "cpp/extractor/summary", + "shortDescription": { + "text": "C++ extractor telemetry" + }, + "fullDescription": { + "text": "C++ extractor telemetry" + }, + "defaultConfiguration": { + "enabled": true + } + } + ], + "rules": [ + { + "id": "cpp/drivers/strict-type-match", + "name": "cpp/drivers/strict-type-match", + "shortDescription": { + "text": "Strict Type Match" + }, + "fullDescription": { + "text": "The argument should exactly match the type" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning" + }, + "properties": { + "tags": [ + "correctness", + "ca_ported" + ], + "description": "The argument should exactly match the type", + "feature.area": "Multiple", + "id": "cpp/drivers/strict-type-match", + "impact": "Insecure Coding Practice", + "kind": "problem", + "name": "Strict Type Match", + "opaqueid": "CQLD-C28139", + "owner.email:": "sdat@microsoft.com", + "platform": "Desktop", + "precision": "medium", + "problem.severity": "warning", + "query-version": "v1", + "repro.text": "", + "scope": "domainspecific" + } + } + ] + }, + "extensions": [ + { + "name": "microsoft/windows-drivers", + "semanticVersion": "1.5.0+2e3c9a766fe03e14d0f16737fa5527c6ab4d2487", + "locations": [ + { + "uri": "file:///C:/codeql-home/WDDST/src/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///C:/codeql-home/WDDST/src/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + }, + { + "name": "codeql/cpp-all", + "semanticVersion": "4.0.0+c524a98eb91c769cb2994b8373181c2ebd27c20f", + "locations": [ + { + "uri": "file:///C:/Users/jronstadt/.codeql/packages/codeql/cpp-all/4.0.0/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///C:/Users/jronstadt/.codeql/packages/codeql/cpp-all/4.0.0/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + } + ] + }, + "invocations": [ + { + "toolExecutionNotifications": [ + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "message": { + "text": "Internal telemetry for the C++ extractor.\n\nNo action needed.", + "markdown": "Internal telemetry for the C++ extractor.\n\nNo action needed." + }, + "level": "note", + "timeUtc": "2025-02-25T02:03:54.179070Z", + "descriptor": { + "id": "cpp/extractor/summary", + "index": 1 + }, + "properties": { + "attributes": { + "cache-hits": 0, + "cache-misses": 1, + "compilers": [ + { + "program": "cl", + "version": "Microsoft (R) C/C++ Optimizing Compiler Version 19.42.34438 for x64" + } + ], + "extractor-failures": 1, + "extractor-successes": 0, + "trap-caching": "disabled" + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + } + ], + "executionSuccessful": true + } + ], + "artifacts": [ + { + "location": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + }, + { + "location": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + }, + { + "location": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + ], + "results": [ + { + "ruleId": "cpp/drivers/strict-type-match", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/strict-type-match", + "index": 0 + }, + "message": { + "text": "Enumerated value in a function call does not match the type specified for the parameter in the function declaration" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 24, + "startColumn": 9, + "endColumn": 18 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "7355117ca6f969b0:1", + "primaryLocationStartColumnFingerprint": "0" + } + } + ], + "columnKind": "utf16CodeUnits", + "properties": { + "semmle.formatSpecifier": "sarifv2.1.0" + } + } + ] +} \ No newline at end of file diff --git a/src/drivers/general/queries/StrictTypeMatch/driver_snippet.c b/src/drivers/general/queries/StrictTypeMatch/driver_snippet.c new file mode 100644 index 00000000..58f06ca7 --- /dev/null +++ b/src/drivers/general/queries/StrictTypeMatch/driver_snippet.c @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +// Macros to enable or disable a code section that may or may not conflict with this test. +#define SET_DISPATCH 1 + +// Template function. Not used for this test. +void top_level_call() +{ +} +KEVENT EventDone; + +void test_func( + MODE WaitMode) +{ + UNREFERENCED_PARAMETER(WaitMode); +}; + +void bad_call() +{ + KeWaitForSingleObject( + &EventDone, + Executive, + Executive, + FALSE, + NULL); + + test_func(Executive); // Doesn't cause warning because the function isn't annotated +} + +void good_call() +{ + KeWaitForSingleObject( + &EventDone, + Executive, + KernelMode, + FALSE, + NULL); + + test_func(KernelMode); + +} \ No newline at end of file diff --git a/src/drivers/general/queries/experimental/DefaultPoolTagExtended/DefaultPoolTagExtended.ql b/src/drivers/general/queries/experimental/DefaultPoolTagExtended/DefaultPoolTagExtended.ql index 2b85432f..06c207c4 100644 --- a/src/drivers/general/queries/experimental/DefaultPoolTagExtended/DefaultPoolTagExtended.ql +++ b/src/drivers/general/queries/experimental/DefaultPoolTagExtended/DefaultPoolTagExtended.ql @@ -14,11 +14,13 @@ * @problem.severity warning * @precision medium * @tags correctness +* ca_ported * @scope domainspecific * @query-version v1 */ + import cpp -import semmle.code.cpp.dataflow.DataFlow +import semmle.code.cpp.dataflow.new.DataFlow /** A pool allocation function (has a ULONG "Tag" field, a "Flags" field, and a size parameter.) */ class PoolTypeFunction extends Function { @@ -28,8 +30,8 @@ class PoolTypeFunction extends Function { p.getName().matches("Tag") and p.getType().getName().matches("ULONG") ) and - this.getAParameter().getName().matches("Flags") - and this.getAParameter().getType().getName().matches("SIZE_T") + this.getAParameter().getName().matches("Flags") and + this.getAParameter().getType().getName().matches("SIZE_T") } } @@ -47,25 +49,24 @@ class GlobalDefaultPoolTag extends GlobalVariable { } /** An interprocedural data-flow analysis looking for flow from bad (default) pool tags. */ -class DefaultPoolTagFlow extends DataFlow::Configuration { - DefaultPoolTagFlow() { this = "DefaultPoolTagFlow" } - - override predicate isSource(DataFlow::Node source) { source.asExpr() instanceof DefaultPoolTag } +module DefaultPoolTagFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source.asExpr() instanceof DefaultPoolTag } - override predicate isSink(DataFlow::Node sink) { sink instanceof DataFlow::ExprNode } + predicate isSink(DataFlow::Node sink) { sink instanceof DataFlow::ExprNode } } +module DefaultPoolTagFlow = DataFlow::Global; -/** An interprocedural data-flow analysis looking for flow from good pool tags. */ -class ValidPoolTagFlow extends DataFlow::Configuration { - ValidPoolTagFlow() { this = "ValidPoolTagFlow" } - override predicate isSource(DataFlow::Node source) { +/** An interprocedural data-flow analysis looking for flow from good pool tags. */ +module ValidPoolTagFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source.asExpr() instanceof Literal and not source.asExpr() instanceof DefaultPoolTag } - override predicate isSink(DataFlow::Node sink) { sink instanceof DataFlow::ExprNode } + predicate isSink(DataFlow::Node sink) { sink instanceof DataFlow::ExprNode } } +module ValidPoolTagFlow = DataFlow::Global; from FunctionCall fc, int i, GlobalDefaultPoolTag gdpt where @@ -76,17 +77,17 @@ where // A bad tag is directly passed in fc.getArgument(i) instanceof DefaultPoolTag or - // A global tag variable is being passed in, and no path exists + // A global tag variable is being passed in, and no path exists // where a good tag has been assigned instead fc.getArgument(i).(VariableAccess).getTarget() = gdpt and - not exists(ValidPoolTagFlow dataFlow, DataFlow::Node source, DataFlow::Node sink | + not exists(DataFlow::Node source, DataFlow::Node sink | sink.asExpr() = fc.getArgument(i) and - dataFlow.hasFlow(source, sink) + ValidPoolTagFlow::flow(source, sink) ) or // A local variable with a bad tag is being passed in - exists(DefaultPoolTagFlow dataFlow, DataFlow::Node source, DataFlow::Node sink | + exists(DataFlow::Node source, DataFlow::Node sink | sink.asExpr() = fc.getArgument(i) and - dataFlow.hasFlow(source, sink) + DefaultPoolTagFlow::flow(source, sink) ) select fc.getArgument(i), "Default pool tag used in function call" diff --git a/src/drivers/general/queries/experimental/DriverIsolationRtlViolation/DriverIsolationRtlViolation.qhelp b/src/drivers/general/queries/experimental/DriverIsolationRtlViolation/DriverIsolationRtlViolation.qhelp new file mode 100644 index 00000000..695f35dd --- /dev/null +++ b/src/drivers/general/queries/experimental/DriverIsolationRtlViolation/DriverIsolationRtlViolation.qhelp @@ -0,0 +1,51 @@ + + + +

    + There is a driver isolation violation if there is an Rtl* registry function call with with a RelativeTo parameter != RTL_REGISTRY_DEVICEMAP or a RelativeTo parameter == RTL_REGISTRY_DEVICEMAP and writes to registry (reads are OK) +

    +
    + +

    + If using an Rtl* registry function, use RelativeTo parameter == RTL_REGISTRY_DEVICEMAP and only read from the registry +

    +
    + +

    + Example of a driver isolation violation. A call to RtlWriteRegistryValue with RelativeTo parameter != RTL_REGISTRY_DEVICEMAP. +

    + + +

    + TODO example 2 +

    + + +
    + +

    + +

    +
    + +
  • + + Driver package isolation + +
  • +
    +
    diff --git a/src/drivers/general/queries/experimental/DriverIsolationRtlViolation/DriverIsolationRtlViolation.ql b/src/drivers/general/queries/experimental/DriverIsolationRtlViolation/DriverIsolationRtlViolation.ql new file mode 100644 index 00000000..2bde2a1b --- /dev/null +++ b/src/drivers/general/queries/experimental/DriverIsolationRtlViolation/DriverIsolationRtlViolation.ql @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. +// 2024-2025 Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +/** + * @id cpp/drivers/driver-isolation-rtl-violation + * @kind problem + * @name Driver Isolation Rtl Violation + * @description Driver isolation violation if there is an Rtl* registry function call with with a RelativeTo parameter != RTL_REGISTRY_DEVICEMAP + * or a RelativeTo parameter == RTL_REGISTRY_DEVICEMAP and writes to registry (reads are OK) + * @platform Desktop + * @feature.area Multiple + * @impact Insecure Coding Practice + * @owner.email: sdat@microsoft.com + * @opaqueid CQLD-D0008 + * @problem.severity warning + * @precision medium + * @tags correctness + * @scope domainspecific + * @query-version v1 + */ + + import drivers.libraries.DriverIsolation + +predicate rtlViolation1(RegistryIsolationFunctionCall f) { + f.getTarget().getName().matches("Rtl%") and + // Violation if RelativeTo parameter is NOT RTL_REGISTRY_DEVICEMAP + exists(MacroInvocation m | + f.getArgument(0) = m.getExpr() and + not m.getMacroName().matches("RTL_REGISTRY_DEVICEMAP") and + not m.getMacroName().matches("RTL_REGISTRY_HANDLE") // These would be caught when the handle is opened + ) +} + +predicate rtlViolation2(RegistryIsolationFunctionCall f) { + // Violation if RelativeTo parameter IS RTL_REGISTRY_DEVICEMAP and not doing a READ + f.getTarget().getName().matches("Rtl%") and + exists(MacroInvocation m | + f.getArgument(0) = m.getExpr() and + m.getMacroName().matches("RTL_REGISTRY_DEVICEMAP") and + not ( + f.getTarget().getName().matches("RtlQueryRegistryValues%") or + f.getTarget().getName().matches("RtlQueryRegistryValuesEx%") or + f.getTarget().getName().matches("RtlCheckRegistryKey%") + ) + ) + // Exception: Rtl Writes OK if key is named SERIALCOMM and RelativeTo parameter is RTL_REGISTRY_DEVICEMAP + and not exception2(f) +} + + +from RegistryIsolationFunctionCall f, string message +where + /* registry violation rtl functions (1/2)*/ + message = + f.getTarget().getName().toString() + + " function call RelativeTo parameter is not RTL_REGISTRY_DEVICEMAP" and + rtlViolation1(f) + or + /* registry violation rtl functions (2/2)*/ + message = + f.getTarget().getName().toString() + + " function call RelativeTo parameter is RTL_REGISTRY_DEVICEMAP but is doing a write" and + rtlViolation2(f) +select f, message diff --git a/src/drivers/general/queries/experimental/DriverIsolationRtlViolation/DriverIsolationRtlViolation.sarif b/src/drivers/general/queries/experimental/DriverIsolationRtlViolation/DriverIsolationRtlViolation.sarif new file mode 100644 index 00000000..c54c3222 --- /dev/null +++ b/src/drivers/general/queries/experimental/DriverIsolationRtlViolation/DriverIsolationRtlViolation.sarif @@ -0,0 +1,171 @@ +{ + "$schema" : "https://json.schemastore.org/sarif-2.1.0.json", + "version" : "2.1.0", + "runs" : [ { + "tool" : { + "driver" : { + "name" : "CodeQL", + "organization" : "GitHub", + "semanticVersion" : "2.15.4", + "notifications" : [ { + "id" : "cpp/baseline/expected-extracted-files", + "name" : "cpp/baseline/expected-extracted-files", + "shortDescription" : { + "text" : "Expected extracted files" + }, + "fullDescription" : { + "text" : "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration" : { + "enabled" : true + }, + "properties" : { + "tags" : [ "expected-extracted-files", "telemetry" ] + } + } ], + "rules" : [ { + "id" : "cpp/drivers/driver-isolation-rtl-violation", + "name" : "cpp/drivers/driver-isolation-rtl-violation", + "shortDescription" : { + "text" : "Driver Isolation Rtl Violation" + }, + "fullDescription" : { + "text" : "Driver isolation violation if there is an Rtl* registry function call with with a RelativeTo parameter != RTL_REGISTRY_DEVICEMAP or a RelativeTo parameter == RTL_REGISTRY_DEVICEMAP and writes to registry (reads are OK)" + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "properties" : { + "tags" : [ "correctness" ], + "description" : "Driver isolation violation if there is an Rtl* registry function call with with a RelativeTo parameter != RTL_REGISTRY_DEVICEMAP\n or a RelativeTo parameter == RTL_REGISTRY_DEVICEMAP and writes to registry (reads are OK)", + "feature.area" : "Multiple", + "id" : "cpp/drivers/driver-isolation-rtl-violation", + "impact" : "Insecure Coding Practice", + "kind" : "problem", + "name" : "Driver Isolation Rtl Violation", + "opaqueid" : "CQLD-D0008", + "owner.email:" : "sdat@microsoft.com", + "platform" : "Desktop", + "precision" : "medium", + "problem.severity" : "warning", + "query-version" : "v1", + "scope" : "domainspecific" + } + } ] + }, + "extensions" : [ { + "name" : "microsoft/windows-drivers", + "semanticVersion" : "1.2.0+39f0f5d626a18b5c1b5b20dbb92237b59b700643", + "locations" : [ { + "uri" : "file:///C:/codeql-home/WDDST/src/", + "description" : { + "text" : "The QL pack root directory." + } + }, { + "uri" : "file:///C:/codeql-home/WDDST/src/qlpack.yml", + "description" : { + "text" : "The QL pack definition file." + } + } ] + } ] + }, + "invocations" : [ { + "toolExecutionNotifications" : [ { + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/fail_driver1.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + } + } + } ], + "message" : { + "text" : "" + }, + "level" : "none", + "descriptor" : { + "id" : "cpp/baseline/expected-extracted-files", + "index" : 0 + }, + "properties" : { + "formattedMessage" : { + "text" : "" + } + } + }, { + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 1 + } + } + } ], + "message" : { + "text" : "" + }, + "level" : "none", + "descriptor" : { + "id" : "cpp/baseline/expected-extracted-files", + "index" : 0 + }, + "properties" : { + "formattedMessage" : { + "text" : "" + } + } + }, { + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/fail_driver1.h", + "uriBaseId" : "%SRCROOT%", + "index" : 2 + } + } + } ], + "message" : { + "text" : "" + }, + "level" : "none", + "descriptor" : { + "id" : "cpp/baseline/expected-extracted-files", + "index" : 0 + }, + "properties" : { + "formattedMessage" : { + "text" : "" + } + } + } ], + "executionSuccessful" : true + } ], + "artifacts" : [ { + "location" : { + "uri" : "driver/fail_driver1.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + } + }, { + "location" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 1 + } + }, { + "location" : { + "uri" : "driver/fail_driver1.h", + "uriBaseId" : "%SRCROOT%", + "index" : 2 + } + } ], + "results" : [ ], + "columnKind" : "utf16CodeUnits", + "properties" : { + "semmle.formatSpecifier" : "sarifv2.1.0" + } + } ] +} \ No newline at end of file diff --git a/src/drivers/general/queries/experimental/DriverIsolationRtlViolation/driver_snippet.c b/src/drivers/general/queries/experimental/DriverIsolationRtlViolation/driver_snippet.c new file mode 100644 index 00000000..f9ce2bc2 --- /dev/null +++ b/src/drivers/general/queries/experimental/DriverIsolationRtlViolation/driver_snippet.c @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +// Macros to enable or disable a code section that may or may not conflict with this test. +#define SET_DISPATCH 1 + +// Template function. Not used for this test. +void top_level_call() +{ +} + +void test_rtl_violation_0() +{ + // RelativeTo != RTL_REGISTRY_DEVICEMAP + HANDLE DriverKey = NULL; + static WCHAR ValueName[] = L"KS", ValueValue[] = L"1"; + + RtlWriteRegistryValue(RTL_REGISTRY_HANDLE, + (PCWSTR)DriverKey, + ValueName, + REG_SZ, + ValueValue, + sizeof ValueValue); + ZwClose(DriverKey); +} + +void test_rtl_violation_1() +{ + // RelativeTo == RTL_REGISTRY_DEVICEMAP AND function writes +} diff --git a/src/drivers/general/queries/experimental/DriverIsolationZwViolation1/DriverIsolationZwViolation1.qhelp b/src/drivers/general/queries/experimental/DriverIsolationZwViolation1/DriverIsolationZwViolation1.qhelp new file mode 100644 index 00000000..2ccd0981 --- /dev/null +++ b/src/drivers/general/queries/experimental/DriverIsolationZwViolation1/DriverIsolationZwViolation1.qhelp @@ -0,0 +1,56 @@ + + + +

    + a Driver isolation violation occurs if there is a Zw* registry function call with OBJECT_ATTRIBUTES parameter passed to it with RootDirectory!=NULL and the handle specified in RootDirectory comes from an unapproved ddi. +

    +
    + +

    + A Zw* registry function call with OBJECT_ATTRIBUTES parameter passed to it with RootDirectory!=NULL should obtain the handle from an approved ddi https://learn.microsoft.com/en-us/windows-hardware/drivers/develop/driver-isolation +

    +
    + +

    + Example of Driver Isolation violation: ZwOpenKey call with an ObjectAttributes parameter with a RootDirectory value from an invalid source +

    + + +

    + Example of no violation +

    + + +
    + +

    + +

    +
    + +
  • + + Driver package isolation + +
  • +
    +
    diff --git a/src/drivers/general/queries/experimental/DriverIsolationZwViolation1/DriverIsolationZwViolation1.ql b/src/drivers/general/queries/experimental/DriverIsolationZwViolation1/DriverIsolationZwViolation1.ql new file mode 100644 index 00000000..d1591204 --- /dev/null +++ b/src/drivers/general/queries/experimental/DriverIsolationZwViolation1/DriverIsolationZwViolation1.ql @@ -0,0 +1,109 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/** + * @id cpp/drivers/driver-isolation-zw-violation-1 + * @kind problem + * @name Driver Isolation Zw Violation 1 + * @description Driver isolation violation if there is a Zw* registry function call with OBJECT_ATTRIBUTES parameter passed to it with + * RootDirectory!=NULL and the handle specified in RootDirectory comes from an unapproved ddi. + * @platform Desktop + * @feature.area Multiple + * @impact Insecure Coding Practice + * @owner.email: sdat@microsoft.com + * @opaqueid CQLD-D0009 + * @problem.severity warning + * @precision medium + * @tags correctness + * @scope domainspecific + * @query-version v1 + */ + +import drivers.libraries.DriverIsolation +import drivers.libraries.SAL + +/* + * OBJECT_ATTRIBUTES->RootDirectory is non-null + */ + +module IsolationDataFlowNonNullRootDirConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + exists(VariableAccess arg | + arg.getType().toString().matches("%HANDLE") and + not arg instanceof FieldAccess and + source.asIndirectExpr() = arg + ) + } + + //barrier prevents flow from source to source + predicate isBarrierIn(DataFlow::Node node) { isSource(node) } + + predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) { + /* Flow from handle to object attributes */ + exists(MacroInvocation m | + succ instanceof NonNullRootDirectoryNode and + pred.asExpr().(VariableAccess).getType().toString().matches("HANDLE") and + not pred.asExpr() instanceof FieldAccess and + m.getAnAffectedElement() = succ.asIndirectExpr() and + m.getAnAffectedElement() = pred.asExpr() and + m.getMacroName().matches("InitializeObjectAttributes") + ) + } + + predicate isSink(DataFlow::Node sink) { + // sink is an argument in a Zw* function call + exists(FunctionCall f | sink.asIndirectExpr() = f.getAnArgument()) and + not isSource(sink) + } +} + +module IsolationDataFlowNonNullRootDir = DataFlow::Global; + +/* + * For debugging, uncomment the following line and: + * change the @kind to "path-problem", + * change the DataFlow::Node to IsolationDataFlowNonNullRootDir::PathNode, + * change IsolationDataFlowNonNullRootDir::flow to IsolationDataFlowNonNullRootDir::flowPath, + * change the select to: select regFuncCall, source, sink, "message" + */ + +//import IsolationDataFlowNonNullRootDir::PathGraph +/* + * registry violation zw functions ( non-null RootDirectory) + * OBJECT_ATTRIBUTES->RootDirectory is non-null and flow from ObjectAttributes to Zw* function + */ + +FunctionCall nonNullRootDirFlowFirstCall(FunctionCall fc) { + if + exists(DataFlow::Node source, DataFlow::Node sink | + IsolationDataFlowNonNullRootDir::flow(source, sink) and + fc.getAnArgument() = sink.asIndirectArgument() + ) + then + exists(DataFlow::Node source, DataFlow::Node sink | + IsolationDataFlowNonNullRootDir::flow(source, sink) and + fc.getAnArgument() = sink.asIndirectArgument() and + result = nonNullRootDirFlowFirstCall(source.asIndirectExpr().getParent+()) + ) + else result = fc +} + +from + RegistryIsolationFunctionCall fc, FunctionCall sourceFuncCall, DataFlow::Node source, + DataFlow::Node sink +where + IsolationDataFlowNonNullRootDir::flow(source, sink) and + sink.asIndirectArgument().getParent*() = fc and + sourceFuncCall = nonNullRootDirFlowFirstCall(source.asIndirectArgument().getParent*()) and + zwCall(fc) and + ( + // non-zw* API calls + not sourceFuncCall.getTarget() instanceof AllowedHandleDDI and + not zwCall(sourceFuncCall) + and sourceFuncCall.getTarget().getADeclarationLocation().getFile().toString().matches("%Windows Kits%") + or + // zw* function calls + sourceFuncCall instanceof NotAllowedHandleRegFuncCall + ) +select fc, + "Potential Driver Isolation Violation: Function call $@ uses handle obtained from unapproved DDI $@", + fc, fc.getTarget().toString(), sourceFuncCall, sourceFuncCall.getTarget().toString() diff --git a/src/drivers/general/queries/experimental/DriverIsolationZwViolation1/DriverIsolationZwViolation1.sarif b/src/drivers/general/queries/experimental/DriverIsolationZwViolation1/DriverIsolationZwViolation1.sarif new file mode 100644 index 00000000..347cc38a --- /dev/null +++ b/src/drivers/general/queries/experimental/DriverIsolationZwViolation1/DriverIsolationZwViolation1.sarif @@ -0,0 +1,171 @@ +{ + "$schema" : "https://json.schemastore.org/sarif-2.1.0.json", + "version" : "2.1.0", + "runs" : [ { + "tool" : { + "driver" : { + "name" : "CodeQL", + "organization" : "GitHub", + "semanticVersion" : "2.15.4", + "notifications" : [ { + "id" : "cpp/baseline/expected-extracted-files", + "name" : "cpp/baseline/expected-extracted-files", + "shortDescription" : { + "text" : "Expected extracted files" + }, + "fullDescription" : { + "text" : "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration" : { + "enabled" : true + }, + "properties" : { + "tags" : [ "expected-extracted-files", "telemetry" ] + } + } ], + "rules" : [ { + "id" : "cpp/drivers/driver-isolation-zw-violation-1", + "name" : "cpp/drivers/driver-isolation-zw-violation-1", + "shortDescription" : { + "text" : "Driver Isolation Zw Violation 1" + }, + "fullDescription" : { + "text" : "Driver isolation violation if there is a Zw* registry function call with OBJECT_ATTRIBUTES parameter passed to it with RootDirectory!=NULL and the handle specified in RootDirectory comes from an unapproved ddi." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "properties" : { + "tags" : [ "correctness" ], + "description" : "Driver isolation violation if there is a Zw* registry function call with OBJECT_ATTRIBUTES parameter passed to it with\n RootDirectory!=NULL and the handle specified in RootDirectory comes from an unapproved ddi.", + "feature.area" : "Multiple", + "id" : "cpp/drivers/driver-isolation-zw-violation-1", + "impact" : "Insecure Coding Practice", + "kind" : "problem", + "name" : "Driver Isolation Zw Violation 1", + "opaqueid" : "CQLD-D0009", + "owner.email:" : "sdat@microsoft.com", + "platform" : "Desktop", + "precision" : "medium", + "problem.severity" : "warning", + "query-version" : "v1", + "scope" : "domainspecific" + } + } ] + }, + "extensions" : [ { + "name" : "microsoft/windows-drivers", + "semanticVersion" : "1.2.0+39f0f5d626a18b5c1b5b20dbb92237b59b700643", + "locations" : [ { + "uri" : "file:///C:/codeql-home/WDDST/src/", + "description" : { + "text" : "The QL pack root directory." + } + }, { + "uri" : "file:///C:/codeql-home/WDDST/src/qlpack.yml", + "description" : { + "text" : "The QL pack definition file." + } + } ] + } ] + }, + "invocations" : [ { + "toolExecutionNotifications" : [ { + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + } + } + } ], + "message" : { + "text" : "" + }, + "level" : "none", + "descriptor" : { + "id" : "cpp/baseline/expected-extracted-files", + "index" : 0 + }, + "properties" : { + "formattedMessage" : { + "text" : "" + } + } + }, { + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/fail_driver1.c", + "uriBaseId" : "%SRCROOT%", + "index" : 1 + } + } + } ], + "message" : { + "text" : "" + }, + "level" : "none", + "descriptor" : { + "id" : "cpp/baseline/expected-extracted-files", + "index" : 0 + }, + "properties" : { + "formattedMessage" : { + "text" : "" + } + } + }, { + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/fail_driver1.h", + "uriBaseId" : "%SRCROOT%", + "index" : 2 + } + } + } ], + "message" : { + "text" : "" + }, + "level" : "none", + "descriptor" : { + "id" : "cpp/baseline/expected-extracted-files", + "index" : 0 + }, + "properties" : { + "formattedMessage" : { + "text" : "" + } + } + } ], + "executionSuccessful" : true + } ], + "artifacts" : [ { + "location" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + } + }, { + "location" : { + "uri" : "driver/fail_driver1.c", + "uriBaseId" : "%SRCROOT%", + "index" : 1 + } + }, { + "location" : { + "uri" : "driver/fail_driver1.h", + "uriBaseId" : "%SRCROOT%", + "index" : 2 + } + } ], + "results" : [ ], + "columnKind" : "utf16CodeUnits", + "properties" : { + "semmle.formatSpecifier" : "sarifv2.1.0" + } + } ] +} \ No newline at end of file diff --git a/src/drivers/general/queries/experimental/DriverIsolationZwViolation1/driver_snippet.c b/src/drivers/general/queries/experimental/DriverIsolationZwViolation1/driver_snippet.c new file mode 100644 index 00000000..cf412290 --- /dev/null +++ b/src/drivers/general/queries/experimental/DriverIsolationZwViolation1/driver_snippet.c @@ -0,0 +1,324 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +// Macros to enable or disable a code section that may or may not conflict with this test. +#define SET_DISPATCH 1 + +// Template function. Not used for this test. +void top_level_call() +{ +} +void test_zw_violation_1() +{ + // OBJECT_ATTRIBUTES->RootDirectory == NULL + // AND OBJECT_ATTRIBUTES->ObjectName starts with "\registry\machine\hardware\" + // AND function writes +} + +// OBJECT_ATTRIBUTES->RootDirectory == NULL +// AND OBJECT_ATTRIBUTES->ObjectName doesn't start with "\registry\machine\hardware\" +void test_zw_not_allowed_read() +{ + + NTSTATUS Status = STATUS_SUCCESS; + HANDLE ChildKey = NULL; + KEY_FULL_INFORMATION FullKeyInformation = {}; + OBJECT_ATTRIBUTES ObjectAttributes; + ULONG ReturnedSize = 0; + UNICODE_STRING FrameRateKey; + + RtlInitUnicodeString(&FrameRateKey, L"\\some\\bad\\path\\test\\test.txt"); + InitializeObjectAttributes(&ObjectAttributes, &FrameRateKey, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, NULL); + ZwOpenKey(&ChildKey, KEY_READ, &ObjectAttributes); + Status = ZwQueryKey(ChildKey, KeyFullInformation, &FullKeyInformation, sizeof(FullKeyInformation), &ReturnedSize); +} + +void test_zw_allowed_read() +{ + + NTSTATUS Status = STATUS_SUCCESS; + HANDLE ChildKey = NULL; + KEY_FULL_INFORMATION FullKeyInformation = {}; + OBJECT_ATTRIBUTES ObjectAttributes; + ULONG ReturnedSize = 0; + UNICODE_STRING FrameRateKey; + + RtlInitUnicodeString(&FrameRateKey, L"\\Registry\\Machine\\Hardware\\test\\test.txt"); + InitializeObjectAttributes(&ObjectAttributes, &FrameRateKey, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, NULL); + ZwOpenKey(&ChildKey, KEY_READ, &ObjectAttributes); + + Status = ZwQueryKey(ChildKey, KeyFullInformation, &FullKeyInformation, sizeof(FullKeyInformation), &ReturnedSize); +} + +void test_zw_multiple_nulls(PUNICODE_STRING RegistryPath) +{ + OBJECT_ATTRIBUTES objectAttributes = {0}; + HANDLE serviceKey = NULL; + HANDLE parametersKey = NULL; + RTL_QUERY_REGISTRY_TABLE parameters[3] = {0}; + + UNICODE_STRING paramStr; + + NTSTATUS status; + // open the service key. + InitializeObjectAttributes(&objectAttributes, + RegistryPath, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL); + + status = ZwOpenKey(&serviceKey, + KEY_READ, + &objectAttributes); + + RtlInitUnicodeString(¶mStr, L"Parameters"); + + InitializeObjectAttributes(&objectAttributes, + L"\\Registry\\Machine\\Hardware\\test\\test.txt", + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL); + + status = ZwOpenKey(¶metersKey, + KEY_READ, + &objectAttributes); +} + +// OBJECT_ATTRIBUTES->RootDirectory == NULL and OBJECT_ATTRIBUTES->ObjectName starts with "\registry\machine\hardware\ BUT Zw function does a write" +#include +#include +void test_zw_violation_3( + PCHAR DeviceName, + ULONG DeviceNumber) +{ + NTSTATUS status; + SCSI_ADDRESS scsiAddress = {0}; + OBJECT_ATTRIBUTES objectAttributes = {0}; + STRING string; + UNICODE_STRING unicodeName = {0}; + UNICODE_STRING unicodeRegistryPath = {0}; + UNICODE_STRING unicodeData = {0}; + HANDLE targetKey; + UCHAR buffer[256] = {0}; + + PAGED_CODE(); + + targetKey = NULL; + status = RtlStringCchPrintfA((NTSTRSAFE_PSTR)buffer, + sizeof(buffer) - 1, + "\\Registry\\Machine\\Hardware\\DeviceMap\\Scsi\\Scsi Port %d\\Scsi Bus %d\\Target Id %d\\Logical Unit Id %d", + scsiAddress.PortNumber, + scsiAddress.PathId, + scsiAddress.TargetId, + scsiAddress.Lun); + + RtlInitString(&string, (PCSZ)buffer); + + status = RtlAnsiStringToUnicodeString(&unicodeRegistryPath, + &string, + TRUE); + + // Open the registry key + + InitializeObjectAttributes(&objectAttributes, + &unicodeRegistryPath, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL); + + status = ZwOpenKey(&targetKey, + KEY_READ, + &objectAttributes); + + RtlInitUnicodeString(&unicodeName, L"DeviceName"); + status = RtlStringCchPrintfA((NTSTRSAFE_PSTR)buffer, sizeof(buffer) - 1, "%s%d", DeviceName, DeviceNumber); + RtlInitString(&string, (PCSZ)buffer); + status = RtlAnsiStringToUnicodeString(&unicodeData, + &string, + TRUE); + if (NT_SUCCESS(status)) + { + status = ZwSetValueKey(targetKey, + &unicodeName, + 0, + REG_SZ, + unicodeData.Buffer, + unicodeData.Length); + } +} + +void test_zw_allowed_rootdirectory_source() +{ + OBJECT_ATTRIBUTES ObjectAttributes; + NTSTATUS Status; + HANDLE ParentKey, RootKey; + UNICODE_STRING UnicodeEnumName; + const WCHAR EnumString[] = L"Enum"; + + PAGED_CODE(); + + PDEVICE_OBJECT PhysicalDeviceObject = NULL; + + Status = IoOpenDeviceRegistryKey(PhysicalDeviceObject, + PLUGPLAY_REGKEY_DRIVER, + STANDARD_RIGHTS_ALL, + &ParentKey); + + RtlInitUnicodeString(&UnicodeEnumName, EnumString); + + InitializeObjectAttributes(&ObjectAttributes, + &UnicodeEnumName, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + ParentKey, + NULL); + + Status = ZwOpenKey(&RootKey, KEY_READ, &ObjectAttributes); + + if (!NT_SUCCESS(Status)) + { + ZwClose(ParentKey); + return Status; + } +} + +BOOLEAN +TestUsingFunctionParam( + IN PUNICODE_STRING RegistryPath) +{ + OBJECT_ATTRIBUTES objectAttributes = {0}; + HANDLE serviceKey = NULL; + HANDLE parametersKey = NULL; + RTL_QUERY_REGISTRY_TABLE parameters[3] = {0}; + + NTSTATUS status; + + PAGED_CODE(); + + // + // open the service key. + // + + InitializeObjectAttributes(&objectAttributes, + RegistryPath, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL); + + status = ZwOpenKey(&serviceKey, + KEY_READ, + &objectAttributes); + + return NT_SUCCESS(status); +} + +void BadCallTestUsingFunctionParam() +{ + UNICODE_STRING RegistryPath; + const WCHAR EnumString[] = L"invalid"; + + RtlInitUnicodeString(&RegistryPath, EnumString); + + TestUsingFunctionParam(&RegistryPath); +} + +void GoodCallTestUsingFunctionParam() +{ + UNICODE_STRING RegistryPath; + const WCHAR EnumString[] = L"\\Registry\\Machine\\Hardware\\DeviceMap\\Scsi\\Scsi Port %d\\Scsi Bus %d\\Target Id %d\\Logical Unit Id %d"; + + RtlInitUnicodeString(&RegistryPath, EnumString); + + TestUsingFunctionParam(&RegistryPath); +} + +void TestZwWithRelativeHandle() +{ + OBJECT_ATTRIBUTES ObjectAttributes; + OBJECT_ATTRIBUTES ObjectAttributes2; + OBJECT_ATTRIBUTES ObjectAttributes3; + NTSTATUS Status; + HANDLE ParentKey, RootKey, RootKey2, RootKey3; + UNICODE_STRING UnicodeEnumName; + const WCHAR EnumString[] = L"Enum"; + + PAGED_CODE(); + + PDEVICE_OBJECT PhysicalDeviceObject = NULL; + + Status = IoOpenDeviceRegistryKey(PhysicalDeviceObject, + PLUGPLAY_REGKEY_DRIVER, + STANDARD_RIGHTS_ALL, + &ParentKey); + + RtlInitUnicodeString(&UnicodeEnumName, EnumString); + + InitializeObjectAttributes(&ObjectAttributes, + &UnicodeEnumName, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + ParentKey, + NULL); + + // Allowed because ParentKey came from IoOpenDeviceRegistryKey + Status = ZwOpenKey(&RootKey, KEY_READ, &ObjectAttributes); + + if (!NT_SUCCESS(Status)) + { + ZwClose(ParentKey); + return Status; + } + + InitializeObjectAttributes(&ObjectAttributes2, + &UnicodeEnumName, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + RootKey, + NULL); + // Allowed because RootKey is a valid handle + Status = ZwOpenKey(&RootKey2, KEY_READ, &ObjectAttributes2); + + + InitializeObjectAttributes(&ObjectAttributes3, + &UnicodeEnumName, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + RootKey2, + NULL); + // Allowed because RootKey is a valid handle + Status = ZwOpenKey(&RootKey3, KEY_READ, &ObjectAttributes3); +} + +void TestZwWithRelativeHandleBad() +{ + OBJECT_ATTRIBUTES ObjectAttributes; + OBJECT_ATTRIBUTES ObjectAttributes2; + NTSTATUS Status; + HANDLE ParentKey = NULL; + HANDLE RootKey, RootKey2; + UNICODE_STRING UnicodeEnumName; + const WCHAR EnumString[] = L"Enum"; + + PAGED_CODE(); + + PDEVICE_OBJECT PhysicalDeviceObject = NULL; + + InitializeObjectAttributes(&ObjectAttributes, + &UnicodeEnumName, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + ParentKey, + NULL); + + // Not allowed + Status = ZwOpenKey(&RootKey, KEY_READ, &ObjectAttributes); + + if (!NT_SUCCESS(Status)) + { + ZwClose(ParentKey); + return Status; + } + + InitializeObjectAttributes(&ObjectAttributes2, + &UnicodeEnumName, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + RootKey, + NULL); + // Not allowed + Status = ZwOpenKey(&RootKey2, KEY_READ, &ObjectAttributes2); +} \ No newline at end of file diff --git a/src/drivers/general/queries/experimental/DriverIsolationZwViolation2/DriverIsolationZwViolation2.qhelp b/src/drivers/general/queries/experimental/DriverIsolationZwViolation2/DriverIsolationZwViolation2.qhelp new file mode 100644 index 00000000..b8fd8d10 --- /dev/null +++ b/src/drivers/general/queries/experimental/DriverIsolationZwViolation2/DriverIsolationZwViolation2.qhelp @@ -0,0 +1,55 @@ + + + +

    + A driver isolation violation occurs if there is a Zw* registry function call with OBJECT_ATTRIBUTES parameter passed to it with RootDirectory=NULL and invalid OBJECT_ATTRIBUTES->ObjectName, or RootDirectory=NULL and valid OBJECT_ATTRIBUTES->ObjectName but with write access. +

    +
    + +

    + For Zw* registry function calls where the OBJECT_ATTRIBUTES parameter passed to it has RootDirectory=NULL, the OBJECT_ATTRIBUTES->ObjectName value should start with L"\\Registry\\Machine\\Hardware and only read. +

    +
    + +

    + Example of a driver isolation violation where the OBJECT_ATTRIBUTES->ObjectName value is invalid +

    + + +

    + Example of a driver isolation violation where the OBJECT_ATTRIBUTES->ObjectName value is valid but with write access +

    + + +
    + +

    + +

    +
    + +
  • + + Driver package isolation + +
  • +
    +
    diff --git a/src/drivers/general/queries/experimental/DriverIsolationZwViolation2/DriverIsolationZwViolation2.ql b/src/drivers/general/queries/experimental/DriverIsolationZwViolation2/DriverIsolationZwViolation2.ql new file mode 100644 index 00000000..d43c0f1b --- /dev/null +++ b/src/drivers/general/queries/experimental/DriverIsolationZwViolation2/DriverIsolationZwViolation2.ql @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/** + * @id cpp/drivers/driver-isolation-zw-violation-2 + * @kind problem + * @name Driver Isolation Zw Violation 2 + * @description Driver isolation violation if there is a Zw* registry function call with OBJECT_ATTRIBUTES parameter passed to it with + * RootDirectory=NULL and invalid OBJECT_ATTRIBUTES->ObjectName, or RootDirectory=NULL and valid OBJECT_ATTRIBUTES->ObjectName but with write access. + * @platform Desktop + * @feature.area Multiple + * @impact Insecure Coding Practice + * @owner.email: sdat@microsoft.com + * @opaqueid CQLD-D0010 + * @problem.severity warning + * @precision medium + * @tags correctness + * @scope domainspecific + * @query-version v1 + */ + +import drivers.libraries.DriverIsolation + +/* + * InitializeObjectAttributes: For second param (PUNICODE_STRING), if fully qualified object name passed, then RootDirectory is NULL. + * If relative path name to the object directory specified by RootDirectory (not null) + */ + +/* + * OBJECT_ATTRIBUTES->RootDirectory is NULL + */ + +from RegistryIsolationFunctionCall f, DataFlow::Node source, DataFlow::Node sink, string message +where + IsolationDataFlowNullRootDir::flow(source, sink) and + ( + // violation if RootDirectory=NULL and writes, even if ObjectName is valid. (Reads are OK) + allowedPath(source.asIndirectExpr()) and + not pathWriteException(source.asIndirectExpr()) and // this path allowed for now + zwWrite(f) and // null RootDirectory, valid ObjectName, write + message = + f.getTarget().toString() + + " call has parameter of type OBJECT_ATTRIBUTES with NULL RootDirectory field uses a valid path $@ for ObjectName field, " + + "but is a Driver Isolation Violation due to having write access " + or + // All other paths are violations for both read and write + not allowedPath(source.asIndirectExpr()) and + zwCall(f) and // null RootDirectory, invalid ObjectName + message = + f.getTarget().toString() + + " call has parameter of type OBJECT_ATTRIBUTES with NULL RootDirectory field uses an invalid path $@ for ObjectName field " + ) and + sink.asIndirectArgument().getParent*() = f +select f, message, source, source.asIndirectExpr().toString() diff --git a/src/drivers/general/queries/experimental/DriverIsolationZwViolation2/DriverIsolationZwViolation2.sarif b/src/drivers/general/queries/experimental/DriverIsolationZwViolation2/DriverIsolationZwViolation2.sarif new file mode 100644 index 00000000..50a21d32 --- /dev/null +++ b/src/drivers/general/queries/experimental/DriverIsolationZwViolation2/DriverIsolationZwViolation2.sarif @@ -0,0 +1,309 @@ +{ + "$schema" : "https://json.schemastore.org/sarif-2.1.0.json", + "version" : "2.1.0", + "runs" : [ { + "tool" : { + "driver" : { + "name" : "CodeQL", + "organization" : "GitHub", + "semanticVersion" : "2.15.4", + "notifications" : [ { + "id" : "cpp/baseline/expected-extracted-files", + "name" : "cpp/baseline/expected-extracted-files", + "shortDescription" : { + "text" : "Expected extracted files" + }, + "fullDescription" : { + "text" : "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration" : { + "enabled" : true + }, + "properties" : { + "tags" : [ "expected-extracted-files", "telemetry" ] + } + } ], + "rules" : [ { + "id" : "cpp/drivers/driver-isolation-zw-violation-2", + "name" : "cpp/drivers/driver-isolation-zw-violation-2", + "shortDescription" : { + "text" : "Driver Isolation Zw Violation 2" + }, + "fullDescription" : { + "text" : "Driver isolation violation if there is a Zw* registry function call with OBJECT_ATTRIBUTES parameter passed to it with RootDirectory=NULL and invalid OBJECT_ATTRIBUTES->ObjectName, or RootDirectory=NULL and valid OBJECT_ATTRIBUTES->ObjectName but with write access." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "properties" : { + "tags" : [ "correctness" ], + "description" : "Driver isolation violation if there is a Zw* registry function call with OBJECT_ATTRIBUTES parameter passed to it with\n RootDirectory=NULL and invalid OBJECT_ATTRIBUTES->ObjectName, or RootDirectory=NULL and valid OBJECT_ATTRIBUTES->ObjectName but with write access.", + "feature.area" : "Multiple", + "id" : "cpp/drivers/driver-isolation-zw-violation-2", + "impact" : "Insecure Coding Practice", + "kind" : "problem", + "name" : "Driver Isolation Zw Violation 2", + "opaqueid" : "CQLD-D0010", + "owner.email:" : "sdat@microsoft.com", + "platform" : "Desktop", + "precision" : "medium", + "problem.severity" : "warning", + "query-version" : "v1", + "scope" : "domainspecific" + } + } ] + }, + "extensions" : [ { + "name" : "microsoft/windows-drivers", + "semanticVersion" : "1.2.0+39f0f5d626a18b5c1b5b20dbb92237b59b700643", + "locations" : [ { + "uri" : "file:///C:/codeql-home/WDDST/src/", + "description" : { + "text" : "The QL pack root directory." + } + }, { + "uri" : "file:///C:/codeql-home/WDDST/src/qlpack.yml", + "description" : { + "text" : "The QL pack definition file." + } + } ] + } ] + }, + "invocations" : [ { + "toolExecutionNotifications" : [ { + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + } + } + } ], + "message" : { + "text" : "" + }, + "level" : "none", + "descriptor" : { + "id" : "cpp/baseline/expected-extracted-files", + "index" : 0 + }, + "properties" : { + "formattedMessage" : { + "text" : "" + } + } + }, { + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/fail_driver1.c", + "uriBaseId" : "%SRCROOT%", + "index" : 1 + } + } + } ], + "message" : { + "text" : "" + }, + "level" : "none", + "descriptor" : { + "id" : "cpp/baseline/expected-extracted-files", + "index" : 0 + }, + "properties" : { + "formattedMessage" : { + "text" : "" + } + } + }, { + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/fail_driver1.h", + "uriBaseId" : "%SRCROOT%", + "index" : 2 + } + } + } ], + "message" : { + "text" : "" + }, + "level" : "none", + "descriptor" : { + "id" : "cpp/baseline/expected-extracted-files", + "index" : 0 + }, + "properties" : { + "formattedMessage" : { + "text" : "" + } + } + } ], + "executionSuccessful" : true + } ], + "artifacts" : [ { + "location" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + } + }, { + "location" : { + "uri" : "driver/fail_driver1.c", + "uriBaseId" : "%SRCROOT%", + "index" : 1 + } + }, { + "location" : { + "uri" : "driver/fail_driver1.h", + "uriBaseId" : "%SRCROOT%", + "index" : 2 + } + } ], + "results" : [ { + "ruleId" : "cpp/drivers/driver-isolation-zw-violation-2", + "ruleIndex" : 0, + "rule" : { + "id" : "cpp/drivers/driver-isolation-zw-violation-2", + "index" : 0 + }, + "message" : { + "text" : "ZwOpenKey call has parameter of type OBJECT_ATTRIBUTES with NULL RootDirectory field uses an invalid path [invalid](1) for ObjectName field " + }, + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + }, + "region" : { + "startLine" : 265, + "startColumn" : 14, + "endColumn" : 23 + } + } + } ], + "partialFingerprints" : { + "primaryLocationLineHash" : "96ebeab8bd79ddaf:1", + "primaryLocationStartColumnFingerprint" : "9" + }, + "relatedLocations" : [ { + "id" : 1, + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + }, + "region" : { + "startLine" : 275, + "startColumn" : 32, + "endColumn" : 42 + } + }, + "message" : { + "text" : "invalid" + } + } ] + }, { + "ruleId" : "cpp/drivers/driver-isolation-zw-violation-2", + "ruleIndex" : 0, + "rule" : { + "id" : "cpp/drivers/driver-isolation-zw-violation-2", + "index" : 0 + }, + "message" : { + "text" : "ZwOpenKey call has parameter of type OBJECT_ATTRIBUTES with NULL RootDirectory field uses a valid path [\\Registry\\Machine\\Hardware\\DeviceMap\\Scsi\\Scsi Port %d\\Scsi Bus %d\\Target Id %d\\Logical Unit Id %d](1) for ObjectName field, but is a Driver Isolation Violation due to having write access " + }, + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + }, + "region" : { + "startLine" : 164, + "startColumn" : 14, + "endColumn" : 23 + } + } + } ], + "partialFingerprints" : { + "primaryLocationLineHash" : "3753272b5a4a918d:1", + "primaryLocationStartColumnFingerprint" : "9" + }, + "relatedLocations" : [ { + "id" : 1, + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + }, + "region" : { + "startLine" : 141, + "startColumn" : 34, + "endColumn" : 143 + } + }, + "message" : { + "text" : "\\Registry\\Machine\\Hardware\\DeviceMap\\Scsi\\Scsi Port %d\\Scsi Bus %d\\Target Id %d\\Logical Unit Id %d" + } + } ] + }, { + "ruleId" : "cpp/drivers/driver-isolation-zw-violation-2", + "ruleIndex" : 0, + "rule" : { + "id" : "cpp/drivers/driver-isolation-zw-violation-2", + "index" : 0 + }, + "message" : { + "text" : "ZwOpenKey call has parameter of type OBJECT_ATTRIBUTES with NULL RootDirectory field uses an invalid path [\\some\\bad\\path\\test\\test.txt](1) for ObjectName field " + }, + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + }, + "region" : { + "startLine" : 37, + "startColumn" : 5, + "endColumn" : 14 + } + } + } ], + "partialFingerprints" : { + "primaryLocationLineHash" : "3b681a6745eb1858:1", + "primaryLocationStartColumnFingerprint" : "0" + }, + "relatedLocations" : [ { + "id" : 1, + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + }, + "region" : { + "startLine" : 35, + "startColumn" : 41, + "endColumn" : 77 + } + }, + "message" : { + "text" : "\\some\\bad\\path\\test\\test.txt" + } + } ] + } ], + "columnKind" : "utf16CodeUnits", + "properties" : { + "semmle.formatSpecifier" : "sarifv2.1.0" + } + } ] +} \ No newline at end of file diff --git a/src/drivers/general/queries/experimental/DriverIsolationZwViolation2/driver_snippet.c b/src/drivers/general/queries/experimental/DriverIsolationZwViolation2/driver_snippet.c new file mode 100644 index 00000000..34080fcd --- /dev/null +++ b/src/drivers/general/queries/experimental/DriverIsolationZwViolation2/driver_snippet.c @@ -0,0 +1,290 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +// Macros to enable or disable a code section that may or may not conflict with this test. +#define SET_DISPATCH 1 + +// Template function. Not used for this test. +void top_level_call() +{ +} +void test_zw_violation_1() +{ + // OBJECT_ATTRIBUTES->RootDirectory == NULL + // AND OBJECT_ATTRIBUTES->ObjectName starts with "\registry\machine\hardware\" + // AND function writes +} + +// OBJECT_ATTRIBUTES->RootDirectory == NULL +// AND OBJECT_ATTRIBUTES->ObjectName doesn't start with "\registry\machine\hardware\" +void test_zw_not_allowed_read() +{ + + NTSTATUS Status = STATUS_SUCCESS; + HANDLE RootKey = NULL; + HANDLE ChildKey = NULL; + KEY_FULL_INFORMATION FullKeyInformation = {}; + OBJECT_ATTRIBUTES ObjectAttributes; + PKEY_BASIC_INFORMATION pKeyInformation = NULL; + ULONG Index = 0; + ULONG InformationSize = 0; + ULONG ReturnedSize = 0; + UNICODE_STRING FrameRateKey; + PUNICODE_STRING pwszSymbolicLink = NULL; + + RtlInitUnicodeString(&FrameRateKey, L"\\some\\bad\\path\\test\\test.txt"); + InitializeObjectAttributes(&ObjectAttributes, &FrameRateKey, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, NULL); + ZwOpenKey(&ChildKey, KEY_READ, &ObjectAttributes); + // Open RGB data file for conversion to YUV space + + Status = ZwQueryKey(ChildKey, KeyFullInformation, &FullKeyInformation, sizeof(FullKeyInformation), &ReturnedSize); +} + +void test_zw_allowed_read() +{ + + NTSTATUS Status = STATUS_SUCCESS; + HANDLE RootKey = NULL; + HANDLE ChildKey = NULL; + KEY_FULL_INFORMATION FullKeyInformation = {}; + OBJECT_ATTRIBUTES ObjectAttributes; + PKEY_BASIC_INFORMATION pKeyInformation = NULL; + ULONG Index = 0; + ULONG InformationSize = 0; + ULONG ReturnedSize = 0; + UNICODE_STRING FrameRateKey; + PUNICODE_STRING pwszSymbolicLink = NULL; + + RtlInitUnicodeString(&FrameRateKey, L"\\Registry\\Machine\\Hardware\\test\\test.txt"); + InitializeObjectAttributes(&ObjectAttributes, &FrameRateKey, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, NULL); + ZwOpenKey(&ChildKey, KEY_READ, &ObjectAttributes); + // Open RGB data file for conversion to YUV space + + Status = ZwQueryKey(ChildKey, KeyFullInformation, &FullKeyInformation, sizeof(FullKeyInformation), &ReturnedSize); +} + +void test_zw_multiple_nulls(PUNICODE_STRING RegistryPath) +{ + OBJECT_ATTRIBUTES objectAttributes = {0}; + HANDLE serviceKey = NULL; + HANDLE parametersKey = NULL; + RTL_QUERY_REGISTRY_TABLE parameters[3] = {0}; + + UNICODE_STRING paramStr; + // + // Default to ENABLING MediaChangeNotification (!) + // + + ULONG mcnRegistryValue = 1; + + NTSTATUS status; + + // + // open the service key. + // + + InitializeObjectAttributes(&objectAttributes, + RegistryPath, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL); + + status = ZwOpenKey(&serviceKey, + KEY_READ, + &objectAttributes); + + // + // Open the parameters key (if any) beneath the services key. + // + + RtlInitUnicodeString(¶mStr, L"Parameters"); + + InitializeObjectAttributes(&objectAttributes, + L"\\Registry\\Machine\\Hardware\\test\\test.txt", + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL); + + status = ZwOpenKey(¶metersKey, + KEY_READ, + &objectAttributes); +} + +// OBJECT_ATTRIBUTES->RootDirectory == NULL and OBJECT_ATTRIBUTES->ObjectName starts with "\registry\machine\hardware\ BUT Zw function does a write" +#include +#include +void test_zw_violation_3(_In_ PDEVICE_OBJECT Fdo, + _In_ PCHAR DeviceName, + _In_ ULONG DeviceNumber, + _In_ ULONG InquiryDataLength) +{ + NTSTATUS status; + SCSI_ADDRESS scsiAddress = {0}; + OBJECT_ATTRIBUTES objectAttributes = {0}; + STRING string; + UNICODE_STRING unicodeName = {0}; + UNICODE_STRING unicodeRegistryPath = {0}; + UNICODE_STRING unicodeData = {0}; + HANDLE targetKey; + IO_STATUS_BLOCK ioStatus; + UCHAR buffer[256] = {0}; + + PAGED_CODE(); + + targetKey = NULL; + + // Issue GET_ADDRESS Ioctl to determine path, target, and lun information. + // + + status = RtlStringCchPrintfA((NTSTRSAFE_PSTR)buffer, + sizeof(buffer) - 1, + "\\Registry\\Machine\\Hardware\\DeviceMap\\Scsi\\Scsi Port %d\\Scsi Bus %d\\Target Id %d\\Logical Unit Id %d", + scsiAddress.PortNumber, + scsiAddress.PathId, + scsiAddress.TargetId, + scsiAddress.Lun); + + RtlInitString(&string, (PCSZ)buffer); + + status = RtlAnsiStringToUnicodeString(&unicodeRegistryPath, + &string, + TRUE); + + // + // Open the registry key for the scsi information for this + // scsibus, target, lun. + // + + InitializeObjectAttributes(&objectAttributes, + &unicodeRegistryPath, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL); + + status = ZwOpenKey(&targetKey, + KEY_READ | KEY_WRITE, + &objectAttributes); + + // + // Now construct and attempt to create the registry value + // specifying the device name in the appropriate place in the + // device map. + // + + RtlInitUnicodeString(&unicodeName, L"DeviceName"); + + status = RtlStringCchPrintfA((NTSTRSAFE_PSTR)buffer, sizeof(buffer) - 1, "%s%d", DeviceName, DeviceNumber); + + RtlInitString(&string, (PCSZ)buffer); + status = RtlAnsiStringToUnicodeString(&unicodeData, + &string, + TRUE); + if (NT_SUCCESS(status)) + { + status = ZwSetValueKey(targetKey, + &unicodeName, + 0, + REG_SZ, + unicodeData.Buffer, + unicodeData.Length); + } + +} // end ClassUpdateInformationInRegistry() + +void test_zw_allowed_rootdirectory_source() +{ + OBJECT_ATTRIBUTES ObjectAttributes; + NTSTATUS Status; + HANDLE ParentKey, RootKey, ChildKey; + UNICODE_STRING UnicodeEnumName; + const WCHAR EnumString[] = L"Enum"; + + PAGED_CODE(); + + PDEVICE_OBJECT PhysicalDeviceObject = NULL; + + Status = IoOpenDeviceRegistryKey(PhysicalDeviceObject, + PLUGPLAY_REGKEY_DRIVER, + STANDARD_RIGHTS_ALL, + &ParentKey); + + // + // create the subkey for the enum section, in the form "\enum" + // + RtlInitUnicodeString(&UnicodeEnumName, EnumString); + + // + // read the registry to determine if children are present. + // + InitializeObjectAttributes(&ObjectAttributes, + &UnicodeEnumName, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + ParentKey, + NULL); + + Status = ZwOpenKey(&RootKey, KEY_READ, &ObjectAttributes); + + if (!NT_SUCCESS(Status)) + { + ZwClose(ParentKey); + return Status; + } +} + +BOOLEAN +TestUsingFunctionParam( + IN PUNICODE_STRING RegistryPath) +{ + OBJECT_ATTRIBUTES objectAttributes = {0}; + HANDLE serviceKey = NULL; + HANDLE parametersKey = NULL; + RTL_QUERY_REGISTRY_TABLE parameters[3] = {0}; + + UNICODE_STRING paramStr; + + // + // Default to ENABLING MediaChangeNotification (!) + // + + ULONG mcnRegistryValue = 1; + + NTSTATUS status; + + PAGED_CODE(); + + // + // open the service key. + // + + InitializeObjectAttributes(&objectAttributes, + RegistryPath, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL); + + status = ZwOpenKey(&serviceKey, + KEY_READ, + &objectAttributes); + + return NT_SUCCESS(status); +} + +void BadCallTestUsingFunctionParam() +{ + UNICODE_STRING RegistryPath; + const WCHAR EnumString[] = L"invalid"; + + RtlInitUnicodeString(&RegistryPath, EnumString); + + TestUsingFunctionParam(&RegistryPath); +} + +void GoodCallTestUsingFunctionParam() +{ + UNICODE_STRING RegistryPath; + const WCHAR EnumString[] = L"\\Registry\\Machine\\Hardware\\DeviceMap\\Scsi\\Scsi Port %d\\Scsi Bus %d\\Target Id %d\\Logical Unit Id %d"; + + RtlInitUnicodeString(&RegistryPath, EnumString); + + TestUsingFunctionParam(&RegistryPath); +} \ No newline at end of file diff --git a/src/drivers/general/queries/experimental/KeSetEventIrql/KeSetEventIrql.sarif b/src/drivers/general/queries/experimental/KeSetEventIrql/KeSetEventIrql.sarif index 2eac8126..c720e080 100644 --- a/src/drivers/general/queries/experimental/KeSetEventIrql/KeSetEventIrql.sarif +++ b/src/drivers/general/queries/experimental/KeSetEventIrql/KeSetEventIrql.sarif @@ -1,264 +1,390 @@ { - "$schema" : "https://json.schemastore.org/sarif-2.1.0.json", - "version" : "2.1.0", - "runs" : [ { - "tool" : { - "driver" : { - "name" : "CodeQL", - "organization" : "GitHub", - "semanticVersion" : "2.15.1", - "notifications" : [ { - "id" : "cpp/baseline/expected-extracted-files", - "name" : "cpp/baseline/expected-extracted-files", - "shortDescription" : { - "text" : "Expected extracted files" - }, - "fullDescription" : { - "text" : "Files appearing in the source archive that are expected to be extracted." - }, - "defaultConfiguration" : { - "enabled" : true - }, - "properties" : { - "tags" : [ "expected-extracted-files", "telemetry" ] - } - } ], - "rules" : [ { - "id" : "cpp/drivers/ke-set-event-irql", - "name" : "cpp/drivers/ke-set-event-irql", - "shortDescription" : { - "text" : "KeSetEvent called at wrong IRQL" - }, - "fullDescription" : { - "text" : "KeSetEvent must be called at DISPATCH_LEVEL or below. If the Wait argument is set to TRUE, it must be called at APC_LEVEL or below." - }, - "defaultConfiguration" : { - "enabled" : true, - "level" : "warning" - }, - "properties" : { - "tags" : [ "correctness", "wddst" ], - "description" : "KeSetEvent must be called at DISPATCH_LEVEL or below. If the Wait argument is set to TRUE, it must be called at APC_LEVEL or below.", - "feature.area" : "Multiple", - "id" : "cpp/drivers/ke-set-event-irql", - "impact" : "Exploitable Design", - "kind" : "problem", - "name" : "KeSetEvent called at wrong IRQL", - "opaqueid" : "CQLD-D0005", - "owner.email" : "sdat@microsoft.com", - "platform" : "Desktop", - "precision" : "medium", - "problem.severity" : "warning", - "query-version" : "v1", - "repro.text" : "The following call to KeSetEvent occurs at too high of an IRQL.", - "scope" : "domainspecific", - "security.severity" : "Low" - } - } ] - }, - "extensions" : [ { - "name" : "microsoft/windows-drivers", - "semanticVersion" : "0.2.0+234ee9b709196a3a802b2c02ad7945ba0dfb0ac0", - "locations" : [ { - "uri" : "file:///C:/codeql-home/Windows-Driver-Developer-Supplemental-Tools/src/", - "description" : { - "text" : "The QL pack root directory." - } - }, { - "uri" : "file:///C:/codeql-home/Windows-Driver-Developer-Supplemental-Tools/src/qlpack.yml", - "description" : { - "text" : "The QL pack definition file." - } - } ] - } ] - }, - "invocations" : [ { - "toolExecutionNotifications" : [ { - "locations" : [ { - "physicalLocation" : { - "artifactLocation" : { - "uri" : "driver/driver_snippet.c", - "uriBaseId" : "%SRCROOT%", - "index" : 0 + "$schema": "https://json.schemastore.org/sarif-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "CodeQL", + "organization": "GitHub", + "semanticVersion": "2.20.4", + "notifications": [ + { + "id": "cpp/baseline/expected-extracted-files", + "name": "cpp/baseline/expected-extracted-files", + "shortDescription": { + "text": "Expected extracted files" + }, + "fullDescription": { + "text": "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration": { + "enabled": true + }, + "properties": { + "tags": [ + "expected-extracted-files", + "telemetry" + ] + } + }, + { + "id": "cpp/extractor/summary", + "name": "cpp/extractor/summary", + "shortDescription": { + "text": "C++ extractor telemetry" + }, + "fullDescription": { + "text": "C++ extractor telemetry" + }, + "defaultConfiguration": { + "enabled": true + } + } + ], + "rules": [ + { + "id": "cpp/drivers/ke-set-event-irql", + "name": "cpp/drivers/ke-set-event-irql", + "shortDescription": { + "text": "KeSetEvent called at wrong IRQL" + }, + "fullDescription": { + "text": "KeSetEvent must be called at DISPATCH_LEVEL or below. If the Wait argument is set to TRUE, it must be called at APC_LEVEL or below." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning" + }, + "properties": { + "tags": [ + "correctness", + "wddst" + ], + "description": "KeSetEvent must be called at DISPATCH_LEVEL or below. If the Wait argument is set to TRUE, it must be called at APC_LEVEL or below.", + "feature.area": "Multiple", + "id": "cpp/drivers/ke-set-event-irql", + "impact": "Exploitable Design", + "kind": "problem", + "name": "KeSetEvent called at wrong IRQL", + "opaqueid": "CQLD-D0005", + "owner.email": "sdat@microsoft.com", + "platform": "Desktop", + "precision": "medium", + "problem.severity": "warning", + "query-version": "v1", + "repro.text": "The following call to KeSetEvent occurs at too high of an IRQL.", + "scope": "domainspecific", + "security.severity": "Low" + } + } + ] + }, + "extensions": [ + { + "name": "microsoft/windows-drivers", + "semanticVersion": "1.3.0+b07e02f3113bb2484479302f733f94b124503172", + "locations": [ + { + "uri": "file:///C:/codeql-home/WDDST/src/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///C:/codeql-home/WDDST/src/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + }, + { + "name": "codeql/cpp-all", + "semanticVersion": "3.1.0+d42788844f7ec0a6b9832140313cc2318e513987", + "locations": [ + { + "uri": "file:///C:/Users/jronstadt/.codeql/packages/codeql/cpp-all/3.1.0/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///C:/Users/jronstadt/.codeql/packages/codeql/cpp-all/3.1.0/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + } + ] + }, + "invocations": [ + { + "toolExecutionNotifications": [ + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "message": { + "text": "Internal telemetry for the C++ extractor.\n\nNo action needed.", + "markdown": "Internal telemetry for the C++ extractor.\n\nNo action needed." + }, + "level": "note", + "timeUtc": "2025-02-11T06:48:26.953567400Z", + "descriptor": { + "id": "cpp/extractor/summary", + "index": 1 + }, + "properties": { + "attributes": { + "cache-hits": 0, + "cache-misses": 1, + "compilers": [ + { + "program": "cl", + "version": "Microsoft (R) C/C++ Optimizing Compiler Version 19.42.34436 for x64" + } + ], + "extractor-failures": 1, + "extractor-successes": 0, + "trap-caching": "disabled" + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + } + ], + "executionSuccessful": true + } + ], + "artifacts": [ + { + "location": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + }, + { + "location": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + }, + { + "location": { + "uri": "driver/fail_driver1.h", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + ], + "results": [ + { + "ruleId": "cpp/drivers/ke-set-event-irql", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/ke-set-event-irql", + "index": 0 + }, + "message": { + "text": "[KeSetEventIrql_Fail1](1): KeSetEvent should not be called above DISPATCH_LEVEL when Wait is set to false." + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 34, + "startColumn": 5, + "endColumn": 15 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "3f0c2c1bba9b015e:1", + "primaryLocationStartColumnFingerprint": "0" + }, + "relatedLocations": [ + { + "id": 1, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 30, + "startColumn": 6, + "endColumn": 26 + } + }, + "message": { + "text": "KeSetEventIrql_Fail1" + } + } + ] + }, + { + "ruleId": "cpp/drivers/ke-set-event-irql", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/ke-set-event-irql", + "index": 0 + }, + "message": { + "text": "[CompletionRoutine](1): KeSetEvent should not be called at or above DISPATCH_LEVEL when Wait is set to true." + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 1 + }, + "region": { + "startLine": 337, + "startColumn": 5, + "endColumn": 15 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "779dfb1bf8eb10c3:1", + "primaryLocationStartColumnFingerprint": "0" + }, + "relatedLocations": [ + { + "id": 1, + "physicalLocation": { + "artifactLocation": { + "uri": "driver/fail_driver1.c", + "uriBaseId": "%SRCROOT%", + "index": 1 + }, + "region": { + "startLine": 316, + "endColumn": 18 + } + }, + "message": { + "text": "CompletionRoutine" + } + } + ] + } + ], + "columnKind": "utf16CodeUnits", + "properties": { + "semmle.formatSpecifier": "sarifv2.1.0" } - } - } ], - "message" : { - "text" : "" - }, - "level" : "none", - "descriptor" : { - "id" : "cpp/baseline/expected-extracted-files", - "index" : 0 - }, - "properties" : { - "formattedMessage" : { - "text" : "" - } } - }, { - "locations" : [ { - "physicalLocation" : { - "artifactLocation" : { - "uri" : "driver/fail_driver1.c", - "uriBaseId" : "%SRCROOT%", - "index" : 1 - } - } - } ], - "message" : { - "text" : "" - }, - "level" : "none", - "descriptor" : { - "id" : "cpp/baseline/expected-extracted-files", - "index" : 0 - }, - "properties" : { - "formattedMessage" : { - "text" : "" - } - } - }, { - "locations" : [ { - "physicalLocation" : { - "artifactLocation" : { - "uri" : "driver/fail_driver1.h", - "uriBaseId" : "%SRCROOT%", - "index" : 2 - } - } - } ], - "message" : { - "text" : "" - }, - "level" : "none", - "descriptor" : { - "id" : "cpp/baseline/expected-extracted-files", - "index" : 0 - }, - "properties" : { - "formattedMessage" : { - "text" : "" - } - } - } ], - "executionSuccessful" : true - } ], - "artifacts" : [ { - "location" : { - "uri" : "driver/driver_snippet.c", - "uriBaseId" : "%SRCROOT%", - "index" : 0 - } - }, { - "location" : { - "uri" : "driver/fail_driver1.c", - "uriBaseId" : "%SRCROOT%", - "index" : 1 - } - }, { - "location" : { - "uri" : "driver/fail_driver1.h", - "uriBaseId" : "%SRCROOT%", - "index" : 2 - } - } ], - "results" : [ { - "ruleId" : "cpp/drivers/ke-set-event-irql", - "ruleIndex" : 0, - "rule" : { - "id" : "cpp/drivers/ke-set-event-irql", - "index" : 0 - }, - "message" : { - "text" : "[KeSetEventIrql_Fail1](1): KeSetEvent should not be called above DISPATCH_LEVEL when Wait is set to false." - }, - "locations" : [ { - "physicalLocation" : { - "artifactLocation" : { - "uri" : "driver/driver_snippet.c", - "uriBaseId" : "%SRCROOT%", - "index" : 0 - }, - "region" : { - "startLine" : 34, - "startColumn" : 5, - "endColumn" : 15 - } - } - } ], - "partialFingerprints" : { - "primaryLocationLineHash" : "3f0c2c1bba9b015e:1", - "primaryLocationStartColumnFingerprint" : "0" - }, - "relatedLocations" : [ { - "id" : 1, - "physicalLocation" : { - "artifactLocation" : { - "uri" : "driver/driver_snippet.c", - "uriBaseId" : "%SRCROOT%", - "index" : 0 - }, - "region" : { - "startLine" : 30, - "startColumn" : 6, - "endColumn" : 26 - } - }, - "message" : { - "text" : "KeSetEventIrql_Fail1" - } - } ] - }, { - "ruleId" : "cpp/drivers/ke-set-event-irql", - "ruleIndex" : 0, - "rule" : { - "id" : "cpp/drivers/ke-set-event-irql", - "index" : 0 - }, - "message" : { - "text" : "[CompletionRoutine](1): KeSetEvent should not be called at or above DISPATCH_LEVEL when Wait is set to true." - }, - "locations" : [ { - "physicalLocation" : { - "artifactLocation" : { - "uri" : "driver/fail_driver1.c", - "uriBaseId" : "%SRCROOT%", - "index" : 1 - }, - "region" : { - "startLine" : 324, - "startColumn" : 5, - "endColumn" : 15 - } - } - } ], - "partialFingerprints" : { - "primaryLocationLineHash" : "779dfb1bf8eb10c3:1", - "primaryLocationStartColumnFingerprint" : "0" - }, - "relatedLocations" : [ { - "id" : 1, - "physicalLocation" : { - "artifactLocation" : { - "uri" : "driver/fail_driver1.c", - "uriBaseId" : "%SRCROOT%", - "index" : 1 - }, - "region" : { - "startLine" : 303, - "endColumn" : 18 - } - }, - "message" : { - "text" : "CompletionRoutine" - } - } ] - } ], - "columnKind" : "utf16CodeUnits", - "properties" : { - "semmle.formatSpecifier" : "sarifv2.1.0" - } - } ] -} \ No newline at end of file + ] +} diff --git a/src/drivers/kmdf/queries/FloatSafeExit/FloatSafeExit.qhelp b/src/drivers/kmdf/queries/FloatSafeExit/FloatSafeExit.qhelp new file mode 100644 index 00000000..ed561fca --- /dev/null +++ b/src/drivers/kmdf/queries/FloatSafeExit/FloatSafeExit.qhelp @@ -0,0 +1,69 @@ + + + +

    + Exiting while holding the right to use floating-point hardware +

    +
    + +

    + The _Kernel_float_restored_ annotation was used to release the right to use floating point, but a path through the function was detected where no function known to perform that operation was successfully called. This warning might indicate that a conditional (_When_) annotation is needed, or it might indicate a coding error. +

    +
    + +

    + Example of function with multiple issues +

    + + +
    + +

    +

    +
    + +
  • + + Warning C28162 + +
  • +
    +
    diff --git a/src/drivers/kmdf/queries/FloatSafeExit/FloatSafeExit.ql b/src/drivers/kmdf/queries/FloatSafeExit/FloatSafeExit.ql new file mode 100644 index 00000000..bc1c45c8 --- /dev/null +++ b/src/drivers/kmdf/queries/FloatSafeExit/FloatSafeExit.ql @@ -0,0 +1,109 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/** + * @id cpp/drivers/kmdf/float-safe-exit + * @kind problem + * @name Float Safe Exit + * @description Exiting while holding the right to use floating-point hardware + * @platform Desktop + * @feature.area Multiple + * @impact Insecure Coding Practice + * @repro.text + * @owner.email: sdat@microsoft.com + * @opaqueid CQLD-C28162 + * @problem.severity warning + * @precision medium + * @tags correctness + * ca_ported + * @scope domainspecific + * @query-version v1 + */ + +import cpp +import semmle.code.cpp.dataflow.new.DataFlow +import drivers.libraries.SAL +import drivers.kmdf.libraries.KmdfDrivers +import semmle.code.cpp.Specifier + +class KernelFloatFunctionAnnotation extends SALAnnotation { + KernelFloatFunctionAnnotation() { this.getMacroName().matches(["_Kernel_float_restored_"]) } +} + +class KernelFloatAnnotatedTypedef extends TypedefType { + KernelFloatFunctionAnnotation kernelFloatAnnotation; + + KernelFloatAnnotatedTypedef() { kernelFloatAnnotation.getTypedefDeclarations() = this } + + KernelFloatFunctionAnnotation getKernelFloatAnnotation() { result = kernelFloatAnnotation } +} + +class KernelFloatAnnotatedFunction extends Function { + KernelFloatFunctionAnnotation kernelFloatAnnotation; + + + KernelFloatAnnotatedFunction() { + ( + // this.hasCLinkage() and + exists( + FunctionDeclarationEntry fde // actual function declarations + | + fde = this.getADeclarationEntry() and + kernelFloatAnnotation.getDeclarationEntry() = fde + ) + or + exists( + FunctionDeclarationEntry fde // typedefs + | + fde.getFunction() = this and + fde.getTypedefType().(KernelFloatAnnotatedTypedef).getKernelFloatAnnotation() = + kernelFloatAnnotation + ) + ) + } +} + +class SafeFloatRestoreFuncCall extends FunctionCall { + SafeFloatRestoreFuncCall() { + this.getTarget().getName() = ("KeRestoreFloatingPointState") or + this.getTarget().getName() = ("EngRestoreFloatingPointState") + } +} + +class SafeFloatRestoreFunc extends Function { + SafeFloatRestoreFunc() { + this.getName() = ("KeRestoreFloatingPointState") or + this.getName() = ("EngRestoreFloatingPointState") + } +} + +predicate unused(Expr e) { e instanceof ExprInVoidContext } + +from FunctionCall unusedFc, KernelFloatAnnotatedFunction kernelFloatFunc, BasicBlock bb, string msg +where + ( + // each path must have a call to a float save function + bb.getEnclosingFunction() = kernelFloatFunc and + not exists(SafeFloatRestoreFuncCall safeFloatRestoreFuncCall, ControlFlowNode node | + bb.getNode(0).getASuccessor*().getBasicBlock().contains(node) and + node = safeFloatRestoreFuncCall.getBasicBlock().getANode() and + safeFloatRestoreFuncCall.getEnclosingFunction() = kernelFloatFunc + ) and + msg = + "Function annotated with _Kernel_float_restored_ but may not call/check return value of a safe float access function for some path(s)" + or + // Paths have call to safe float access function but return value is not used + unusedFc instanceof SafeFloatRestoreFuncCall and + unusedFc.getEnclosingFunction() = kernelFloatFunc and + msg = + "Function annotated with _Kernel_float_restored_ but may not call/check return value of a safe float access function for some path(s)" and + ( + // return value isn't used at all + unused(unusedFc) + or + // return value saved to variable but not used + definition(_, unusedFc.getParent()) and + not definitionUsePair(_, unusedFc.getParent(), _) + ) + ) and + not kernelFloatFunc instanceof SafeFloatRestoreFunc +select kernelFloatFunc, msg diff --git a/src/drivers/kmdf/queries/FloatSafeExit/FloatSafeExit.sarif b/src/drivers/kmdf/queries/FloatSafeExit/FloatSafeExit.sarif new file mode 100644 index 00000000..6e47be5c --- /dev/null +++ b/src/drivers/kmdf/queries/FloatSafeExit/FloatSafeExit.sarif @@ -0,0 +1,610 @@ +{ + "$schema": "https://json.schemastore.org/sarif-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "CodeQL", + "organization": "GitHub", + "semanticVersion": "2.21.0", + "notifications": [ + { + "id": "cpp/baseline/expected-extracted-files", + "name": "cpp/baseline/expected-extracted-files", + "shortDescription": { + "text": "Expected extracted files" + }, + "fullDescription": { + "text": "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration": { + "enabled": true + }, + "properties": { + "tags": [ + "expected-extracted-files", + "telemetry" + ] + } + }, + { + "id": "cpp/extractor/summary", + "name": "cpp/extractor/summary", + "shortDescription": { + "text": "C++ extractor telemetry" + }, + "fullDescription": { + "text": "C++ extractor telemetry" + }, + "defaultConfiguration": { + "enabled": true + } + } + ], + "rules": [ + { + "id": "cpp/drivers/kmdf/float-safe-exit", + "name": "cpp/drivers/kmdf/float-safe-exit", + "shortDescription": { + "text": "Float Safe Exit" + }, + "fullDescription": { + "text": "Exiting while holding the right to use floating-point hardware" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning" + }, + "properties": { + "tags": [ + "correctness", + "ca_ported" + ], + "description": "Exiting while holding the right to use floating-point hardware", + "feature.area": "Multiple", + "id": "cpp/drivers/kmdf/float-safe-exit", + "impact": "Insecure Coding Practice", + "kind": "problem", + "name": "Float Safe Exit", + "opaqueid": "CQLD-C28162", + "owner.email:": "sdat@microsoft.com", + "platform": "Desktop", + "precision": "medium", + "problem.severity": "warning", + "query-version": "v1", + "repro.text": "", + "scope": "domainspecific" + } + } + ] + }, + "extensions": [ + { + "name": "microsoft/windows-drivers", + "semanticVersion": "1.6.0+13a2c3dbefb34580b35797eff87bc81f9f7eb6b9", + "locations": [ + { + "uri": "file:///C:/codeql-home/Windows-Driver-Developer-Supplemental-Tools/src/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///C:/codeql-home/Windows-Driver-Developer-Supplemental-Tools/src/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + }, + { + "name": "codeql/cpp-all", + "semanticVersion": "4.2.0+2409bcc0d62644acbc432900bc59c2e3ff33bd56", + "locations": [ + { + "uri": "file:///C:/Users/jronstadt/.codeql/packages/codeql/cpp-all/4.2.0/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///C:/Users/jronstadt/.codeql/packages/codeql/cpp-all/4.2.0/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + } + ] + }, + "invocations": [ + { + "toolExecutionNotifications": [ + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "Driver.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "Queue.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "Public.h", + "uriBaseId": "%SRCROOT%", + "index": 3 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "Trace.h", + "uriBaseId": "%SRCROOT%", + "index": 4 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "Device.h", + "uriBaseId": "%SRCROOT%", + "index": 5 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "Driver.c", + "uriBaseId": "%SRCROOT%", + "index": 6 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "Device.c", + "uriBaseId": "%SRCROOT%", + "index": 7 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "Queue.h", + "uriBaseId": "%SRCROOT%", + "index": 8 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "message": { + "text": "Internal telemetry for the C++ extractor.\n\nNo action needed.", + "markdown": "Internal telemetry for the C++ extractor.\n\nNo action needed." + }, + "level": "note", + "timeUtc": "2025-06-21T06:00:24.321948300Z", + "descriptor": { + "id": "cpp/extractor/summary", + "index": 1 + }, + "properties": { + "attributes": { + "cache-hits": 0, + "cache-misses": 1, + "compilers": [ + { + "program": "cl", + "version": "Microsoft (R) C/C++ Optimizing Compiler Version 19.44.35208 for x64" + } + ], + "extractor-failures": 1, + "extractor-successes": 0, + "trap-caching": "disabled" + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + } + ], + "executionSuccessful": true + } + ], + "artifacts": [ + { + "location": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + }, + { + "location": { + "uri": "Driver.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + }, + { + "location": { + "uri": "Queue.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + }, + { + "location": { + "uri": "Public.h", + "uriBaseId": "%SRCROOT%", + "index": 3 + } + }, + { + "location": { + "uri": "Trace.h", + "uriBaseId": "%SRCROOT%", + "index": 4 + } + }, + { + "location": { + "uri": "Device.h", + "uriBaseId": "%SRCROOT%", + "index": 5 + } + }, + { + "location": { + "uri": "Driver.c", + "uriBaseId": "%SRCROOT%", + "index": 6 + } + }, + { + "location": { + "uri": "Device.c", + "uriBaseId": "%SRCROOT%", + "index": 7 + } + }, + { + "location": { + "uri": "Queue.h", + "uriBaseId": "%SRCROOT%", + "index": 8 + } + } + ], + "results": [ + { + "ruleId": "cpp/drivers/kmdf/float-safe-exit", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/kmdf/float-safe-exit", + "index": 0 + }, + "message": { + "text": "Function annotated with _Kernel_float_restored_ but may not call/check return value of a safe float access function for some path(s)" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 69, + "startColumn": 30, + "endColumn": 45 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "e6e573b685934f8e:1", + "primaryLocationStartColumnFingerprint": "29" + } + }, + { + "ruleId": "cpp/drivers/kmdf/float-safe-exit", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/kmdf/float-safe-exit", + "index": 0 + }, + "message": { + "text": "Function annotated with _Kernel_float_restored_ but may not call/check return value of a safe float access function for some path(s)" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 60, + "startColumn": 30, + "endColumn": 45 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "6c5b6cd6863fe3a0:1", + "primaryLocationStartColumnFingerprint": "29" + } + }, + { + "ruleId": "cpp/drivers/kmdf/float-safe-exit", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/kmdf/float-safe-exit", + "index": 0 + }, + "message": { + "text": "Function annotated with _Kernel_float_restored_ but may not call/check return value of a safe float access function for some path(s)" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 43, + "startColumn": 30, + "endColumn": 45 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "8df2b9dba2f4259a:1", + "primaryLocationStartColumnFingerprint": "29" + } + }, + { + "ruleId": "cpp/drivers/kmdf/float-safe-exit", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/kmdf/float-safe-exit", + "index": 0 + }, + "message": { + "text": "Function annotated with _Kernel_float_restored_ but may not call/check return value of a safe float access function for some path(s)" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 19, + "startColumn": 30, + "endColumn": 46 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "e257ac675879444e:1", + "primaryLocationStartColumnFingerprint": "29" + } + } + ], + "columnKind": "utf16CodeUnits", + "properties": { + "semmle.formatSpecifier": "sarifv2.1.0" + } + } + ] +} \ No newline at end of file diff --git a/src/drivers/kmdf/queries/FloatSafeExit/driver_snippet.c b/src/drivers/kmdf/queries/FloatSafeExit/driver_snippet.c new file mode 100644 index 00000000..86104687 --- /dev/null +++ b/src/drivers/kmdf/queries/FloatSafeExit/driver_snippet.c @@ -0,0 +1,104 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +// Macros to enable or disable a code section that may or may not conflict with this test. +#define SET_DISPATCH 1 +int _fltused; + +// Template function. Not used for this test. +void top_level_call() +{ +} + +void float_used_good0() +{ + float f = 0.0f; + f = f + 1.0f; +} + +_Kernel_float_restored_ void float_used_good1() +{ + KFLOATING_SAVE saveData; + NTSTATUS status; + float f = 0.0f; + status = KeSaveFloatingPointState(&saveData); + if (status != STATUS_SUCCESS) + { + status = KeRestoreFloatingPointState(&saveData); + return; + } + for (int i = 0; i < 100; i++) + { + f = f + 1.0f; + } + status = KeRestoreFloatingPointState(&saveData); + if (!NT_SUCCESS(status)) + { + ; + // handle error + } +} + +// Fail cases +_Kernel_float_restored_ void float_used_bad1() +{ + KFLOATING_SAVE saveData; + NTSTATUS status; + float f = 0.0f; + status = KeSaveFloatingPointState(&saveData); + if (!NT_SUCCESS(status)) + { + return; + } + for (int i = 0; i < 100; i++) + { + f = f + 1.0f; + } + // doesn't check the return value of KeRestoreFloatingPointState + KeRestoreFloatingPointState(&saveData); +} +_Kernel_float_restored_ void float_used_bad2() +{ + float f = 0.0f; + for (int i = 0; i < 100; i++) + { + f = f + 1.0f; + } + // No call to KeRestoreFloatingPointState +} +_Kernel_float_restored_ void float_used_bad3() +{ + float f = 0.0f; + int some_condition = 1; + KFLOATING_SAVE saveData; + NTSTATUS status; + + if (some_condition) + { + status = KeSaveFloatingPointState(&saveData); + if (!NT_SUCCESS(status)) + { + return; + } + for (int i = 0; i < 100; i++) + { + f = f + 1; + } + // doesn't restore the floating point state + } + else + { + status = KeSaveFloatingPointState(&saveData); + if (!NT_SUCCESS(status)) + { + return; + } + for (int i = 0; i < 100; i++) + { + f = f + 1.0f; + } + + // doesn't check the return value of KeRestoreFloatingPointState + status = KeRestoreFloatingPointState(&saveData); + } +} diff --git a/src/drivers/kmdf/queries/FloatUnsafeExit/FloatUnsafeExit.qhelp b/src/drivers/kmdf/queries/FloatUnsafeExit/FloatUnsafeExit.qhelp new file mode 100644 index 00000000..354fe0bc --- /dev/null +++ b/src/drivers/kmdf/queries/FloatUnsafeExit/FloatUnsafeExit.qhelp @@ -0,0 +1,82 @@ + + + +

    + Exiting without acquiring the right to use floating hardware +

    +
    + +

    + The _Kernel_float_saved_ annotation was used to acquire the right to use floating point, but a path through the function was detected where no function known to perform that operation was successfully called. This warning might indicate that a conditional (_When_) annotation is needed, or it might indicate a coding error. +

    +
    + +

    + Function has _Kernel_float_saved_ annotation but has a path where the floating point state is not saved. Additionally, KeSaveFloatingPointState return value is not checked so the call might fail +

    + + +

    + Good example +

    + + +
    + +

    +

    +
    + +
  • + + Warning C28161 + +
  • +
    +
    diff --git a/src/drivers/kmdf/queries/FloatUnsafeExit/FloatUnsafeExit.ql b/src/drivers/kmdf/queries/FloatUnsafeExit/FloatUnsafeExit.ql new file mode 100644 index 00000000..e4e7ebbc --- /dev/null +++ b/src/drivers/kmdf/queries/FloatUnsafeExit/FloatUnsafeExit.ql @@ -0,0 +1,99 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/** + * @id cpp/drivers/kmdf/float-unsafe-exit + * @kind problem + * @name Float Unsafe Exit + * @description Exiting without acquiring the right to use floating hardware + * @platform Desktop + * @feature.area Multiple + * @impact Insecure Coding Practice + * @repro.text + * @owner.email: sdat@microsoft.com + * @opaqueid CQLD-C28161 + * @problem.severity warning + * @precision medium + * @tags correctness + * ca_ported + * @scope domainspecific + * @query-version v1 + */ + +import cpp +import semmle.code.cpp.dataflow.new.DataFlow +import drivers.libraries.SAL +import drivers.kmdf.libraries.KmdfDrivers +import semmle.code.cpp.Specifier + +class KernelFloatFunctionAnnotation extends SALAnnotation { + KernelFloatFunctionAnnotation() { this.getMacroName().matches(["_Kernel_float_saved_"]) } +} + +class KernelFloatAnnotatedTypedef extends TypedefType { + KernelFloatFunctionAnnotation kernelFloatAnnotation; + + KernelFloatAnnotatedTypedef() { kernelFloatAnnotation.getTypedefDeclarations() = this } + + KernelFloatFunctionAnnotation getKernelFloatAnnotation() { result = kernelFloatAnnotation } +} + +class KernelFloatAnnotatedFunction extends Function { + KernelFloatFunctionAnnotation kernelFloatAnnotation; + + + KernelFloatAnnotatedFunction() { + ( + // this.hasCLinkage() and + exists( + FunctionDeclarationEntry fde // actual function declarations + | + fde = this.getADeclarationEntry() and + kernelFloatAnnotation.getDeclarationEntry() = fde + ) + or + exists( + FunctionDeclarationEntry fde // typedefs + | + fde.getFunction() = this and + fde.getTypedefType().(KernelFloatAnnotatedTypedef).getKernelFloatAnnotation() = + kernelFloatAnnotation + ) + ) + } +} + +class SafeFloatAccessFuncCall extends FunctionCall { + SafeFloatAccessFuncCall() { + this.getTarget().getName() = ("KeSaveFloatingPointState") or + this.getTarget().getName() = ("EngSaveFloatingPointState") + } +} + +predicate unused(Expr e) { e instanceof ExprInVoidContext } + +from FunctionCall unusedFc, KernelFloatAnnotatedFunction kernelFloatFunc, BasicBlock bb, string msg +where + // each path must have a call to a float save function + bb.getEnclosingFunction() = kernelFloatFunc and + not exists(SafeFloatAccessFuncCall safeFloatAccessFuncCall, ControlFlowNode node | + bb.getNode(0).getAPredecessor*().getBasicBlock().contains(node) and + node = safeFloatAccessFuncCall.getBasicBlock().getANode() and + safeFloatAccessFuncCall.getEnclosingFunction() = kernelFloatFunc + ) and + msg = + "Function annotated with _Kernel_float_saved_ but may not call/check return values of safe float access function for some path(s)" + or + // Paths have call to safe float access function but return value is not used + unusedFc instanceof SafeFloatAccessFuncCall and + unusedFc.getEnclosingFunction() = kernelFloatFunc and + msg = + "Function annotated with _Kernel_float_saved_ but may not call/check return values of safe float access function for some path(s)" and + ( + // return value isn't used at all + unused(unusedFc) + or + // return value saved to variable but not used + definition(_, unusedFc.getParent()) and + not definitionUsePair(_, unusedFc.getParent(), _) + ) +select kernelFloatFunc, msg diff --git a/src/drivers/kmdf/queries/FloatUnsafeExit/FloatUnsafeExit.sarif b/src/drivers/kmdf/queries/FloatUnsafeExit/FloatUnsafeExit.sarif new file mode 100644 index 00000000..1e476a6d --- /dev/null +++ b/src/drivers/kmdf/queries/FloatUnsafeExit/FloatUnsafeExit.sarif @@ -0,0 +1,579 @@ +{ + "$schema": "https://json.schemastore.org/sarif-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "CodeQL", + "organization": "GitHub", + "semanticVersion": "2.21.0", + "notifications": [ + { + "id": "cpp/baseline/expected-extracted-files", + "name": "cpp/baseline/expected-extracted-files", + "shortDescription": { + "text": "Expected extracted files" + }, + "fullDescription": { + "text": "Files appearing in the source archive that are expected to be extracted." + }, + "defaultConfiguration": { + "enabled": true + }, + "properties": { + "tags": [ + "expected-extracted-files", + "telemetry" + ] + } + }, + { + "id": "cpp/extractor/summary", + "name": "cpp/extractor/summary", + "shortDescription": { + "text": "C++ extractor telemetry" + }, + "fullDescription": { + "text": "C++ extractor telemetry" + }, + "defaultConfiguration": { + "enabled": true + } + } + ], + "rules": [ + { + "id": "cpp/drivers/kmdf/float-unsafe-exit", + "name": "cpp/drivers/kmdf/float-unsafe-exit", + "shortDescription": { + "text": "Float Unsafe Exit" + }, + "fullDescription": { + "text": "Exiting without acquiring the right to use floating hardware" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning" + }, + "properties": { + "tags": [ + "correctness", + "ca_ported" + ], + "description": "Exiting without acquiring the right to use floating hardware", + "feature.area": "Multiple", + "id": "cpp/drivers/kmdf/float-unsafe-exit", + "impact": "Insecure Coding Practice", + "kind": "problem", + "name": "Float Unsafe Exit", + "opaqueid": "CQLD-C28161", + "owner.email:": "sdat@microsoft.com", + "platform": "Desktop", + "precision": "medium", + "problem.severity": "warning", + "query-version": "v1", + "repro.text": "", + "scope": "domainspecific" + } + } + ] + }, + "extensions": [ + { + "name": "microsoft/windows-drivers", + "semanticVersion": "1.6.0+13a2c3dbefb34580b35797eff87bc81f9f7eb6b9", + "locations": [ + { + "uri": "file:///C:/codeql-home/Windows-Driver-Developer-Supplemental-Tools/src/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///C:/codeql-home/Windows-Driver-Developer-Supplemental-Tools/src/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + }, + { + "name": "codeql/cpp-all", + "semanticVersion": "4.2.0+2409bcc0d62644acbc432900bc59c2e3ff33bd56", + "locations": [ + { + "uri": "file:///C:/Users/jronstadt/.codeql/packages/codeql/cpp-all/4.2.0/", + "description": { + "text": "The QL pack root directory." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackRoot" + ] + } + }, + { + "uri": "file:///C:/Users/jronstadt/.codeql/packages/codeql/cpp-all/4.2.0/qlpack.yml", + "description": { + "text": "The QL pack definition file." + }, + "properties": { + "tags": [ + "CodeQL/LocalPackDefinitionFile" + ] + } + } + ] + } + ] + }, + "invocations": [ + { + "toolExecutionNotifications": [ + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "Driver.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "Queue.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "Trace.h", + "uriBaseId": "%SRCROOT%", + "index": 3 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "Public.h", + "uriBaseId": "%SRCROOT%", + "index": 4 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "Device.h", + "uriBaseId": "%SRCROOT%", + "index": 5 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "Device.c", + "uriBaseId": "%SRCROOT%", + "index": 6 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "Driver.c", + "uriBaseId": "%SRCROOT%", + "index": 7 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "Queue.h", + "uriBaseId": "%SRCROOT%", + "index": 8 + } + } + } + ], + "message": { + "text": "" + }, + "level": "none", + "descriptor": { + "id": "cpp/baseline/expected-extracted-files", + "index": 0 + }, + "properties": { + "formattedMessage": { + "text": "" + } + } + }, + { + "message": { + "text": "Internal telemetry for the C++ extractor.\n\nNo action needed.", + "markdown": "Internal telemetry for the C++ extractor.\n\nNo action needed." + }, + "level": "note", + "timeUtc": "2025-06-21T05:55:48.895842600Z", + "descriptor": { + "id": "cpp/extractor/summary", + "index": 1 + }, + "properties": { + "attributes": { + "cache-hits": 0, + "cache-misses": 1, + "compilers": [ + { + "program": "cl", + "version": "Microsoft (R) C/C++ Optimizing Compiler Version 19.44.35208 for x64" + } + ], + "extractor-failures": 1, + "extractor-successes": 0, + "trap-caching": "disabled" + }, + "visibility": { + "statusPage": false, + "telemetry": true + } + } + } + ], + "executionSuccessful": true + } + ], + "artifacts": [ + { + "location": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + } + }, + { + "location": { + "uri": "Driver.h", + "uriBaseId": "%SRCROOT%", + "index": 1 + } + }, + { + "location": { + "uri": "Queue.c", + "uriBaseId": "%SRCROOT%", + "index": 2 + } + }, + { + "location": { + "uri": "Trace.h", + "uriBaseId": "%SRCROOT%", + "index": 3 + } + }, + { + "location": { + "uri": "Public.h", + "uriBaseId": "%SRCROOT%", + "index": 4 + } + }, + { + "location": { + "uri": "Device.h", + "uriBaseId": "%SRCROOT%", + "index": 5 + } + }, + { + "location": { + "uri": "Device.c", + "uriBaseId": "%SRCROOT%", + "index": 6 + } + }, + { + "location": { + "uri": "Driver.c", + "uriBaseId": "%SRCROOT%", + "index": 7 + } + }, + { + "location": { + "uri": "Queue.h", + "uriBaseId": "%SRCROOT%", + "index": 8 + } + } + ], + "results": [ + { + "ruleId": "cpp/drivers/kmdf/float-unsafe-exit", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/kmdf/float-unsafe-exit", + "index": 0 + }, + "message": { + "text": "Function annotated with _Kernel_float_saved_ but may not call/check return values of safe float access function for some path(s)" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 65, + "startColumn": 27, + "endColumn": 42 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "706b2da57d361807:1", + "primaryLocationStartColumnFingerprint": "26" + } + }, + { + "ruleId": "cpp/drivers/kmdf/float-unsafe-exit", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/kmdf/float-unsafe-exit", + "index": 0 + }, + "message": { + "text": "Function annotated with _Kernel_float_saved_ but may not call/check return values of safe float access function for some path(s)" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 56, + "startColumn": 27, + "endColumn": 42 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "395b95498034a767:1", + "primaryLocationStartColumnFingerprint": "26" + } + }, + { + "ruleId": "cpp/drivers/kmdf/float-unsafe-exit", + "ruleIndex": 0, + "rule": { + "id": "cpp/drivers/kmdf/float-unsafe-exit", + "index": 0 + }, + "message": { + "text": "Function annotated with _Kernel_float_saved_ but may not call/check return values of safe float access function for some path(s)" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "driver/driver_snippet.c", + "uriBaseId": "%SRCROOT%", + "index": 0 + }, + "region": { + "startLine": 43, + "startColumn": 27, + "endColumn": 42 + } + } + } + ], + "partialFingerprints": { + "primaryLocationLineHash": "f41ff8f76c17b365:1", + "primaryLocationStartColumnFingerprint": "26" + } + } + ], + "columnKind": "utf16CodeUnits", + "properties": { + "semmle.formatSpecifier": "sarifv2.1.0" + } + } + ] +} \ No newline at end of file diff --git a/src/drivers/kmdf/queries/FloatUnsafeExit/driver_snippet.c b/src/drivers/kmdf/queries/FloatUnsafeExit/driver_snippet.c new file mode 100644 index 00000000..53bd71c0 --- /dev/null +++ b/src/drivers/kmdf/queries/FloatUnsafeExit/driver_snippet.c @@ -0,0 +1,90 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +// Macros to enable or disable a code section that may or may not conflict with this test. +#define SET_DISPATCH 1 +int _fltused; + +// Template function. Not used for this test. +void top_level_call() +{ +} + +void float_used_good0() +{ + float f = 0.0f; + f = f + 1.0f; +} + +_Kernel_float_saved_ void float_used_good1() +{ + KFLOATING_SAVE saveData; + NTSTATUS status; + float f = 0.0f; + status = KeSaveFloatingPointState(&saveData); + if (status != STATUS_SUCCESS) + { + status = KeRestoreFloatingPointState(&saveData); + return; + } + for (int i = 0; i < 100; i++) + { + f = f + 1.0f; + } + status = KeRestoreFloatingPointState(&saveData); + if (!NT_SUCCESS(status)) + { + ; + // handle error + } +} + +// Fail cases +_Kernel_float_saved_ void float_used_bad1() +{ + KFLOATING_SAVE saveData; + NTSTATUS status; + float f = 0.0f; + status = KeSaveFloatingPointState(&saveData); + // Status not checked here so the call to KeSaveFloatingPointState may fail + for (int i = 0; i < 100; i++) + { + f = f + 1.0f; + } + KeRestoreFloatingPointState(&saveData); +} +_Kernel_float_saved_ void float_used_bad2() +{ + float f = 0.0f; + // Status not checked here + for (int i = 0; i < 100; i++) + { + f = f + 1.0f; + } +} +_Kernel_float_saved_ void float_used_bad3() +{ + float f = 0.0f; + // Status not checked here + int some_condition = 1; + KFLOATING_SAVE saveData; + NTSTATUS status; + + if (some_condition) + { + // This code path doesn't save the floating point state + for (int i = 0; i < 100; i++) + { + f = f + 1; + } + } + else + { + status = KeSaveFloatingPointState(&saveData); + // Status not checked here + for (int i = 0; i < 100; i++) + { + f = f + 1.0f; + } + } +} diff --git a/src/drivers/kmdf/queries/experimental/DeviceInitApi/DeviceInitApi.ql b/src/drivers/kmdf/queries/experimental/DeviceInitApi/DeviceInitApi.ql index 7a58cf65..6c8fae84 100644 --- a/src/drivers/kmdf/queries/experimental/DeviceInitApi/DeviceInitApi.ql +++ b/src/drivers/kmdf/queries/experimental/DeviceInitApi/DeviceInitApi.ql @@ -7,14 +7,13 @@ * @kind path-problem * @problem.severity error * @precision medium - * @id cpp/windows/wdk/kmdf/DeviceInitApi + * @id cpp/windows/wdk/kmdf/device-init-api * @tags correctness * @query-version v1 */ import drivers.kmdf.libraries.KmdfDrivers -import semmle.code.cpp.dataflow.DataFlow -import DataFlow::PathGraph +import semmle.code.cpp.dataflow.new.DataFlow /** A function that initializes or changes a WDFDEVICE_INIT struct, and which must not be called after WDFDeviceCreate. */ class WdfInitializationApi extends Function { @@ -73,24 +72,23 @@ predicate isChildExpr(Expr e, FunctionCall func) { * A data-flow model to determine if a use of a WDFDEVICE_INIT struct is * used in an initialization function after WdfDeviceCreate is called. */ -class InitAPIDataFlow extends DataFlow::Configuration { - InitAPIDataFlow() { this = "KMDFDeviceInitApiFlow" } +module InitAPIDataFlowConfig implements DataFlow::ConfigSig { - override predicate isSource(DataFlow::Node source) { + predicate isSource(DataFlow::Node source) { exists(FunctionCall fc | fc.getTarget().getName().matches("WdfDeviceCreate") and fc.getArgument(0).getAChild*() = source.asExpr() ) } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { exists(FunctionCall fc | fc.getTarget() instanceof WdfInitializationApi and fc.getArgument(0).getAChild*() = sink.asExpr() ) } - override predicate isAdditionalFlowStep(DataFlow::Node source, DataFlow::Node sink) { + predicate isAdditionalFlowStep(DataFlow::Node source, DataFlow::Node sink) { exists(FunctionCall fc | fc.getTarget().getName().matches("WdfDeviceCreate") and fc.getTarget() = sink.getFunction() and @@ -99,13 +97,16 @@ class InitAPIDataFlow extends DataFlow::Configuration { } } -from InitAPIDataFlow iadf, DataFlow::PathNode e1, DataFlow::PathNode e2 +module InitAPIDataFlow = DataFlow::Global; +import InitAPIDataFlow::PathGraph + +from InitAPIDataFlow::PathNode e1, InitAPIDataFlow::PathNode e2 where exists(FunctionCall driverCreateCall, WdfInitiailzationApiCall apiCall | driverCreateCall.getAChild*() = e1.getNode().asExpr() and isChildExpr(e2.getNode().asExpr(), apiCall) and driverCreateCall.getASuccessor*() = apiCall ) and - iadf.hasFlowPath(e1, e2) + InitAPIDataFlow::flowPath(e1, e2) select e1.getNode(), e1, e2, "A WDF device object initialization method was called after WdfDeviceCreate was called on the same WDFDEVICE_INIT struct. This can lead to system instability." diff --git a/src/drivers/kmdf/queries/wfp/ConnectRedirectHandleCreation/ConnectRedirectHandleCreation.qlhelp b/src/drivers/kmdf/queries/wfp/ConnectRedirectHandleCreation/ConnectRedirectHandleCreation.qlhelp new file mode 100644 index 00000000..4eddecad --- /dev/null +++ b/src/drivers/kmdf/queries/wfp/ConnectRedirectHandleCreation/ConnectRedirectHandleCreation.qlhelp @@ -0,0 +1,23 @@ + + + +

    + A callout should call FwpsRedirectHandleCreate0 only once and cache the handle and use it during classify. +

    +
    + +

    + The following function does not call FwpsRedirectHandleCreate0 or calls it multiple times and does not cache the handle. +

    +
    + + + + +
  • + + Callout Drivers - Windows Drivers + +
  • +
    +
    \ No newline at end of file diff --git a/src/drivers/kmdf/queries/wfp/ConnectRedirectHandleCreation/ConnectRedirectHandleCreation.sarif b/src/drivers/kmdf/queries/wfp/ConnectRedirectHandleCreation/ConnectRedirectHandleCreation.sarif new file mode 100644 index 00000000..f0422de0 --- /dev/null +++ b/src/drivers/kmdf/queries/wfp/ConnectRedirectHandleCreation/ConnectRedirectHandleCreation.sarif @@ -0,0 +1,141 @@ +{ + "$schema" : "https://json.schemastore.org/sarif-2.1.0.json", + "version" : "2.1.0", + "runs" : [ { + "tool" : { + "driver" : { + "name" : "CodeQL", + "organization" : "GitHub", + "semanticVersion" : "2.11.5", + "rules" : [ { + "id" : "cpp/windows/drivers/kmdf/queries/wfp/connect-redirect-handle-creation", + "name" : "cpp/windows/drivers/kmdf/queries/wfp/connect-redirect-handle-creation", + "shortDescription" : { + "text" : "A callout should call FwpsRedirectHandleCreate0 only once and cache the handle and use it during classify." + }, + "fullDescription" : { + "text" : "A callout should call FwpsRedirectHandleCreate0 only once and cache the handle and use it during classify." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "properties" : { + "tags" : [ "correctness" ], + "description" : "A callout should call FwpsRedirectHandleCreate0 only once and cache the handle and use it during classify.", + "feature.area" : "Multiple", + "id" : "cpp/windows/drivers/kmdf/queries/wfp/connect-redirect-handle-creation", + "impact" : "Insecure Coding Practice", + "kind" : "problem", + "name" : "A callout should call FwpsRedirectHandleCreate0 only once and cache the handle and use it during classify.", + "opaqueid" : "CQLD-C2900", + "owner.email:" : "elhouha@microsoft.com", + "platform" : "Desktop", + "precision" : "low", + "problem.severity" : "warning", + "query-version" : "v1", + "repro.text" : "The following function does not call FwpsRedirectHandleCreate0 or calls it multiple times and does not cache the handle.", + "scope" : "domainspecific" + } + } ] + }, + "extensions" : [ { + "name" : "microsoft/windows-drivers", + "semanticVersion" : "0.1.0+933e876f096a70922173e4d5ad604d99d4481af4", + "locations" : [ { + "uri" : "file:///C:/codeql-home/Windows-Driver-Developer-Supplemental-Tools/src/", + "description" : { + "text" : "The QL pack root directory." + } + }, { + "uri" : "file:///C:/codeql-home/Windows-Driver-Developer-Supplemental-Tools/src/qlpack.yml", + "description" : { + "text" : "The QL pack definition file." + } + } ] + }, { + "name" : "legacy-upgrades", + "semanticVersion" : "0.0.0", + "locations" : [ { + "uri" : "file:///C:/codeql-home/codeql/legacy-upgrades/", + "description" : { + "text" : "The QL pack root directory." + } + }, { + "uri" : "file:///C:/codeql-home/codeql/legacy-upgrades/qlpack.yml", + "description" : { + "text" : "The QL pack definition file." + } + } ] + } ] + }, + "artifacts" : [ { + "location" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + } + } ], + "results" : [ { + "ruleId" : "cpp/windows/drivers/kmdf/queries/wfp/connect-redirect-handle-creation", + "ruleIndex" : 0, + "rule" : { + "id" : "cpp/windows/drivers/kmdf/queries/wfp/connect-redirect-handle-creation", + "index" : 0 + }, + "message" : { + "text" : "Connect Redirect Callout properly uses FwpsRedirectHandleCreate0." + }, + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + }, + "region" : { + "startLine" : 26, + "startColumn" : 46, + "endColumn" : 52 + } + } + } ], + "partialFingerprints" : { + "primaryLocationLineHash" : "56b74a54af745f59:1", + "primaryLocationStartColumnFingerprint" : "41" + } + }, { + "ruleId" : "cpp/windows/drivers/kmdf/queries/wfp/connect-redirect-handle-creation", + "ruleIndex" : 0, + "rule" : { + "id" : "cpp/windows/drivers/kmdf/queries/wfp/connect-redirect-handle-creation", + "index" : 0 + }, + "message" : { + "text" : "Connect Redirect Callout properly uses FwpsRedirectHandleCreate0." + }, + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + }, + "region" : { + "startLine" : 27, + "startColumn" : 46, + "endColumn" : 52 + } + } + } ], + "partialFingerprints" : { + "primaryLocationLineHash" : "eb0e8f748a3f7699:1", + "primaryLocationStartColumnFingerprint" : "41" + } + } ], + "columnKind" : "utf16CodeUnits", + "properties" : { + "semmle.formatSpecifier" : "sarifv2.1.0" + } + } ] + } \ No newline at end of file diff --git a/src/drivers/kmdf/queries/wfp/ConnectRedirectHandleCreation/ConnectRedirectMultipleCallsHandleCreation.ql b/src/drivers/kmdf/queries/wfp/ConnectRedirectHandleCreation/ConnectRedirectMultipleCallsHandleCreation.ql new file mode 100644 index 00000000..e12e1fb7 --- /dev/null +++ b/src/drivers/kmdf/queries/wfp/ConnectRedirectHandleCreation/ConnectRedirectMultipleCallsHandleCreation.ql @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/** + * @name Connect Redirect Callouts + * @description A callout should call FwpsRedirectHandleCreate0 only once and cache the handle and use it during classify. + * @platform Desktop + * @feature.area Multiple + * @repro.text The following function does not call FwpsRedirectHandleCreate0 or calls it multiple times and does not cache the handle. + * @kind problem + * @id cpp/windows/wdk/kmdf/wfp/connect-reirect-handle-creation + * @problem.severity warning + * @precision low + * @tags correctness + * @query-version v1 + */ + +import cpp +import drivers.libraries.wfp + +class RedirectCalloutFunction extends Function { + WfpConnectRedirect scr; + RedirectCalloutFunction() { this.getADeclarationEntry() = scr.getDeclarationEntry() } +} + +// Returns TRUE if the callout function is marked as a connect redirect callout and +// calls FwpsRedirectHandleCreate0 multiple times in the function. + +from RedirectCalloutFunction waf +where + exists(RedirectHandleCreateFunctionCall c | c.getNumberOfCalls() > 1) +select waf, +"A connect redirect callout " + waf.getName() + "calls FwpsRedirectHandleCreate0 multiple times this is a contract violation." \ No newline at end of file diff --git a/src/drivers/kmdf/queries/wfp/ConnectRedirectHandleCreation/driver_snippet.c b/src/drivers/kmdf/queries/wfp/ConnectRedirectHandleCreation/driver_snippet.c new file mode 100644 index 00000000..98c1f897 --- /dev/null +++ b/src/drivers/kmdf/queries/wfp/ConnectRedirectHandleCreation/driver_snippet.c @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +// +// driver_snippet.c +// + +#define SET_DISPATCH 1 + +// Template. Not called in this test. +void top_level_call() {} + +_Wfp_connect_redirect_classify_ +VOID ConnectRedirectClassify() { // Passes + HANDLE handle = FwpsRedirectHandleCreate0(); +} + +_Wfp_connect_redirect_classify_ +VOID ImproperConnectRedirectClassify() { // Fails + + HANDLE handle = FwpsRedirectHandleCreate0( + providerGuid, + flags, + redirectHandle); + + handle = FwpsRedirectHandleCreate0( + providerGuid, + flags, + redirectHandle); +} \ No newline at end of file diff --git a/src/drivers/kmdf/queries/wfp/ConnectRedirectPendClassify/ConnectRedirectPendClassify.ql b/src/drivers/kmdf/queries/wfp/ConnectRedirectPendClassify/ConnectRedirectPendClassify.ql new file mode 100644 index 00000000..a3e6cfd7 --- /dev/null +++ b/src/drivers/kmdf/queries/wfp/ConnectRedirectPendClassify/ConnectRedirectPendClassify.ql @@ -0,0 +1,66 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/** + * @name Connect Redirect Callout Pend Classify + * @description If a callout pends a classify by calling FwpsPendClassify0, the callout must set action to + * FWP_ACTION_BLOCK and clear the FWPS_RIGHT_ACTION_WRITE right flag. This is to let other + * (lower weight) callouts know that they shouldn’t take any action while the classify is pending + * @platform Desktop + * @feature.area Multiple + * @repro.text The following function does not FWP_ACTION_BLOCK and/or clear the FWPS_RIGHT_ACTION_WRITE flag before calling FwpsPendClassify0 + * @kind problem + * @id cpp/windows/wdk/kmdf/wfp/connect-redirect-pend-classify + * @problem.severity warning + * @precision low + * @tags correctness + * @query-version v1 + */ + +import cpp +import drivers.libraries.wfp + +// CORRECT AND FUNCTIONING + +// MIGHT also be applicable to STREAM double check this + +// Contract +// If a callout pends a classify by calling FwpsPendClassify0, the callout must set action to +// FWP_ACTION_BLOCK and clear the FWPS_RIGHT_ACTION_WRITE right flag. This is to let other +// (lower weight) callouts know that they shouldn’t take any action while the classify is pending + +// Return TRUE if a connect/redirect layer was tagged and FwpsPendClassify is called and +// FWP_ACTION_BLOCK or FWPS_RIGHT_ACTION_WRITE right flag is NOT set. +class RedirectCalloutFunction extends Function { + WfpConnectRedirect scr; + RedirectCalloutFunction() { this.getADeclarationEntry() = scr.getDeclarationEntry()} +} +predicate matchesPendClassifyApi(string input) { + input = any(["FwpsPendClassify", "FwpsPendClassify0"]) + } + + class FwpsPendClassifyApi extends Function { + string name; + FwpsPendClassifyApi() { + matchesPendClassifyApi(this.getName()) and + name = this.getName() + } + } + + class FwpsPendClassifyApiCall extends FunctionCall { + FwpsPendClassifyApiCall(){ this.getTarget() instanceof FwpsPendClassifyApi} + } + + class FwpsPendClassifyCall extends Element { + string name; + FwpsPendClassifyCall() { + name = this.(FwpsPendClassifyApiCall).getTarget().getName() + } + } + +from RedirectCalloutFunction waf +where + exists(FwpsPendClassifyCall pend) and + (not exists(ActionTypeExpr exp | isBlockExpression(exp)) or + not exists(WriteActionFlagSet flag)) +select waf, + "A connect redirect callout " + waf.getName() + "called FwpsPendClassify0 and does not FWP_ACTION BLOCK or FWPS_RIGHT_ACTION_WRITE flag." \ No newline at end of file diff --git a/src/drivers/kmdf/queries/wfp/ConnectRedirectPendClassify/ConnectRedirectPendClassify.qlhelp b/src/drivers/kmdf/queries/wfp/ConnectRedirectPendClassify/ConnectRedirectPendClassify.qlhelp new file mode 100644 index 00000000..799fe5c0 --- /dev/null +++ b/src/drivers/kmdf/queries/wfp/ConnectRedirectPendClassify/ConnectRedirectPendClassify.qlhelp @@ -0,0 +1,23 @@ + + + +

    + If a callout pends a classify by calling FwpsPendClassify0, the callout must set action to FWP_ACTION_BLOCK and clear the FWPS_RIGHT_ACTION_WRITE right flag. This is to let other (lower weight) callouts know that they shouldn’t take any action while the classify is pending +

    +
    + +

    + The following function does not FWP_ACTION_BLOCK and/or clear the FWPS_RIGHT_ACTION_WRITE flag before calling FwpsPendClassify0. +

    +
    + + + + +
  • + + Callout Drivers - Windows Drivers + +
  • +
    +
    \ No newline at end of file diff --git a/src/drivers/kmdf/queries/wfp/ConnectRedirectPendClassify/ConnectRedirectPendClassify.sarif b/src/drivers/kmdf/queries/wfp/ConnectRedirectPendClassify/ConnectRedirectPendClassify.sarif new file mode 100644 index 00000000..b451e401 --- /dev/null +++ b/src/drivers/kmdf/queries/wfp/ConnectRedirectPendClassify/ConnectRedirectPendClassify.sarif @@ -0,0 +1,141 @@ +{ + "$schema" : "https://json.schemastore.org/sarif-2.1.0.json", + "version" : "2.1.0", + "runs" : [ { + "tool" : { + "driver" : { + "name" : "CodeQL", + "organization" : "GitHub", + "semanticVersion" : "2.11.5", + "rules" : [ { + "id" : "cpp/windows/drivers/kmdf/queries/wfp/connect-redirect-pend-classify", + "name" : "cpp/windows/drivers/kmdf/queries/wfp/connect-redirect-pend-classify", + "shortDescription" : { + "text" : "The callout must set action to FWP_ACTION_BLOCK and clear the FWPS_RIGHT_ACTION_WRITE right flag if FwpsPendClassify0 is called." + }, + "fullDescription" : { + "text" : "If a callout pends a classify by calling FwpsPendClassify0, the callout must set action to FWP_ACTION_BLOCK and clear the FWPS_RIGHT_ACTION_WRITE right flag. This is to let other (lower weight) callouts know that they should not take any action while the classify is pending." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "properties" : { + "tags" : [ "correctness" ], + "description" : "The callout must set action to FWP_ACTION_BLOCK and clear the FWPS_RIGHT_ACTION_WRITE right flag if FwpsPendClassify0 is called.", + "feature.area" : "Multiple", + "id" : "cpp/windows/drivers/kmdf/queries/wfp/connect-redirect-pend-classify", + "impact" : "Insecure Coding Practice", + "kind" : "problem", + "name" : "the callout must set action to FWP_ACTION_BLOCK and clear the FWPS_RIGHT_ACTION_WRITE right flag if FwpsPendClassify0 is called.", + "opaqueid" : "CQLD-C2900", + "owner.email:" : "elhouha@microsoft.com", + "platform" : "Desktop", + "precision" : "low", + "problem.severity" : "warning", + "query-version" : "v1", + "repro.text" : "The following function calls FwpsPendClassify and FWP_ACTION_BLOCK or FWPS_RIGHT_ACTION_WRITE right flag is NOT set.", + "scope" : "domainspecific" + } + } ] + }, + "extensions" : [ { + "name" : "microsoft/windows-drivers", + "semanticVersion" : "0.1.0+933e876f096a70922173e4d5ad604d99d4481af4", + "locations" : [ { + "uri" : "file:///C:/codeql-home/Windows-Driver-Developer-Supplemental-Tools/src/", + "description" : { + "text" : "The QL pack root directory." + } + }, { + "uri" : "file:///C:/codeql-home/Windows-Driver-Developer-Supplemental-Tools/src/qlpack.yml", + "description" : { + "text" : "The QL pack definition file." + } + } ] + }, { + "name" : "legacy-upgrades", + "semanticVersion" : "0.0.0", + "locations" : [ { + "uri" : "file:///C:/codeql-home/codeql/legacy-upgrades/", + "description" : { + "text" : "The QL pack root directory." + } + }, { + "uri" : "file:///C:/codeql-home/codeql/legacy-upgrades/qlpack.yml", + "description" : { + "text" : "The QL pack definition file." + } + } ] + } ] + }, + "artifacts" : [ { + "location" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + } + } ], + "results" : [ { + "ruleId" : "cpp/windows/drivers/kmdf/queries/wfp/connect-redirect-pend-classify", + "ruleIndex" : 0, + "rule" : { + "id" : "cpp/windows/drivers/kmdf/queries/wfp/connect-redirect-pend-classify", + "index" : 0 + }, + "message" : { + "text" : "Connect Redirect Callout properly uses FwpsRedirectHandleCreate0." + }, + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + }, + "region" : { + "startLine" : 26, + "startColumn" : 46, + "endColumn" : 52 + } + } + } ], + "partialFingerprints" : { + "primaryLocationLineHash" : "56b74a54af745f59:1", + "primaryLocationStartColumnFingerprint" : "41" + } + }, { + "ruleId" : "cpp/windows/drivers/kmdf/queries/wfp/connect-redirect-pend-classify", + "ruleIndex" : 0, + "rule" : { + "id" : "cpp/windows/drivers/kmdf/queries/wfp/connect-redirect-pend-classify", + "index" : 0 + }, + "message" : { + "text" : "The following function calls FwpsPendClassify and FWP_ACTION_BLOCK or FWPS_RIGHT_ACTION_WRITE right flag is NOT set." + }, + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + }, + "region" : { + "startLine" : 27, + "startColumn" : 46, + "endColumn" : 52 + } + } + } ], + "partialFingerprints" : { + "primaryLocationLineHash" : "eb0e8f748a3f7699:1", + "primaryLocationStartColumnFingerprint" : "41" + } + } ], + "columnKind" : "utf16CodeUnits", + "properties" : { + "semmle.formatSpecifier" : "sarifv2.1.0" + } + } ] + } \ No newline at end of file diff --git a/src/drivers/kmdf/queries/wfp/ConnectRedirectPendClassify/driver_snippet.c b/src/drivers/kmdf/queries/wfp/ConnectRedirectPendClassify/driver_snippet.c new file mode 100644 index 00000000..6d1c0e1e --- /dev/null +++ b/src/drivers/kmdf/queries/wfp/ConnectRedirectPendClassify/driver_snippet.c @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +// +// driver_snippet.c +// + +#define SET_DISPATCH 1 + +// Template. Not called in this test. +void top_level_call() {} + +_Wfp_connect_redirect_classify_ +VOID ConnectRedirectClassify() { // Passes + HANDLE handle = FwpsRedirectHandleCreate0(); +} + +_Wfp_connect_redirect_classify_ +VOID ImproperConnectRedirectClassify() { // Fails + + HANDLE handle = FwpsRedirectHandleCreate0( + providerGuid, + flags, + redirectHandle); + + handle = FwpsRedirectHandleCreate0( + providerGuid, + flags, + redirectHandle); +} \ No newline at end of file diff --git a/src/drivers/kmdf/queries/wfp/FlowLayerCalloutReturnsBlock/FlowLayerCalloutReturnsBlock.ql b/src/drivers/kmdf/queries/wfp/FlowLayerCalloutReturnsBlock/FlowLayerCalloutReturnsBlock.ql new file mode 100644 index 00000000..4d55408c --- /dev/null +++ b/src/drivers/kmdf/queries/wfp/FlowLayerCalloutReturnsBlock/FlowLayerCalloutReturnsBlock.ql @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/** + * @name Flow Layer Callouts + * @description Checks that a Flow Layer Callout does not return FWP_ACTION_BLOCK + * @platform Desktop + * @feature.area Multiple + * @repro.text The following function sets FWP_ACTION_BLOCK on a callout registered to ALE_FLOW_ESTABLISHED_LAYERS + * @kind problem + * @id cpp/windows/wdk/kmdf/wfp/flow-layer-returns-block + * @problem.severity warning + * @precision low + * @tags correctness + * @query-version v1 + */ + +import cpp +import drivers.libraries.wfp + +class FlowInspectionClassifyFunction extends Function { + WfpFlowInspection scr; + + FlowInspectionClassifyFunction() { this.getADeclarationEntry() = scr.getDeclarationEntry() } + } + +// Contract +// If a callout is added to the ALE_FLOW_ESTABLISHED it CANNOT return +// FWP_ACTION_BLOCK, unless an error which is a security risk occurs. + +// Returns True if a Flow established callout is tagged and +// the actionType value is set to FWP_ACTION_BLOCK + +from FlowInspectionClassifyFunction waf, ActionTypeExpr blk +where + isBlockExpression(blk) +select waf, + "Flow Established Callout Classify Function: " + waf.getName() + + " sets an FWPS_ACTION_TYPE to FWP_ACTION_BLOCK. This is a contract violation. " + blk.getLocation().getFile() + ". Line: " + + blk.getLocation().getStartLine() diff --git a/src/drivers/kmdf/queries/wfp/FlowLayerCalloutReturnsBlock/FlowLayerCalloutReturnsBlock.qlhelp b/src/drivers/kmdf/queries/wfp/FlowLayerCalloutReturnsBlock/FlowLayerCalloutReturnsBlock.qlhelp new file mode 100644 index 00000000..4e0ce949 --- /dev/null +++ b/src/drivers/kmdf/queries/wfp/FlowLayerCalloutReturnsBlock/FlowLayerCalloutReturnsBlock.qlhelp @@ -0,0 +1,22 @@ + + + +

    + Checks that a Flow Layer Callout does not return FWP_ACTION_BLOCK. + + +

    + The following function does not FWP_ACTION_BLOCK and/or clear the FWPS_RIGHT_ACTION_WRITE flag before calling FwpsPendClassify0. +

    + + + + + +
  • + + Callout Drivers - Windows Drivers + +
  • +
    +
    \ No newline at end of file diff --git a/src/drivers/kmdf/queries/wfp/FlowLayerCalloutReturnsBlock/FlowLayerCalloutReturnsBlock.sarif b/src/drivers/kmdf/queries/wfp/FlowLayerCalloutReturnsBlock/FlowLayerCalloutReturnsBlock.sarif new file mode 100644 index 00000000..a03846a6 --- /dev/null +++ b/src/drivers/kmdf/queries/wfp/FlowLayerCalloutReturnsBlock/FlowLayerCalloutReturnsBlock.sarif @@ -0,0 +1,141 @@ +{ + "$schema" : "https://json.schemastore.org/sarif-2.1.0.json", + "version" : "2.1.0", + "runs" : [ { + "tool" : { + "driver" : { + "name" : "CodeQL", + "organization" : "GitHub", + "semanticVersion" : "2.11.5", + "rules" : [ { + "id" : "cpp/windows/drivers/kmdf/queries/wfp/flow-layer-callout-returns-block", + "name" : "cpp/windows/drivers/kmdf/queries/wfp/flow-layer-callout-returns-block", + "shortDescription" : { + "text" : "Checks that a Flow Layer Callout does not return FWP_ACTION_BLOCK." + }, + "fullDescription" : { + "text" : "Checks that a Flow Layer Callout does not return FWP_ACTION_BLOCK" + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "properties" : { + "tags" : [ "correctness" ], + "description" : "Checks that a Flow Layer Callout does not return FWP_ACTION_BLOCK.", + "feature.area" : "Multiple", + "id" : "cpp/windows/drivers/kmdf/queries/wfp/flow-layer-callout-returns-block", + "impact" : "Insecure Coding Practice", + "kind" : "problem", + "name" : "Checks that a Flow Layer Callout does not return FWP_ACTION_BLOCK.", + "opaqueid" : "CQLD-C2900", + "owner.email:" : "elhouha@microsoft.com", + "platform" : "Desktop", + "precision" : "low", + "problem.severity" : "warning", + "query-version" : "v1", + "repro.text" : "The following function sets FWP_ACTION_BLOCK on a callout registered to ALE_FLOW_ESTABLISHED_LAYERS.", + "scope" : "domainspecific" + } + } ] + }, + "extensions" : [ { + "name" : "microsoft/windows-drivers", + "semanticVersion" : "0.1.0+933e876f096a70922173e4d5ad604d99d4481af4", + "locations" : [ { + "uri" : "file:///C:/codeql-home/Windows-Driver-Developer-Supplemental-Tools/src/", + "description" : { + "text" : "The QL pack root directory." + } + }, { + "uri" : "file:///C:/codeql-home/Windows-Driver-Developer-Supplemental-Tools/src/qlpack.yml", + "description" : { + "text" : "The QL pack definition file." + } + } ] + }, { + "name" : "legacy-upgrades", + "semanticVersion" : "0.0.0", + "locations" : [ { + "uri" : "file:///C:/codeql-home/codeql/legacy-upgrades/", + "description" : { + "text" : "The QL pack root directory." + } + }, { + "uri" : "file:///C:/codeql-home/codeql/legacy-upgrades/qlpack.yml", + "description" : { + "text" : "The QL pack definition file." + } + } ] + } ] + }, + "artifacts" : [ { + "location" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + } + } ], + "results" : [ { + "ruleId" : "cpp/windows/drivers/kmdf/queries/wfp/flow-layer-callout-returns-block", + "ruleIndex" : 0, + "rule" : { + "id" : "cpp/windows/drivers/kmdf/queries/wfp/flow-layer-callout-returns-block", + "index" : 0 + }, + "message" : { + "text" : "Connect Redirect Callout properly uses FwpsRedirectHandleCreate0." + }, + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + }, + "region" : { + "startLine" : 26, + "startColumn" : 46, + "endColumn" : 52 + } + } + } ], + "partialFingerprints" : { + "primaryLocationLineHash" : "56b74a54af745f59:1", + "primaryLocationStartColumnFingerprint" : "41" + } + }, { + "ruleId" : "cpp/windows/drivers/kmdf/queries/wfp/flow-layer-callout-returns-block", + "ruleIndex" : 0, + "rule" : { + "id" : "cpp/windows/drivers/kmdf/queries/wfp/flow-layer-callout-returns-block", + "index" : 0 + }, + "message" : { + "text" : "The following function calls FwpsPendClassify and FWP_ACTION_BLOCK or FWPS_RIGHT_ACTION_WRITE right flag is NOT set." + }, + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + }, + "region" : { + "startLine" : 27, + "startColumn" : 46, + "endColumn" : 52 + } + } + } ], + "partialFingerprints" : { + "primaryLocationLineHash" : "eb0e8f748a3f7699:1", + "primaryLocationStartColumnFingerprint" : "41" + } + } ], + "columnKind" : "utf16CodeUnits", + "properties" : { + "semmle.formatSpecifier" : "sarifv2.1.0" + } + } ] + } \ No newline at end of file diff --git a/src/drivers/kmdf/queries/wfp/FlowLayerCalloutReturnsBlock/driver_snippet.c b/src/drivers/kmdf/queries/wfp/FlowLayerCalloutReturnsBlock/driver_snippet.c new file mode 100644 index 00000000..0bf1ecf3 --- /dev/null +++ b/src/drivers/kmdf/queries/wfp/FlowLayerCalloutReturnsBlock/driver_snippet.c @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +// +// driver_snippet.c +// + +#define SET_DISPATCH 1 + +// Template. Not called in this test. +void top_level_call() {} + +_Wfp_flow_inspection_classify_ +VOID FlowInspectionClassify() { // Passes + return FWP_ACTION_CONTINUE; +} + +_Wfp_flow_inspection_classify_ +VOID ImproperFlowInspectionClassify() { // Fails + + return FWP_ACTION_BLOCK; +} \ No newline at end of file diff --git a/src/drivers/kmdf/queries/wfp/InlineConnectRedirect/InlineConnectRedirectCalloutShouldNotSetReauthorize.ql b/src/drivers/kmdf/queries/wfp/InlineConnectRedirect/InlineConnectRedirectCalloutShouldNotSetReauthorize.ql new file mode 100644 index 00000000..7c313a3b --- /dev/null +++ b/src/drivers/kmdf/queries/wfp/InlineConnectRedirect/InlineConnectRedirectCalloutShouldNotSetReauthorize.ql @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/** + * @name Connect Redirect Inline Callout cannot set reauth + * @description Checks that an Inline Connect Redirect Callout does not ask for reauthorization + * @platform Desktop + * @feature.area Multiple + * @repro.text The following function asks for reauthorization and is an inline callout this is a contract violation + * @kind problem + * @id cpp/windows/wdk/kmdf/wfp/inline-connect-redirect + * @problem.severity warning + * @precision medium + * @tags correctness + * @query-version v1 + */ + + import cpp + import drivers.libraries.wfp + +// Contract +// Inline callout shouldn’t ask for re-authorization i.e., +// shouldn’t set FWPS_CLASSIFY_FLAG_REAUTHORIZE_IF_MODIFIED_BY_OTHERS + +// Returns TRUE if the callout function is a inline callout in +// the connect redirect layers and sets the +// FWPS_CLASSIFY_FLAG_REAUTHORIZE_IF_MODIFIED_BY_OTHERS flag + +class WfpConnectRedirectInline extends WfpAnnotation { + WfpConnectRedirectInline() { + this.getMacro().(WfpMacro).getName() = ["_Wfp_connect_redirect_inline_classify_"] + } +} + +class ConnectRedirectClassifyFunction extends Function { + WfpConnectRedirectInline src; + ConnectRedirectClassifyFunction() { this.getADeclarationEntry() = src.getDeclarationEntry()} +} + +class ClassifyReauthorizeFlag extends AssignExpr { + ClassifyReauthorizeFlag(){ + this.getLValue().getType().getName().matches(["UINT32"]) and + this.getRValue().getFullyConverted().getType().getName().matches(["FWPS_CLASSIFY_FLAG_REAUTHORIZE_IF_MODIFIED_BY_OTHERS"]) + } +} + +from ConnectRedirectClassifyFunction waf +where + exists(ClassifyReauthorizeFlag flag) +select waf, +"A connect redirect callout " + waf.getName() + " is marked inline and sets FWPS_CLASSIFY_FLAG_REAUTHORIZE_IF_MODIFIED_BY_OTHERS. This is a contract violation." \ No newline at end of file diff --git a/src/drivers/kmdf/queries/wfp/InlineConnectRedirect/InlineConnectRedirectCalloutShouldNotSetReauthorize.qlhelp b/src/drivers/kmdf/queries/wfp/InlineConnectRedirect/InlineConnectRedirectCalloutShouldNotSetReauthorize.qlhelp new file mode 100644 index 00000000..22aad94b --- /dev/null +++ b/src/drivers/kmdf/queries/wfp/InlineConnectRedirect/InlineConnectRedirectCalloutShouldNotSetReauthorize.qlhelp @@ -0,0 +1,22 @@ + + + +

    + Checks that an Inline Connect Redirect Callout does not ask for reauthorization. + + +

    + The following function asks for reauthorization and is an inline callout this is a contract violation. +

    + + + + + +
  • + + Callout Drivers - Windows Drivers + +
  • +
    +
    \ No newline at end of file diff --git a/src/drivers/kmdf/queries/wfp/InlineConnectRedirect/InlineConnectRedirectCalloutShouldNotSetReauthorize.sarif b/src/drivers/kmdf/queries/wfp/InlineConnectRedirect/InlineConnectRedirectCalloutShouldNotSetReauthorize.sarif new file mode 100644 index 00000000..ff148157 --- /dev/null +++ b/src/drivers/kmdf/queries/wfp/InlineConnectRedirect/InlineConnectRedirectCalloutShouldNotSetReauthorize.sarif @@ -0,0 +1,141 @@ +{ + "$schema" : "https://json.schemastore.org/sarif-2.1.0.json", + "version" : "2.1.0", + "runs" : [ { + "tool" : { + "driver" : { + "name" : "CodeQL", + "organization" : "GitHub", + "semanticVersion" : "2.11.5", + "rules" : [ { + "id" : "cpp/windows/drivers/kmdf/queries/wfp/inline-connect-redirect-callout-should-not-set-reauthorize", + "name" : "cpp/windows/drivers/kmdf/queries/wfp/inline-connect-redirect-callout-should-not-set-reauthorize", + "shortDescription" : { + "text" : "Checks that an Inline Connect Redirect Callout does not ask for reauthorization" + }, + "fullDescription" : { + "text" : "Checks that an Inline Connect Redirect Callout does not ask for reauthorization." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "properties" : { + "tags" : [ "correctness" ], + "description" : "Checks that an Inline Connect Redirect Callout does not ask for reauthorization", + "feature.area" : "Multiple", + "id" : "cpp/windows/drivers/kmdf/queries/wfp/inline-connect-redirect-callout-should-not-set-reauthorize", + "impact" : "Insecure Coding Practice", + "kind" : "problem", + "name" : "Checks that an Inline Connect Redirect Callout does not ask for reauthorization", + "opaqueid" : "CQLD-C2900", + "owner.email:" : "elhouha@microsoft.com", + "platform" : "Desktop", + "precision" : "low", + "problem.severity" : "warning", + "query-version" : "v1", + "repro.text" : " The following function asks for reauthorization and is an inline callout this is a contract violation.", + "scope" : "domainspecific" + } + } ] + }, + "extensions" : [ { + "name" : "microsoft/windows-drivers", + "semanticVersion" : "0.1.0+933e876f096a70922173e4d5ad604d99d4481af4", + "locations" : [ { + "uri" : "file:///C:/codeql-home/Windows-Driver-Developer-Supplemental-Tools/src/", + "description" : { + "text" : "The QL pack root directory." + } + }, { + "uri" : "file:///C:/codeql-home/Windows-Driver-Developer-Supplemental-Tools/src/qlpack.yml", + "description" : { + "text" : "The QL pack definition file." + } + } ] + }, { + "name" : "legacy-upgrades", + "semanticVersion" : "0.0.0", + "locations" : [ { + "uri" : "file:///C:/codeql-home/codeql/legacy-upgrades/", + "description" : { + "text" : "The QL pack root directory." + } + }, { + "uri" : "file:///C:/codeql-home/codeql/legacy-upgrades/qlpack.yml", + "description" : { + "text" : "The QL pack definition file." + } + } ] + } ] + }, + "artifacts" : [ { + "location" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + } + } ], + "results" : [ { + "ruleId" : "cpp/windows/drivers/kmdf/queries/wfp/inline-connect-redirect-callout-should-not-set-reauthorize", + "ruleIndex" : 0, + "rule" : { + "id" : "cpp/windows/drivers/kmdf/queries/wfp/inline-connect-redirect-callout-should-not-set-reauthorize", + "index" : 0 + }, + "message" : { + "text" : "The following function asks for reauthorization and is an inline callout this is a contract violation." + }, + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + }, + "region" : { + "startLine" : 26, + "startColumn" : 46, + "endColumn" : 52 + } + } + } ], + "partialFingerprints" : { + "primaryLocationLineHash" : "56b74a54af745f59:1", + "primaryLocationStartColumnFingerprint" : "41" + } + }, { + "ruleId" : "cpp/windows/drivers/kmdf/queries/wfp/inline-connect-redirect-callout-should-not-set-reauthorize", + "ruleIndex" : 0, + "rule" : { + "id" : "cpp/windows/drivers/kmdf/queries/wfp/inline-connect-redirect-callout-should-not-set-reauthorize", + "index" : 0 + }, + "message" : { + "text" : "The following function asks for reauthorization and is an inline callout this is a contract violation." + }, + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + }, + "region" : { + "startLine" : 27, + "startColumn" : 46, + "endColumn" : 52 + } + } + } ], + "partialFingerprints" : { + "primaryLocationLineHash" : "eb0e8f748a3f7699:1", + "primaryLocationStartColumnFingerprint" : "41" + } + } ], + "columnKind" : "utf16CodeUnits", + "properties" : { + "semmle.formatSpecifier" : "sarifv2.1.0" + } + } ] + } \ No newline at end of file diff --git a/src/drivers/kmdf/queries/wfp/InlineConnectRedirect/driver_snippet.c b/src/drivers/kmdf/queries/wfp/InlineConnectRedirect/driver_snippet.c new file mode 100644 index 00000000..98facb90 --- /dev/null +++ b/src/drivers/kmdf/queries/wfp/InlineConnectRedirect/driver_snippet.c @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +// +// driver_snippet.c +// + +#define SET_DISPATCH 1 + +// Template. Not called in this test. +void top_level_call() {} + +_Wfp_connect_redirect_inline_classify_ +VOID ConnectRedirectInlineClassify() { // Passes + UINT32 flags = ALE_TEST_FLAG_REAUTH_ON_MODIFIED_BY_OTHERS; +} + +_Wfp_connect_redirect_inline_classify_ +VOID ImproperConnectRedirectInlinenClassify() { // Fails + + UINT32 flags = ALE_TEST_FLAG_REAUTH_ON_MODIFIED_BY_OTHERS | FWPS_CLASSIFY_FLAG_REAUTHORIZE_IF_MODIFIED_BY_OTHERS; +} \ No newline at end of file diff --git a/src/drivers/kmdf/queries/wfp/OobStreamInjection/OobStreamInjectionReturnsBlock.ql b/src/drivers/kmdf/queries/wfp/OobStreamInjection/OobStreamInjectionReturnsBlock.ql new file mode 100644 index 00000000..aaa646b1 --- /dev/null +++ b/src/drivers/kmdf/queries/wfp/OobStreamInjection/OobStreamInjectionReturnsBlock.ql @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/** + * @name Stream Injection Action Type explicitly set to FWP_ACTION_BLOCK + * @description checks that the action type is explicitly set for stream injection callouts + * @platform Desktop + * @feature.area Multiple + * @repro.text The following function does not correctly set an action type for stream injection OOB + * @kind problem + * @id cpp/windows/wdk/kmdf/wfp/oob-stream-injection + * @problem.severity warning + * @precision medium + * @tags correctness + * @query-version v1 + */ + + // COMPLETED AND FUNCTIONING +import cpp +import drivers.libraries.wfp + +/** A function that is annotated with Wfp stream callout annotation. */ +class StreamCalloutFunction extends Function { + WfpStreamInjection scr; + + StreamCalloutFunction() { this.getADeclarationEntry() = scr.getDeclarationEntry() } + } + +// Contract +// When injecting data out-of-band, the callout must return FWP_ACTION_BLOCK +// on all indicated segments to guarantee integrity of the resulting stream + +// returns TRUE when a stream injection classify call does not explicitly +// set the FWPS_ACTION_TYPE to FWP_ACTION_BLOCK + +//Denote to specify for out of band + +from StreamCalloutFunction waf +where + not exists(ActionTypeExpr exp | isBlockExpression(exp)) +select waf, + "Stream Injection Classify Function: " + waf.getName() + " does not explicity set an FWPS_ACTION_TYPE to FWP_ACTION_BLOCK " + "in the FWPS_CLASSIFY_OUT structure." diff --git a/src/drivers/kmdf/queries/wfp/OobStreamInjection/OobStreamInjectionReturnsBlock.qlhelp b/src/drivers/kmdf/queries/wfp/OobStreamInjection/OobStreamInjectionReturnsBlock.qlhelp new file mode 100644 index 00000000..03ce93f0 --- /dev/null +++ b/src/drivers/kmdf/queries/wfp/OobStreamInjection/OobStreamInjectionReturnsBlock.qlhelp @@ -0,0 +1,22 @@ + + + +

    + Checks that the action type is explicitly set for stream injection callouts. + + +

    + The following function does not correctly set an action type for stream injection OOB. +

    + + + + + +
  • + + Callout Drivers - Windows Drivers + +
  • +
    +
    \ No newline at end of file diff --git a/src/drivers/kmdf/queries/wfp/OobStreamInjection/OobStreamInjectionReturnsBlock.sarif b/src/drivers/kmdf/queries/wfp/OobStreamInjection/OobStreamInjectionReturnsBlock.sarif new file mode 100644 index 00000000..f9ffd63e --- /dev/null +++ b/src/drivers/kmdf/queries/wfp/OobStreamInjection/OobStreamInjectionReturnsBlock.sarif @@ -0,0 +1,141 @@ +{ + "$schema" : "https://json.schemastore.org/sarif-2.1.0.json", + "version" : "2.1.0", + "runs" : [ { + "tool" : { + "driver" : { + "name" : "CodeQL", + "organization" : "GitHub", + "semanticVersion" : "2.11.5", + "rules" : [ { + "id" : "cpp/windows/drivers/kmdf/queries/wfp/oob-stream-injection-returns-block", + "name" : "cpp/windows/drivers/kmdf/queries/wfp/oob-stream-injection-returns-block", + "shortDescription" : { + "text" : "Checks that the action type is explicitly set for stream injection callouts." + }, + "fullDescription" : { + "text" : "Checks that the action type is explicitly set for stream injection callouts." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "properties" : { + "tags" : [ "correctness" ], + "description" : "Checks that the action type is explicitly set for stream injection callouts.", + "feature.area" : "Multiple", + "id" : "cpp/windows/drivers/kmdf/queries/wfp/oob-stream-injection-returns-block", + "impact" : "Insecure Coding Practice", + "kind" : "problem", + "name" : "Checks that the action type is explicitly set for stream injection callouts.", + "opaqueid" : "CQLD-C2900", + "owner.email:" : "elhouha@microsoft.com", + "platform" : "Desktop", + "precision" : "low", + "problem.severity" : "warning", + "query-version" : "v1", + "repro.text" : " The following function does not correctly set an action type for stream injection OOB.", + "scope" : "domainspecific" + } + } ] + }, + "extensions" : [ { + "name" : "microsoft/windows-drivers", + "semanticVersion" : "0.1.0+933e876f096a70922173e4d5ad604d99d4481af4", + "locations" : [ { + "uri" : "file:///C:/codeql-home/Windows-Driver-Developer-Supplemental-Tools/src/", + "description" : { + "text" : "The QL pack root directory." + } + }, { + "uri" : "file:///C:/codeql-home/Windows-Driver-Developer-Supplemental-Tools/src/qlpack.yml", + "description" : { + "text" : "The QL pack definition file." + } + } ] + }, { + "name" : "legacy-upgrades", + "semanticVersion" : "0.0.0", + "locations" : [ { + "uri" : "file:///C:/codeql-home/codeql/legacy-upgrades/", + "description" : { + "text" : "The QL pack root directory." + } + }, { + "uri" : "file:///C:/codeql-home/codeql/legacy-upgrades/qlpack.yml", + "description" : { + "text" : "The QL pack definition file." + } + } ] + } ] + }, + "artifacts" : [ { + "location" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + } + } ], + "results" : [ { + "ruleId" : "cpp/windows/drivers/kmdf/queries/wfp/oob-stream-injection-returns-block", + "ruleIndex" : 0, + "rule" : { + "id" : "cpp/windows/drivers/kmdf/queries/wfp/oob-stream-injection-returns-block", + "index" : 0 + }, + "message" : { + "text" : "The following function does not correctly set an action type for stream injection OOB." + }, + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + }, + "region" : { + "startLine" : 26, + "startColumn" : 46, + "endColumn" : 52 + } + } + } ], + "partialFingerprints" : { + "primaryLocationLineHash" : "56b74a54af745f59:1", + "primaryLocationStartColumnFingerprint" : "41" + } + }, { + "ruleId" : "cpp/windows/drivers/kmdf/queries/wfp/oob-stream-injection-returns-block", + "ruleIndex" : 0, + "rule" : { + "id" : "cpp/windows/drivers/kmdf/queries/wfp/oob-stream-injection-returns-block", + "index" : 0 + }, + "message" : { + "text" : "The following function asks for reauthorization and is an inline callout this is a contract violation." + }, + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + }, + "region" : { + "startLine" : 27, + "startColumn" : 46, + "endColumn" : 52 + } + } + } ], + "partialFingerprints" : { + "primaryLocationLineHash" : "eb0e8f748a3f7699:1", + "primaryLocationStartColumnFingerprint" : "41" + } + } ], + "columnKind" : "utf16CodeUnits", + "properties" : { + "semmle.formatSpecifier" : "sarifv2.1.0" + } + } ] + } \ No newline at end of file diff --git a/src/drivers/kmdf/queries/wfp/OobStreamInjection/driver_snippet.c b/src/drivers/kmdf/queries/wfp/OobStreamInjection/driver_snippet.c new file mode 100644 index 00000000..00b4d68e --- /dev/null +++ b/src/drivers/kmdf/queries/wfp/OobStreamInjection/driver_snippet.c @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +// +// driver_snippet.c +// + +#define SET_DISPATCH 1 + +// Template. Not called in this test. +void top_level_call() {} + +_Wfp_stream_injection_classify_ +VOID StreamInjectionClassify() { // Passes + FWP_ACTION_TYPE action = FWP_ACTION_BLOCK; + return action; +} + +_Wfp_stream_injection_classify_ +VOID ImproperStreamInjectionnClassify() { // Fails + FWP_ACTION_TYPE action; + action = FWP_ACTION_NONE; + return action; +} \ No newline at end of file diff --git a/src/drivers/kmdf/queries/wfp/StreamCalloutsSetActionType/StreamCalloutSetActionType.qlhelp b/src/drivers/kmdf/queries/wfp/StreamCalloutsSetActionType/StreamCalloutSetActionType.qlhelp new file mode 100644 index 00000000..e241e478 --- /dev/null +++ b/src/drivers/kmdf/queries/wfp/StreamCalloutsSetActionType/StreamCalloutSetActionType.qlhelp @@ -0,0 +1,22 @@ + + + +

    + Checks that a stream non-inspection callout sets the action type. + + +

    + The following function does not correctly set an action type for non-inspection Stream callouts. +

    + + + + + +
  • + + Callout Drivers - Windows Drivers + +
  • +
    +
    \ No newline at end of file diff --git a/src/drivers/kmdf/queries/wfp/StreamCalloutsSetActionType/StreamCalloutSetActionType.sarif b/src/drivers/kmdf/queries/wfp/StreamCalloutsSetActionType/StreamCalloutSetActionType.sarif new file mode 100644 index 00000000..aa02b577 --- /dev/null +++ b/src/drivers/kmdf/queries/wfp/StreamCalloutsSetActionType/StreamCalloutSetActionType.sarif @@ -0,0 +1,141 @@ +{ + "$schema" : "https://json.schemastore.org/sarif-2.1.0.json", + "version" : "2.1.0", + "runs" : [ { + "tool" : { + "driver" : { + "name" : "CodeQL", + "organization" : "GitHub", + "semanticVersion" : "2.11.5", + "rules" : [ { + "id" : "cpp/windows/drivers/kmdf/queries/wfp/stream-callout-set-action-type", + "name" : "cpp/windows/drivers/kmdf/queries/wfp/stream-callout-set-action-type", + "shortDescription" : { + "text" : "checks that a stream callout sets the action type." + }, + "fullDescription" : { + "text" : "checks that a stream callout sets the action type." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "properties" : { + "tags" : [ "correctness" ], + "description" : "checks that a stream callout sets the action type.", + "feature.area" : "Multiple", + "id" : "cpp/windows/drivers/kmdf/queries/wfp/stream-callout-set-action-type", + "impact" : "Insecure Coding Practice", + "kind" : "problem", + "name" : "checks that a stream callout sets the action type.", + "opaqueid" : "CQLD-C2900", + "owner.email:" : "elhouha@microsoft.com", + "platform" : "Desktop", + "precision" : "low", + "problem.severity" : "warning", + "query-version" : "v1", + "repro.text" : "The following function does not correctly set an action type for non-inspection Stream callouts.", + "scope" : "domainspecific" + } + } ] + }, + "extensions" : [ { + "name" : "microsoft/windows-drivers", + "semanticVersion" : "0.1.0+933e876f096a70922173e4d5ad604d99d4481af4", + "locations" : [ { + "uri" : "file:///C:/codeql-home/Windows-Driver-Developer-Supplemental-Tools/src/", + "description" : { + "text" : "The QL pack root directory." + } + }, { + "uri" : "file:///C:/codeql-home/Windows-Driver-Developer-Supplemental-Tools/src/qlpack.yml", + "description" : { + "text" : "The QL pack definition file." + } + } ] + }, { + "name" : "legacy-upgrades", + "semanticVersion" : "0.0.0", + "locations" : [ { + "uri" : "file:///C:/codeql-home/codeql/legacy-upgrades/", + "description" : { + "text" : "The QL pack root directory." + } + }, { + "uri" : "file:///C:/codeql-home/codeql/legacy-upgrades/qlpack.yml", + "description" : { + "text" : "The QL pack definition file." + } + } ] + } ] + }, + "artifacts" : [ { + "location" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + } + } ], + "results" : [ { + "ruleId" : "cpp/windows/drivers/kmdf/queries/wfp/stream-callout-set-action-type", + "ruleIndex" : 0, + "rule" : { + "id" : "cpp/windows/drivers/kmdf/queries/wfp/stream-callout-set-action-type", + "index" : 0 + }, + "message" : { + "text" : "The following function does not correctly set an action type for stream injection OOB." + }, + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + }, + "region" : { + "startLine" : 26, + "startColumn" : 46, + "endColumn" : 52 + } + } + } ], + "partialFingerprints" : { + "primaryLocationLineHash" : "56b74a54af745f59:1", + "primaryLocationStartColumnFingerprint" : "41" + } + }, { + "ruleId" : "cpp/windows/drivers/kmdf/queries/wfp/stream-callout-set-action-type", + "ruleIndex" : 0, + "rule" : { + "id" : "cpp/windows/drivers/kmdf/queries/wfp/stream-callout-set-action-type", + "index" : 0 + }, + "message" : { + "text" : "The following function does not correctly set an action type for stream injection OOB." + }, + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + }, + "region" : { + "startLine" : 27, + "startColumn" : 46, + "endColumn" : 52 + } + } + } ], + "partialFingerprints" : { + "primaryLocationLineHash" : "eb0e8f748a3f7699:1", + "primaryLocationStartColumnFingerprint" : "41" + } + } ], + "columnKind" : "utf16CodeUnits", + "properties" : { + "semmle.formatSpecifier" : "sarifv2.1.0" + } + } ] + } \ No newline at end of file diff --git a/src/drivers/kmdf/queries/wfp/StreamCalloutsSetActionType/StreamCalloutsSetActionType.ql b/src/drivers/kmdf/queries/wfp/StreamCalloutsSetActionType/StreamCalloutsSetActionType.ql new file mode 100644 index 00000000..708a7eb6 --- /dev/null +++ b/src/drivers/kmdf/queries/wfp/StreamCalloutsSetActionType/StreamCalloutsSetActionType.ql @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/** + * @name For non-inspection (injection) Stream callouts must set the actionType regardless + * @description checks that a stream callout sets the action type + * @platform Desktop + * @feature.area Multiple + * @repro.text The following function does not correctly set an action type for non-inspection Stream callouts + * @kind problem + * @id cpp/windows/wdk/kmdf/wfp/stream-callout-set-action-type + * @problem.severity warning + * @precision medium + * @tags correctness + * @query-version v1 + */ + +import cpp +import drivers.libraries.wfp + +/** A function that is annotated with Wfp stream callout annotation. */ +class StreamCalloutFunction extends Function { + WfpStreamInjection scr; + + StreamCalloutFunction() { this.getADeclarationEntry() = scr.getDeclarationEntry() } + } + +// Contract +// Every non-inspect callout at the stream layer must explicitly assign a value to the +// actionType member of the classifyOut parameter regardless of what value may have been previously set in that parameter + +// Returns TRUE when a non-inspection stream callout is tagged and the function does not +// set the actionType member of the classifyOut parameter + +from StreamCalloutFunction w + where not exists(ActionTypeExpr exp) +select w, + "Found a Stream Injection Classify function that does not properly set an FWP_ACTION_TYPE: " + w.getName() diff --git a/src/drivers/kmdf/queries/wfp/StreamCalloutsSetActionType/driver_snippet.c b/src/drivers/kmdf/queries/wfp/StreamCalloutsSetActionType/driver_snippet.c new file mode 100644 index 00000000..00b4d68e --- /dev/null +++ b/src/drivers/kmdf/queries/wfp/StreamCalloutsSetActionType/driver_snippet.c @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +// +// driver_snippet.c +// + +#define SET_DISPATCH 1 + +// Template. Not called in this test. +void top_level_call() {} + +_Wfp_stream_injection_classify_ +VOID StreamInjectionClassify() { // Passes + FWP_ACTION_TYPE action = FWP_ACTION_BLOCK; + return action; +} + +_Wfp_stream_injection_classify_ +VOID ImproperStreamInjectionnClassify() { // Fails + FWP_ACTION_TYPE action; + action = FWP_ACTION_NONE; + return action; +} \ No newline at end of file diff --git a/src/drivers/kmdf/queries/wfp/StreamInspectionCallViolation/StreamInspectionFunctionCallViolation.ql b/src/drivers/kmdf/queries/wfp/StreamInspectionCallViolation/StreamInspectionFunctionCallViolation.ql new file mode 100644 index 00000000..f6f07aca --- /dev/null +++ b/src/drivers/kmdf/queries/wfp/StreamInspectionCallViolation/StreamInspectionFunctionCallViolation.ql @@ -0,0 +1,93 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/** + * @name Stream Inspection OOB (Out of Band) functional contract + * @description checks that FwpsStreamContinue0 and FwpsStreamInjectAsync0 are not called in the same function + * @platform Desktop + * @feature.area Multiple + * @repro.text The following function calls both FwpsStreamContinue and FwpsStreamInjectAsync + * @kind problem + * @id cpp/windows/wdk/kmdf/wfp/stream-inspection-call-violation + * @problem.severity warning + * @precision medium + * @tags correctness + * @query-version v1 + */ + + + // COMPLETED AND FUNCTIONING +import cpp +import drivers.libraries.wfp + +predicate matchesStreamInjectApi(string input) { + input = any(["FwpsStreamInjectAsync", "FwpsStreamInjectAsync0"]) + } + + class FwpsInjectStreamApi extends Function { + string name; + FwpsInjectStreamApi() { + matchesStreamInjectApi(this.getName()) and + name = this.getName() + } + } + + class FwpsInjectStreamApiCall extends FunctionCall { + FwpsInjectStreamApiCall(){ this.getTarget() instanceof FwpsInjectStreamApi} + } + + class StreamInjectCall extends Element { + string name; + StreamInjectCall() { + name = this.(FwpsInjectStreamApiCall).getTarget().getName() + } + } + + + predicate matchesStreamContinueApi(string input) { + input = any(["FwpsStreamContinue0", "FwpsStreamContinue"]) + } + + class FwpsContinueStreamApi extends Function { + string name; + FwpsContinueStreamApi() { + matchesStreamContinueApi(this.getName()) and + name = this.getName() + } + } + + class FwpsContinueStreamApiCall extends FunctionCall { + FwpsContinueStreamApiCall(){ this.getTarget() instanceof FwpsContinueStreamApi} + } + + class StreamContinueCall extends Element { + string name; + StreamContinueCall() { + name = this.(FwpsContinueStreamApiCall).getTarget().getName() + } + } + + /** A function that is annotated with Wfp stream callout annotation. */ +class StreamCalloutFunction extends Function { + WfpStreamInspection scr; + + StreamCalloutFunction() { this.getADeclarationEntry() = scr.getDeclarationEntry() } + } + +// Contract +// For out-of-band stream inspection callouts, FwpsStreamContinue0 +// must not be called while the FwpsStreamInjectAsync0 function is called. +// ^^ reevaluate on MSDN - to consider + +// This query will return TRUE if +// A stream inspection callout is tagged, FwpsStreamContinue0 is called if +// FwpsStreamInjectAsync0 is called in the same function. + +// Denote as out of band if possible +// Stream Inspection +from StreamCalloutFunction waf +where + exists(StreamContinueCall continue, StreamInjectCall inject) +select waf, + "WFP CodeQL found a Stream Inspection Function: " + waf.getName() + + "where FwpsStreamContinue is called while FwpsStreamInjectAsync is called." + + "This is a Stream contract violation." \ No newline at end of file diff --git a/src/drivers/kmdf/queries/wfp/StreamInspectionCallViolation/StreamInspectionFunctionCallViolation.qlhelp b/src/drivers/kmdf/queries/wfp/StreamInspectionCallViolation/StreamInspectionFunctionCallViolation.qlhelp new file mode 100644 index 00000000..e7a001c7 --- /dev/null +++ b/src/drivers/kmdf/queries/wfp/StreamInspectionCallViolation/StreamInspectionFunctionCallViolation.qlhelp @@ -0,0 +1,22 @@ + + + +

    + Checks that FwpsStreamContinue0 and FwpsStreamInjectAsync0 are not called in the same function. + + +

    + The following function calls both FwpsStreamContinue and FwpsStreamInjectAsync. +

    + + + + + +
  • + + Callout Drivers - Windows Drivers + +
  • +
    +
    \ No newline at end of file diff --git a/src/drivers/kmdf/queries/wfp/StreamInspectionCallViolation/StreamInspectionFunctionCallViolation.sarif b/src/drivers/kmdf/queries/wfp/StreamInspectionCallViolation/StreamInspectionFunctionCallViolation.sarif new file mode 100644 index 00000000..b4148ee1 --- /dev/null +++ b/src/drivers/kmdf/queries/wfp/StreamInspectionCallViolation/StreamInspectionFunctionCallViolation.sarif @@ -0,0 +1,141 @@ +{ + "$schema" : "https://json.schemastore.org/sarif-2.1.0.json", + "version" : "2.1.0", + "runs" : [ { + "tool" : { + "driver" : { + "name" : "CodeQL", + "organization" : "GitHub", + "semanticVersion" : "2.11.5", + "rules" : [ { + "id" : "cpp/windows/drivers/kmdf/queries/wfp/stream-inspection-function-call-violation", + "name" : "cpp/windows/drivers/kmdf/queries/wfp/stream-inspection-function-call-violation", + "shortDescription" : { + "text" : "checks that FwpsStreamContinue0 and FwpsStreamInjectAsync0 are not called in the same function." + }, + "fullDescription" : { + "text" : "checks that FwpsStreamContinue0 and FwpsStreamInjectAsync0 are not called in the same function." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "properties" : { + "tags" : [ "correctness" ], + "description" : "checks that FwpsStreamContinue0 and FwpsStreamInjectAsync0 are not called in the same function.", + "feature.area" : "Multiple", + "id" : "cpp/windows/drivers/kmdf/queries/wfp/stream-inspection-function-call-violation", + "impact" : "Insecure Coding Practice", + "kind" : "problem", + "name" : "checks that FwpsStreamContinue0 and FwpsStreamInjectAsync0 are not called in the same function.", + "opaqueid" : "CQLD-C2900", + "owner.email:" : "elhouha@microsoft.com", + "platform" : "Desktop", + "precision" : "low", + "problem.severity" : "warning", + "query-version" : "v1", + "repro.text" : "The following function calls both FwpsStreamContinue and FwpsStreamInjectAsync.", + "scope" : "domainspecific" + } + } ] + }, + "extensions" : [ { + "name" : "microsoft/windows-drivers", + "semanticVersion" : "0.1.0+933e876f096a70922173e4d5ad604d99d4481af4", + "locations" : [ { + "uri" : "file:///C:/codeql-home/Windows-Driver-Developer-Supplemental-Tools/src/", + "description" : { + "text" : "The QL pack root directory." + } + }, { + "uri" : "file:///C:/codeql-home/Windows-Driver-Developer-Supplemental-Tools/src/qlpack.yml", + "description" : { + "text" : "The QL pack definition file." + } + } ] + }, { + "name" : "legacy-upgrades", + "semanticVersion" : "0.0.0", + "locations" : [ { + "uri" : "file:///C:/codeql-home/codeql/legacy-upgrades/", + "description" : { + "text" : "The QL pack root directory." + } + }, { + "uri" : "file:///C:/codeql-home/codeql/legacy-upgrades/qlpack.yml", + "description" : { + "text" : "The QL pack definition file." + } + } ] + } ] + }, + "artifacts" : [ { + "location" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + } + } ], + "results" : [ { + "ruleId" : "cpp/windows/drivers/kmdf/queries/wfp/stream-inspection-function-call-violation", + "ruleIndex" : 0, + "rule" : { + "id" : "cpp/windows/drivers/kmdf/queries/wfp/stream-inspection-function-call-violation", + "index" : 0 + }, + "message" : { + "text" : "The following function calls both FwpsStreamContinue and FwpsStreamInjectAsync." + }, + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + }, + "region" : { + "startLine" : 26, + "startColumn" : 46, + "endColumn" : 52 + } + } + } ], + "partialFingerprints" : { + "primaryLocationLineHash" : "56b74a54af745f59:1", + "primaryLocationStartColumnFingerprint" : "41" + } + }, { + "ruleId" : "cpp/windows/drivers/kmdf/queries/wfp/stream-inspection-function-call-violation", + "ruleIndex" : 0, + "rule" : { + "id" : "cpp/windows/drivers/kmdf/queries/wfp/stream-inspection-function-call-violation", + "index" : 0 + }, + "message" : { + "text" : "The following function calls both FwpsStreamContinue and FwpsStreamInjectAsync." + }, + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + }, + "region" : { + "startLine" : 27, + "startColumn" : 46, + "endColumn" : 52 + } + } + } ], + "partialFingerprints" : { + "primaryLocationLineHash" : "eb0e8f748a3f7699:1", + "primaryLocationStartColumnFingerprint" : "41" + } + } ], + "columnKind" : "utf16CodeUnits", + "properties" : { + "semmle.formatSpecifier" : "sarifv2.1.0" + } + } ] + } \ No newline at end of file diff --git a/src/drivers/kmdf/queries/wfp/StreamInspectionCallViolation/driver_snippet.c b/src/drivers/kmdf/queries/wfp/StreamInspectionCallViolation/driver_snippet.c new file mode 100644 index 00000000..2a0c7bec --- /dev/null +++ b/src/drivers/kmdf/queries/wfp/StreamInspectionCallViolation/driver_snippet.c @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +// +// driver_snippet.c +// + +#define SET_DISPATCH 1 + +// Template. Not called in this test. +void top_level_call() {} + +_Wfp_stream_inspection_classify_ +VOID StreamInjectionClassify() { // Passes + UINT64 flowId = 12; + UINT32 calloutId = 101; + FwpsStreamContinue0( + flowId, + calloutId, + FWPS_LAYER_STREAM_V4, + 0); +} + + +_Wfp_stream_inspection_classify_ +VOID StreamInjectionClassify() { // Passes + UINT64 flowId = 12; + UINT32 calloutId = 101; + FwpsStreamInjectAsync0( + injectionHandle, + injectionContext, + flags, + flowId, + calloutId, + FWPS_LAYER_STREAM_V4, + streamFlags, + *netBufferList, + dataLength, + completionFn, + completionContext); + } + +_Wfp_stream_inspection_classify_ +VOID ImproperStreamInjectionnClassify() { // Fails + UINT64 flowId = 12; + UINT32 calloutId = 101; + FwpsStreamContinue0( + flowId, + calloutId, + FWPS_LAYER_STREAM_V4, + 0); + + FwpsStreamInjectAsync0( + injectionHandle, + injectionContext, + flags, + flowId, + calloutId, + FWPS_LAYER_STREAM_V4, + streamFlags, + *netBufferList, + dataLength, + completionFn, + completionContext); +} \ No newline at end of file diff --git a/src/drivers/kmdf/queries/wfp/TransportLayerCannotInjectCloneDuringClassify/TransportLayerCannotInjectCloneDuringClassify.ql b/src/drivers/kmdf/queries/wfp/TransportLayerCannotInjectCloneDuringClassify/TransportLayerCannotInjectCloneDuringClassify.ql new file mode 100644 index 00000000..cb172989 --- /dev/null +++ b/src/drivers/kmdf/queries/wfp/TransportLayerCannotInjectCloneDuringClassify/TransportLayerCannotInjectCloneDuringClassify.ql @@ -0,0 +1,90 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/** + * @name Transport Layer Cannot Inject Clone During Classify + * @description checks that injections of clones at the transport do not happen + * @platform Desktop + * @feature.area Multiple + * @repro.text The following function does inject a clone at the transport layers + * @kind problem + * @id cpp/windows/wdk/kmdf/wfp/transport-layer-cannot-inject-clone-during-classify + * @problem.severity warning + * @precision medium + * @tags correctness + * @query-version v1 + */ + + import cpp + import drivers.libraries.wfp + +// COMPETED AND FUNCTIONING + + predicate matchesInjectApi(string input) { + input = any(["FwpsInjectTransportSendAsync", "FwpsInjectTransportSendAsync0", "FwpsInjectTransportReceiveAsync0", + "FwpsInjectTransportReceiveAsync", "FwpsInjectTransportSendAsync1"]) + } + + class FwpsInjectTransportApi extends Function { + string name; + FwpsInjectTransportApi() { + matchesInjectApi(this.getName()) and + name = this.getName() + } + } + + class FwpsInjectTransportApiCall extends FunctionCall { + FwpsInjectTransportApiCall(){ this.getTarget() instanceof FwpsInjectTransportApi} + } + + class TransportInjectCall extends Element { + string name; + TransportInjectCall() { + name = this.(FwpsInjectTransportApiCall).getTarget().getName() + } + } + + predicate matchesCloneApi(string input) { + input = any(["FwpsAllocateNetBufferAndNetBufferList0", "FwpsAllocateCloneNetBufferList0", "FwpsAllocateDeepCloneNetBufferList0", + "FwpsAllocateNetBufferAndNetBufferList", "FwpsAllocateCloneNetBufferList", "FwpsAllocateDeepCloneNetBufferList"]) + } + + class FwpsCloneApi extends Function { + string name; + FwpsCloneApi() { + matchesCloneApi(this.getName()) and + name = this.getName() + } + } + + class FwpsCloneApiCall extends FunctionCall { + FwpsCloneApiCall(){ this.getTarget() instanceof FwpsCloneApi} + } + + class TransportCloneCall extends Element { + string name; + TransportCloneCall() { + name = this.(FwpsCloneApiCall).getTarget().getName() + } + } + + + class TransportInjectionClassifyFunction extends Function { + WfpTransportInspection scr; + + TransportInjectionClassifyFunction() { this.getADeclarationEntry() = scr.getDeclarationEntry() } + } + + // Contract + // Callouts at the transport layer cannot inject a new or cloned + // TCP packet from the classifyFn callout function + // Source: https://learn.microsoft.com/en-us/windows-hardware/drivers/network/callout-driver-programming-considerations + + // Returns TRUE when a transport layer classify callout function tries to inject + // a cloned TCP Packet + + from TransportInjectionClassifyFunction waf +where + exists( TransportInjectCall injectCall, TransportCloneCall cloneCall | + cloneCall.getLocation().getStartLine() < injectCall.getLocation().getStartLine()) +select waf, + "Transport Injection Classify Function: " + waf.getName() + " tries to inject a cloned TCP packet. This is contract violation." diff --git a/src/drivers/kmdf/queries/wfp/TransportLayerCannotInjectCloneDuringClassify/TransportLayerCannotInjectCloneDuringClassify.qlhelp b/src/drivers/kmdf/queries/wfp/TransportLayerCannotInjectCloneDuringClassify/TransportLayerCannotInjectCloneDuringClassify.qlhelp new file mode 100644 index 00000000..eae46a18 --- /dev/null +++ b/src/drivers/kmdf/queries/wfp/TransportLayerCannotInjectCloneDuringClassify/TransportLayerCannotInjectCloneDuringClassify.qlhelp @@ -0,0 +1,22 @@ + + + +

    + Checks that injections of clones at the transport do not happen. + + +

    + The following function does inject a clone at the transport layers. +

    + + + + + +
  • + + Callout Drivers - Windows Drivers + +
  • +
    +
    \ No newline at end of file diff --git a/src/drivers/kmdf/queries/wfp/TransportLayerCannotInjectCloneDuringClassify/TransportLayerCannotInjectCloneDuringclassify.sarif b/src/drivers/kmdf/queries/wfp/TransportLayerCannotInjectCloneDuringClassify/TransportLayerCannotInjectCloneDuringclassify.sarif new file mode 100644 index 00000000..f751aac6 --- /dev/null +++ b/src/drivers/kmdf/queries/wfp/TransportLayerCannotInjectCloneDuringClassify/TransportLayerCannotInjectCloneDuringclassify.sarif @@ -0,0 +1,141 @@ +{ + "$schema" : "https://json.schemastore.org/sarif-2.1.0.json", + "version" : "2.1.0", + "runs" : [ { + "tool" : { + "driver" : { + "name" : "CodeQL", + "organization" : "GitHub", + "semanticVersion" : "2.11.5", + "rules" : [ { + "id" : "cpp/windows/drivers/kmdf/queries/wfp/transport-layer-cannot-inject-clone-during-classify", + "name" : "cpp/windows/drivers/kmdf/queries/wfp/transport-layer-cannot-inject-clone-during-classify", + "shortDescription" : { + "text" : "Checks that injections of clones at the transport do not happen." + }, + "fullDescription" : { + "text" : "Checks that injections of clones at the transport do not happen." + }, + "defaultConfiguration" : { + "enabled" : true, + "level" : "warning" + }, + "properties" : { + "tags" : [ "correctness" ], + "description" : "Checks that injections of clones at the transport do not happen.", + "feature.area" : "Multiple", + "id" : "cpp/windows/drivers/kmdf/queries/wfp/transport-layer-cannot-inject-clone-during-classify", + "impact" : "Insecure Coding Practice", + "kind" : "problem", + "name" : "Checks that injections of clones at the transport do not happen.", + "opaqueid" : "CQLD-C2900", + "owner.email:" : "elhouha@microsoft.com", + "platform" : "Desktop", + "precision" : "low", + "problem.severity" : "warning", + "query-version" : "v1", + "repro.text" : "The following function does inject a clone at the transport layers.", + "scope" : "domainspecific" + } + } ] + }, + "extensions" : [ { + "name" : "microsoft/windows-drivers", + "semanticVersion" : "0.1.0+933e876f096a70922173e4d5ad604d99d4481af4", + "locations" : [ { + "uri" : "file:///C:/codeql-home/Windows-Driver-Developer-Supplemental-Tools/src/", + "description" : { + "text" : "The QL pack root directory." + } + }, { + "uri" : "file:///C:/codeql-home/Windows-Driver-Developer-Supplemental-Tools/src/qlpack.yml", + "description" : { + "text" : "The QL pack definition file." + } + } ] + }, { + "name" : "legacy-upgrades", + "semanticVersion" : "0.0.0", + "locations" : [ { + "uri" : "file:///C:/codeql-home/codeql/legacy-upgrades/", + "description" : { + "text" : "The QL pack root directory." + } + }, { + "uri" : "file:///C:/codeql-home/codeql/legacy-upgrades/qlpack.yml", + "description" : { + "text" : "The QL pack definition file." + } + } ] + } ] + }, + "artifacts" : [ { + "location" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + } + } ], + "results" : [ { + "ruleId" : "cpp/windows/drivers/kmdf/queries/wfp/transport-layer-cannot-inject-clone-during-classify", + "ruleIndex" : 0, + "rule" : { + "id" : "cpp/windows/drivers/kmdf/queries/wfp/transport-layer-cannot-inject-clone-during-classify", + "index" : 0 + }, + "message" : { + "text" : "The following function does inject a clone at the transport layers." + }, + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + }, + "region" : { + "startLine" : 26, + "startColumn" : 46, + "endColumn" : 52 + } + } + } ], + "partialFingerprints" : { + "primaryLocationLineHash" : "56b74a54af745f59:1", + "primaryLocationStartColumnFingerprint" : "41" + } + }, { + "ruleId" : "cpp/windows/drivers/kmdf/queries/wfp/transport-layer-cannot-inject-clone-during-classify", + "ruleIndex" : 0, + "rule" : { + "id" : "cpp/windows/drivers/kmdf/queries/wfp/transport-layer-cannot-inject-clone-during-classify", + "index" : 0 + }, + "message" : { + "text" : "The following function does inject a clone at the transport layers." + }, + "locations" : [ { + "physicalLocation" : { + "artifactLocation" : { + "uri" : "driver/driver_snippet.c", + "uriBaseId" : "%SRCROOT%", + "index" : 0 + }, + "region" : { + "startLine" : 27, + "startColumn" : 46, + "endColumn" : 52 + } + } + } ], + "partialFingerprints" : { + "primaryLocationLineHash" : "eb0e8f748a3f7699:1", + "primaryLocationStartColumnFingerprint" : "41" + } + } ], + "columnKind" : "utf16CodeUnits", + "properties" : { + "semmle.formatSpecifier" : "sarifv2.1.0" + } + } ] + } \ No newline at end of file diff --git a/src/drivers/kmdf/queries/wfp/TransportLayerCannotInjectCloneDuringClassify/driver_snippet.c b/src/drivers/kmdf/queries/wfp/TransportLayerCannotInjectCloneDuringClassify/driver_snippet.c new file mode 100644 index 00000000..8ac80f37 --- /dev/null +++ b/src/drivers/kmdf/queries/wfp/TransportLayerCannotInjectCloneDuringClassify/driver_snippet.c @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +// +// driver_snippet.c +// + +#define SET_DISPATCH 1 + +// Template. Not called in this test. +void top_level_call() {} + +_Wfp_transport_injection_classify_ +VOID TransportInjectionClassify() { // Passes + FwpsInjectTransportSendAsync(); +} + +_Wfp_transport_injection_classify_ +VOID ImproperTransportInjectionClassify() { // Fails + FwpsAllocateCloneNetBufferList(); + FwpsInjectTransportSendAsync(); +} \ No newline at end of file diff --git a/src/drivers/libraries/DriverIsolation.qll b/src/drivers/libraries/DriverIsolation.qll new file mode 100644 index 00000000..a7e068d0 --- /dev/null +++ b/src/drivers/libraries/DriverIsolation.qll @@ -0,0 +1,309 @@ +import cpp +import semmle.code.cpp.dataflow.new.DataFlow +import semmle.code.cpp.dataflow.new.TaintTracking +import semmle.code.cpp.controlflow.ControlFlowGraph + +class RtlRegistryIsolationFunction extends Function { + RtlRegistryIsolationFunction() { + this.getName().matches("RtlCreateRegistryKey") or + this.getName().matches("RtlCheckRegistryKey") or + this.getName().matches("RtlDeleteRegistryValue") or + this.getName().matches("RtlWriteRegistryValue") or + this.getName().matches("RtlQueryRegistryValues") or + this.getName().matches("RtlxQueryRegistryValues") or + this.getName().matches("RtlQueryRegistryValuesEx") + } +} + +class ZwRegistryIsolationFunction extends Function { + ZwRegistryIsolationFunction() { + this.getName().matches("ZwCreateKey") or + this.getName().matches("ZwCreateKeyTransacted") or + this.getName().matches("ZwDeleteKey") or + this.getName().matches("ZwDeleteValueKey") or + this.getName().matches("ZwOpenKey") or + this.getName().matches("ZwOpenKeyTransacted") or + this.getName().matches("ZwOpenKeyEx") or + this.getName().matches("ZwOpenKeyTransactedEx") or + this.getName().matches("ZwQueryKey") or + this.getName().matches("ZwRenameKey") or + this.getName().matches("ZwSetInformationKey") or + this.getName().matches("ZwSetValueKey") or + this.getName().matches("ZwQueryValueKey") + } +} + +class WdfRegistryIsolationFunction extends Function { + WdfRegistryIsolationFunction() { + this.getName().matches("WdfRegistry%") and + not this.getName().matches("WdfRegistryClose") + } +} + +class RegistryIsolationFunction extends Function { + RegistryIsolationFunction() { + this instanceof RtlRegistryIsolationFunction or + this instanceof ZwRegistryIsolationFunction or + this instanceof WdfRegistryIsolationFunction + } +} + +class RegistryIsolationFunctionCall extends FunctionCall { + RegistryIsolationFunctionCall() { this.getTarget() instanceof RegistryIsolationFunction } +} + +class NullRootDirectoryObjAttr extends VariableAccess { + NullRootDirectoryObjAttr() { + exists(FieldAccess fa, VariableAccess va | + fa.getTarget().getName().matches("RootDirectory") and + va.getType().toString().matches("OBJECT_ATTRIBUTES") and // TODO need wild cards? + va.getParent+() = fa.getParent+() and + exists(Expr assignedValue | + assignedValue = fa.getTarget().getAnAssignedValue() and + assignedValue.getParent+() = va.getParent+() and + assignedValue.getValue().toString().matches("%") // assignedValue only has a value when it's constant + ) and + this = va + ) + } +} + +class NullRootDirectoryNode extends DataFlow::Node { + NullRootDirectoryNode() { + exists(FieldAccess fa, VariableAccess va | + fa.getTarget().getName().matches("RootDirectory") and + va.getType().toString().matches("OBJECT_ATTRIBUTES") and // TODO need wild cards? + va.getParent+() = fa.getParent+() and + exists(Expr assignedValue | + assignedValue = fa.getTarget().getAnAssignedValue() and + assignedValue.getParent+() = va.getParent+() and + assignedValue.getValue().toString().matches("%") // assignedValue only has a value when it's constant + ) and + this.asIndirectExpr() = va + ) + } +} + +class NonNullRootDirectoryNode extends DataFlow::Node { + NonNullRootDirectoryNode() { + exists(FieldAccess fa, VariableAccess va | + fa.getTarget().getName().matches("RootDirectory") and + va.getType().toString().matches("OBJECT_ATTRIBUTES") and // TODO need wild cards? + va.getParent+() = fa.getParent+() and + not exists(Expr assignedValue | + assignedValue = fa.getTarget().getAnAssignedValue() and + assignedValue.getParent+() = va.getParent+() and + assignedValue.getValue().toString().matches("%") // assignedValue only has a value when it's constant + ) and + this.asIndirectExpr() = va + ) + } +} + +module IsolationDataFlowNullRootDirConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + source.asIndirectExpr().getValue().toString().toLowerCase().matches("%") + //source.asIndirectExpr().getType().toString().toLowerCase().matches("%string%") //potential violations + } + + // barrier prevents flow from source to source + predicate isBarrierIn(DataFlow::Node node) { + isSource(node) or node instanceof NonNullRootDirectoryNode + } + + predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) { + // flow between input/output of string functions + exists(Expr va0, Expr va1, FunctionCall fc | + ( + fc.getTarget().getName().matches("RtlInitUnicodeString") or + fc.getTarget().getName().matches("RtlUnicodeStringInit") or + fc.getTarget().getName().matches("RtlInitString") or + fc.getTarget().getName().matches("RtlAnsiStringToUnicodeString") or + fc.getTarget().getName().matches("RtlStringCchPrintf%") + ) and + va0 = fc.getArgument(0) and // output of the above functions is the first argument + va1 = fc.getAnArgument() and + va0 != va1 and + pred.asIndirectExpr() = va1 and + succ.asIndirectExpr() = va0 + ) + or + // flow between parameters of InitializeObjectAttributes macro + exists(FieldAccess fa, VariableAccess va, Expr assignedValue, MacroInvocation m | + fa.getTarget().getName().matches("ObjectName") and + va.getType().toString().matches("%OBJECT_ATTRIBUTES%") and + va.getParent*() = fa.getParent*() and + assignedValue = fa.getTarget().getAnAssignedValue() and + assignedValue.getParent*() = va.getParent*() and + pred.asIndirectExpr() = assignedValue and + pred.asIndirectExpr().isAffectedByMacro() and + succ.asIndirectExpr().isAffectedByMacro() and + m.getAnAffectedElement() = succ.asIndirectExpr() and + m.getAnAffectedElement() = pred.asIndirectExpr() + ) and + succ instanceof NullRootDirectoryNode + } + + /* + * Allows tracking flow from UNICODE_STRING.Buffer to OBJECT_ATTRIBUTES.RootDirectory as in example below + * KeyName.Buffer = SOME_CONSTANT; + * InitializeObjectAttributes(&ObjectAttributes,&KeyName,OBJ_CASE_INSENSITIVE,NULL,NULL); + */ + + predicate allowImplicitRead(DataFlow::Node n, DataFlow::ContentSet cs) { + isAdditionalFlowStep(n, any(DataFlow::Node succ)) and + cs.getAReadContent().(DataFlow::FieldContent).getField().hasName(["Buffer"]) + } + + predicate isSink(DataFlow::Node sink) { + exists(FunctionCall f | + zwCall(f) and + sink.asIndirectExpr() = f.getAnArgument() and + sink.asIndirectExpr() + .(VariableAccess) + .getTarget() + .getType() + .toString() + .matches("%OBJECT_ATTRIBUTES%") + ) + } +} + +module IsolationDataFlowNullRootDir = DataFlow::Global; + +class AllowedHandleRegFuncCall extends RegistryIsolationFunctionCall { + AllowedHandleRegFuncCall() { + exists(DataFlow::Node source, DataFlow::Node sink | + IsolationDataFlowNullRootDir::flow(source, sink) and + ( + allowedPath(source.asIndirectExpr()) and + zwRead(this) + or + // null RootDirectory, valid ObjectName, read is allowed + pathWriteException(source.asIndirectExpr()) and + zwWrite(this) + ) and + sink.asIndirectExpr().getParent*() = this + ) + } +} + +class NotAllowedHandleRegFuncCall extends RegistryIsolationFunctionCall { + NotAllowedHandleRegFuncCall() { + exists(DataFlow::Node source, DataFlow::Node sink | + IsolationDataFlowNullRootDir::flow(source, sink) and + ( + // violation if RootDirectory=NULL and writes, even if ObjectName is valid. (Reads are OK) + allowedPath(source.asIndirectExpr()) and + not pathWriteException(source.asIndirectExpr()) and // this path allowed for now + zwWrite(this) // null RootDirectory, valid ObjectName, write + or + // All other paths are violations for both read and write + not allowedPath(source.asIndirectExpr()) and + zwCall(this) // null RootDirectory, invalid ObjectName + ) and + sink.asIndirectArgument().getParent*() = this + ) + } +} + +class AllowedHandleDDI extends Function { + AllowedHandleDDI() { + this.getName().matches("IoOpenDeviceRegistryKey") or + this.getName().matches("IoOpenDeviceInterfaceRegistryKey") or + this.getName().matches("IoOpenDriverRegistryKey") or + this.getName().matches("WdfDriverOpenParametersRegistryKey") or + this.getName().matches("WdfDriverOpenPersistentStateRegistryKey") or + this.getName().matches("WdfDeviceOpenRegistryKey") or + this.getName().matches("WdfFdoInitOpenRegistryKey") or + this.getName().matches("CM_Open_DevNode_Key") + } +} + +/* + * Call to a Zw* registry function that reads only + */ + +predicate zwRead(RegistryIsolationFunctionCall f) { + ( + f.getTarget().getName().matches("ZwQueryKey") or + f.getTarget().getName().matches("ZwQueryValueKey") + ) + or + f.getTarget().getName().matches("Zw%") and + f.getAnArgument().getType().toString().matches("ACCESS_MASK") and + exists(MacroInvocation m | + f.getAnArgument() = m.getExpr() and + ( + m.getMacroName().matches("KEY_QUERY_VALUE") or + m.getMacroName().matches("KEY_ENUMERATE_SUB_KEYS") or + m.getMacroName().matches("KEY_CREATE_LINK") or + m.getMacroName().matches("KEY_NOTIFY") or + m.getMacroName().matches("KEY_READ") or + m.getMacroName().matches("KEY_EXECUTE") + ) + ) + or + f.getTarget().getName().matches("Zw%") and + exists(Expr e, int n | + e = f.getArgument(n) and + ( + e.getValue() = "131097" or // KEY_READ and KEY_EXECUTE + e.getValue() = "1" or // KEY_QUERY_VALUE + e.getValue() = "8" or // KEY_ENUMERATE_SUB_KEYS + e.getValue() = "32" or // KEY_CREATE_LINK + e.getValue() = "16" // KEY_NOTIFY + ) and + n = 1 + ) +} + +/* + * Call to a Zw* registry function that writes + */ + +predicate zwWrite(RegistryIsolationFunctionCall f) { + ( + f.getTarget().getName().matches("ZwDeleteKey") or + f.getTarget().getName().matches("ZwDeleteValueKey") or + f.getTarget().getName().matches("ZwSetValueKey") + ) + or + f.getTarget().getName().matches("Zw%") and + not zwRead(f) +} + +/* + * Any call to a Zw* registry function that reads only + */ + +predicate zwCall(RegistryIsolationFunctionCall f) { + zwRead(f) + or + zwWrite(f) +} + +// Exceptions to rules +predicate exception2(RegistryIsolationFunctionCall f) { + // Exception: Rtl Writes OK if key is named SERIALCOMM or SERIALCOMM\* and RelativeTo parameter is RTL_REGISTRY_DEVICEMAP + f.getArgument(1).getValue().toString().toLowerCase().matches("serialcomm") or + f.getArgument(1).getValue().toString().toLowerCase().matches("serialcomm\\%") +} + +predicate pathWriteException(Expr n1) { + // Exception: zwWrite OK with this path + n1.getValue() + .toString() + .toLowerCase() + .matches("\\registry\\machine\\hardware\\devicemap\\serialcomm%") +} + +predicate pathException(Expr e) { + e.getValue().toString().toLowerCase().matches("%registry\\machine\\software%") or + e.getValue().toString().toLowerCase().matches("%registry\\machine\\system%") +} + +predicate allowedPath(Expr e) { + e.getValue().toString().toLowerCase().matches("%registry%machine%hardware%") or + pathException(e) +} diff --git a/src/drivers/libraries/Irql.qll b/src/drivers/libraries/Irql.qll index c4b7c295..19907787 100644 --- a/src/drivers/libraries/Irql.qll +++ b/src/drivers/libraries/Irql.qll @@ -12,768 +12,875 @@ * implicit behaviors of the WDM driver model, etc. is not yet supported. */ -import cpp -import drivers.libraries.SAL -import drivers.wdm.libraries.WdmDrivers -import drivers.libraries.IrqlDataFlow -import drivers.libraries.Page - -/** - * A macro in wdm.h that represents an IRQL level, - * such as PASSIVE_LEVEL, DISPATCH_LEVEL, etc. - */ -cached -class IrqlMacro extends Macro { - int irqlLevelAsInt; - - cached - IrqlMacro() { - this.getName().matches("%_LEVEL") and - this.getFile().getBaseName() = "wdm.h" and - this.getBody().toInt() = irqlLevelAsInt and - irqlLevelAsInt >= 0 and - irqlLevelAsInt <= 31 - } - - /** Returns the integer value of this IRQL level. */ - cached - int getIrqlLevel() { result = irqlLevelAsInt } -} - -/** - * Returns the highest IRQL in wdm.h across this database. - * May cause incorrect results if database contains both 32-bit - * and 64-bit builds. - */ -cached -int getGlobalMaxIrqlLevel() { - result = - any(int i | - exists(IrqlMacro im | - i = im.getIrqlLevel() and - not exists(IrqlMacro im2 | im2 != im and im2.getIrqlLevel() > im.getIrqlLevel()) - ) - ) -} - -/** - * Represents a real (not -1) Irql level, between 0 and the max for the architecture this - * database was built to target. - */ -class IrqlValue extends int { - IrqlValue() { this = [0 .. getGlobalMaxIrqlLevel()] } -} - -/** An \_IRQL\_saves\_global\_(parameter, kind) annotation. */ -cached -class IrqlSavesGlobalAnnotation extends SALAnnotation { - MacroInvocation irqlMacroInvocation; - - cached - IrqlSavesGlobalAnnotation() { - // Needs to include other function and parameter annotations too - this.getMacroName() = ["__drv_savesIRQLGlobal", "_IRQL_saves_global_"] and - irqlMacroInvocation.getParentInvocation() = this - } -} - -/** An \_IRQL\_restores\_global\_(parameter, kind) annotation. */ -cached -class IrqlRestoresGlobalAnnotation extends SALAnnotation { - MacroInvocation irqlMacroInvocation; - - cached - IrqlRestoresGlobalAnnotation() { - // Needs to include other function and parameter annotations too - this.getMacroName() = ["__drv_restoresIRQLGlobal", "_IRQL_restores_global_"] and - irqlMacroInvocation.getParentInvocation() = this - } -} - -/** - * Standard IRQL annotations which apply to entire functions and manipulate or constrain the IRQL. - */ -cached -class IrqlFunctionAnnotation extends SALAnnotation { - string irqlLevel; - string irqlAnnotationName; - - cached - IrqlFunctionAnnotation() { - ( - this.getMacroName() = - [ - "__drv_requiresIRQL", "_IRQL_requires_", "_drv_minIRQL", "_IRQL_requires_min_", - "_drv_maxIRQL", "_IRQL_requires_max_", "__drv_raisesIRQL", "_IRQL_raises_", - "__drv_maxFunctionIRQL", "_IRQL_always_function_max_", "__drv_minFunctionIRQL", - "_IRQL_always_function_min_" - ] and - irqlLevel = this.getUnexpandedArgument(0) - or - // Special case: _IRQL_saves_ annotations can apply to a whole function, - // but do not have an associated IRQL value. - this.getMacroName() = ["__drv_savesIRQL", "_IRQL_saves_"] and - irqlLevel = "NA_IRQL_SAVES" - ) and - irqlAnnotationName = this.getMacroName() - } - - /** Returns the raw text of the IRQL value used in this annotation. */ - cached - string getIrqlLevelString() { result = irqlLevel } - - /** Returns the text of this annotation (i.e. \_IRQL\_requires\_, etc.) */ - cached - string getIrqlMacroName() { result = irqlAnnotationName } - - /** - * Evaluate the IRQL specified in this annotation, if possible. - * - * This will return -1 if the IRQL specified is anything other than a standard - * IRQL level (i.e. PASSIVE_LEVEL). This includes statements like "DPC_LEVEL - 1". - */ - cached - int getIrqlLevel() { - // Special case for DPC_LEVEL, which is not defined normally - if this.getIrqlLevelString() = "DPC_LEVEL" - then result = 2 - else - if exists(IrqlMacro im | im.getHead().matches(this.getIrqlLevelString())) - then - result = - any(int i | - exists(IrqlMacro im | - im.getIrqlLevel() = i and - im.getHead().matches(this.getIrqlLevelString()) - ) - ) - else - if exists(int i | i = this.getIrqlLevelString().toInt()) - then result = this.getIrqlLevelString().toInt() - else result = -1 - } -} - -/** Represents an "\_IRQL\_requires\_same\_" annotation. */ -class IrqlSameAnnotation extends SALAnnotation { - string irqlAnnotationName; - - IrqlSameAnnotation() { - this.getMacroName() = ["__drv_sameIRQL", "_IRQL_requires_same_"] and - irqlAnnotationName = this.getMacroName() - } - - string getIrqlMacroName() { result = irqlAnnotationName } -} - -/** An "\_IRQL\_requires\_max\_" annotation. */ -class IrqlMaxAnnotation extends IrqlFunctionAnnotation { - IrqlMaxAnnotation() { this.getMacroName() = ["_drv_maxIRQL", "_IRQL_requires_max_"] } -} - -/** An "\_IRQL\_raises\_" annotation. */ -class IrqlRaisesAnnotation extends IrqlFunctionAnnotation { - IrqlRaisesAnnotation() { this.getMacroName() = ["__drv_raisesIRQL", "_IRQL_raises_"] } -} - -/** An "\_IRQL\_requires\_min\_" annotation. */ -class IrqlMinAnnotation extends IrqlFunctionAnnotation { - IrqlMinAnnotation() { this.getMacroName() = ["_drv_minIRQL", "_IRQL_requires_min_"] } -} - -/** An "\_IRQL\_requires\_" annotation. */ -class IrqlRequiresAnnotation extends IrqlFunctionAnnotation { - IrqlRequiresAnnotation() { this.getMacroName() = ["__drv_requiresIRQL", "_IRQL_requires_"] } -} - -/** An "\_IRQL\_always\_function\_max\_" annotation. */ -class IrqlAlwaysMaxAnnotation extends IrqlFunctionAnnotation { - IrqlAlwaysMaxAnnotation() { - this.getMacroName() = ["__drv_maxFunctionIRQL", "_IRQL_always_function_max_"] - } -} - -/** An "\_IRQL\_always\_function\_min\_" annotation. */ -class IrqlAlwaysMinAnnotation extends IrqlFunctionAnnotation { - IrqlAlwaysMinAnnotation() { - this.getMacroName() = ["__drv_minFunctionIRQL", "_IRQL_always_function_min_"] - } -} - -/** - * A SAL annotation indicating that the parameter in - * question is used to store or restore the IRQL. - */ -class IrqlParameterAnnotation extends SALAnnotation { - string irqlAnnotationName; - - IrqlParameterAnnotation() { - this.getMacroName() = - ["__drv_restoresIRQL", "_IRQL_restores_", "__drv_savesIRQL", "_IRQL_saves_"] and - irqlAnnotationName = this.getMacroName() and - exists(MacroInvocation mi | mi.getParentInvocation() = this) - } - - /** Get the text of the annotation. */ - string getIrqlMacroName() { result = irqlAnnotationName } -} - -/** - * A SAL annotation indicating that the parameter in - * question contains an IRQL value that the system will be set to. - */ -class IrqlRestoreAnnotation extends IrqlParameterAnnotation { - IrqlRestoreAnnotation() { this.getMacroName() = ["__drv_restoresIRQL", "_IRQL_restores_"] } -} - -/** - * A SAL annotation indicating that can be used in two ways: - * - If applied to a function, the function returns the previous IRQL or otherwise saves the IRQL. - * - If applied to a parameter, the function saves the IRQL to the parameter. - */ -class IrqlSaveAnnotation extends IrqlFunctionAnnotation { - IrqlSaveAnnotation() { this.getMacroName() = ["__drv_savesIRQL", "_IRQL_saves_"] } -} - -/** A parameter that is annotated with "\_IRQL\_restores\_". */ -class IrqlRestoreParameter extends Parameter { - IrqlRestoreParameter() { exists(IrqlRestoreAnnotation ira | ira.getDeclaration() = this) } -} - -/** A parameter that is annotated with "\_IRQL\_saves\_". */ -class IrqlSaveParameter extends Parameter { - IrqlSaveParameter() { exists(IrqlSaveAnnotation isa | isa.getDeclaration() = this) } -} - -/** A typedef that has IRQL annotations applied to it. */ -class IrqlAnnotatedTypedef extends TypedefType { - IrqlFunctionAnnotation irqlAnnotation; - - IrqlAnnotatedTypedef() { irqlAnnotation.getTypedefDeclarations() = this } - - IrqlFunctionAnnotation getIrqlAnnotation() { result = irqlAnnotation } -} - -/** - * A function that is annotated in such a way that - * either its entry or exit IRQL is restricted, either by having a min/max value, - * a required value, or by raising the IRQL to a known value. - */ -cached -class IrqlRestrictsFunction extends Function { - IrqlFunctionAnnotation irqlAnnotation; - - cached - IrqlRestrictsFunction() { - exists(FunctionDeclarationEntry fde | - fde = this.getADeclarationEntry() and - irqlAnnotation.getDeclarationEntry() = fde - ) - or - exists(FunctionDeclarationEntry fde | - fde.getFunction() = this and - fde.getTypedefType().(IrqlAnnotatedTypedef).getIrqlAnnotation() = irqlAnnotation - ) - } - - cached - string getFuncIrqlAnnotation() { result = irqlAnnotation.getIrqlMacroName() } -} - -/** A function that changes the IRQL. */ -abstract class IrqlChangesFunction extends Function { } - -/** A function that is explicitly annotated to enter and exit at the same IRQL. */ -class IrqlRequiresSameAnnotatedFunction extends Function { - IrqlSameAnnotation irqlAnnotation; - - IrqlRequiresSameAnnotatedFunction() { - exists(FunctionDeclarationEntry fde | - fde = this.getADeclarationEntry() and - irqlAnnotation.getDeclarationEntry() = fde - ) - } -} - -/** A function that is annotated to run at a specific IRQL. */ -class IrqlRequiresAnnotatedFunction extends IrqlRestrictsFunction { - IrqlRequiresAnnotatedFunction() { irqlAnnotation instanceof IrqlRequiresAnnotation } - - int getIrqlLevel() { result = irqlAnnotation.(IrqlRequiresAnnotation).getIrqlLevel() } -} - -/** A function that is annotated to enter at or below a given IRQL. */ -class IrqlMaxAnnotatedFunction extends IrqlRestrictsFunction { - IrqlMaxAnnotatedFunction() { irqlAnnotation instanceof IrqlMaxAnnotation } - - int getIrqlLevel() { result = irqlAnnotation.(IrqlMaxAnnotation).getIrqlLevel() } -} - -/** A function that is annotated to enter at or above a given IRQL. */ -class IrqlMinAnnotatedFunction extends IrqlRestrictsFunction { - IrqlMinAnnotatedFunction() { irqlAnnotation instanceof IrqlMinAnnotation } - - int getIrqlLevel() { result = irqlAnnotation.(IrqlMinAnnotation).getIrqlLevel() } -} - -/** A function that is annotated to raise the IRQL to a given value. */ -class IrqlRaisesAnnotatedFunction extends IrqlRestrictsFunction, IrqlChangesFunction { - IrqlRaisesAnnotatedFunction() { irqlAnnotation instanceof IrqlRaisesAnnotation } - - int getIrqlLevel() { result = irqlAnnotation.(IrqlRaisesAnnotation).getIrqlLevel() } -} - -/** A function that is never allowed to run with the IRQL above a given value. */ -class IrqlAlwaysMaxFunction extends IrqlRestrictsFunction { - IrqlAlwaysMaxFunction() { irqlAnnotation instanceof IrqlAlwaysMaxAnnotation } - - int getIrqlLevel() { result = irqlAnnotation.(IrqlAlwaysMaxAnnotation).getIrqlLevel() } -} - -/** A function that is never allowed to run with the IRQL above a given value. */ -class IrqlAlwaysMinFunction extends IrqlRestrictsFunction { - IrqlAlwaysMinFunction() { irqlAnnotation instanceof IrqlAlwaysMinAnnotation } - - int getIrqlLevel() { result = irqlAnnotation.(IrqlAlwaysMinAnnotation).getIrqlLevel() } -} - -/** A function annotated to save the IRQL at the specified location upon entry. */ -class IrqlSavesGlobalAnnotatedFunction extends IrqlChangesFunction { - IrqlSavesGlobalAnnotation irqlAnnotation; - string irqlKind; - int irqlParamIndex; - - IrqlSavesGlobalAnnotatedFunction() { - exists(FunctionDeclarationEntry fde | - fde = this.getADeclarationEntry() and - irqlAnnotation.getDeclarationEntry() = fde - ) and - irqlKind = irqlAnnotation.getExpandedArgument(0) and - this.getParameter(irqlParamIndex).getName().matches(irqlAnnotation.getExpandedArgument(1)) - } - - string getIrqlKind() { result = irqlKind } - - int getIrqlParameterSlot() { result = irqlParamIndex } -} - -/** A function annotated to restore the IRQL from the specified location upon exit. */ -class IrqlRestoresGlobalAnnotatedFunction extends IrqlChangesFunction { - IrqlRestoresGlobalAnnotation irqlAnnotation; - string irqlKind; - int irqlParamIndex; - - IrqlRestoresGlobalAnnotatedFunction() { - exists(FunctionDeclarationEntry fde | - fde = this.getADeclarationEntry() and - irqlAnnotation.getDeclarationEntry() = fde - ) and - irqlKind = irqlAnnotation.getExpandedArgument(0) and - this.getParameter(irqlParamIndex).getName().matches(irqlAnnotation.getExpandedArgument(1)) - } - - string getIrqlKind() { result = irqlKind } - - int getIrqlParameterSlot() { result = irqlParamIndex } -} - -/** - * An abstract class for functions that use the \_IRQL\_saves\_ annotation, - * either on the function definition or on a specific parameter. - */ -abstract class IrqlSavesFunction extends Function { } - -/** A function that has a parameter annotated \_IRQL\_saves\_. */ -class IrqlSavesToParameterFunction extends IrqlSavesFunction { - IrqlSaveParameter saveParameter; - int irqlParamIndex; - - IrqlSavesToParameterFunction() { this.getParameter(irqlParamIndex) = saveParameter } - - int getIrqlParameterSlot() { result = irqlParamIndex } -} - -/** A function that saves the IRQL as a return value. */ -class IrqlSavesViaReturnFunction extends IrqlSavesFunction { - IrqlSaveAnnotation irqlAnnotation; - - IrqlSavesViaReturnFunction() { - exists(FunctionDeclarationEntry fde | - fde = this.getADeclarationEntry() and - irqlAnnotation.getDeclarationEntry() = fde - ) - } -} - -/** A function that has a parameter annotated \_IRQL\_restores\_. */ -class IrqlRestoreAnnotatedFunction extends Function { - IrqlRestoreParameter restoreParameter; - int irqlParamIndex; - - IrqlRestoreAnnotatedFunction() { this.getParameter(irqlParamIndex) = restoreParameter } - - int getIrqlParameterSlot() { result = irqlParamIndex } -} - -/** A call to a function that has a parameter annotated \_IRQL\_restores\_. */ -class IrqlRestoreCall extends FunctionCall { - IrqlRestoreCall() { this.getTarget() instanceof IrqlRestoreAnnotatedFunction } - - /** - * A heuristic evaluation of the IRQL that the system is changing to. This is defined as - * "the IRQL before the corresponding save global call." - */ - int getIrqlLevel() { - result = any(getPotentialExitIrqlAtCfn(this.getMostRecentRaise().getAPredecessor())) - } - - /** Returns the matching call to a function that saved the IRQL. */ - IrqlSaveCall getMostRecentRaise() { - result = - any(IrqlSaveCall sgic | - this.getAPredecessor*() = sgic and - matchingSaveCall(sgic) and - not exists(SavesGlobalIrqlCall sgic2 | - sgic2 != sgic and sgic2.getAPredecessor*() = sgic and matchingSaveCall(sgic2) - ) - ) - } - - /** - * Holds if a given call to an \_IRQL\_saves\_global\_ annotated function is using the same IRQL location as this. - */ - private predicate matchingSaveCall(IrqlSaveCall sgic) { - // Attempting to match all expr children leads to an explosion in runtime, so for now just compare - // the expr itself and the first child of each argument. This covers the common &variable case. - exists(int i, int j | - i = this.getTarget().(IrqlRestoreAnnotatedFunction).getIrqlParameterSlot() and - j = sgic.getTarget().(IrqlSavesToParameterFunction).getIrqlParameterSlot() and - exprsMatchText(this.getArgument(i), sgic.getArgument(j)) - ) - or - exists(int i | - i = this.getTarget().(IrqlRestoreAnnotatedFunction).getIrqlParameterSlot() and - sgic.getTarget() instanceof IrqlSavesViaReturnFunction and - exprsMatchText(this.getArgument(i), sgic.getSavedValue()) - ) and - this.getControlFlowScope() = sgic.getControlFlowScope() - } -} - -/** A call to a function that has is annotated \_IRQL\_saves\_. */ -class IrqlSaveCall extends FunctionCall { - IrqlSaveCall() { this.getTarget() instanceof IrqlSavesFunction } - - Expr getSavedValue() { - result = - any(Expr e | - exists(AssignExpr ae | - ae.getLValue() = e and - ae.getRValue() = this and - this.getTarget() instanceof IrqlSavesViaReturnFunction - ) - ) - } -} - -/** A call to a KeRaiseIRQL API that directly raises the IRQL. */ -class KeRaiseIrqlCall extends FunctionCall { - KeRaiseIrqlCall() { - this.getTarget().getName() = - ["KeRaiseIrql", "KfRaiseIrql", "KeRaiseIrqlToDPCLevel", "KfRaiseIrqlToDPCLevel"] - } - - int getIrqlLevel() { - if this.getTarget().getName() = ["KeRaiseIrqlToDPCLevel", "KfRaiseIrqlToDPCLevel"] - then result = 2 - else result = this.getArgument(0).(Literal).getValue().toInt() - } -} - -/** A direct call to a function that lowers the IRQL. */ -class KeLowerIrqlCall extends FunctionCall { - KeLowerIrqlCall() { this.getTarget().getName() = ["KeLowerIrql", "KfLowerIrql"] } - - /** - * A heuristic evaluation of the IRQL that the system is lowering to. This is defined as - * "the IRQL before the most recent KeRaiseIrql call". - */ - int getIrqlLevel() { - result = - any(getPotentialExitIrqlAtCfn(this.getMostRecentRaiseInterprocedural().getAPredecessor())) - } - - /** - * Get the most recent KeRaiseIrql call before this call. - * - * This performs a local (intraprocedural) analysis only. It is unused in the library today, - * but can be inserted in place of the interprocedural analysis by modifying the getIrqlLevel() - * function above. - */ - KeRaiseIrqlCall getMostRecentRaise() { - result = - any(KeRaiseIrqlCall sgic | - this.getAPredecessor*() = sgic and - not exists(KeRaiseIrqlCall kric2 | kric2 != sgic and kric2.getAPredecessor*() = sgic) - ) - } - - /** - * Get the corresponding KeRaiseIrql call that preceded this KeLowerIrql call. - * - * This performs an interprocedural analysis using CodeQL's DataFlow classes. - */ - KeRaiseIrqlCall getMostRecentRaiseInterprocedural() { - result = - any(KeRaiseIrqlCall kric | - exists(IrqlRaiseLowerFlow irlf | - irlf.hasFlow(DataFlow::exprNode(kric), DataFlow::exprNode(this.getAnArgument())) - ) - ) - } -} - -/** A call to a function that restores the IRQL from a specified state. */ -class SavesGlobalIrqlCall extends FunctionCall { - SavesGlobalIrqlCall() { this.getTarget() instanceof IrqlSavesGlobalAnnotatedFunction } -} - -/** A call to a function that restores the IRQL from a specified state. */ -class RestoresGlobalIrqlCall extends FunctionCall { - RestoresGlobalIrqlCall() { this.getTarget() instanceof IrqlRestoresGlobalAnnotatedFunction } - - /** - * A heuristic evaluation of the IRQL that the system is changing to. This is defined as - * "the IRQL before the corresponding save global call." - */ - int getIrqlLevel() { - result = any(getPotentialExitIrqlAtCfn(this.getMostRecentRaise().getAPredecessor())) - } - - /** - * Returns the matching call to a function that saved the IRQL to a global state. - * - * This is a strictly intraprocedural analysis. - */ - SavesGlobalIrqlCall getMostRecentRaise() { - result = - any(SavesGlobalIrqlCall sgic | - this.getAPredecessor*() = sgic and - matchingSaveCall(sgic) and - not exists(SavesGlobalIrqlCall sgic2 | - sgic2 != sgic and sgic2.getAPredecessor*() = sgic and matchingSaveCall(sgic2) - ) - ) - } - - /** - * Holds if a given call to an _IRQL_saves_global_ annotated function is using the same IRQL location as this. - */ - private predicate matchingSaveCall(SavesGlobalIrqlCall sgic) { - // Attempting to match all expr children leads to an explosion in runtime, so for now just compare - // the expr itself and the first child of each argument. This covers the common &variable case. - exists(int i, int j | - i = this.getTarget().(IrqlRestoresGlobalAnnotatedFunction).getIrqlParameterSlot() and - j = sgic.getTarget().(IrqlSavesGlobalAnnotatedFunction).getIrqlParameterSlot() and - exprsMatchText(this.getArgument(i), sgic.getArgument(j)) - ) and - this.getTarget().(IrqlRestoresGlobalAnnotatedFunction).getIrqlKind() = - sgic.getTarget().(IrqlSavesGlobalAnnotatedFunction).getIrqlKind() - } -} - -/** - * A utility function to determine if two exprs are (textually) the same. - * Because checking all children of the expression causes an explosion in evaluation time, we just - * check the first child. - * - * This function is obviously _not_ a guarantee that two expressions refer to the same thing. - * Use this locally and with caution. - * - * TODO: Compare with global value numbering (both accuracy and performance) - */ -pragma[inline] -private predicate exprsMatchText(Expr e1, Expr e2) { - e1.toString().matches(e2.toString()) and - exists(Expr child | - child = e1.getAChild() and - e1.getChild(0).toString().matches(e2.getChild(0).toString()) - ) - or - not exists(Expr child | child = e1.getAChild() or child = e2.getAChild()) -} - -/** - * Attempt to provide the IRQL **once this control flow node exits**, based on annotations and direct calls to raising/lowering functions. - * This predicate functions as follows: - * - If calling a "Raise IRQL" function, then it returns the value of the argument passed in (the target IRQL). - * - If calling a "Lower IRQL" function, then it returns the value of the argument passed in (the target IRQL). - * - If calling a function annotated to restore the IRQL from a previously saved spot, then the result is the IRQL before that save call. - * - If calling a function annotated to raise the IRQL, then it returns the annotated value (the target IRQL). - * - If calling a function annotated to maintain the same IRQL, then the result is the IRQL at the previous CFN. - * - If this node is calling a function with no annotations, the result is the IRQL that function exits at. - * - If there is a prior CFN in the CFG, the result is the result for that prior CFN. - * - If there is no prior CFN, then the result is whatever the IRQL was at a statement prior to a function call to this function (a lazy interprocedural analysis.) - * - If there are no prior CFNs and no calls to this function, then the IRQL is determined by annotations applied to this function. - * - Failing all this, we set the IRQL to 0. - * - * Not implemented: _IRQL_limited_to_ - */ -cached -int getPotentialExitIrqlAtCfn(ControlFlowNode cfn) { - if cfn instanceof KeRaiseIrqlCall - then result = cfn.(KeRaiseIrqlCall).getIrqlLevel() - else - if cfn instanceof KeLowerIrqlCall - then result = cfn.(KeLowerIrqlCall).getIrqlLevel() - else - if cfn instanceof RestoresGlobalIrqlCall - then result = cfn.(RestoresGlobalIrqlCall).getIrqlLevel() - else - if cfn instanceof IrqlRestoreCall - then result = cfn.(IrqlRestoreCall).getIrqlLevel() - else - if - cfn instanceof FunctionCall and - cfn.(FunctionCall).getTarget() instanceof IrqlRaisesAnnotatedFunction - then result = cfn.(FunctionCall).getTarget().(IrqlRaisesAnnotatedFunction).getIrqlLevel() - else - if - cfn instanceof FunctionCall and - cfn.(FunctionCall).getTarget() instanceof IrqlRequiresSameAnnotatedFunction - then result = any(getPotentialExitIrqlAtCfn(cfn.getAPredecessor())) - else - if cfn instanceof FunctionCall - then - result = - any(getPotentialExitIrqlAtCfn(getExitPointsOfFunction(cfn.(FunctionCall) - .getTarget())) - ) - else - if exists(ControlFlowNode cfn2 | cfn2 = cfn.getAPredecessor()) - then result = any(getPotentialExitIrqlAtCfn(cfn.getAPredecessor())) - else - if - exists(FunctionCall fc, ControlFlowNode cfn2 | - fc.getTarget() = cfn.getControlFlowScope() and - cfn2.getASuccessor() = fc - ) - then - // TODO: Check that this node is actually a function entry point and not just - // an isolated part of the CFN. Sometimes we get nodes that are "in" a function's - // CFN, but have no predecessors, but are not function entry. (Why?) - result = - any(getPotentialExitIrqlAtCfn(any(ControlFlowNode cfn2 | - cfn2.getASuccessor().(FunctionCall).getTarget() = - cfn.getControlFlowScope() - )) - ) - else - if - cfn.getControlFlowScope() instanceof IrqlRestrictsFunction and - getAllowableIrqlLevel(cfn.getControlFlowScope()) != -1 - then result = getAllowableIrqlLevel(cfn.getControlFlowScope()) - else result = 0 -} - -import semmle.code.cpp.controlflow.Dominance - -/** Utility function to get exit points of a function. */ -private ControlFlowNode getExitPointsOfFunction(Function f) { - result = - any(ControlFlowNode cfn | - cfn.getControlFlowScope() = f and - functionExit(cfn) - ) -} - -/** - * Attempt to find the range of valid IRQL values when **entering** a given IRQL-annotated function. - * This is used as a heuristic when no other IRQL information is available (i.e. we are at the top - * of a call stack.) - * - * Note: we implicitly apply DISPATCH_LEVEL as the max when a max is not specified but a minimum is, - * and the global max if the minimum is > DISPATCH_LEVEL. - */ -cached -int getAllowableIrqlLevel(Function func) { - if - exists(IrqlValue lowerBound, IrqlValue upperBound | - hasLowerBound(func, lowerBound) and hasUpperBound(func, upperBound) - ) - then - result = - [any(IrqlValue lowerBound | hasLowerBound(func, lowerBound)) .. any(IrqlValue upperBound | - hasUpperBound(func, upperBound) - )] - else - if - exists(IrqlValue upperBound | hasUpperBound(func, upperBound)) and - not exists(IrqlValue lowerBound | hasLowerBound(func, lowerBound)) - then result = [0 .. any(IrqlValue upperBound | hasUpperBound(func, upperBound))] - else - if - not exists(IrqlValue upperBound | hasUpperBound(func, upperBound)) and - exists(IrqlValue lowerBound | hasLowerBound(func, lowerBound) and lowerBound > 2) - then - result = - [any(IrqlValue lowerBound | hasLowerBound(func, lowerBound)) .. getGlobalMaxIrqlLevel()] - else - if - not exists(IrqlValue upperBound | hasUpperBound(func, upperBound)) and - exists(IrqlValue lowerBound | hasLowerBound(func, lowerBound) and lowerBound <= 2) - then result = [any(IrqlValue lowerBound | hasLowerBound(func, lowerBound)) .. 2] - else result = -1 -} - -/** Attempts to find an upper bound on the expected entry IRQL for a function. */ -private predicate hasUpperBound(Function func, IrqlValue upperBound) { - // The instanceof checks may seem redundant, but in fact they let us - // implement a "priority" if there are multiple, possibly conflicting annotations. - ( - func.(IrqlMaxAnnotatedFunction).getIrqlLevel() = upperBound - or - not func instanceof IrqlMaxAnnotatedFunction and - func.(IrqlAlwaysMaxFunction).getIrqlLevel() = upperBound - or - ( - not func instanceof IrqlMaxAnnotatedFunction and - not func instanceof IrqlAlwaysMaxFunction - ) and - func.(IrqlRequiresAnnotatedFunction).getIrqlLevel() = upperBound - or - ( - not func instanceof IrqlMaxAnnotatedFunction and - not func instanceof IrqlAlwaysMaxFunction and - not func instanceof IrqlRequiresAnnotatedFunction - ) and - func instanceof PagedFunctionDeclaration and - upperBound = 1 - ) -} - -/** Attempts to find a lower bound on the expected entry IRQL for a function. */ -private predicate hasLowerBound(Function func, IrqlValue lowerBound) { - // The instanceof checks may seem redundant, but in fact they let us - // implement a "priority" if there are multiple, possibly conflicting annotations. - ( - func.(IrqlMinAnnotatedFunction).getIrqlLevel() = lowerBound - or - not func instanceof IrqlMinAnnotatedFunction and - func.(IrqlAlwaysMinFunction).getIrqlLevel() = lowerBound - or - ( - not func instanceof IrqlMinAnnotatedFunction and - not func instanceof IrqlAlwaysMinFunction - ) and - func.(IrqlRequiresAnnotatedFunction).getIrqlLevel() = lowerBound - or - ( - not func instanceof IrqlMinAnnotatedFunction and - not func instanceof IrqlAlwaysMinFunction and - not func instanceof IrqlRequiresAnnotatedFunction - ) and - func instanceof PagedFunctionDeclaration and - lowerBound = 0 - ) -} + import cpp + import drivers.libraries.SAL + import drivers.wdm.libraries.WdmDrivers + import drivers.libraries.IrqlDataFlow + import drivers.libraries.Page + import drivers.libraries.RoleTypes + + + + + /** + * A macro in wdm.h that represents an IRQL level, + * such as PASSIVE_LEVEL, DISPATCH_LEVEL, etc. + */ + + class IrqlMacro extends Macro { + int irqlLevelAsInt; + + + IrqlMacro() { + this.getName().matches("%_LEVEL") and + this.getFile().getBaseName() = "wdm.h" and + this.getBody().toInt() = irqlLevelAsInt and + irqlLevelAsInt >= 0 and + irqlLevelAsInt <= 31 + } + + /** Returns the integer value of this IRQL level. */ + + int getIrqlLevel() { result = irqlLevelAsInt } + } + + /** + * Returns the highest IRQL in wdm.h across this database. + * May cause incorrect results if database contains both 32-bit + * and 64-bit builds. + */ + + int getGlobalMaxIrqlLevel() { + result = + any(int i | + exists(IrqlMacro im | + i = im.getIrqlLevel() and + not exists(IrqlMacro im2 | im2 != im and im2.getIrqlLevel() > im.getIrqlLevel()) + ) + ) + } + + /** + * Represents a real (not -1) Irql level, between 0 and the max for the architecture this + * database was built to target. + */ + class IrqlValue extends int { + IrqlValue() { this = [0 .. getGlobalMaxIrqlLevel()] } + } + + /** An \_IRQL\_saves\_global\_(parameter, kind) annotation. */ + + class IrqlSavesGlobalAnnotation extends SALAnnotation { + MacroInvocation irqlMacroInvocation; + + + IrqlSavesGlobalAnnotation() { + // Needs to include other function and parameter annotations too + this.getMacroName() = ["__drv_savesIRQLGlobal", "_IRQL_saves_global_"] and + irqlMacroInvocation.getParentInvocation() = this + } + } + + /** An \_IRQL\_restores\_global\_(parameter, kind) annotation. */ + + class IrqlRestoresGlobalAnnotation extends SALAnnotation { + MacroInvocation irqlMacroInvocation; + + + IrqlRestoresGlobalAnnotation() { + // Needs to include other function and parameter annotations too + this.getMacroName() = ["__drv_restoresIRQLGlobal", "_IRQL_restores_global_"] and + irqlMacroInvocation.getParentInvocation() = this + } + } + + /** + * Standard IRQL annotations which apply to entire functions and manipulate or constrain the IRQL. + */ + + class IrqlFunctionAnnotation extends SALAnnotation { + string irqlLevel; + string irqlAnnotationName; + string innerAnnotationName; + string fullInnerAnnotationString; + + + IrqlFunctionAnnotation() { + ( + this.getMacroName() = + [ + "__drv_requiresIRQL", "_IRQL_requires_", "_drv_minIRQL", "_IRQL_requires_min_", + "_drv_maxIRQL", "_IRQL_requires_max_", "__drv_raisesIRQL", "_IRQL_raises_", + "__drv_maxFunctionIRQL", "_IRQL_always_function_max_", "__drv_minFunctionIRQL", + "_IRQL_always_function_min_" + ] and + irqlLevel = this.getUnexpandedArgument(0) and + innerAnnotationName = this.getMacroName() and + fullInnerAnnotationString = this.getMacroName() + "(" + irqlLevel + ")" + or + // Special case: _IRQL_saves_ annotations can apply to a whole function, + // but do not have an associated IRQL value. + this.getMacroName() = ["__drv_savesIRQL", "_IRQL_saves_"] and + irqlLevel = "NA_IRQL_SAVES" and + innerAnnotationName = this.getMacroName() and + fullInnerAnnotationString = this.getMacroName() + "(" + irqlLevel + ")" + or + // // Conditional IRQL annotations within a _When_ annotation + this.getMacroName() = ["_When_"] and + fullInnerAnnotationString = this.getUnexpandedArgument(1).toString() and + ( + fullInnerAnnotationString.matches("__drv_requiresIRQL%") and + innerAnnotationName = "__drv_requiresIRQL" + or + fullInnerAnnotationString.matches("_IRQL_requires_%") and + innerAnnotationName = "_IRQL_requires_" + or + fullInnerAnnotationString.matches("_drv_minIRQL%") and + innerAnnotationName = "_drv_minIRQL" + or + fullInnerAnnotationString.matches("_drv_maxIRQL%") and + innerAnnotationName = "_drv_maxIRQL" + or + fullInnerAnnotationString.matches("_IRQL_requires_max_%") and + innerAnnotationName = "_IRQL_requires_max_" + or + fullInnerAnnotationString.matches("_IRQL_raises_%") and + innerAnnotationName = "_IRQL_raises_" + or + fullInnerAnnotationString.matches("__drv_maxFunctionIRQL%") and + innerAnnotationName = "__drv_maxFunctionIRQL" + or + fullInnerAnnotationString.matches("_IRQL_always_function_max_%") and + innerAnnotationName = "_IRQL_always_function_max_" + or + fullInnerAnnotationString.matches("__drv_minFunctionIRQL%") and + innerAnnotationName = "__drv_minFunctionIRQL" + or + fullInnerAnnotationString.matches("_IRQL_always_function_min_%") and + innerAnnotationName = "_IRQL_always_function_min_" + ) and + irqlLevel = + fullInnerAnnotationString + .substring(fullInnerAnnotationString.indexOf(innerAnnotationName + "(") + 1 + + innerAnnotationName.length(), + fullInnerAnnotationString + .indexOf(")", 0, + fullInnerAnnotationString.indexOf(innerAnnotationName + "(") + 1 + + innerAnnotationName.length())) + ) and + irqlAnnotationName = this.getMacroName() + } + + /** Returns the text of this annotation (i.e. \_IRQL\_requires\_, etc.) */ + + string getIrqlMacroName() { + if this.getMacroName() = ["_When_"] + then result = innerAnnotationName + else result = irqlAnnotationName + } + + + string getIrqlLevelString() { result = irqlLevel } + + /** + * Evaluate the IRQL specified in this annotation, if possible. + * + * This will return -1 if the IRQL specified is anything other than a standard + * IRQL level (i.e. PASSIVE_LEVEL). This includes statements like "DPC_LEVEL - 1". + */ + + int getIrqlLevel() { + // Special case for DPC_LEVEL, which is not defined normally + if this.getIrqlLevelString() = "DPC_LEVEL" + then result = 2 + else + if exists(IrqlMacro im | im.getHead().matches(this.getIrqlLevelString())) + then + result = + any(int i | + exists(IrqlMacro im | + im.getIrqlLevel() = i and + im.getHead().matches(this.getIrqlLevelString()) + ) + ) + else + if exists(int i | i = this.getIrqlLevelString().toInt()) + then result = this.getIrqlLevelString().toInt() + else result = -1 + } + } + + /** Represents an "\_IRQL\_requires\_same\_" annotation. */ + class IrqlSameAnnotation extends SALAnnotation { + string irqlAnnotationName; + + IrqlSameAnnotation() { + this.getMacroName() = ["__drv_sameIRQL", "_IRQL_requires_same_"] and + irqlAnnotationName = this.getMacroName() + } + + string getIrqlMacroName() { result = irqlAnnotationName } + } + + /** An "\_IRQL\_requires\_max\_" annotation. */ + class IrqlMaxAnnotation extends IrqlFunctionAnnotation { + IrqlMaxAnnotation() { this.getIrqlMacroName() = ["_drv_maxIRQL", "_IRQL_requires_max_"] } + } + + /** An "\_IRQL\_raises\_" annotation. */ + class IrqlRaisesAnnotation extends IrqlFunctionAnnotation { + IrqlRaisesAnnotation() { this.getIrqlMacroName() = ["__drv_raisesIRQL", "_IRQL_raises_"] } + } + + /** An "\_IRQL\_requires\_min\_" annotation. */ + class IrqlMinAnnotation extends IrqlFunctionAnnotation { + IrqlMinAnnotation() { this.getIrqlMacroName() = ["_drv_minIRQL", "_IRQL_requires_min_"] } + } + + /** An "\_IRQL\_requires\_" annotation. */ + class IrqlRequiresAnnotation extends IrqlFunctionAnnotation { + IrqlRequiresAnnotation() { this.getIrqlMacroName() = ["__drv_requiresIRQL", "_IRQL_requires_"] } + } + + /** An "\_IRQL\_always\_function\_max\_" annotation. */ + class IrqlAlwaysMaxAnnotation extends IrqlFunctionAnnotation { + IrqlAlwaysMaxAnnotation() { + this.getIrqlMacroName() = ["__drv_maxFunctionIRQL", "_IRQL_always_function_max_"] + } + } + + /** An "\_IRQL\_always\_function\_min\_" annotation. */ + class IrqlAlwaysMinAnnotation extends IrqlFunctionAnnotation { + IrqlAlwaysMinAnnotation() { + this.getIrqlMacroName() = ["__drv_minFunctionIRQL", "_IRQL_always_function_min_"] + } + } + + /** + * A SAL annotation indicating that the parameter in + * question is used to store or restore the IRQL. + */ + class IrqlParameterAnnotation extends SALAnnotation { + string irqlAnnotationName; + + IrqlParameterAnnotation() { + this.getMacroName() = + ["__drv_restoresIRQL", "_IRQL_restores_", "__drv_savesIRQL", "_IRQL_saves_"] and + irqlAnnotationName = this.getMacroName() and + exists(MacroInvocation mi | mi.getParentInvocation() = this) + } + + /** Get the text of the annotation. */ + string getIrqlMacroName() { result = irqlAnnotationName } + } + + /** + * A SAL annotation indicating that the parameter in + * question contains an IRQL value that the system will be set to. + */ + class IrqlRestoreAnnotation extends IrqlParameterAnnotation { + IrqlRestoreAnnotation() { this.getMacroName() = ["__drv_restoresIRQL", "_IRQL_restores_"] } + } + + /** + * A SAL annotation indicating that can be used in two ways: + * - If applied to a function, the function returns the previous IRQL or otherwise saves the IRQL. + * - If applied to a parameter, the function saves the IRQL to the parameter. + */ + class IrqlSaveAnnotation extends IrqlFunctionAnnotation { + IrqlSaveAnnotation() { this.getIrqlMacroName() = ["__drv_savesIRQL", "_IRQL_saves_"] } + } + + /** A parameter that is annotated with "\_IRQL\_restores\_". */ + class IrqlRestoreParameter extends Parameter { + IrqlRestoreParameter() { exists(IrqlRestoreAnnotation ira | ira.getDeclaration() = this) } + } + + /** A parameter that is annotated with "\_IRQL\_saves\_". */ + class IrqlSaveParameter extends Parameter { + IrqlSaveParameter() { exists(IrqlSaveAnnotation isa | isa.getDeclaration() = this) } + } + + /** A typedef that has IRQL annotations applied to it. */ + class IrqlAnnotatedTypedef extends TypedefType { + IrqlFunctionAnnotation irqlAnnotation; + + IrqlAnnotatedTypedef() { irqlAnnotation.getTypedefDeclarations() = this } + + IrqlFunctionAnnotation getIrqlAnnotation() { result = irqlAnnotation } + } + + /** + * A function that is annotated in such a way that + * either its entry or exit IRQL is restricted, either by having a min/max value, + * a required value, or by raising the IRQL to a known value. + */ + + class IrqlRestrictsFunction extends Function { + IrqlFunctionAnnotation irqlAnnotation; + + + IrqlRestrictsFunction() { + exists(FunctionDeclarationEntry fde | + fde = this.getADeclarationEntry() and + irqlAnnotation.getDeclarationEntry() = fde + ) + or + exists(FunctionDeclarationEntry fde | + fde.getFunction() = this and + fde.getTypedefType().(IrqlAnnotatedTypedef).getIrqlAnnotation() = irqlAnnotation + ) + or + exists(ImplicitRoleTypeFunction irtf | + irtf = this and + irtf.getExpectedRoleTypeType().(IrqlAnnotatedTypedef).getIrqlAnnotation() = irqlAnnotation + ) + } + + + IrqlFunctionAnnotation getFuncIrqlAnnotation() { result = irqlAnnotation } + } + + /** A function that changes the IRQL. */ + abstract class IrqlChangesFunction extends Function { } + + /** A function that is explicitly annotated to enter and exit at the same IRQL. */ + class IrqlRequiresSameAnnotatedFunction extends Function { + IrqlSameAnnotation irqlAnnotation; + + IrqlRequiresSameAnnotatedFunction() { + exists(FunctionDeclarationEntry fde | + fde = this.getADeclarationEntry() and + irqlAnnotation.getDeclarationEntry() = fde + ) + } + } + + /** A function that is annotated to run at a specific IRQL. */ + class IrqlRequiresAnnotatedFunction extends IrqlRestrictsFunction { + IrqlRequiresAnnotatedFunction() { irqlAnnotation instanceof IrqlRequiresAnnotation } + + int getIrqlLevel() { result = irqlAnnotation.(IrqlRequiresAnnotation).getIrqlLevel() } + } + + /** A function that is annotated to enter at or below a given IRQL. */ + class IrqlMaxAnnotatedFunction extends IrqlRestrictsFunction { + IrqlMaxAnnotatedFunction() { irqlAnnotation instanceof IrqlMaxAnnotation } + + int getIrqlLevel() { result = irqlAnnotation.(IrqlMaxAnnotation).getIrqlLevel() } + } + + /** A function that is annotated to enter at or above a given IRQL. */ + class IrqlMinAnnotatedFunction extends IrqlRestrictsFunction { + IrqlMinAnnotatedFunction() { irqlAnnotation instanceof IrqlMinAnnotation } + + int getIrqlLevel() { result = irqlAnnotation.(IrqlMinAnnotation).getIrqlLevel() } + } + + /** A function that is annotated to raise the IRQL to a given value. */ + class IrqlRaisesAnnotatedFunction extends IrqlRestrictsFunction, IrqlChangesFunction { + IrqlRaisesAnnotatedFunction() { irqlAnnotation instanceof IrqlRaisesAnnotation } + + int getIrqlLevel() { result = irqlAnnotation.(IrqlRaisesAnnotation).getIrqlLevel() } + } + + /** A function that is never allowed to run with the IRQL above a given value. */ + class IrqlAlwaysMaxFunction extends IrqlRestrictsFunction { + IrqlAlwaysMaxFunction() { irqlAnnotation instanceof IrqlAlwaysMaxAnnotation } + + int getIrqlLevel() { result = irqlAnnotation.(IrqlAlwaysMaxAnnotation).getIrqlLevel() } + } + + /** A function that is never allowed to run with the IRQL above a given value. */ + class IrqlAlwaysMinFunction extends IrqlRestrictsFunction { + IrqlAlwaysMinFunction() { irqlAnnotation instanceof IrqlAlwaysMinAnnotation } + + int getIrqlLevel() { result = irqlAnnotation.(IrqlAlwaysMinAnnotation).getIrqlLevel() } + } + + /** A function annotated to save the IRQL at the specified location upon entry. */ + class IrqlSavesGlobalAnnotatedFunction extends IrqlChangesFunction { + IrqlSavesGlobalAnnotation irqlAnnotation; + string irqlKind; + int irqlParamIndex; + + IrqlSavesGlobalAnnotatedFunction() { + exists(FunctionDeclarationEntry fde | + fde = this.getADeclarationEntry() and + irqlAnnotation.getDeclarationEntry() = fde + ) and + irqlKind = irqlAnnotation.getExpandedArgument(0) and + this.getParameter(irqlParamIndex).getName().matches(irqlAnnotation.getExpandedArgument(1)) + } + + string getIrqlKind() { result = irqlKind } + + int getIrqlParameterSlot() { result = irqlParamIndex } + } + + /** A function annotated to restore the IRQL from the specified location upon exit. */ + class IrqlRestoresGlobalAnnotatedFunction extends IrqlChangesFunction { + IrqlRestoresGlobalAnnotation irqlAnnotation; + string irqlKind; + int irqlParamIndex; + + IrqlRestoresGlobalAnnotatedFunction() { + exists(FunctionDeclarationEntry fde | + fde = this.getADeclarationEntry() and + irqlAnnotation.getDeclarationEntry() = fde + ) and + irqlKind = irqlAnnotation.getExpandedArgument(0) and + this.getParameter(irqlParamIndex).getName().matches(irqlAnnotation.getExpandedArgument(1)) + } + + string getIrqlKind() { result = irqlKind } + + int getIrqlParameterSlot() { result = irqlParamIndex } + } + + /** + * An abstract class for functions that use the \_IRQL\_saves\_ annotation, + * either on the function definition or on a specific parameter. + */ + abstract class IrqlSavesFunction extends Function { } + + /** A function that has a parameter annotated \_IRQL\_saves\_. */ + class IrqlSavesToParameterFunction extends IrqlSavesFunction { + IrqlSaveParameter saveParameter; + int irqlParamIndex; + + IrqlSavesToParameterFunction() { this.getParameter(irqlParamIndex) = saveParameter } + + int getIrqlParameterSlot() { result = irqlParamIndex } + } + + /** A function that saves the IRQL as a return value. */ + class IrqlSavesViaReturnFunction extends IrqlSavesFunction { + IrqlSaveAnnotation irqlAnnotation; + + IrqlSavesViaReturnFunction() { + exists(FunctionDeclarationEntry fde | + fde = this.getADeclarationEntry() and + irqlAnnotation.getDeclarationEntry() = fde + ) + } + } + + /** A function that has a parameter annotated \_IRQL\_restores\_. */ + class IrqlRestoreAnnotatedFunction extends Function { + IrqlRestoreParameter restoreParameter; + int irqlParamIndex; + + IrqlRestoreAnnotatedFunction() { this.getParameter(irqlParamIndex) = restoreParameter } + + int getIrqlParameterSlot() { result = irqlParamIndex } + } + + /** A call to a function that has a parameter annotated \_IRQL\_restores\_. */ + class IrqlRestoreCall extends FunctionCall { + IrqlRestoreCall() { this.getTarget() instanceof IrqlRestoreAnnotatedFunction } + + /** + * A heuristic evaluation of the IRQL that the system is changing to. This is defined as + * "the IRQL before the corresponding save global call." + */ + int getIrqlLevel() { + result = any(getPotentialExitIrqlAtCfn(this.getMostRecentRaise().getAPredecessor())) + } + + int getIrqlLevelExplicit() { + result = any(getExplicitExitIrqlAtCfn(this.getMostRecentRaise().getAPredecessor())) + } + + /** Returns the matching call to a function that saved the IRQL. */ + IrqlSaveCall getMostRecentRaise() { + result = + any(IrqlSaveCall sgic | + this.getAPredecessor*() = sgic and + matchingSaveCall(sgic) and + not exists(SavesGlobalIrqlCall sgic2 | + sgic2 != sgic and sgic2.getAPredecessor*() = sgic and matchingSaveCall(sgic2) + ) + ) + } + + /** + * Holds if a given call to an \_IRQL\_saves\_global\_ annotated function is using the same IRQL location as this. + */ + private predicate matchingSaveCall(IrqlSaveCall sgic) { + // Attempting to match all expr children leads to an explosion in runtime, so for now just compare + // the expr itself and the first child of each argument. This covers the common &variable case. + exists(int i, int j | + i = this.getTarget().(IrqlRestoreAnnotatedFunction).getIrqlParameterSlot() and + j = sgic.getTarget().(IrqlSavesToParameterFunction).getIrqlParameterSlot() and + exprsMatchText(this.getArgument(i), sgic.getArgument(j)) + ) + or + exists(int i | + i = this.getTarget().(IrqlRestoreAnnotatedFunction).getIrqlParameterSlot() and + sgic.getTarget() instanceof IrqlSavesViaReturnFunction and + exprsMatchText(this.getArgument(i), sgic.getSavedValue()) + ) and + this.getControlFlowScope() = sgic.getControlFlowScope() + } + } + + /** A call to a function that has is annotated \_IRQL\_saves\_. */ + class IrqlSaveCall extends FunctionCall { + IrqlSaveCall() { this.getTarget() instanceof IrqlSavesFunction } + + Expr getSavedValue() { + result = + any(Expr e | + exists(AssignExpr ae | + ae.getLValue() = e and + ae.getRValue() = this and + this.getTarget() instanceof IrqlSavesViaReturnFunction + ) + ) + } + } + + /** A call to a KeRaiseIRQL API that directly raises the IRQL. */ + class KeRaiseIrqlCall extends FunctionCall { + KeRaiseIrqlCall() { + this.getTarget().getName() = + ["KeRaiseIrql", "KfRaiseIrql", "KeRaiseIrqlToDPCLevel", "KfRaiseIrqlToDPCLevel"] + } + + int getIrqlLevel() { + if this.getTarget().getName() = ["KeRaiseIrqlToDPCLevel", "KfRaiseIrqlToDPCLevel"] + then result = 2 + else result = this.getArgument(0).(Literal).getValue().toInt() + } + } + + /** A direct call to a function that lowers the IRQL. */ + class KeLowerIrqlCall extends FunctionCall { + KeLowerIrqlCall() { this.getTarget().getName() = ["KeLowerIrql", "KfLowerIrql"] } + + /** + * A heuristic evaluation of the IRQL that the system is lowering to. This is defined as + * "the IRQL before the most recent KeRaiseIrql call". + */ + int getIrqlLevel() { + result = + any(getPotentialExitIrqlAtCfn(this.getMostRecentRaiseInterprocedural().getAPredecessor())) + } + + int getIrqlLevelExplicit() { + result = + any(getExplicitExitIrqlAtCfn(this.getMostRecentRaiseInterprocedural().getAPredecessor())) + } + + /** + * Get the most recent KeRaiseIrql call before this call. + * + * This performs a local (intraprocedural) analysis only. It is unused in the library today, + * but can be inserted in place of the interprocedural analysis by modifying the getIrqlLevel() + * function above. + */ + KeRaiseIrqlCall getMostRecentRaise() { + result = + any(KeRaiseIrqlCall sgic | + this.getAPredecessor*() = sgic and + not exists(KeRaiseIrqlCall kric2 | kric2 != sgic and kric2.getAPredecessor*() = sgic) + ) + } + + + /** + * Get the corresponding KeRaiseIrql call that preceded this KeLowerIrql call. + * + * This performs an interprocedural analysis using CodeQL's DataFlow classes. + */ + KeRaiseIrqlCall getMostRecentRaiseInterprocedural() { + result = + any(KeRaiseIrqlCall kric | + IrqlRaiseLowerFlow::flow(DataFlow::exprNode(kric), DataFlow::exprNode(this.getAnArgument())) + ) + } + } + + /** A call to a function that restores the IRQL from a specified state. */ + class SavesGlobalIrqlCall extends FunctionCall { + SavesGlobalIrqlCall() { this.getTarget() instanceof IrqlSavesGlobalAnnotatedFunction } + } + + /** A call to a function that restores the IRQL from a specified state. */ + class RestoresGlobalIrqlCall extends FunctionCall { + RestoresGlobalIrqlCall() { this.getTarget() instanceof IrqlRestoresGlobalAnnotatedFunction } + + /** + * A heuristic evaluation of the IRQL that the system is changing to. This is defined as + * "the IRQL before the corresponding save global call." + */ + int getIrqlLevel() { + result = any(getPotentialExitIrqlAtCfn(this.getMostRecentRaise().getAPredecessor())) + } + + int getIrqlLevelExplicit() { + result = any(getExplicitExitIrqlAtCfn(this.getMostRecentRaise().getAPredecessor())) + } + + /** + * Returns the matching call to a function that saved the IRQL to a global state. + * + * This is a strictly intraprocedural analysis. + */ + SavesGlobalIrqlCall getMostRecentRaise() { + result = + any(SavesGlobalIrqlCall sgic | + this.getAPredecessor*() = sgic and + matchingSaveCall(sgic) and + not exists(SavesGlobalIrqlCall sgic2 | + sgic2 != sgic and sgic2.getAPredecessor*() = sgic and matchingSaveCall(sgic2) + ) + ) + } + + /** + * Holds if a given call to an _IRQL_saves_global_ annotated function is using the same IRQL location as this. + */ + private predicate matchingSaveCall(SavesGlobalIrqlCall sgic) { + // Attempting to match all expr children leads to an explosion in runtime, so for now just compare + // the expr itself and the first child of each argument. This covers the common &variable case. + exists(int i, int j | + i = this.getTarget().(IrqlRestoresGlobalAnnotatedFunction).getIrqlParameterSlot() and + j = sgic.getTarget().(IrqlSavesGlobalAnnotatedFunction).getIrqlParameterSlot() and + exprsMatchText(this.getArgument(i), sgic.getArgument(j)) + ) and + this.getTarget().(IrqlRestoresGlobalAnnotatedFunction).getIrqlKind() = + sgic.getTarget().(IrqlSavesGlobalAnnotatedFunction).getIrqlKind() + } + } + + /** + * A utility function to determine if two exprs are (textually) the same. + * Because checking all children of the expression causes an explosion in evaluation time, we just + * check the first child. + * + * This function is obviously _not_ a guarantee that two expressions refer to the same thing. + * Use this locally and with caution. + * + * TODO: Compare with global value numbering (both accuracy and performance) + */ + pragma[inline] + private predicate exprsMatchText(Expr e1, Expr e2) { + e1.toString().matches(e2.toString()) and + exists(Expr child | + child = e1.getAChild() and + e1.getChild(0).toString().matches(e2.getChild(0).toString()) + ) + or + not exists(Expr child | child = e1.getAChild() or child = e2.getAChild()) + } + + /** + * Attempt to provide the IRQL **once this control flow node exits**, based on annotations and direct calls to raising/lowering functions. + * This predicate functions as follows: + * - If calling a "Raise IRQL" function, then it returns the value of the argument passed in (the target IRQL). + * - If calling a "Lower IRQL" function, then it returns the value of the argument passed in (the target IRQL). + * - If calling a function annotated to restore the IRQL from a previously saved spot, then the result is the IRQL before that save call. + * - If calling a function annotated to raise the IRQL, then it returns the annotated value (the target IRQL). + * - If calling a function annotated to maintain the same IRQL, then the result is the IRQL at the previous CFN. + * - If this node is calling a function with no annotations, the result is the IRQL that function exits at. + * - If there is a prior CFN in the CFG, the result is the result for that prior CFN. + * - If there is no prior CFN, then the result is whatever the IRQL was at a statement prior to a function call to this function (a lazy interprocedural analysis.) + * - If there are no prior CFNs and no calls to this function, then the IRQL is determined by annotations applied to this function. + * - Failing all this, we set the IRQL to 0. + * + * Not implemented: _IRQL_limited_to_ + */ + + int getPotentialExitIrqlAtCfn(ControlFlowNode cfn) { + if cfn instanceof KeRaiseIrqlCall + then result = cfn.(KeRaiseIrqlCall).getIrqlLevel() + else + if cfn instanceof KeLowerIrqlCall + then result = cfn.(KeLowerIrqlCall).getIrqlLevel() + else + if cfn instanceof RestoresGlobalIrqlCall + then result = cfn.(RestoresGlobalIrqlCall).getIrqlLevel() + else + if cfn instanceof IrqlRestoreCall + then result = cfn.(IrqlRestoreCall).getIrqlLevel() + else + if + cfn instanceof FunctionCall and + cfn.(FunctionCall).getTarget() instanceof IrqlRaisesAnnotatedFunction + then result = cfn.(FunctionCall).getTarget().(IrqlRaisesAnnotatedFunction).getIrqlLevel() + else + if + cfn instanceof FunctionCall and + cfn.(FunctionCall).getTarget() instanceof IrqlRequiresSameAnnotatedFunction + then result = any(getPotentialExitIrqlAtCfn(cfn.getAPredecessor())) + else + if cfn instanceof FunctionCall + then + result = + any(getPotentialExitIrqlAtCfn(getExitPointsOfFunction(cfn.(FunctionCall) + .getTarget())) + ) + else + if exists(ControlFlowNode cfn2 | cfn2 = cfn.getAPredecessor()) + then result = any(getPotentialExitIrqlAtCfn(cfn.getAPredecessor())) + else + if + exists(FunctionCall fc, ControlFlowNode cfn2 | + fc.getTarget() = cfn.getControlFlowScope() and + cfn2.getASuccessor() = fc + ) + then + // TODO: Check that this node is actually a function entry point and not just + // an isolated part of the CFN. Sometimes we get nodes that are "in" a function's + // CFN, but have no predecessors, but are not function entry. (Why?) + result = + any(getPotentialExitIrqlAtCfn(any(ControlFlowNode cfn2 | + cfn2.getASuccessor().(FunctionCall).getTarget() = + cfn.getControlFlowScope() + )) + ) + else + if + cfn.getControlFlowScope() instanceof IrqlRestrictsFunction and + getAllowableIrqlLevel(cfn.getControlFlowScope()) != -1 + then result = getAllowableIrqlLevel(cfn.getControlFlowScope()) + else result = 0 + } + + + /* + * Similar to above, but only exit points where the Irql is explicit + */ + + + int getExplicitExitIrqlAtCfn(ControlFlowNode cfn) { + if cfn instanceof KeRaiseIrqlCall + then result = cfn.(KeRaiseIrqlCall).getIrqlLevel() + else + if cfn instanceof KeLowerIrqlCall + then result = cfn.(KeLowerIrqlCall).getIrqlLevelExplicit() + else + if cfn instanceof RestoresGlobalIrqlCall + then result = cfn.(RestoresGlobalIrqlCall).getIrqlLevelExplicit() + else + if cfn instanceof IrqlRestoreCall + then result = cfn.(IrqlRestoreCall).getIrqlLevelExplicit() + else + if + cfn instanceof FunctionCall and + cfn.(FunctionCall).getTarget() instanceof IrqlRaisesAnnotatedFunction + then result = cfn.(FunctionCall).getTarget().(IrqlRaisesAnnotatedFunction).getIrqlLevel() + else + if + cfn instanceof FunctionCall and + cfn.(FunctionCall).getTarget() instanceof IrqlRequiresSameAnnotatedFunction + then result = any(getExplicitExitIrqlAtCfn(cfn.getAPredecessor())) + else ( + if exists(ControlFlowNode cfn2 | cfn2 = cfn.getAPredecessor()) + then result = any(getExplicitExitIrqlAtCfn(cfn.getAPredecessor())) + else + result = + any(getExplicitExitIrqlAtCfn(any(ControlFlowNode cfn2 | + cfn2.getASuccessor().(FunctionCall).getTarget() = + cfn.getControlFlowScope() + )) + ) + ) + } + + import semmle.code.cpp.controlflow.Dominance + + /** Utility function to get exit points of a function. */ + private ControlFlowNode getExitPointsOfFunction(Function f) { + result = + any(ControlFlowNode cfn | + cfn.getControlFlowScope() = f and + functionExit(cfn) + ) + } + + /** + * Attempt to find the range of valid IRQL values when **entering** a given IRQL-annotated function. + * This is used as a heuristic when no other IRQL information is available (i.e. we are at the top + * of a call stack.) + * + * Note: we implicitly apply DISPATCH_LEVEL as the max when a max is not specified but a minimum is, + * and the global max if the minimum is > DISPATCH_LEVEL. + */ + + int getAllowableIrqlLevel(Function func) { + exists(IrqlValue lowerBound, IrqlValue upperBound | + hasLowerBound(func, lowerBound) and hasUpperBound(func, upperBound) + ) and + result = + [any(IrqlValue lowerBound | hasLowerBound(func, lowerBound)) .. any(IrqlValue upperBound | + hasUpperBound(func, upperBound) + )] + or + exists(IrqlValue upperBound | hasUpperBound(func, upperBound)) and + not exists(IrqlValue lowerBound | hasLowerBound(func, lowerBound)) and + result = [0 .. any(IrqlValue upperBound | hasUpperBound(func, upperBound))] + or + not exists(IrqlValue upperBound | hasUpperBound(func, upperBound)) and + exists(IrqlValue lowerBound | hasLowerBound(func, lowerBound) and lowerBound > 2) and + result = [any(IrqlValue lowerBound | hasLowerBound(func, lowerBound)) .. getGlobalMaxIrqlLevel()] + or + not exists(IrqlValue upperBound | hasUpperBound(func, upperBound)) and + exists(IrqlValue lowerBound | hasLowerBound(func, lowerBound) and lowerBound <= 2) and + result = [any(IrqlValue lowerBound | hasLowerBound(func, lowerBound)) .. 2] + } + + /** Attempts to find an upper bound on the expected entry IRQL for a function. */ + private predicate hasUpperBound(Function func, IrqlValue upperBound) { + // The instanceof checks may seem redundant, but in fact they let us + // implement a "priority" if there are multiple, possibly conflicting annotations. + ( + func.(IrqlMaxAnnotatedFunction).getIrqlLevel() = upperBound + or + not func instanceof IrqlMaxAnnotatedFunction and + func.(IrqlAlwaysMaxFunction).getIrqlLevel() = upperBound + or + ( + not func instanceof IrqlMaxAnnotatedFunction and + not func instanceof IrqlAlwaysMaxFunction + ) and + func.(IrqlRequiresAnnotatedFunction).getIrqlLevel() = upperBound + or + ( + not func instanceof IrqlMaxAnnotatedFunction and + not func instanceof IrqlAlwaysMaxFunction and + not func instanceof IrqlRequiresAnnotatedFunction + ) and + func instanceof PagedFunctionDeclaration and + upperBound = 1 + ) + } + + /** Attempts to find a lower bound on the expected entry IRQL for a function. */ + private predicate hasLowerBound(Function func, IrqlValue lowerBound) { + // The instanceof checks may seem redundant, but in fact they let us + // implement a "priority" if there are multiple, possibly conflicting annotations. + ( + func.(IrqlMinAnnotatedFunction).getIrqlLevel() = lowerBound + or + not func instanceof IrqlMinAnnotatedFunction and + func.(IrqlAlwaysMinFunction).getIrqlLevel() = lowerBound + or + ( + not func instanceof IrqlMinAnnotatedFunction and + not func instanceof IrqlAlwaysMinFunction + ) and + func.(IrqlRequiresAnnotatedFunction).getIrqlLevel() = lowerBound + or + ( + not func instanceof IrqlMinAnnotatedFunction and + not func instanceof IrqlAlwaysMinFunction and + not func instanceof IrqlRequiresAnnotatedFunction + ) and + func instanceof PagedFunctionDeclaration and + lowerBound = 0 + ) + } + \ No newline at end of file diff --git a/src/drivers/libraries/IrqlDataFlow.qll b/src/drivers/libraries/IrqlDataFlow.qll index bf32d1bf..fe2950e7 100644 --- a/src/drivers/libraries/IrqlDataFlow.qll +++ b/src/drivers/libraries/IrqlDataFlow.qll @@ -9,22 +9,22 @@ import cpp import drivers.libraries.Irql import semmle.code.cpp.dataflow.new.DataFlow + /** * A data-flow configuration describing flow from a * KeRaiseIrqlCall to a KeLowerIrqlCall. */ -class IrqlRaiseLowerFlow extends DataFlow::Configuration { - IrqlRaiseLowerFlow() { this = "IrqlRaiseLowerFlow" } - - override predicate isSource(DataFlow::Node source) { source.asExpr() instanceof KeRaiseIrqlCall } - - override predicate isSink(DataFlow::Node sink) { - exists(KeLowerIrqlCall firf | - sink.asExpr() = firf.getArgument(firf.getTarget().(IrqlRestoreFunction).getIrqlIndex()) - ) - } +module IrqlRaiseLowerFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source.asExpr() instanceof KeRaiseIrqlCall } + + predicate isSink(DataFlow::Node sink) { + exists(KeLowerIrqlCall firf | + sink.asExpr() = firf.getArgument(firf.getTarget().(IrqlRestoreFunction).getIrqlIndex()) + ) + } } +module IrqlRaiseLowerFlow = DataFlow::Global; /** * A function that has at least one parameter annotated with "\_IRQL\_restores\_". */ diff --git a/src/drivers/libraries/IrqlDebug.qll b/src/drivers/libraries/IrqlDebug.qll index 609ccc9d..5249623f 100644 --- a/src/drivers/libraries/IrqlDebug.qll +++ b/src/drivers/libraries/IrqlDebug.qll @@ -4,7 +4,7 @@ import drivers.libraries.Irql /** * A debugging function used to print the rationale for why a given CFN has the IRQL value it does. */ -cached + string getIrqlDebugInfoAtCfn(ControlFlowNode cfn) { if cfn instanceof KeRaiseIrqlCall then result = "This is a KeRaiseIRQL call" diff --git a/src/drivers/libraries/Page.qll b/src/drivers/libraries/Page.qll index 5723d67b..5584e2d4 100644 --- a/src/drivers/libraries/Page.qll +++ b/src/drivers/libraries/Page.qll @@ -3,9 +3,9 @@ import cpp //Represents functions where a function has either PAGED_CODE or PAGED_CODE_LOCKED macro invocations -cached + class PagedFunc extends Function { - cached + PagedFunc() { exists(MacroInvocation mi | mi.getEnclosingFunction() = this and @@ -56,11 +56,11 @@ predicate isPagedSegSetWithMacroAbove(Function f) { } //Represents functions for whom code_seg() is set -cached + class FunctionWithPageReset extends Function { DefaultCodeSegPragma dcs; - cached + FunctionWithPageReset() { exists(CodeSegPragma csp, DefaultCodeSegPragma dcsp | this.getLocation().getStartLine() > csp.getLocation().getStartLine() and @@ -71,14 +71,14 @@ class FunctionWithPageReset extends Function { ) } - cached + DefaultCodeSegPragma getCodeSeg() { result = dcs } } //Represents functions for whom code_seg("PAGE") is set -cached + class FunctionWithPageSet extends Function { - cached + FunctionWithPageSet() { exists(CodeSegPragma csp | this.getLocation().getStartLine() > csp.getLocation().getStartLine() and @@ -93,9 +93,9 @@ class FunctionWithPageSet extends Function { } //Represents a paged section -cached + class PagedFunctionDeclaration extends Function { - cached + PagedFunctionDeclaration() { isPagedSegSetWithMacroAbove(this) or diff --git a/src/drivers/libraries/RoleTypes.qll b/src/drivers/libraries/RoleTypes.qll index 737e9c9f..4f8fb9ed 100644 --- a/src/drivers/libraries/RoleTypes.qll +++ b/src/drivers/libraries/RoleTypes.qll @@ -72,11 +72,9 @@ class RoleTypeAnnotatedTypedef extends TypedefType { /** * A function that is annotated to specify role type */ -cached class RoleTypeAnnotatedFunction extends Function { RoleTypeFunctionAnnotation roleTypeAnnotation; - cached RoleTypeAnnotatedFunction() { ( this.hasCLinkage() and @@ -96,10 +94,8 @@ class RoleTypeAnnotatedFunction extends Function { ) } - cached string getFuncRoleTypeAnnotation() { result = roleTypeAnnotation.getRoleTypeMacroName() } - cached RoleTypeFunctionAnnotation getRoleTypeAnnotation() { result = roleTypeAnnotation } } @@ -157,12 +153,10 @@ class DriverObjectFunctionAccess extends FunctionAccess { /** * Declared functions that are used as if they have a role type, wether or not they do */ -cached class ImplicitRoleTypeFunction extends Function { RoleTypeType rttExpected; FunctionAccess funcUse; - cached ImplicitRoleTypeFunction() { ( exists(FunctionCall fc, int n | fc.getArgument(n) instanceof FunctionAccess | @@ -180,13 +174,10 @@ class ImplicitRoleTypeFunction extends Function { this.hasCLinkage() } - cached string getExpectedRoleTypeString() { result = rttExpected.getName() } - cached RoleTypeType getExpectedRoleTypeType() { result = rttExpected } - cached string getActualRoleTypeString() { if not this instanceof RoleTypeFunction then result = "" @@ -194,19 +185,16 @@ class ImplicitRoleTypeFunction extends Function { } // TODO: add this back in - // cached // int getExpectedIrqlLevel() { // if rttExpected instanceof IrqlAnnotatedTypedef // then result = getAlloweableIrqlLevel(rttExpected) // else result = -1 // } - // cached // int getFoundIrqlLevel() { // if this instanceof IrqlRestrictsFunction // then result = getAllowableIrqlLevel(this) // else result = -1 // } - cached FunctionAccess getFunctionUse() { result = funcUse } } @@ -220,3 +208,144 @@ predicate roleTypeAssignment(AssignExpr ae) { ae.getRValue() instanceof AssignExpr and roleTypeAssignment(ae.getRValue().(AssignExpr)) } + +class ImplicitWdmRoutine extends Function { + ImplicitWdmRoutine(){ + this instanceof ImplicitRoleTypeFunction + } +} +/** A WDM DriverEntry callback routine. */ +class ImplicitWdmDriverEntry extends ImplicitWdmRoutine { + ImplicitWdmDriverEntry() { this.(ImplicitRoleTypeFunction).getExpectedRoleTypeString().matches("DRIVER_INITIALIZE") } + + string getExpectedMaxIrqlLevelString() { result = "PASSIVE_LEVEL" } + + string getExpectedMinIrqlLevelString() { result = "PASSIVE_LEVEL" } +} + +/** A WDM DrierStartIo callback routine */ +class ImplicitWdmDriverStartIo extends ImplicitWdmRoutine { + ImplicitWdmDriverStartIo() { this.(ImplicitRoleTypeFunction).getExpectedRoleTypeString().matches("DRIVER_STARTIO") } + + string getExpectedMaxIrqlLevelString() { result = "DISPATCH_LEVEL" } + + string getExpectedMinIrqlLevelString() { result = "DRIVER_STARTIO" } +} + +/** + * A WDM DriverUnload callback routine. + */ +class ImplicitWdmDriverUnload extends ImplicitWdmRoutine { + ImplicitWdmDriverUnload() { this.(ImplicitRoleTypeFunction).getExpectedRoleTypeString().matches("DRIVER_UNLOAD") } + + string getExpectedMaxIrqlLevelString() { result = "PASSIVE_LEVEL" } + + string getExpectedMinIrqlLevelString() { result = "PASSIVE_LEVEL" } +} + +// NOTE duplicate for backward compatibility with other query. Remove when other query is updated. +/** A WDM AddDevice callback routine. */ +class ImplicitWdmAddDevice extends ImplicitWdmRoutine { + ImplicitWdmAddDevice() { this.(ImplicitRoleTypeFunction).getExpectedRoleTypeString().matches("DRIVER_ADD_DEVICE") } +} + +/** + * A WDM DriverAddDevice callback routine. + */ +class ImplicitWdmDriverAddDevice extends ImplicitWdmRoutine { + ImplicitWdmDriverAddDevice() { this.(ImplicitRoleTypeFunction).getExpectedRoleTypeString().matches("DRIVER_ADD_DEVICE") } + + string getExpectedMaxIrqlLevelString() { result = "PASSIVE_LEVEL" } + + string getExpectedMinIrqlLevelString() { result = "PASSIVE_LEVEL" } +} + +/** + * A WDM DriverDispatch callback routine. + */ +class ImplicitWdmDriverDispatch extends ImplicitWdmRoutine { + ImplicitWdmDriverDispatch() { this.(ImplicitRoleTypeFunction).getExpectedRoleTypeString().matches("DRIVER_DISPATCH") } + + string getExpectedMaxIrqlLevelString() { result = "PASSIVE_LEVEL" } + + string getExpectedMinIrqlLevelString() { result = "PASSIVE_LEVEL" } +} + +/** + * A WDM IO completion routine. + */ +class ImplicitWdmDriverCompletionRoutine extends ImplicitWdmRoutine { + ImplicitWdmDriverCompletionRoutine() { this.(ImplicitRoleTypeFunction).getExpectedRoleTypeString().matches("IO_COMPLETION_ROUTINE") } + + string getExpectedMaxIrqlLevelString() { result = "DISPATCH_LEVEL" } + + string getExpectedMinIrqlLevelString() { result = "PASSIVE_LEVEL" } +} + +/** + * A WDM DriverCancel callback routine. + */ +class ImplicitWdmDriverCancel extends ImplicitWdmRoutine { + ImplicitWdmDriverCancel() { this.(ImplicitRoleTypeFunction).getExpectedRoleTypeString().matches("DRIVER_CANCEL") } + + string getExpectedMaxIrqlLevelString() { result = "DISPATCH_LEVEL" } + string getExpectedMinIrqlLevelString() { result = "DISPATCH_LEVEL" } + +} + +/** + * A WDM DriverDpcRoutine callback routine. + */ +class ImplicitWdmDriverDpcRoutine extends ImplicitWdmRoutine { + ImplicitWdmDriverDpcRoutine() { + this.(ImplicitRoleTypeFunction).getExpectedRoleTypeString().matches("IO_DPC_ROUTINE") + } + + string getExpectedMaxIrqlLevelString() { result = "DISPATCH_LEVEL" } + + string getExpectedMinIrqlLevelString() { result = "DISPATCH_LEVEL" } +} + +/** + * A WDM DriverDeferredRoutine callback routine. + */ +class ImplicitWdmDriverDeferredRoutine extends ImplicitWdmRoutine { + ImplicitWdmDriverDeferredRoutine() { this.(ImplicitRoleTypeFunction).getExpectedRoleTypeString().matches("KDEFERRED_ROUTINE") } + + string getExpectedMaxIrqlLevelString() { result = "DISPATCH_LEVEL" } + + string getExpectedMinIrqlLevelString() { result = "DISPATCH_LEVEL" } +} + +/** + * A WDM DriverServiceRoutine callback routine. + */ +class ImplicitWdmDriverServiceRoutine extends ImplicitWdmRoutine { + ImplicitWdmDriverServiceRoutine() { this.(ImplicitRoleTypeFunction).getExpectedRoleTypeString().matches("KSERVICE_ROUTINE") } + + string getExpectedMaxIrqlLevelString() { result = "DIRQL" } + + string getExpectedMinIrqlLevelString() { result = "DIRQL" } +} + +/** + * A WDM DriverPowerComplete callback routine. + */ +class ImplicitWdmDriverPowerComplete extends ImplicitWdmRoutine { + ImplicitWdmDriverPowerComplete() { this.(ImplicitRoleTypeFunction).getExpectedRoleTypeString().matches("REQUEST_POWER_COMPLETE") } + + string getExpectedMaxIrqlLevelString() { result = "DISPATCH_LEVEL" } + + string getExpectedMinIrqlLevelString() { result = "PASSIVE_LEVEL" } +} + +/** + * A WDM DriverWorkerThreadRoutine callback routine. + */ +class ImplicitWdmDriverWorkerThreadRoutine extends ImplicitWdmRoutine { + ImplicitWdmDriverWorkerThreadRoutine() { this.(ImplicitRoleTypeFunction).getExpectedRoleTypeString().matches("WORKER_THREAD_ROUTINE") } + + string getExpectedMaxIrqlLevelString() { result = "PASSIVE_LEVEL" } + + string getExpectedMinIrqlLevelString() { result = "PASSIVE_LEVEL" } +} \ No newline at end of file diff --git a/src/drivers/libraries/SAL.qll b/src/drivers/libraries/SAL.qll index 8c0d704c..bff715a4 100644 --- a/src/drivers/libraries/SAL.qll +++ b/src/drivers/libraries/SAL.qll @@ -121,6 +121,8 @@ class SALParameter extends Parameter { predicate isOut() { a.getMacroName().toLowerCase().matches("%\\_out%") } predicate isInOut() { a.getMacroName().toLowerCase().matches("%\\_inout%") } + + SALAnnotation getAnnotation() {result = a} } /////////////////////////////////////////////////////////////////////////////// diff --git a/src/drivers/libraries/Suppression.qll b/src/drivers/libraries/Suppression.qll index 34d9c084..a4223912 100644 --- a/src/drivers/libraries/Suppression.qll +++ b/src/drivers/libraries/Suppression.qll @@ -168,7 +168,7 @@ class SuppressPragma extends CASuppression { /** Finds the offset (in line count) to the closest non-pragma element after this suppression. */ pragma[nomagic] - cached + int getMinimumLocationOffset() { result = min(int i | @@ -281,7 +281,7 @@ class SuppressionPushPopSegment extends Location { } /** Returns a disable pragma within this push/pop segment. */ - cached + DisablePragma getADisablePragma() { result = any(DisablePragma p | this.isInPushPopSegment(p.getLocation())) } diff --git a/src/drivers/libraries/wfp.qll b/src/drivers/libraries/wfp.qll new file mode 100644 index 00000000..4210d236 --- /dev/null +++ b/src/drivers/libraries/wfp.qll @@ -0,0 +1,250 @@ +/** + * Provides classes for identifying and reasoning about Microsoft Windows Filtering Platform Callout + * (WFP) Annotation + * + * This version of this file has been updated to include fwpk.h as a valid WFP header. + * It will be removed if and when that change is upstreamed to the main CodeQL repo. + * + */ + + import cpp + + /** + * A WFP macro defined in `fwpk.h` or a similar header file. + */ +class WfpMacro extends Macro { + WfpMacro() { + this.getFile().getBaseName() = + [ + "fwpl.h", "Wfp_Annotations.h" + ] and + // Dialect for Windows 11 and above + this.getName().matches("\\_%\\_") + } + } + + pragma[noinline] + private predicate isTopLevelMacroAccess(MacroAccess ma) { not exists(ma.getParentInvocation()) } + + /** + * An invocation of a WFP macro (excluding invocations inside other macros). + */ + class WfpAnnotation extends MacroInvocation { + WfpAnnotation() { + this.getMacro() instanceof WfpMacro and + isTopLevelMacroAccess(this) + } + + /** Gets the `Declaration` annotated by `this`. */ + Declaration getDeclaration() { + annotatesAt(this, result.getADeclarationEntry(), _, _) and + not result instanceof Type // exclude typedefs + } + + Declaration getTypedefDeclarations() { + annotatesAt(this, result.getADeclarationEntry(), _, _) and + result instanceof Type // include + } + + /** Gets the `DeclarationEntry` annotated by `this`. */ + DeclarationEntry getDeclarationEntry() { + annotatesAt(this, result, _, _) and + not result instanceof TypeDeclarationEntry // exclude typedefs + } + } + +/** + * A Wfp macro indicating that the callout function is a stream layer function (inspection callout) + */ +class WfpStreamInspection extends WfpAnnotation { + WfpStreamInspection() { + this.getMacro().(WfpMacro).getName() = ["_Wfp_stream_inspection_classify_","_Wfp_stream_inspection_notify_"] + } + } + +/** + * A Wfp macro indicating that the callout function is a stream layer function (injection callout) + */ +class WfpStreamInjection extends WfpAnnotation { + WfpStreamInjection() { + this.getMacro().(WfpMacro).getName() = ["_Wfp_stream_injection_classify_"] + } + } + +/** + * A Wfp macro indicating that the callout function is a flow layer function (inspection callout) + */ +class WfpFlowInspection extends WfpAnnotation { + WfpFlowInspection() { + this.getMacro().(WfpMacro).getName() = ["_Wfp_flow_inspection_notify_", "_Wfp_flow_inspection_classify_"] + } + } + +/** + * A Wfp macro indicating that the callout function is a flow layer function (injection callout) + */ +class WfpFlowInjection extends WfpAnnotation { + WfpFlowInjection() { + this.getMacro().(WfpMacro).getName() = ["_Wfp_flow_injection_classify_"] + } +} + +/** + * A Wfp macro indicating that the callout function is a transport layer function + */ +class WfpTransportInjection extends WfpAnnotation { + WfpTransportInjection() { + this.getMacro().(WfpMacro).getName() = ["_Wfp_transport_injection_classify_","_Wfp_transport_injection_classify_inline_"] + } + } + + /** + * A Wfp macro indicating that the callout function is a transport layer function + */ +class WfpTransportInspection extends WfpAnnotation { + WfpTransportInspection() { + this.getMacro().(WfpMacro).getName() = ["_Wfp_Transport_inspection_classify_"] + } + } + + + /** + * A Wfp macro indicating that the callout function is a connect-redirect layer function + */ +class WfpConnectRedirect extends WfpAnnotation { + WfpConnectRedirect() { + this.getMacro().(WfpMacro).getName() = ["_Wfp_connect_redirect_classify_", "_Wfp_connect_redirect_inline_classify_"] + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Implementation details +/** + * Holds if `a` annotates the declaration entry `d` and + * its start position is the `idx`th position in `file` that holds a WFP element. + */ +private predicate annotatesAt(WfpAnnotation a, DeclarationEntry d, File file, int idx) { + annotatesAtPosition(a.(WfpElement).getStartPosition(), d, file, idx) + } + + /** + * Holds if `pos` is the `idx`th position in `file` that holds a Wfp element, + * which annotates the declaration entry `d` (by occurring before it without + * any other declaration entries in between). + */ + // For performance reasons, do not mention the annotation itself here, + // but compute with positions instead. This performs better on databases + // with many annotations at the same position. + private predicate annotatesAtPosition(WfpPosition pos, DeclarationEntry d, File file, int idx) { + pos = wfpRelevantPositionAt(file, idx) and + wfpAnnotationPos(pos) and + ( + // Base case: `pos` right before `d` + d.(WfpElement).getStartPosition() = wfpRelevantPositionAt(file, idx + 1) + or + // Recursive case: `pos` right before some annotation on `d` + annotatesAtPosition(_, d, file, idx + 1) + ) + } + +/** + * A Wfp element, that is, a Wfp annotation or a declaration entry + * that may have Wfp annotations. + */ +library class WfpElement extends Element { + WfpElement() { + containsWfpAnnotation(this.(DeclarationEntry).getFile()) or + this instanceof WfpAnnotation + } + + predicate hasStartPosition(File file, int line, int col) { + exists(Location loc | loc = this.getLocation() | + file = loc.getFile() and + line = loc.getStartLine() and + col = loc.getStartColumn() + ) + } + + predicate hasEndPosition(File file, int line, int col) { + exists(Location loc | + loc = this.(FunctionDeclarationEntry).getBlock().getLocation() + or + this = + any(VariableDeclarationEntry vde | + vde.isDefinition() and + loc = vde.getVariable().getInitializer().getLocation() + ) + | + file = loc.getFile() and + line = loc.getEndLine() and + col = loc.getEndColumn() + ) + } + + WfpPosition getStartPosition() { + exists(File file, int line, int col | + this.hasStartPosition(file, line, col) and + result = MkWfpPosition(file, line, col) + ) + } + } + + /** Holds if `file` contains a Wfp annotation. */ + pragma[noinline] + private predicate containsWfpAnnotation(File file) { any(WfpAnnotation a).getFile() = file } + + /** + * A source-file position of a `WfpElement`. Unlike location, this denotes a + * point in the file rather than a range. + */ + private newtype WfpPosition = + MkWfpPosition(File file, int line, int col) { + exists(WfpElement e | + e.hasStartPosition(file, line, col) + or + e.hasEndPosition(file, line, col) + ) + } + +/** Holds if `pos` is the start position of a SAL annotation. */ +pragma[noinline] +private predicate wfpAnnotationPos(WfpPosition pos) { + any(WfpAnnotation a).(WfpElement).getStartPosition() = pos +} + +/** + * Gets the `idx`th position in `file` that holds a Wfp element, + * ordering positions lexicographically by their start line and start column. + */ +private WfpPosition wfpRelevantPositionAt(File file, int idx) { + result = + rank[idx](WfpPosition pos, int line, int col | + pos = MkWfpPosition(file, line, col) + | + pos order by line, col + ) +} + +class ActionTypeExpr extends AssignExpr{ + ActionTypeExpr(){ + this.getLValue().getType().getName().matches(["FWP_ACTION_TYPE"]) and + this.getRValue().getFullyConverted().getType().getName().matches(["FWP_ACTION_TYPE"]) + } +} + +class RedirectHandleCreateFunctionCall extends MetricFunction { + RedirectHandleCreateFunctionCall() { + this.getQualifiedName().toString().matches(["FwpsRedirectHandleCreate%"]) + } +} + +predicate isBlockExpression(ActionTypeExpr exp) { + exp.getRValue().getFullyConverted().getFullyConverted().getValue().matches(["FWP_ACTION_BLOCK", "4097"]) +} + +class WriteActionFlagSet extends AssignExpr { + WriteActionFlagSet(){ + this.getLValue().getType().getName().matches(["UINT32"]) and + this.getRValue().getFullyConverted().getValue().toInt() = 1 //FWPS_RIGHT_ACTION_WRITE + } +} \ No newline at end of file diff --git a/src/drivers/ndis/libraries/NdisDrivers.qll b/src/drivers/ndis/libraries/NdisDrivers.qll index a0a7ce27..8220edb8 100644 --- a/src/drivers/ndis/libraries/NdisDrivers.qll +++ b/src/drivers/ndis/libraries/NdisDrivers.qll @@ -15,7 +15,6 @@ import drivers.libraries.SAL * an assignment in DriverEntry that assigns the function to * the dispatch table. */ -cached class NdisDispatchRoutine extends NdisCallbackRoutine { /** * The OID type covered by this dispatch routine. @@ -30,7 +29,6 @@ class NdisDispatchRoutine extends NdisCallbackRoutine { * This characteristic predicate thus looks for assignments of this form * where the right-side value is a function with the NDIS_DISPATCH typedef. */ - cached NdisDispatchRoutine() { callbackType.getName().matches("NDIS_DISPATCH") and exists( @@ -47,11 +45,11 @@ class NdisDispatchRoutine extends NdisCallbackRoutine { } /** Gets the OID type this dispatch routine handles, as a number. */ - cached + Literal getDispatchType() { result = dispatchType } /** Gets the DriverEntry this dispatch routine was assigned in. */ - cached + NdisDriverEntry getDriverEntry() { result = driverEntry } } @@ -70,7 +68,7 @@ class NdisCallbackRoutineAssignment extends AssignExpr { NdisCallbackRoutineAssignment() { isNdisCallbackRoutineAssignment(this) } /** Gets the callback routine that this dispatch routine assignment is targeting. */ - cached + NdisCallbackRoutine getTarget() { if exists(FunctionAccess fa | diff --git a/src/drivers/test/TestTemplates/ApplicationForDriversTestTemplate/ApplicationForDriversTestTemplate.sln b/src/drivers/test/TestTemplates/ApplicationForDriversTestTemplate/ApplicationForDriversTestTemplate.sln new file mode 100644 index 00000000..8ccbce38 --- /dev/null +++ b/src/drivers/test/TestTemplates/ApplicationForDriversTestTemplate/ApplicationForDriversTestTemplate.sln @@ -0,0 +1,40 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.12.35707.178 d17.12 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ApplicationForDriversTestTemplate", "ApplicationForDriversTestTemplate.vcxproj", "{7EB8FC7A-AD1E-4560-8513-A986DDE4F05F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM = Debug|ARM + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|ARM = Release|ARM + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7EB8FC7A-AD1E-4560-8513-A986DDE4F05F}.Debug|ARM.ActiveCfg = Debug|ARM + {7EB8FC7A-AD1E-4560-8513-A986DDE4F05F}.Debug|ARM.Build.0 = Debug|ARM + {7EB8FC7A-AD1E-4560-8513-A986DDE4F05F}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {7EB8FC7A-AD1E-4560-8513-A986DDE4F05F}.Debug|ARM64.Build.0 = Debug|ARM64 + {7EB8FC7A-AD1E-4560-8513-A986DDE4F05F}.Debug|x64.ActiveCfg = Debug|x64 + {7EB8FC7A-AD1E-4560-8513-A986DDE4F05F}.Debug|x64.Build.0 = Debug|x64 + {7EB8FC7A-AD1E-4560-8513-A986DDE4F05F}.Debug|x86.ActiveCfg = Debug|Win32 + {7EB8FC7A-AD1E-4560-8513-A986DDE4F05F}.Debug|x86.Build.0 = Debug|Win32 + {7EB8FC7A-AD1E-4560-8513-A986DDE4F05F}.Release|ARM.ActiveCfg = Release|ARM + {7EB8FC7A-AD1E-4560-8513-A986DDE4F05F}.Release|ARM.Build.0 = Release|ARM + {7EB8FC7A-AD1E-4560-8513-A986DDE4F05F}.Release|ARM64.ActiveCfg = Release|ARM64 + {7EB8FC7A-AD1E-4560-8513-A986DDE4F05F}.Release|ARM64.Build.0 = Release|ARM64 + {7EB8FC7A-AD1E-4560-8513-A986DDE4F05F}.Release|x64.ActiveCfg = Release|x64 + {7EB8FC7A-AD1E-4560-8513-A986DDE4F05F}.Release|x64.Build.0 = Release|x64 + {7EB8FC7A-AD1E-4560-8513-A986DDE4F05F}.Release|x86.ActiveCfg = Release|Win32 + {7EB8FC7A-AD1E-4560-8513-A986DDE4F05F}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/src/drivers/test/TestTemplates/ApplicationForDriversTestTemplate/ApplicationForDriversTestTemplate.vcxproj b/src/drivers/test/TestTemplates/ApplicationForDriversTestTemplate/ApplicationForDriversTestTemplate.vcxproj new file mode 100644 index 00000000..a2ce289c --- /dev/null +++ b/src/drivers/test/TestTemplates/ApplicationForDriversTestTemplate/ApplicationForDriversTestTemplate.vcxproj @@ -0,0 +1,192 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + Debug + ARM + + + Release + ARM + + + Debug + ARM64 + + + Release + ARM64 + + + + + + + {7EB8FC7A-AD1E-4560-8513-A986DDE4F05F} + {504102d4-2172-473c-8adf-cd96e308f257} + v4.5 + 12.0 + Debug + Win32 + ApplicationForDriversTestTemplate + + + + Windows10 + true + WindowsApplicationForDrivers10.0 + Application + Universal + Unicode + + + Windows10 + false + WindowsApplicationForDrivers10.0 + Application + Universal + Unicode + + + Windows10 + true + WindowsApplicationForDrivers10.0 + Application + Universal + Unicode + + + Windows10 + false + WindowsApplicationForDrivers10.0 + Application + Universal + Unicode + + + Windows10 + true + WindowsApplicationForDrivers10.0 + Application + Universal + Unicode + + + Windows10 + false + WindowsApplicationForDrivers10.0 + Application + Universal + Unicode + + + Windows10 + true + WindowsApplicationForDrivers10.0 + Application + Universal + Unicode + + + Windows10 + false + WindowsApplicationForDrivers10.0 + Application + Universal + Unicode + + + + + + + + + + + _DEBUG;WINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP;WINAPI_PARTITION_DESKTOP=1;WINAPI_PARTITION_SYSTEM=1;WINAPI_PARTITION_APP=1;WINAPI_PARTITION_PC_APP=1;%(PreprocessorDefinitions) + MultiThreadedDebugDLL + + + %(AdditionalDependencies);onecoreuap.lib + + + + + WINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP;WINAPI_PARTITION_DESKTOP=1;WINAPI_PARTITION_SYSTEM=1;WINAPI_PARTITION_APP=1;WINAPI_PARTITION_PC_APP=1;%(PreprocessorDefinitions) + + + %(AdditionalDependencies);onecoreuap.lib + + + + + _DEBUG;WINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP;WINAPI_PARTITION_DESKTOP=1;WINAPI_PARTITION_SYSTEM=1;WINAPI_PARTITION_APP=1;WINAPI_PARTITION_PC_APP=1;%(PreprocessorDefinitions) + MultiThreadedDebugDLL + + + %(AdditionalDependencies);onecoreuap.lib + + + + + WINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP;WINAPI_PARTITION_DESKTOP=1;WINAPI_PARTITION_SYSTEM=1;WINAPI_PARTITION_APP=1;WINAPI_PARTITION_PC_APP=1;%(PreprocessorDefinitions) + + + %(AdditionalDependencies);onecoreuap.lib + + + + + _DEBUG;WINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP;WINAPI_PARTITION_DESKTOP=1;WINAPI_PARTITION_SYSTEM=1;WINAPI_PARTITION_APP=1;WINAPI_PARTITION_PC_APP=1;%(PreprocessorDefinitions) + MultiThreadedDebugDLL + + + %(AdditionalDependencies);onecoreuap.lib + + + + + WINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP;WINAPI_PARTITION_DESKTOP=1;WINAPI_PARTITION_SYSTEM=1;WINAPI_PARTITION_APP=1;WINAPI_PARTITION_PC_APP=1;%(PreprocessorDefinitions) + + + %(AdditionalDependencies);onecoreuap.lib + + + + + _DEBUG;WINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP;WINAPI_PARTITION_DESKTOP=1;WINAPI_PARTITION_SYSTEM=1;WINAPI_PARTITION_APP=1;WINAPI_PARTITION_PC_APP=1;%(PreprocessorDefinitions) + MultiThreadedDebugDLL + + + %(AdditionalDependencies);onecoreuap.lib + + + + + WINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP;WINAPI_PARTITION_DESKTOP=1;WINAPI_PARTITION_SYSTEM=1;WINAPI_PARTITION_APP=1;WINAPI_PARTITION_PC_APP=1;%(PreprocessorDefinitions) + + + %(AdditionalDependencies);onecoreuap.lib + + + + + + \ No newline at end of file diff --git a/src/drivers/test/TestTemplates/ApplicationForDriversTestTemplate/ApplicationForDriversTestTemplate.vcxproj.filters b/src/drivers/test/TestTemplates/ApplicationForDriversTestTemplate/ApplicationForDriversTestTemplate.vcxproj.filters new file mode 100644 index 00000000..395e675b --- /dev/null +++ b/src/drivers/test/TestTemplates/ApplicationForDriversTestTemplate/ApplicationForDriversTestTemplate.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/src/drivers/test/TestTemplates/ApplicationForDriversTestTemplate/ApplicationForDriversTestTemplate.vcxproj.user b/src/drivers/test/TestTemplates/ApplicationForDriversTestTemplate/ApplicationForDriversTestTemplate.vcxproj.user new file mode 100644 index 00000000..88a55094 --- /dev/null +++ b/src/drivers/test/TestTemplates/ApplicationForDriversTestTemplate/ApplicationForDriversTestTemplate.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/drivers/test/TestTemplates/ApplicationForDriversTestTemplate/Source.cpp b/src/drivers/test/TestTemplates/ApplicationForDriversTestTemplate/Source.cpp new file mode 100644 index 00000000..16daf4c6 --- /dev/null +++ b/src/drivers/test/TestTemplates/ApplicationForDriversTestTemplate/Source.cpp @@ -0,0 +1,23 @@ +#include +#include +#include +#include +#include +#include + + +#include "driver_snippet.c" + +int __cdecl +main( + _In_ ULONG argc, + _In_reads_(argc) PCHAR argv[] +) +{ + UNREFERENCED_PARAMETER(argc); + UNREFERENCED_PARAMETER(argv); + printf("Hello World!\n"); + return 0; + + +} \ No newline at end of file diff --git a/src/drivers/test/TestTemplates/ApplicationForDriversTestTemplate/driver_snippet.c b/src/drivers/test/TestTemplates/ApplicationForDriversTestTemplate/driver_snippet.c new file mode 100644 index 00000000..e69de29b diff --git a/src/drivers/test/CppKMDFTestTemplate/CppKMDFTestTemplate.inf b/src/drivers/test/TestTemplates/CppKMDFTestTemplate/CppKMDFTestTemplate.inf similarity index 100% rename from src/drivers/test/CppKMDFTestTemplate/CppKMDFTestTemplate.inf rename to src/drivers/test/TestTemplates/CppKMDFTestTemplate/CppKMDFTestTemplate.inf diff --git a/src/drivers/test/CppKMDFTestTemplate/CppKMDFTestTemplate.sln b/src/drivers/test/TestTemplates/CppKMDFTestTemplate/CppKMDFTestTemplate.sln similarity index 100% rename from src/drivers/test/CppKMDFTestTemplate/CppKMDFTestTemplate.sln rename to src/drivers/test/TestTemplates/CppKMDFTestTemplate/CppKMDFTestTemplate.sln diff --git a/src/drivers/test/CppKMDFTestTemplate/CppKMDFTestTemplate.vcxproj b/src/drivers/test/TestTemplates/CppKMDFTestTemplate/CppKMDFTestTemplate.vcxproj similarity index 100% rename from src/drivers/test/CppKMDFTestTemplate/CppKMDFTestTemplate.vcxproj rename to src/drivers/test/TestTemplates/CppKMDFTestTemplate/CppKMDFTestTemplate.vcxproj diff --git a/src/drivers/test/CppKMDFTestTemplate/CppKMDFTestTemplate.vcxproj.filters b/src/drivers/test/TestTemplates/CppKMDFTestTemplate/CppKMDFTestTemplate.vcxproj.filters similarity index 100% rename from src/drivers/test/CppKMDFTestTemplate/CppKMDFTestTemplate.vcxproj.filters rename to src/drivers/test/TestTemplates/CppKMDFTestTemplate/CppKMDFTestTemplate.vcxproj.filters diff --git a/src/drivers/test/CppKMDFTestTemplate/Device.c b/src/drivers/test/TestTemplates/CppKMDFTestTemplate/Device.c similarity index 100% rename from src/drivers/test/CppKMDFTestTemplate/Device.c rename to src/drivers/test/TestTemplates/CppKMDFTestTemplate/Device.c diff --git a/src/drivers/test/CppKMDFTestTemplate/Device.h b/src/drivers/test/TestTemplates/CppKMDFTestTemplate/Device.h similarity index 100% rename from src/drivers/test/CppKMDFTestTemplate/Device.h rename to src/drivers/test/TestTemplates/CppKMDFTestTemplate/Device.h diff --git a/src/drivers/test/CppKMDFTestTemplate/Driver.cpp b/src/drivers/test/TestTemplates/CppKMDFTestTemplate/Driver.cpp similarity index 100% rename from src/drivers/test/CppKMDFTestTemplate/Driver.cpp rename to src/drivers/test/TestTemplates/CppKMDFTestTemplate/Driver.cpp diff --git a/src/drivers/test/CppKMDFTestTemplate/Driver.h b/src/drivers/test/TestTemplates/CppKMDFTestTemplate/Driver.h similarity index 100% rename from src/drivers/test/CppKMDFTestTemplate/Driver.h rename to src/drivers/test/TestTemplates/CppKMDFTestTemplate/Driver.h diff --git a/src/drivers/test/CppKMDFTestTemplate/Public.h b/src/drivers/test/TestTemplates/CppKMDFTestTemplate/Public.h similarity index 100% rename from src/drivers/test/CppKMDFTestTemplate/Public.h rename to src/drivers/test/TestTemplates/CppKMDFTestTemplate/Public.h diff --git a/src/drivers/test/CppKMDFTestTemplate/Queue.c b/src/drivers/test/TestTemplates/CppKMDFTestTemplate/Queue.c similarity index 100% rename from src/drivers/test/CppKMDFTestTemplate/Queue.c rename to src/drivers/test/TestTemplates/CppKMDFTestTemplate/Queue.c diff --git a/src/drivers/test/CppKMDFTestTemplate/Queue.h b/src/drivers/test/TestTemplates/CppKMDFTestTemplate/Queue.h similarity index 100% rename from src/drivers/test/CppKMDFTestTemplate/Queue.h rename to src/drivers/test/TestTemplates/CppKMDFTestTemplate/Queue.h diff --git a/src/drivers/test/CppKMDFTestTemplate/ReadMe.txt b/src/drivers/test/TestTemplates/CppKMDFTestTemplate/ReadMe.txt similarity index 100% rename from src/drivers/test/CppKMDFTestTemplate/ReadMe.txt rename to src/drivers/test/TestTemplates/CppKMDFTestTemplate/ReadMe.txt diff --git a/src/drivers/test/CppKMDFTestTemplate/Trace.h b/src/drivers/test/TestTemplates/CppKMDFTestTemplate/Trace.h similarity index 100% rename from src/drivers/test/CppKMDFTestTemplate/Trace.h rename to src/drivers/test/TestTemplates/CppKMDFTestTemplate/Trace.h diff --git a/src/drivers/test/CppKMDFTestTemplate/driver/driver_snippet.cpp b/src/drivers/test/TestTemplates/CppKMDFTestTemplate/driver/driver_snippet.cpp similarity index 100% rename from src/drivers/test/CppKMDFTestTemplate/driver/driver_snippet.cpp rename to src/drivers/test/TestTemplates/CppKMDFTestTemplate/driver/driver_snippet.cpp diff --git a/src/drivers/test/KMDFTestTemplate/Device.c b/src/drivers/test/TestTemplates/KMDFTestTemplate/Device.c similarity index 100% rename from src/drivers/test/KMDFTestTemplate/Device.c rename to src/drivers/test/TestTemplates/KMDFTestTemplate/Device.c diff --git a/src/drivers/test/KMDFTestTemplate/Device.h b/src/drivers/test/TestTemplates/KMDFTestTemplate/Device.h similarity index 100% rename from src/drivers/test/KMDFTestTemplate/Device.h rename to src/drivers/test/TestTemplates/KMDFTestTemplate/Device.h diff --git a/src/drivers/test/KMDFTestTemplate/Driver.c b/src/drivers/test/TestTemplates/KMDFTestTemplate/Driver.c similarity index 100% rename from src/drivers/test/KMDFTestTemplate/Driver.c rename to src/drivers/test/TestTemplates/KMDFTestTemplate/Driver.c diff --git a/src/drivers/test/KMDFTestTemplate/Driver.h b/src/drivers/test/TestTemplates/KMDFTestTemplate/Driver.h similarity index 100% rename from src/drivers/test/KMDFTestTemplate/Driver.h rename to src/drivers/test/TestTemplates/KMDFTestTemplate/Driver.h diff --git a/src/drivers/test/KMDFTestTemplate/KMDFTestTemplate.inf b/src/drivers/test/TestTemplates/KMDFTestTemplate/KMDFTestTemplate.inf similarity index 100% rename from src/drivers/test/KMDFTestTemplate/KMDFTestTemplate.inf rename to src/drivers/test/TestTemplates/KMDFTestTemplate/KMDFTestTemplate.inf diff --git a/src/drivers/test/KMDFTestTemplate/KMDFTestTemplate.sln b/src/drivers/test/TestTemplates/KMDFTestTemplate/KMDFTestTemplate.sln similarity index 100% rename from src/drivers/test/KMDFTestTemplate/KMDFTestTemplate.sln rename to src/drivers/test/TestTemplates/KMDFTestTemplate/KMDFTestTemplate.sln diff --git a/src/drivers/test/KMDFTestTemplate/KMDFTestTemplate.vcxproj b/src/drivers/test/TestTemplates/KMDFTestTemplate/KMDFTestTemplate.vcxproj similarity index 100% rename from src/drivers/test/KMDFTestTemplate/KMDFTestTemplate.vcxproj rename to src/drivers/test/TestTemplates/KMDFTestTemplate/KMDFTestTemplate.vcxproj diff --git a/src/drivers/test/KMDFTestTemplate/KMDFTestTemplate.vcxproj.filters b/src/drivers/test/TestTemplates/KMDFTestTemplate/KMDFTestTemplate.vcxproj.filters similarity index 100% rename from src/drivers/test/KMDFTestTemplate/KMDFTestTemplate.vcxproj.filters rename to src/drivers/test/TestTemplates/KMDFTestTemplate/KMDFTestTemplate.vcxproj.filters diff --git a/src/drivers/test/KMDFTestTemplate/Public.h b/src/drivers/test/TestTemplates/KMDFTestTemplate/Public.h similarity index 100% rename from src/drivers/test/KMDFTestTemplate/Public.h rename to src/drivers/test/TestTemplates/KMDFTestTemplate/Public.h diff --git a/src/drivers/test/KMDFTestTemplate/Queue.c b/src/drivers/test/TestTemplates/KMDFTestTemplate/Queue.c similarity index 100% rename from src/drivers/test/KMDFTestTemplate/Queue.c rename to src/drivers/test/TestTemplates/KMDFTestTemplate/Queue.c diff --git a/src/drivers/test/KMDFTestTemplate/Queue.h b/src/drivers/test/TestTemplates/KMDFTestTemplate/Queue.h similarity index 100% rename from src/drivers/test/KMDFTestTemplate/Queue.h rename to src/drivers/test/TestTemplates/KMDFTestTemplate/Queue.h diff --git a/src/drivers/test/KMDFTestTemplate/ReadMe.txt b/src/drivers/test/TestTemplates/KMDFTestTemplate/ReadMe.txt similarity index 100% rename from src/drivers/test/KMDFTestTemplate/ReadMe.txt rename to src/drivers/test/TestTemplates/KMDFTestTemplate/ReadMe.txt diff --git a/src/drivers/test/KMDFTestTemplate/Trace.h b/src/drivers/test/TestTemplates/KMDFTestTemplate/Trace.h similarity index 100% rename from src/drivers/test/KMDFTestTemplate/Trace.h rename to src/drivers/test/TestTemplates/KMDFTestTemplate/Trace.h diff --git a/src/drivers/test/KMDFTestTemplate/driver/driver_snippet.c b/src/drivers/test/TestTemplates/KMDFTestTemplate/driver/driver_snippet.c similarity index 100% rename from src/drivers/test/KMDFTestTemplate/driver/driver_snippet.c rename to src/drivers/test/TestTemplates/KMDFTestTemplate/driver/driver_snippet.c diff --git a/src/drivers/general/queries/QueryTemplate/QueryTemplate.qhelp b/src/drivers/test/TestTemplates/QueryTemplate/QueryTemplate.qhelp similarity index 100% rename from src/drivers/general/queries/QueryTemplate/QueryTemplate.qhelp rename to src/drivers/test/TestTemplates/QueryTemplate/QueryTemplate.qhelp diff --git a/src/drivers/general/queries/QueryTemplate/QueryTemplate.ql b/src/drivers/test/TestTemplates/QueryTemplate/QueryTemplate.ql similarity index 100% rename from src/drivers/general/queries/QueryTemplate/QueryTemplate.ql rename to src/drivers/test/TestTemplates/QueryTemplate/QueryTemplate.ql diff --git a/src/drivers/general/queries/QueryTemplate/QueryTemplate.sarif b/src/drivers/test/TestTemplates/QueryTemplate/QueryTemplate.sarif similarity index 100% rename from src/drivers/general/queries/QueryTemplate/QueryTemplate.sarif rename to src/drivers/test/TestTemplates/QueryTemplate/QueryTemplate.sarif diff --git a/src/drivers/general/queries/QueryTemplate/driver_snippet.c b/src/drivers/test/TestTemplates/QueryTemplate/driver_snippet.c similarity index 100% rename from src/drivers/general/queries/QueryTemplate/driver_snippet.c rename to src/drivers/test/TestTemplates/QueryTemplate/driver_snippet.c diff --git a/src/drivers/test/WDMTestTemplate/README.md b/src/drivers/test/TestTemplates/WDMTestTemplate/README.md similarity index 100% rename from src/drivers/test/WDMTestTemplate/README.md rename to src/drivers/test/TestTemplates/WDMTestTemplate/README.md diff --git a/src/drivers/test/WDMTestTemplate/WDMTestTemplate.sln b/src/drivers/test/TestTemplates/WDMTestTemplate/WDMTestTemplate.sln similarity index 69% rename from src/drivers/test/WDMTestTemplate/WDMTestTemplate.sln rename to src/drivers/test/TestTemplates/WDMTestTemplate/WDMTestTemplate.sln index b204d010..22ae6e56 100644 --- a/src/drivers/test/WDMTestTemplate/WDMTestTemplate.sln +++ b/src/drivers/test/TestTemplates/WDMTestTemplate/WDMTestTemplate.sln @@ -1,31 +1,38 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.7.34302.85 -MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fail_driver1", "driver\fail_driver1.vcxproj", "{7F88D5C5-F05F-4817-89F5-C811053277A0}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Debug|x64 = Debug|x64 - Release|Win32 = Release|Win32 - Release|x64 = Release|x64 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {7F88D5C5-F05F-4817-89F5-C811053277A0}.Debug|Win32.ActiveCfg = Debug|Win32 - {7F88D5C5-F05F-4817-89F5-C811053277A0}.Debug|Win32.Build.0 = Debug|Win32 - {7F88D5C5-F05F-4817-89F5-C811053277A0}.Debug|x64.ActiveCfg = Debug|x64 - {7F88D5C5-F05F-4817-89F5-C811053277A0}.Debug|x64.Build.0 = Debug|x64 - {7F88D5C5-F05F-4817-89F5-C811053277A0}.Release|Win32.ActiveCfg = Release|Win32 - {7F88D5C5-F05F-4817-89F5-C811053277A0}.Release|Win32.Build.0 = Release|Win32 - {7F88D5C5-F05F-4817-89F5-C811053277A0}.Release|x64.ActiveCfg = Release|x64 - {7F88D5C5-F05F-4817-89F5-C811053277A0}.Release|x64.Build.0 = Release|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {B5DD9EDC-06C8-4BC7-BEC8-179828286FF8} - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.7.34302.85 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fail_driver1", "driver\fail_driver1.vcxproj", "{7F88D5C5-F05F-4817-89F5-C811053277A0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM64 = Debug|ARM64 + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|ARM64 = Release|ARM64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7F88D5C5-F05F-4817-89F5-C811053277A0}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {7F88D5C5-F05F-4817-89F5-C811053277A0}.Debug|ARM64.Build.0 = Debug|ARM64 + {7F88D5C5-F05F-4817-89F5-C811053277A0}.Debug|ARM64.Deploy.0 = Debug|ARM64 + {7F88D5C5-F05F-4817-89F5-C811053277A0}.Debug|Win32.ActiveCfg = Debug|x64 + {7F88D5C5-F05F-4817-89F5-C811053277A0}.Debug|Win32.Build.0 = Debug|x64 + {7F88D5C5-F05F-4817-89F5-C811053277A0}.Debug|x64.ActiveCfg = Debug|x64 + {7F88D5C5-F05F-4817-89F5-C811053277A0}.Debug|x64.Build.0 = Debug|x64 + {7F88D5C5-F05F-4817-89F5-C811053277A0}.Release|ARM64.ActiveCfg = Release|ARM64 + {7F88D5C5-F05F-4817-89F5-C811053277A0}.Release|ARM64.Build.0 = Release|ARM64 + {7F88D5C5-F05F-4817-89F5-C811053277A0}.Release|ARM64.Deploy.0 = Release|ARM64 + {7F88D5C5-F05F-4817-89F5-C811053277A0}.Release|Win32.ActiveCfg = Release|x64 + {7F88D5C5-F05F-4817-89F5-C811053277A0}.Release|x64.ActiveCfg = Release|x64 + {7F88D5C5-F05F-4817-89F5-C811053277A0}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {B5DD9EDC-06C8-4BC7-BEC8-179828286FF8} + EndGlobalSection +EndGlobal diff --git a/src/drivers/test/WDMTestTemplate/driver/driver_snippet.c b/src/drivers/test/TestTemplates/WDMTestTemplate/driver/driver_snippet.c similarity index 100% rename from src/drivers/test/WDMTestTemplate/driver/driver_snippet.c rename to src/drivers/test/TestTemplates/WDMTestTemplate/driver/driver_snippet.c diff --git a/src/drivers/test/WDMTestTemplate/driver/fail_driver1.c b/src/drivers/test/TestTemplates/WDMTestTemplate/driver/fail_driver1.c similarity index 96% rename from src/drivers/test/WDMTestTemplate/driver/fail_driver1.c rename to src/drivers/test/TestTemplates/WDMTestTemplate/driver/fail_driver1.c index 6e606fed..52b455da 100644 --- a/src/drivers/test/WDMTestTemplate/driver/fail_driver1.c +++ b/src/drivers/test/TestTemplates/WDMTestTemplate/driver/fail_driver1.c @@ -190,12 +190,25 @@ DispatchCancel ( PIRP Irp ) { - //Doesn't do anything UNREFERENCED_PARAMETER(DeviceObject); - UNREFERENCED_PARAMETER(Irp); + UNREFERENCED_PARAMETER(Irp); return STATUS_SUCCESS; } +_Use_decl_annotations_ +VOID +DriverCancel( + struct _DEVICE_OBJECT* DeviceObject, + struct _IRP* Irp +) +{ + +#ifdef IRQL_CANCEL_CHECK + IoReleaseCancelSpinLock(PASSIVE_LEVEL); // This is a failing case for IRQL check. C28144 +#else + IoReleaseCancelSpinLock(Irp->CancelIrql); +#endif +} _Use_decl_annotations_ NTSTATUS diff --git a/src/drivers/test/WDMTestTemplate/driver/fail_driver1.h b/src/drivers/test/TestTemplates/WDMTestTemplate/driver/fail_driver1.h similarity index 97% rename from src/drivers/test/WDMTestTemplate/driver/fail_driver1.h rename to src/drivers/test/TestTemplates/WDMTestTemplate/driver/fail_driver1.h index 512bee92..8c889304 100644 --- a/src/drivers/test/WDMTestTemplate/driver/fail_driver1.h +++ b/src/drivers/test/TestTemplates/WDMTestTemplate/driver/fail_driver1.h @@ -74,3 +74,5 @@ KSERVICE_ROUTINE InterruptServiceRoutine; IO_DPC_ROUTINE DpcForIsrRoutine; DRIVER_UNLOAD DriverUnload; + +DRIVER_CANCEL DriverCancel; \ No newline at end of file diff --git a/src/drivers/test/WDMTestTemplate/driver/fail_driver1.vcxproj b/src/drivers/test/TestTemplates/WDMTestTemplate/driver/fail_driver1.vcxproj similarity index 89% rename from src/drivers/test/WDMTestTemplate/driver/fail_driver1.vcxproj rename to src/drivers/test/TestTemplates/WDMTestTemplate/driver/fail_driver1.vcxproj index 1d6f2dce..91c859c8 100644 --- a/src/drivers/test/WDMTestTemplate/driver/fail_driver1.vcxproj +++ b/src/drivers/test/TestTemplates/WDMTestTemplate/driver/fail_driver1.vcxproj @@ -1,169 +1,171 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - {7F88D5C5-F05F-4817-89F5-C811053277A0} - $(MSBuildProjectName) - false - Debug - Win32 - {5B7623CB-C6FA-4BD9-B152-49D2AF3AFFF9} - - - - Windows10 - False - Desktop - WDM - WindowsKernelModeDriver10.0 - Driver - - - Windows10 - True - Desktop - WDM - WindowsKernelModeDriver10.0 - Driver - - - Windows10 - False - Desktop - WDM - WindowsKernelModeDriver10.0 - Driver - - - Windows10 - True - Desktop - WDM - WindowsKernelModeDriver10.0 - Driver - - - - $(IntDir) - - - - - - - - - - - - - - - - fail_driver1 - - - fail_driver1 - - - fail_driver1 - - - fail_driver1 - - - - false - Level4 - - - - - false - Level4 - - - - - false - Level4 - - - - - false - Level4 - - - - - - - - - - USE_NTIFS=$(UseNTIFS);%(PreprocessorDefinitions) - - - sha256 - - - - - - - USE_NTIFS=$(UseNTIFS);%(PreprocessorDefinitions) - - - sha256 - - - - - - - - - sha256 - - - - - - - - - sha256 - - - - - - - - - - - - - - - + + + + + Debug + ARM64 + + + Debug + x64 + + + Release + ARM64 + + + Release + x64 + + + + {7F88D5C5-F05F-4817-89F5-C811053277A0} + $(MSBuildProjectName) + false + Debug + Win32 + {5B7623CB-C6FA-4BD9-B152-49D2AF3AFFF9} + + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + fail_driver1 + + + fail_driver1 + + + fail_driver1 + + + fail_driver1 + + + + false + Level4 + + + + + false + Level4 + + + + + false + Level4 + + + + + false + Level4 + + + + + + + + + + USE_NTIFS=$(UseNTIFS);%(PreprocessorDefinitions) + + + sha256 + + + + + + + USE_NTIFS=$(UseNTIFS);%(PreprocessorDefinitions) + + + sha256 + + + + + + + USE_NTIFS=$(UseNTIFS);%(PreprocessorDefinitions) + + + sha256 + + + + + + + USE_NTIFS=$(UseNTIFS);%(PreprocessorDefinitions) + + + sha256 + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/drivers/test/WDMTestTemplate/driver/fail_driver1.vcxproj.Filters b/src/drivers/test/TestTemplates/WDMTestTemplate/driver/fail_driver1.vcxproj.Filters similarity index 100% rename from src/drivers/test/WDMTestTemplate/driver/fail_driver1.vcxproj.Filters rename to src/drivers/test/TestTemplates/WDMTestTemplate/driver/fail_driver1.vcxproj.Filters diff --git a/src/drivers/test/WDMTestTemplate/driver/fail_driver1.vcxproj.user b/src/drivers/test/TestTemplates/WDMTestTemplate/driver/fail_driver1.vcxproj.user similarity index 97% rename from src/drivers/test/WDMTestTemplate/driver/fail_driver1.vcxproj.user rename to src/drivers/test/TestTemplates/WDMTestTemplate/driver/fail_driver1.vcxproj.user index 429333de..966b4ffb 100644 --- a/src/drivers/test/WDMTestTemplate/driver/fail_driver1.vcxproj.user +++ b/src/drivers/test/TestTemplates/WDMTestTemplate/driver/fail_driver1.vcxproj.user @@ -1,6 +1,6 @@ - - - - true - + + + + true + \ No newline at end of file diff --git a/src/drivers/test/build_create_analyze_test.py b/src/drivers/test/build_create_analyze_test.py index 8fd025d2..28c89278 100644 --- a/src/drivers/test/build_create_analyze_test.py +++ b/src/drivers/test/build_create_analyze_test.py @@ -16,18 +16,6 @@ import openpyxl # Not directly used but this will make sure it is installed import argparse - from azure.storage.file import ( - ContentSettings, - FileService, - ) - from azure.storage.blob import ( - BlobServiceClient, - BlobClient, - ContainerClient - ) - from azure.identity import DefaultAzureCredential - from azure.data.tables import TableServiceClient - except ImportError as e: print("Import error: " + str(e) + "\nPlease install the required modules using pip install -r requirements.txt") exit(1) @@ -38,6 +26,20 @@ detailed_health_df = pd.DataFrame() +def print_conditionally(*message): + """ + Prints the message if the verbose flag is set. + + Args: + message (str): The message to be printed. + + Returns: + None + """ + if args.verbose: + for m in message: + print(m, end=" ") + print("") # Any attributes specific to a test should be added to this class # This also allows for things to be conditionally added to the test call, such as the UseNTIFS parameter @@ -67,6 +69,7 @@ def __init__(self, external_drivers=[], no_attributes=False, use_cpp=False, use_ self.ql_location = ql_location self.use_cpp = use_cpp self.no_attributes = no_attributes + self.output_file = "" def get_use_cpp(self): return self.use_cpp @@ -120,67 +123,12 @@ def get_external_drivers(self): def set_external_drivers(self, external_drivers): self.external_drivers = external_drivers - -def upload_blob_to_azure(file_name): - """ - Uploads a file to Azure Blob Storage. - - Args: - file_name (str): The name of the file to be uploaded. - - Returns: - None - """ - print("Uploading file to Azure: " + file_name) - account_url = "https://"+ args.storage_account_name +".blob.core.windows.net" - blob_service_client = BlobServiceClient(account_url, credential=args.storage_account_key) - blob_client = blob_service_client.get_blob_client(container=args.container_name, blob=file_name) - with open(file=file_name, mode="rb") as data: - blob_client.upload_blob(data, overwrite=True) - -def download_blob_from_azure(file_name): - """ - Downloads a blob from Azure Blob Storage. - - Args: - file_name (str): The name of the blob file to download. - - Returns: - None - """ - account_url = "https://"+ args.storage_account_name +".blob.core.windows.net" - blob_service_client = BlobServiceClient(account_url, credential=args.storage_account_key) - blob_client = blob_service_client.get_blob_client(container=args.container_name, blob=file_name) - with open(file=file_name, mode="wb") as data: - data.write(blob_client.download_blob().readall()) - -def upload_results_to_azure(file_to_upload, file_name, file_directory): - """ - Uploads the results to Azure. - - Args: - None - - Returns: - None - """ - print("Upload results to file share: " + file_to_upload) - file_service = FileService(connection_string=args.connection_string) - file_service.create_file_from_path(share_name=args.share_name, file_name=file_name, directory_name=file_directory, local_file_path=file_to_upload, content_settings=ContentSettings(content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')) - -def download_file_from_azure(file_to_download, file_name, file_directory): - """ - Downloads a file from Azure. - - Args: - None - - Returns: - None - """ - file_service = FileService(connection_string=args.connection_string) - file = file_service.get_file_to_path(share_name=args.share_name, file_name=file_name, directory_name=file_directory, file_path=file_to_download) - return file.name + + # output sarif file + def set_output_file(self, output_file): + self.output_file = output_file + def get_output_file(self): + return self.output_file def get_git_root(): """ @@ -226,6 +174,15 @@ def find_ql_test_paths(directory, extension): ql_files_map = {} for root, dirs, files in os.walk(directory): + # exclude wfp folder until correct test template is added + ignore_paths = ["wfp", "QueryTemplate", "TestTemplate", ".vs"] + root = root.replace("\\", "/") + if any(path in ignore_paths for path in root.split("/")): + print_conditionally("Skipping: " + root) + continue + if "TestTemplate" in root: + print_conditionally("Skipping: " + root) + continue if fnmatch.filter(files, "driver_snippet.*"): use_ntifs = check_use_ntifs(os.path.join(root, fnmatch.filter(files, "driver_snippet.*")[0])) for file in files: @@ -235,6 +192,7 @@ def find_ql_test_paths(directory, extension): ql_obj = ql_test_attributes(use_ntifs=use_ntifs, use_cpp=use_cpp) ql_obj.set_use_cpp(use_cpp) ql_files_map[os.path.join(root, file)] = ql_obj + print_conditionally("Found: " + os.path.join(root, file)) return ql_files_map @@ -264,7 +222,6 @@ def find_project_configs(sln_files): lines = lines[:end_index] lines = lines.split("\n") lines = [x for x in lines if x.strip() != ""] - #print(lines) for l in lines: if "Debug" in l: l = l[l.find("Debug"):] @@ -310,7 +267,7 @@ def test_setup_external_drivers(sln_files): # TODO make this multi threaded for config, platform in configs[sln_file]: - print("Building: " + sln_file + " " + str(config) + " " + str(platform)) + print_conditionally("Building: " + sln_file + " " + str(config) + " " + str(platform)) out = subprocess.run(["msbuild", sln_file, "-clp:Verbosity=m", "-t:clean,build", "-property:Configuration="+config, "-property:Platform="+platform, "-p:TargetVersion=Windows10", "-p:SignToolWS=/fdws", "-p:DriverCFlagAddOn=/wd4996", "-noLogo"], shell=True, capture_output=no_output) @@ -338,24 +295,32 @@ def test_setup(ql_test): """ - current_working_dir = os.path.join(os.getcwd(), "working\\"+ql_test.get_ql_name()+'\\') + current_working_dir = os.path.join(g_test_dir, "working",ql_test.get_ql_name()+'\\') # if os.getcwd().split("\\")[-1] == "test": # else: if os.path.exists(current_working_dir.strip()): shutil.rmtree(current_working_dir) - print("Creating working directory: " + current_working_dir) + print_conditionally("Creating working directory: " + current_working_dir) shutil.copytree(ql_test.get_template(), current_working_dir) - print("Copying files to working directory: " + current_working_dir) - test_file_loc = os.path.join(g_template_dir,"..\\"+ql_test.get_ql_type()+"\\"+ql_test.get_ql_location()+"\\"+ql_test.get_ql_name()) + print_conditionally("Copying files to working directory: " + current_working_dir) + test_file_loc = os.path.dirname(ql_test.get_ql_file()) + # Copy files to driver directory + for file in os.listdir(test_file_loc): - shutil.copyfile(os.path.join(test_file_loc,file), os.path.join(current_working_dir+"\\driver\\",file)) + src = os.path.join(test_file_loc,file) + if(ql_test.get_ql_type() == "apps"): + dst = os.path.join(current_working_dir+"\\",file) + else: + dst = os.path.join(current_working_dir+"\\driver\\",file) + print_conditionally("Copying file "+file + " from " + src + " to " + dst) + shutil.copyfile(src, dst) # Rebuild the project using msbuild if not args.no_build: - print("Building: " + ql_test.get_ql_name()) + print_conditionally("Building: " + ql_test.get_ql_name()) out1 = subprocess.run(["msbuild", "/t:rebuild", "/p:platform=x64", "/p:UseNTIFS="+ql_test.get_use_ntifs()+""],cwd=current_working_dir, shell=True, capture_output=no_output ) if out1.returncode != 0: print("Error in msbuild: " + ql_test.get_ql_name()) @@ -394,7 +359,7 @@ def db_create_for_external_driver(sln_file, config, platform): # TODO either clear these, ask for overwrite, or use a different name db_loc = os.getcwd() + "\\dbs\\"+sln_file.split("\\")[-1].replace(".sln", "")+"_"+config+"_"+platform - print("Creating database: ", db_loc) + print_conditionally("Creating database: ", db_loc) out2 = subprocess.run([codeql_path, "database", "create", db_loc, "--overwrite", "-l", "cpp", "--source-root="+workdir, "--command=msbuild "+ sln_file+ " -clp:Verbosity=m -t:clean,build -property:Configuration="+config+" -property:Platform="+platform + " -p:TargetVersion=Windows10 -p:SignToolWS=/fdws -p:DriverCFlagAddOn=/wd4996 -noLogo" ], cwd=workdir, @@ -408,7 +373,7 @@ def db_create_for_external_driver(sln_file, config, platform): return None else: - print(".... done!") + print_conditionally(".... done!") return db_loc @@ -427,22 +392,21 @@ def create_codeql_test_database(ql_test): """ # Create the CodeQL database - # print("current working directory: ", os.getcwd()) + test_db_dir = os.path.join(g_test_dir, "TestDB") + os.makedirs(test_db_dir, exist_ok=True) + if os.path.exists(os.path.join(test_db_dir,ql_test.get_ql_name())): + shutil.rmtree(os.path.join(test_db_dir,ql_test.get_ql_name())) - os.makedirs(os.path.join(os.getcwd(), "TestDB"), exist_ok=True) - if os.path.exists(os.path.join(os.getcwd(), "TestDB\\"+ql_test.get_ql_name())): - shutil.rmtree(os.path.join(os.getcwd(), "TestDB\\"+ql_test.get_ql_name())) + source_dir=os.path.join(g_test_dir, "working\\"+ql_test.get_ql_name()+"\\") + db_loc = os.path.join(test_db_dir, ql_test.get_ql_name()+"\\") - source_dir=os.path.join(os.getcwd(), "working\\"+ql_test.get_ql_name()+"\\") - db_loc = os.path.join(os.getcwd(), "TestDB\\"+ql_test.get_ql_name()+"\\") - - - #print("-- Database location: " + db_loc) - #print("-- Source directory: " + source_dir) - #print("-- -- Command to run:", [codeql_path, "database", "create", "-l", "cpp", "-s", source_dir, "-c", "msbuild /p:Platform=x64;UseNTIFS="+ql_test.get_use_ntifs()+ " /t:rebuild " + source_dir + ql_test.get_template().split("\\")[-1] + ".sln", db_loc]) - out2 = subprocess.run([codeql_path, "database", "create", "-l", "cpp", "-s", source_dir, "-c", "msbuild /p:Platform=x64;UseNTIFS="+ql_test.get_use_ntifs()+ " /t:rebuild " + source_dir + ql_test.get_template().split("\\")[-1] + ".sln", db_loc], - - shell=True, capture_output=no_output ) + codeql_command = [codeql_path, "database", "create", "-l", "cpp", "-s", source_dir, "--threads=0", "-c", "msbuild /p:Platform=x64;UseNTIFS="+ql_test.get_use_ntifs()+ + " /t:rebuild " + source_dir + ql_test.get_template().split("\\")[-1] + ".sln", db_loc] + print_conditionally(" - Database location: " + db_loc) + print_conditionally(" - Source directory: " + source_dir) + print_conditionally(" - Command to run: " + str(codeql_command)) + out2 = subprocess.run(codeql_command, + shell=True, capture_output=no_output ) if out2.returncode != 0: print("Error in codeql database create: " + ql_test.get_ql_name()) try: @@ -468,33 +432,29 @@ def analyze_codeql_database(ql_test, db_path=None): None """ + print_conditionally("Analyzing database: " + db_path) + # Analyze the CodeQL database - if not os.path.exists("AnalysisFiles\Test Samples"): - os.makedirs("AnalysisFiles\Test Samples", exist_ok=True) if db_path is not None: # When using external drivers, save databases in current directory database_loc = db_path if args.existing_database: - if not os.path.exists(os.path.join(os.getcwd(),"AnalysisFiles\\Test Samples\\"+ql_test.get_ql_name())): - os.makedirs(os.path.join(os.getcwd(),"AnalysisFiles\\Test Samples\\"+ql_test.get_ql_name()), exist_ok=True) - - output_file = os.path.join(os.getcwd(),"AnalysisFiles\\Test Samples\\"+ql_test.get_ql_name()+ "\\" + db_path.split('\\')[-1]+".sarif") + if not os.path.exists(os.path.join(g_analysis_file_dir,ql_test.get_ql_name())): + os.makedirs(os.path.join(g_analysis_file_dir,ql_test.get_ql_name()), exist_ok=True) + output_file = os.path.join(g_analysis_file_dir,ql_test.get_ql_name(), db_path.split('\\')[-1]+".sarif") else: - output_file = os.path.join(os.getcwd(),"AnalysisFiles\\Test Samples\\"+ql_test.get_ql_name()+".sarif") - + output_file = os.path.join(g_analysis_file_dir,ql_test.get_ql_name()+".sarif") + ql_test.set_output_file(output_file) else: print("No database path provided!") return None - ql_file_path = g_template_dir + "..\\"+ql_test.get_ql_type()+"\\"+ql_test.get_ql_location()+"\\"+ql_test.get_ql_name()+"\*.ql" - if args.use_codeql_repo: - proc_command = [codeql_path, "database", "analyze", database_loc, "--format=sarifv2.1.0", "--output="+output_file,ql_file_path, "--additional-packs", args.use_codeql_repo] - + proc_command = [codeql_path, "database", "analyze", database_loc, "--format=sarifv2.1.0", "--output="+output_file,ql_test.get_ql_file(), "--additional-packs", args.use_codeql_repo, "--threads=0"] else: - proc_command = [codeql_path, "database", "analyze", database_loc, "--format=sarifv2.1.0", "--output="+output_file, ql_file_path ] + proc_command = [codeql_path, "database", "analyze", database_loc, "--format=sarifv2.1.0", "--output="+output_file, ql_test.get_ql_file(), "--threads=0"] - print("Saving SARIF file to: " + output_file) + print_conditionally("Sarif output location: " + output_file) out3 = subprocess.run(proc_command, shell=True, capture_output=no_output ) @@ -520,9 +480,10 @@ def sarif_diff(ql_test): Returns: The output of the SARIF diff command if successful, None otherwise. """ - sarif_prev = os.path.join(g_template_dir, "..\\"+ql_test.get_ql_type()+"\\"+ql_test.get_ql_location()+"\\"+ql_test.get_ql_name()+"\\"+ql_test.get_ql_name()+".sarif") - sarif_new = os.path.join(g_template_dir, "AnalysisFiles\Test Samples\\"+ql_test.get_ql_name()+".sarif") - sarif_out = os.path.join(g_template_dir, "diff\\"+ql_test.get_ql_name()+".sarif") + sarif_prev = os.path.join(ql_test.get_path(),ql_test.get_ql_name()+".sarif") + sarif_new = ql_test.get_output_file() + sarif_out = os.path.join(g_diff_dir, ql_test.get_ql_name()+".sarif") + print_conditionally("Running sarif diff: " + ql_test.get_ql_name(), "\n - Previous: ", sarif_prev, "\n - New: ", sarif_new) out4 = subprocess.run(["sarif", "diff", "-o", sarif_out ,sarif_prev, sarif_new], shell=True, capture_output=no_output ) if out4.returncode != 0: @@ -532,7 +493,7 @@ def sarif_diff(ql_test): except: print(out4.stderr) return None - print("Saving output to:", sarif_out) + print_conditionally("Diff output location:", sarif_out) return out4 @@ -576,14 +537,14 @@ def run_test(ql_test): # Print test attributes print_mutex.acquire() - print(ql_test.get_ql_name(), "\n", - "Template: ", ql_test.get_template(), "\n", - "Driver Framework: ", ql_test.get_ql_type(), "\n", - "Query location: ", ql_test.get_ql_location(), "\n", - "UseNTIFS flag: ", ql_test.get_use_ntifs(),"\n") + print("\nRunning test: " + ql_test.get_ql_name()) + + print_conditionally(" - Template: ", ql_test.get_template(), "\n", + "- Driver Framework: ", ql_test.get_ql_type(), "\n", + "- Query location: ", ql_test.get_ql_location(), "\n", + "- UseNTIFS flag: ", ql_test.get_use_ntifs()) print_mutex.release() - print("Running test: " + ql_test.get_ql_name()) create_codeql_database_result = None if not args.existing_database: test_setup_result = test_setup(ql_test) @@ -591,7 +552,7 @@ def run_test(ql_test): print("Error setting up test: " + ql_test.get_ql_name(),"Skipping...") return None - print("Creating test database") + print_conditionally("Creating test database") create_codeql_database_result = create_codeql_test_database(ql_test) if create_codeql_database_result is None: print("Error creating database: " + ql_test.get_ql_name(),"Skipping...") @@ -600,9 +561,11 @@ def run_test(ql_test): db_path = create_codeql_database_result else: db_path=args.existing_database - print("Using existing database: " + db_path) + print_conditionally("Using existing database: " + db_path) + + if args.build_database_only: + return None - print("Analyzing database: " + db_path) analyze_codeql_database_result = analyze_codeql_database(ql_test, db_path) # result is path to sarif file if successful if analyze_codeql_database_result is None: print("Error analyzing database: " + db_path,"Skipping...") @@ -633,9 +596,11 @@ def parse_attributes(queries): if "working" in query or "TestDB" in query: continue path = os.path.normpath(query) + ql_file = path + path = path.split(os.sep) - ql_file = path[-1] ql_name = path[-2] + di = path.index("drivers") ql_type = path[di+1] @@ -648,9 +613,11 @@ def parse_attributes(queries): template += "WDMTestTemplate" elif(ql_type == "kmdf"): template += "KMDFTestTemplate" + elif(ql_type == "apps"): + template += "ApplicationForDriversTestTemplate" else: pass - + if args.override_template: template = args.override_template @@ -662,7 +629,7 @@ def parse_attributes(queries): ql_location = "/".join(path[path.index(ql_type)+1:path.index(ql_name)]) queries[query].set_use_ntifs(use_ntifs) queries[query].set_template(template) - queries[query].set_path(path) + queries[query].set_path(os.path.dirname("/".join(path))) queries[query].set_ql_file(ql_file) queries[query].set_ql_name(ql_name) queries[query].set_ql_type(ql_type) @@ -693,13 +660,13 @@ def run_tests_external_drivers(ql_tests_dict): total = len(configs.keys()) count = 0 if args.existing_database: - print("Using existing database: " + args.existing_database) + print_conditionally("Using existing database: " + args.existing_database) folder_names = [os.path.join(args.existing_database, name) for name in os.listdir(args.existing_database) if os.path.isdir(os.path.join(args.existing_database, name))] created_databases = folder_names total = len(folder_names) else: for sln_file in configs.keys(): - print("Creating databases for " + sln_file + " ---> " + str(count) + "/" + str(total)) + print_conditionally("Creating databases for " + sln_file + " ---> " + str(count) + "/" + str(total)) for config, platform in configs[sln_file]: create_codeql_database_result = db_create_for_external_driver(sln_file, config, platform) if create_codeql_database_result is None: @@ -711,13 +678,16 @@ def run_tests_external_drivers(ql_tests_dict): created_databases.append(create_codeql_database_result) count += 1 # Analyze created databses + if not args.build_database_only: + return + ql_tests_with_attributes = parse_attributes(ql_tests_dict) count = 0 total = len(created_databases)*len(ql_tests_with_attributes) for ql_test in ql_tests_with_attributes: - print("Run query: " + ql_test.get_ql_name()) + print_conditionally("Run query: " + ql_test.get_ql_name()) for db in created_databases: - print("...... on database " + str(count) + "/" + str(total)+ ": " + db) + print_conditionally("...... on database " + str(count) + "/" + str(total)+ ": " + db) count += 1 try: result_sarif = analyze_codeql_database(ql_test, db) @@ -734,19 +704,18 @@ def run_tests_external_drivers(ql_tests_dict): health_df.at[db.split("\\")[-1], ql_test.get_ql_name()] = str(analysis_results['error'] + analysis_results['warning'] + analysis_results['note']) detailed_health_df.at[db.split("\\")[-1], ql_test.get_ql_name()] = str(detailed_analysis_results) # save results - result_file = "results.xlsx" + result_file = "external_drivers_results.xlsx" with pd.ExcelWriter(result_file) as writer: health_df.to_excel(writer, sheet_name="Results") - codeql_version_df.to_excel(writer, sheet_name="CodeQL Version") - codeql_packs_df.to_excel(writer, sheet_name="CodeQL Packs") - system_info_df.to_excel(writer, sheet_name="System Info") + local_codeql_version_df.to_excel(writer, sheet_name="Local CodeQL Version") + local_codeql_packs_df.to_excel(writer, sheet_name="Local CodeQL Packs") + local_system_info_df.to_excel(writer, sheet_name="Local System Info") with pd.ExcelWriter("detailed" + result_file) as writer: detailed_health_df.to_excel(writer, sheet_name="Results") - codeql_version_df.to_excel(writer, sheet_name="CodeQL Version") - codeql_packs_df.to_excel(writer, sheet_name="CodeQL Packs") - system_info_df.to_excel(writer, sheet_name="System Info") + local_codeql_version_df.to_excel(writer, sheet_name="Local CodeQL Version") + local_codeql_packs_df.to_excel(writer, sheet_name="Local CodeQL Packs") + local_system_info_df.to_excel(writer, sheet_name="Local System Info") if args.compare_results: - compare_health_results(result_file) compare_health_results("detailed"+result_file) @@ -766,15 +735,15 @@ def find_last_xlsx_file(curr_results_path): else: detailed = False - curr_results_timestamp = curr_results_path.split("--")[-1].replace(".xlsx", "") files = os.listdir() files = [x for x in files if "diff" not in x] if detailed: - files = [x for x in files if x.endswith(".xlsx") and "detailed" in x and curr_results_timestamp not in x] + files = [x for x in files if x.endswith(".xlsx") and "detailed" in x and x != curr_results_path] else: - files = [x for x in files if x.endswith(".xlsx") and "detailed" not in x and curr_results_timestamp not in x] + files = [x for x in files if x.endswith(".xlsx") and "detailed" not in x and x != curr_results_path] files.sort(key=os.path.getmtime, reverse=True) - + if len(files) == 0: + return None return files[0] @@ -788,85 +757,52 @@ def compare_health_results(curr_results_path): Returns: None """ - if not args.connection_string or not args.share_name and not args.local_result_storage: - raise Exception("Azure credentials not provided. Cannot compare results.") - - # get most recent xlsx results file - if args.local_result_storage: - prev_results = find_last_xlsx_file(curr_results_path) - if prev_results == None: - print("No previous results found.") - return None - else: - try: - prev_results = 'azure-'+curr_results_path - _ = download_file_from_azure(file_to_download=prev_results, - file_name=curr_results_path, file_directory="") - - except Exception as e: - if "ResourceNotFound" in str(e): - print("No previous results found. Uploading current results to Azure...") - upload_results_to_azure(file_to_upload=curr_results_path, - file_name=curr_results_path, file_directory="") - exit(1) - else: - print("Error downloading previous results ") - exit(1) + + prev_results = 'azure-'+curr_results_path prev_results_df = pd.read_excel(prev_results, index_col=0, sheet_name=0) prev_results_codeql_version_df = pd.read_excel(prev_results, index_col=0, sheet_name=1) prev_results_codeql_packs_df = pd.read_excel(prev_results, index_col=0, sheet_name=2) - prev_results_system_info_df = pd.read_excel(prev_results, index_col=0, sheet_name=3) + prev_results_local_system_info_df = pd.read_excel(prev_results, index_col=0, sheet_name=3) curr_results_df = pd.read_excel(curr_results_path, index_col=0, sheet_name=0) - print("Comparing results...") - try: - for row in prev_results_df.index: - if row not in curr_results_df.index: - curr_results_df.loc[row] = None - print("Adding row to current results: ", row) - for row in curr_results_df.index: - if row not in prev_results_df.index: - prev_results_df.loc[row] = None - print("Adding row to previous results: ", row) - prev_results_df = prev_results_df.sort_index() - curr_results_df = curr_results_df.sort_index() - diff_results = curr_results_df.compare(prev_results_df, keep_shape=True, result_names=("Current", "Previous")) - except Exception as e: - print("Error comparing results: ", e, "Uploading previous results back to Azure as", prev_results, "and current results back to Azure as", curr_results_path) - upload_results_to_azure(file_to_upload=prev_results, - file_name=prev_results, file_directory="") - upload_results_to_azure(file_to_upload=curr_results_path, - file_name=curr_results_path, file_directory="") - exit(1) - with pd.ExcelWriter("diff" + curr_results_path) as writer: - diff_results.to_excel(writer, sheet_name="Diff") - codeql_version_df.to_excel(writer, sheet_name="Current CodeQL Version") - codeql_packs_df.to_excel(writer, sheet_name="Current CodeQL Packs") - system_info_df.to_excel(writer, sheet_name="Current System Info") - prev_results_codeql_version_df.to_excel(writer, sheet_name="Previous CodeQL Version") - prev_results_codeql_packs_df.to_excel(writer, sheet_name="Previous CodeQL Packs") - prev_results_system_info_df.to_excel(writer, sheet_name="Previous System Info") - - print("Saved diff results") - # upload new results to Azure - if not args.local_result_storage: - - print("Uploading results") - upload_results_to_azure(file_to_upload=curr_results_path, - file_name=curr_results_path, file_directory="") - # upload diff to Azure - print("Uploading diff results") - upload_results_to_azure(file_to_upload="diff" + curr_results_path, - file_name="diff" + curr_results_path, file_directory="") - - #upload_blob_to_azure("diff"+curr_results_path) # TODO this doesnt work in github actions + print_conditionally("Comparing results...") + print_conditionally("Previous results: ", prev_results) + print_conditionally("Current results: ", curr_results_path) + for row in prev_results_df.index: + if row not in curr_results_df.index: + curr_results_df.loc[row] = None + print_conditionally("Adding row to current results: ", row) + + for row in curr_results_df.index: + if row not in prev_results_df.index: + prev_results_df.loc[row] = None + print_conditionally("Adding row to previous results: ", row) + + prev_results_df = prev_results_df.sort_index() + curr_results_df = curr_results_df.sort_index() - # delete downloaded file + #TODO this is a tempoary fix for the issue with the backslashes in the results in codeql cli version 2.18.x+ + prev_results_df = prev_results_df.replace({r'\\': ''}, regex=True) + curr_results_df = curr_results_df.replace({r'\\': ''}, regex=True) + + diff_results = curr_results_df.compare(prev_results_df, keep_shape=True, result_names=("Current", "Previous")) - os.remove(prev_results) - print("Deleted previous results") + if not all(diff_results.isnull().all()) : + print("Differences found in results!") + with pd.ExcelWriter("diff" + curr_results_path) as writer: + diff_results.to_excel(writer, sheet_name="Diff") + local_codeql_version_df.to_excel(writer, sheet_name="Local CodeQL Version") + local_codeql_packs_df.to_excel(writer, sheet_name="Local CodeQL Packs") + local_system_info_df.to_excel(writer, sheet_name="Local System Info") + prev_results_codeql_version_df.to_excel(writer, sheet_name="Last Stored CodeQL Version") + prev_results_codeql_packs_df.to_excel(writer, sheet_name="Last Stored CodeQL Packs") + prev_results_local_system_info_df.to_excel(writer, sheet_name="Last Stored System Info") + os.remove(prev_results) + else: + print("No differences found in results") + os.remove(prev_results) + exit(0) - def run_tests(ql_tests_dict): """ Run the given CodeQL tests. @@ -881,30 +817,27 @@ def run_tests(ql_tests_dict): for ql_test in ql_tests_with_attributes: result_sarif = run_test(ql_test) - if not result_sarif: - print("Error running test: " + ql_test.get_ql_name(),"Skipping...") - continue - analysis_results, detailed_analysis_results = sarif_results(ql_test, result_sarif) - # health_df.at[ql_test.get_ql_name(), "Template"] = ql_test.get_template() - health_df.at[ql_test.get_ql_name(), "Result"] = str(int(analysis_results['error'])+int(analysis_results['warning'])+int(analysis_results['note'])) - - #detailed_health_df.at[ql_test.get_ql_name(), "Template"] = ql_test.get_template() - detailed_health_df.at[ql_test.get_ql_name(), "Result"] = str(detailed_analysis_results) + if not args.build_database_only: + if not result_sarif: + print("Error running test: " + ql_test.get_ql_name(),"Skipping...") + continue + analysis_results, detailed_analysis_results = sarif_results(ql_test, result_sarif) + health_df.at[ql_test.get_ql_name(), "Result"] = str(int(analysis_results['error'])+int(analysis_results['warning'])+int(analysis_results['note'])) + detailed_health_df.at[ql_test.get_ql_name(), "Result"] = str(detailed_analysis_results) # save results result_file = "functiontestresults.xlsx" with pd.ExcelWriter(result_file) as writer: health_df.to_excel(writer, sheet_name="Results") - codeql_version_df.to_excel(writer, sheet_name="CodeQL Version") - codeql_packs_df.to_excel(writer, sheet_name="CodeQL Packs") - system_info_df.to_excel(writer, sheet_name="System Info") + local_codeql_version_df.to_excel(writer, sheet_name="Local CodeQL Version") + local_codeql_packs_df.to_excel(writer, sheet_name="Local CodeQL Packs") + local_system_info_df.to_excel(writer, sheet_name="Local System Info") with pd.ExcelWriter("detailed"+result_file) as writer: detailed_health_df.to_excel(writer, sheet_name="Results") - codeql_version_df.to_excel(writer, sheet_name="CodeQL Version") - codeql_packs_df.to_excel(writer, sheet_name="CodeQL Packs") - system_info_df.to_excel(writer, sheet_name="System Info") + local_codeql_version_df.to_excel(writer, sheet_name="Local CodeQL Version") + local_codeql_packs_df.to_excel(writer, sheet_name="Local CodeQL Packs") + local_system_info_df.to_excel(writer, sheet_name="Local System Info") if args.compare_results: - compare_health_results(result_file) compare_health_results("detailed"+result_file) def find_g_template_dir(template): @@ -917,9 +850,9 @@ def find_g_template_dir(template): Returns: str: The path to the template directory. """ - for root, dirs, files in os.walk("./"): + for root, dirs, files in os.walk(os.getcwd()): if template in dirs: - return root + return os.path.abspath(os.path.join(root, template) )+ "\\" return None @@ -943,126 +876,42 @@ def find_sln_file(path): if __name__ == "__main__": # Sys input flags - parser = argparse.ArgumentParser(description='Build, create, and analyze CodeQL databases for testing queries. Running this script without any flags will run all queries on their respective driver template project with injected tests from driver_snippet.c') - parser.add_argument('-e', '--external_drivers', - help='Use external drivers at for testing instead of drivers in the repo', - type=str, - required=False, - ) - parser.add_argument('-l', '--no_clean', - help='Do not clean the working directory before running tests', - action='store_true', - required=False, - ) - parser.add_argument('-b', '--existing_database', - help='Use existing database at for testing instead of creating a new one', - type=str, - required=False, - ) - parser.add_argument('-d', '--debug_only', - help='Debug only', - action='store_true', - required=False, - ) - parser.add_argument('-r', '--release_only', - help='Release only', - action='store_true', - required=False, - ) - parser.add_argument('-n', '--no_build', - help='Do not build the driver before running the test', - action='store_true', - required=False, - ) - parser.add_argument('-o', '--override_template', - help='Override the template used for the test using