virt: add irqfd trait and implement for mshv and KVM #3463
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: "Unsafe Check" | |
| # SECURITY NOTE: This workflow uses pull_request_target to get write permissions | |
| # for managing labels and comments. To avoid executing untrusted code, we: | |
| # 1. Do NOT checkout the PR code at all | |
| # 2. Fetch file contents exclusively via GitHub API | |
| # 3. Never execute, eval, or interpret the fetched content | |
| # This eliminates filesystem-based attacks (symlinks, large files, race conditions) | |
| on: | |
| pull_request_target | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| jobs: | |
| check-unsafe: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check for unsafe code and manage labels | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10 MB limit per file | |
| // Get the list of changed files with pagination | |
| let allFiles = []; | |
| let page = 1; | |
| while (page <= 30) { | |
| const { data: files } = await github.rest.pulls.listFiles({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: context.payload.pull_request.number, | |
| per_page: 100, | |
| page: page, | |
| }); | |
| if (files.length === 0) break; | |
| allFiles = allFiles.concat(files); | |
| if (files.length < 100) break; | |
| page++; | |
| } | |
| // Filter to just Rust files that weren't deleted | |
| const rustFiles = allFiles.filter(file => | |
| file.filename.endsWith('.rs') && file.status !== 'removed' | |
| ); | |
| console.log(`Checking ${rustFiles.length} Rust files for unsafe code...`); | |
| let unsafeFound = false; | |
| const prHead = context.payload.pull_request.head; | |
| // Check each Rust file for unsafety by fetching content via API | |
| for (const file of rustFiles) { | |
| // Skip files that are too large (size is in bytes) | |
| if (file.size > MAX_FILE_SIZE) { | |
| throw new Error(`${file.filename}: file too large (${file.size} bytes)`); | |
| } | |
| // Fetch file content from the PR head via API (not filesystem) | |
| // This avoids all filesystem-based attacks | |
| const { data: fileContent } = await github.rest.repos.getContent({ | |
| owner: prHead.repo.owner.login, | |
| repo: prHead.repo.name, | |
| path: file.filename, | |
| ref: prHead.sha, | |
| }); | |
| if (fileContent.type !== 'file') { | |
| throw new Error(`${file.filename}: not a regular file (type: ${fileContent.type})`); | |
| } | |
| // Decode base64 content | |
| const content = Buffer.from(fileContent.content, 'base64').toString('utf8'); | |
| // Look for "unsafe ", the space ensures we don't catch words like the "unsafe_code" lint | |
| // Also look for "unsafe(" to catch unsafe attributes | |
| // The separate rustfmt check will ensure all code matches these formatting standards | |
| const unsafeRegex = /unsafe[( ]/; | |
| if (unsafeRegex.test(content)) { | |
| console.log(`Found unsafe code in: ${file.filename}`); | |
| unsafeFound = true; | |
| } | |
| } | |
| // Manage the label (use pull_request number from payload for pull_request_target) | |
| const prNumber = context.payload.pull_request.number; | |
| if (unsafeFound) { | |
| console.log('Adding unsafe label...'); | |
| await github.rest.issues.addLabels({ | |
| issue_number: prNumber, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| labels: ['unsafe'] | |
| }); | |
| // Post a warning comment | |
| const comment = `⚠️ **Unsafe Code Detected** | |
| This PR modifies files containing \`unsafe\` Rust code. Extra scrutiny is required during review. | |
| For more on why we check whole files, instead of just diffs, check out [the Rustonomicon](https://doc.rust-lang.org/nomicon/working-with-unsafe.html)`; | |
| // Check if we already posted this comment | |
| const { data: comments } = await github.rest.issues.listComments({ | |
| issue_number: prNumber, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| }); | |
| const botComment = comments.find(c => | |
| c.user.type === 'Bot' && c.body.includes('Unsafe Code Detected') | |
| ); | |
| if (!botComment) { | |
| console.log('Posting warning comment...'); | |
| await github.rest.issues.createComment({ | |
| issue_number: prNumber, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| body: comment | |
| }); | |
| } else { | |
| console.log('Warning comment already exists'); | |
| } | |
| } else { | |
| console.log('No unsafe code found, removing label if present...'); | |
| try { | |
| await github.rest.issues.removeLabel({ | |
| issue_number: prNumber, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| name: 'unsafe' | |
| }); | |
| } catch (error) { | |
| // Label might not exist, that's okay | |
| console.log('Label does not exist or could not be removed:', error.message); | |
| } | |
| } |