@vigolium/piolium 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +117 -0
- package/agents/access-auditor.md +300 -0
- package/agents/assumption-breaker.md +154 -0
- package/agents/attack-designer.md +116 -0
- package/agents/code-scanner.md +139 -0
- package/agents/concurrency-auditor.md +238 -0
- package/agents/confirm-writer.md +257 -0
- package/agents/context-reviewer.md +274 -0
- package/agents/cross-verifier.md +165 -0
- package/agents/cve-scout.md +381 -0
- package/agents/env-builder.md +282 -0
- package/agents/env-profiler.md +205 -0
- package/agents/evidence-collector.md +140 -0
- package/agents/finding-grader.md +142 -0
- package/agents/finding-writer.md +148 -0
- package/agents/flow-tracer.md +106 -0
- package/agents/goal-backtracer.md +146 -0
- package/agents/history-miner.md +467 -0
- package/agents/independent-verifier.md +118 -0
- package/agents/intent-mapper.md +183 -0
- package/agents/longshot-collector.md +128 -0
- package/agents/longshot-prober.md +126 -0
- package/agents/patch-auditor.md +73 -0
- package/agents/poc-author.md +124 -0
- package/agents/poc-runner.md +194 -0
- package/agents/probe-lead.md +269 -0
- package/agents/red-challenger.md +101 -0
- package/agents/report-composer.md +208 -0
- package/agents/review-adjudicator.md +216 -0
- package/agents/spec-auditor.md +155 -0
- package/agents/taint-tracer.md +265 -0
- package/agents/test-locator.md +209 -0
- package/agents/threat-modeler.md +132 -0
- package/agents/variant-scanner.md +108 -0
- package/agents/variant-spotter.md +110 -0
- package/bin/piolium.mjs +376 -0
- package/extensions/piolium/_vendor/yaml.bundle.d.mts +6 -0
- package/extensions/piolium/_vendor/yaml.bundle.mjs +139 -0
- package/extensions/piolium/agent-runner.ts +322 -0
- package/extensions/piolium/agents.ts +266 -0
- package/extensions/piolium/audit-state.ts +522 -0
- package/extensions/piolium/bundled-resources.ts +97 -0
- package/extensions/piolium/candidate-scan.ts +966 -0
- package/extensions/piolium/command-target.ts +177 -0
- package/extensions/piolium/console-stream.ts +57 -0
- package/extensions/piolium/export-results.ts +380 -0
- package/extensions/piolium/findings.ts +448 -0
- package/extensions/piolium/heartbeat.ts +182 -0
- package/extensions/piolium/help.ts +234 -0
- package/extensions/piolium/index.ts +1865 -0
- package/extensions/piolium/longshot.ts +530 -0
- package/extensions/piolium/matcher-suggestions.ts +196 -0
- package/extensions/piolium/matcher-utils.ts +83 -0
- package/extensions/piolium/modes/balanced.ts +750 -0
- package/extensions/piolium/modes/confirm-bootstrap.ts +186 -0
- package/extensions/piolium/modes/confirm.ts +697 -0
- package/extensions/piolium/modes/deep.ts +917 -0
- package/extensions/piolium/modes/diff.ts +177 -0
- package/extensions/piolium/modes/lite.ts +540 -0
- package/extensions/piolium/modes/longshot.ts +595 -0
- package/extensions/piolium/modes/merge.ts +204 -0
- package/extensions/piolium/modes/phase-runner.ts +267 -0
- package/extensions/piolium/modes/reinvest.ts +546 -0
- package/extensions/piolium/modes/revisit.ts +279 -0
- package/extensions/piolium/modes.ts +48 -0
- package/extensions/piolium/phase-labels.ts +123 -0
- package/extensions/piolium/phase-status-strip.ts +92 -0
- package/extensions/piolium/prompt-prefix-editor.ts +39 -0
- package/extensions/piolium/providers/anthropic-vertex.ts +836 -0
- package/extensions/piolium/recon.ts +409 -0
- package/extensions/piolium/result-stats.ts +105 -0
- package/extensions/piolium/retry.ts +120 -0
- package/extensions/piolium/scheduler.ts +212 -0
- package/extensions/piolium/secrets.ts +368 -0
- package/extensions/piolium/tools/web-tools.ts +148 -0
- package/package.json +77 -0
- package/skills/agentic-actions-auditor/SKILL.md +327 -0
- package/skills/agentic-actions-auditor/references/action-profiles.md +186 -0
- package/skills/agentic-actions-auditor/references/cross-file-resolution.md +209 -0
- package/skills/agentic-actions-auditor/references/foundations.md +94 -0
- package/skills/agentic-actions-auditor/references/vector-a-env-var-intermediary.md +77 -0
- package/skills/agentic-actions-auditor/references/vector-b-direct-expression-injection.md +83 -0
- package/skills/agentic-actions-auditor/references/vector-c-cli-data-fetch.md +83 -0
- package/skills/agentic-actions-auditor/references/vector-d-pr-target-checkout.md +88 -0
- package/skills/agentic-actions-auditor/references/vector-e-error-log-injection.md +88 -0
- package/skills/agentic-actions-auditor/references/vector-f-subshell-expansion.md +82 -0
- package/skills/agentic-actions-auditor/references/vector-g-eval-of-ai-output.md +91 -0
- package/skills/agentic-actions-auditor/references/vector-h-dangerous-sandbox-configs.md +102 -0
- package/skills/agentic-actions-auditor/references/vector-i-wildcard-allowlists.md +88 -0
- package/skills/audit/SKILL.md +562 -0
- package/skills/audit/assets/icon.svg +7 -0
- package/skills/audit/hooks/scripts/validate_phase_output.py +550 -0
- package/skills/audit/references/adversarial-review.md +148 -0
- package/skills/audit/references/architecture-aware-sast.md +306 -0
- package/skills/audit/references/audit-workflow.md +737 -0
- package/skills/audit/references/chamber-protocol.md +384 -0
- package/skills/audit/references/creative-attack-modes.md +221 -0
- package/skills/audit/references/deep-analysis.md +273 -0
- package/skills/audit/references/domain-attack-playbooks.md +1129 -0
- package/skills/audit/references/knowledge-base-template.md +513 -0
- package/skills/audit/references/real-env-validation.md +191 -0
- package/skills/audit/references/report-templates.md +417 -0
- package/skills/audit/references/triage-and-prereqs.md +134 -0
- package/skills/audit/scripts/consolidate_drafts.py +554 -0
- package/skills/audit/scripts/partition_findings.py +152 -0
- package/skills/audit/scripts/rg-hotspots.sh +121 -0
- package/skills/audit/scripts/stamp_file_state.py +349 -0
- package/skills/code-reviewer/SKILL.md +65 -0
- package/skills/codeql/SKILL.md +281 -0
- package/skills/codeql/references/build-fixes.md +90 -0
- package/skills/codeql/references/diagnostic-query-templates.md +339 -0
- package/skills/codeql/references/extension-yaml-format.md +209 -0
- package/skills/codeql/references/important-only-suite.md +153 -0
- package/skills/codeql/references/language-details.md +207 -0
- package/skills/codeql/references/macos-arm64e-workaround.md +179 -0
- package/skills/codeql/references/performance-tuning.md +111 -0
- package/skills/codeql/references/quality-assessment.md +172 -0
- package/skills/codeql/references/ruleset-catalog.md +63 -0
- package/skills/codeql/references/run-all-suite.md +92 -0
- package/skills/codeql/references/sarif-processing.md +79 -0
- package/skills/codeql/references/threat-models.md +51 -0
- package/skills/codeql/workflows/build-database.md +280 -0
- package/skills/codeql/workflows/create-data-extensions.md +261 -0
- package/skills/codeql/workflows/run-analysis.md +301 -0
- package/skills/differential-review/SKILL.md +220 -0
- package/skills/differential-review/adversarial.md +203 -0
- package/skills/differential-review/methodology.md +234 -0
- package/skills/differential-review/patterns.md +300 -0
- package/skills/differential-review/reporting.md +369 -0
- package/skills/fp-check/SKILL.md +125 -0
- package/skills/fp-check/references/bug-class-verification.md +114 -0
- package/skills/fp-check/references/deep-verification.md +143 -0
- package/skills/fp-check/references/evidence-templates.md +91 -0
- package/skills/fp-check/references/false-positive-patterns.md +115 -0
- package/skills/fp-check/references/gate-reviews.md +27 -0
- package/skills/fp-check/references/standard-verification.md +78 -0
- package/skills/insecure-defaults/SKILL.md +117 -0
- package/skills/insecure-defaults/references/examples.md +409 -0
- package/skills/last30days/SKILL.md +444 -0
- package/skills/sarif-parsing/SKILL.md +483 -0
- package/skills/sarif-parsing/resources/jq-queries.md +162 -0
- package/skills/sarif-parsing/resources/sarif_helpers.py +331 -0
- package/skills/security-threat-model/LICENSE.txt +201 -0
- package/skills/security-threat-model/SKILL.md +81 -0
- package/skills/security-threat-model/agents/openai.yaml +4 -0
- package/skills/security-threat-model/references/prompt-template.md +255 -0
- package/skills/security-threat-model/references/security-controls-and-assets.md +32 -0
- package/skills/semgrep/SKILL.md +212 -0
- package/skills/semgrep/references/rulesets.md +162 -0
- package/skills/semgrep/references/scan-modes.md +110 -0
- package/skills/semgrep/references/scanner-task-prompt.md +140 -0
- package/skills/semgrep/scripts/merge_sarif.py +203 -0
- package/skills/semgrep/workflows/scan-workflow.md +311 -0
- package/skills/semgrep-rule-creator/SKILL.md +168 -0
- package/skills/semgrep-rule-creator/references/quick-reference.md +202 -0
- package/skills/semgrep-rule-creator/references/workflow.md +240 -0
- package/skills/semgrep-rule-variant-creator/SKILL.md +205 -0
- package/skills/semgrep-rule-variant-creator/references/applicability-analysis.md +250 -0
- package/skills/semgrep-rule-variant-creator/references/language-syntax-guide.md +324 -0
- package/skills/semgrep-rule-variant-creator/references/workflow.md +518 -0
- package/skills/sharp-edges/SKILL.md +292 -0
- package/skills/sharp-edges/references/auth-patterns.md +252 -0
- package/skills/sharp-edges/references/case-studies.md +274 -0
- package/skills/sharp-edges/references/config-patterns.md +333 -0
- package/skills/sharp-edges/references/crypto-apis.md +190 -0
- package/skills/sharp-edges/references/lang-c.md +205 -0
- package/skills/sharp-edges/references/lang-csharp.md +285 -0
- package/skills/sharp-edges/references/lang-go.md +270 -0
- package/skills/sharp-edges/references/lang-java.md +263 -0
- package/skills/sharp-edges/references/lang-javascript.md +269 -0
- package/skills/sharp-edges/references/lang-kotlin.md +265 -0
- package/skills/sharp-edges/references/lang-php.md +245 -0
- package/skills/sharp-edges/references/lang-python.md +274 -0
- package/skills/sharp-edges/references/lang-ruby.md +273 -0
- package/skills/sharp-edges/references/lang-rust.md +272 -0
- package/skills/sharp-edges/references/lang-swift.md +287 -0
- package/skills/sharp-edges/references/language-specific.md +588 -0
- package/skills/spec-to-code-compliance/SKILL.md +357 -0
- package/skills/spec-to-code-compliance/resources/COMPLETENESS_CHECKLIST.md +69 -0
- package/skills/spec-to-code-compliance/resources/IR_EXAMPLES.md +417 -0
- package/skills/spec-to-code-compliance/resources/OUTPUT_REQUIREMENTS.md +105 -0
- package/skills/supply-chain-risk-auditor/SKILL.md +67 -0
- package/skills/supply-chain-risk-auditor/resources/results-template.md +41 -0
- package/skills/variant-analysis/METHODOLOGY.md +327 -0
- package/skills/variant-analysis/SKILL.md +142 -0
- package/skills/variant-analysis/resources/codeql/cpp.ql +119 -0
- package/skills/variant-analysis/resources/codeql/go.ql +69 -0
- package/skills/variant-analysis/resources/codeql/java.ql +71 -0
- package/skills/variant-analysis/resources/codeql/javascript.ql +63 -0
- package/skills/variant-analysis/resources/codeql/python.ql +80 -0
- package/skills/variant-analysis/resources/semgrep/cpp.yaml +98 -0
- package/skills/variant-analysis/resources/semgrep/go.yaml +63 -0
- package/skills/variant-analysis/resources/semgrep/java.yaml +61 -0
- package/skills/variant-analysis/resources/semgrep/javascript.yaml +60 -0
- package/skills/variant-analysis/resources/semgrep/python.yaml +72 -0
- package/skills/variant-analysis/resources/variant-report-template.md +75 -0
- package/skills/vuln-report/SKILL.md +137 -0
- package/skills/vuln-report/agents/openai.yaml +4 -0
- package/skills/vuln-report/references/report-template.md +135 -0
- package/skills/wooyun-legacy/SKILL.md +367 -0
- package/skills/wooyun-legacy/references/bank-penetration.md +222 -0
- package/skills/wooyun-legacy/references/checklists/command-execution-checklist.md +119 -0
- package/skills/wooyun-legacy/references/checklists/csrf-checklist.md +74 -0
- package/skills/wooyun-legacy/references/checklists/file-upload-checklist.md +108 -0
- package/skills/wooyun-legacy/references/checklists/info-disclosure-checklist.md +114 -0
- package/skills/wooyun-legacy/references/checklists/logic-flaws-checklist.md +95 -0
- package/skills/wooyun-legacy/references/checklists/misconfig-checklist.md +124 -0
- package/skills/wooyun-legacy/references/checklists/path-traversal-checklist.md +87 -0
- package/skills/wooyun-legacy/references/checklists/rce-checklist.md +93 -0
- package/skills/wooyun-legacy/references/checklists/sql-injection-checklist.md +97 -0
- package/skills/wooyun-legacy/references/checklists/ssrf-checklist.md +99 -0
- package/skills/wooyun-legacy/references/checklists/unauthorized-access-checklist.md +89 -0
- package/skills/wooyun-legacy/references/checklists/weak-password-checklist.md +115 -0
- package/skills/wooyun-legacy/references/checklists/xss-checklist.md +103 -0
- package/skills/wooyun-legacy/references/checklists/xxe-checklist.md +130 -0
- package/skills/wooyun-legacy/references/info-disclosure.md +975 -0
- package/skills/wooyun-legacy/references/logic-flaws.md +721 -0
- package/skills/wooyun-legacy/references/path-traversal.md +1191 -0
- package/skills/wooyun-legacy/references/telecom-penetration.md +156 -0
- package/skills/wooyun-legacy/references/unauthorized-access.md +980 -0
- package/skills/wooyun-legacy/references/xss.md +746 -0
- package/skills/zeroize-audit/SKILL.md +371 -0
- package/skills/zeroize-audit/configs/c.yaml +21 -0
- package/skills/zeroize-audit/configs/default.yaml +128 -0
- package/skills/zeroize-audit/configs/rust.yaml +83 -0
- package/skills/zeroize-audit/prompts/report_template.md +238 -0
- package/skills/zeroize-audit/prompts/system.md +163 -0
- package/skills/zeroize-audit/prompts/task.md +97 -0
- package/skills/zeroize-audit/references/compile-commands.md +231 -0
- package/skills/zeroize-audit/references/detection-strategy.md +191 -0
- package/skills/zeroize-audit/references/ir-analysis.md +252 -0
- package/skills/zeroize-audit/references/mcp-analysis.md +221 -0
- package/skills/zeroize-audit/references/poc-generation.md +470 -0
- package/skills/zeroize-audit/references/rust-zeroization-patterns.md +867 -0
- package/skills/zeroize-audit/schemas/input.json +83 -0
- package/skills/zeroize-audit/schemas/output.json +140 -0
- package/skills/zeroize-audit/tools/analyze_asm.sh +202 -0
- package/skills/zeroize-audit/tools/analyze_cfg.py +381 -0
- package/skills/zeroize-audit/tools/analyze_heap.sh +211 -0
- package/skills/zeroize-audit/tools/analyze_ir_semantic.py +429 -0
- package/skills/zeroize-audit/tools/diff_ir.sh +135 -0
- package/skills/zeroize-audit/tools/diff_rust_mir.sh +189 -0
- package/skills/zeroize-audit/tools/emit_asm.sh +67 -0
- package/skills/zeroize-audit/tools/emit_ir.sh +77 -0
- package/skills/zeroize-audit/tools/emit_rust_asm.sh +178 -0
- package/skills/zeroize-audit/tools/emit_rust_ir.sh +150 -0
- package/skills/zeroize-audit/tools/emit_rust_mir.sh +158 -0
- package/skills/zeroize-audit/tools/extract_compile_flags.py +284 -0
- package/skills/zeroize-audit/tools/generate_poc.py +1329 -0
- package/skills/zeroize-audit/tools/mcp/apply_confidence_gates.py +113 -0
- package/skills/zeroize-audit/tools/mcp/check_mcp.sh +68 -0
- package/skills/zeroize-audit/tools/mcp/normalize_mcp_evidence.py +125 -0
- package/skills/zeroize-audit/tools/scripts/check_llvm_patterns.py +481 -0
- package/skills/zeroize-audit/tools/scripts/check_mir_patterns.py +554 -0
- package/skills/zeroize-audit/tools/scripts/check_rust_asm.py +424 -0
- package/skills/zeroize-audit/tools/scripts/check_rust_asm_aarch64.py +300 -0
- package/skills/zeroize-audit/tools/scripts/check_rust_asm_x86.py +283 -0
- package/skills/zeroize-audit/tools/scripts/find_dangerous_apis.py +375 -0
- package/skills/zeroize-audit/tools/scripts/semantic_audit.py +923 -0
- package/skills/zeroize-audit/tools/track_dataflow.sh +196 -0
- package/skills/zeroize-audit/tools/validate_rust_toolchain.sh +298 -0
- package/skills/zeroize-audit/workflows/phase-0-preflight.md +150 -0
- package/skills/zeroize-audit/workflows/phase-1-source-analysis.md +144 -0
- package/skills/zeroize-audit/workflows/phase-2-compiler-analysis.md +139 -0
- package/skills/zeroize-audit/workflows/phase-3-interim-report.md +46 -0
- package/skills/zeroize-audit/workflows/phase-4-poc-generation.md +46 -0
- package/skills/zeroize-audit/workflows/phase-5-poc-validation.md +136 -0
- package/skills/zeroize-audit/workflows/phase-6-final-report.md +44 -0
- package/skills/zeroize-audit/workflows/phase-7-test-generation.md +42 -0
- package/themes/piolium-srcery.json +94 -0
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# /// script
|
|
3
|
+
# requires-python = ">=3.11"
|
|
4
|
+
# dependencies = []
|
|
5
|
+
# ///
|
|
6
|
+
"""
|
|
7
|
+
check_rust_asm_aarch64.py — AArch64 Rust assembly analysis backend.
|
|
8
|
+
|
|
9
|
+
⚠ EXPERIMENTAL — AArch64 support is incomplete. Findings should be treated as
|
|
10
|
+
indicative only and require manual verification before inclusion in a report.
|
|
11
|
+
|
|
12
|
+
Known limitations:
|
|
13
|
+
- x29 (frame pointer) and x30 (link register) are always saved in the prologue
|
|
14
|
+
via `stp x29, x30, [sp, #-N]!`. These appear as REGISTER_SPILL findings
|
|
15
|
+
because both are in AARCH64_CALLEE_SAVED. They are almost never carrying
|
|
16
|
+
secret values — reviewers should verify in context.
|
|
17
|
+
- `dc zva` (Data Cache Zero by Virtual Address) is not detected as a zero-store.
|
|
18
|
+
This instruction is rare in Rust-generated code but may be used in
|
|
19
|
+
highly-optimised zeroize implementations.
|
|
20
|
+
- AArch64 has no red zone (neither Linux nor macOS AAPCS64 define one). Leaf
|
|
21
|
+
functions must allocate stack space explicitly; no red-zone analysis needed.
|
|
22
|
+
- Apple AArch64 (M1/M2) and Linux AArch64 both use AAPCS64 with no red zone;
|
|
23
|
+
the analysis is platform-agnostic.
|
|
24
|
+
|
|
25
|
+
Called by check_rust_asm.py. Not intended for direct invocation.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
import re
|
|
29
|
+
|
|
30
|
+
# ---------------------------------------------------------------------------
|
|
31
|
+
# AArch64 register sets (AAPCS64)
|
|
32
|
+
# ---------------------------------------------------------------------------
|
|
33
|
+
|
|
34
|
+
AARCH64_CALLER_SAVED = {
|
|
35
|
+
# Integer/pointer: argument registers and temporaries
|
|
36
|
+
"x0",
|
|
37
|
+
"x1",
|
|
38
|
+
"x2",
|
|
39
|
+
"x3",
|
|
40
|
+
"x4",
|
|
41
|
+
"x5",
|
|
42
|
+
"x6",
|
|
43
|
+
"x7",
|
|
44
|
+
"x8",
|
|
45
|
+
"x9",
|
|
46
|
+
"x10",
|
|
47
|
+
"x11",
|
|
48
|
+
"x12",
|
|
49
|
+
"x13",
|
|
50
|
+
"x14",
|
|
51
|
+
"x15",
|
|
52
|
+
"x16",
|
|
53
|
+
"x17",
|
|
54
|
+
# SIMD/FP: v0–v7 and v16–v31 are caller-saved (argument/scratch)
|
|
55
|
+
"v0",
|
|
56
|
+
"v1",
|
|
57
|
+
"v2",
|
|
58
|
+
"v3",
|
|
59
|
+
"v4",
|
|
60
|
+
"v5",
|
|
61
|
+
"v6",
|
|
62
|
+
"v7",
|
|
63
|
+
"v16",
|
|
64
|
+
"v17",
|
|
65
|
+
"v18",
|
|
66
|
+
"v19",
|
|
67
|
+
"v20",
|
|
68
|
+
"v21",
|
|
69
|
+
"v22",
|
|
70
|
+
"v23",
|
|
71
|
+
"v24",
|
|
72
|
+
"v25",
|
|
73
|
+
"v26",
|
|
74
|
+
"v27",
|
|
75
|
+
"v28",
|
|
76
|
+
"v29",
|
|
77
|
+
"v30",
|
|
78
|
+
"v31",
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
AARCH64_CALLEE_SAVED = {
|
|
82
|
+
# Integer: x19–x28 must be preserved if used
|
|
83
|
+
"x19",
|
|
84
|
+
"x20",
|
|
85
|
+
"x21",
|
|
86
|
+
"x22",
|
|
87
|
+
"x23",
|
|
88
|
+
"x24",
|
|
89
|
+
"x25",
|
|
90
|
+
"x26",
|
|
91
|
+
"x27",
|
|
92
|
+
"x28",
|
|
93
|
+
# x29 = frame pointer (fp), x30 = link register (lr)
|
|
94
|
+
# NOTE: x29 and x30 are always saved in prologues; see limitations above.
|
|
95
|
+
"x29",
|
|
96
|
+
"x30",
|
|
97
|
+
# SIMD/FP: lower 64 bits of v8–v15 must be preserved
|
|
98
|
+
"v8",
|
|
99
|
+
"v9",
|
|
100
|
+
"v10",
|
|
101
|
+
"v11",
|
|
102
|
+
"v12",
|
|
103
|
+
"v13",
|
|
104
|
+
"v14",
|
|
105
|
+
"v15",
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
# ---------------------------------------------------------------------------
|
|
109
|
+
# Patterns (ARM GNU syntax, as emitted by LLVM for AArch64)
|
|
110
|
+
# ---------------------------------------------------------------------------
|
|
111
|
+
|
|
112
|
+
# Frame allocation
|
|
113
|
+
# Most common: pre-index pair store that saves fp/lr and decrements sp
|
|
114
|
+
RE_A64_FRAME_STP = re.compile(r"stp\s+x29,\s+x30,\s+\[sp,\s+#-(\d+)\]!")
|
|
115
|
+
# Alternative: explicit sub
|
|
116
|
+
RE_A64_FRAME_SUB = re.compile(r"sub\s+sp,\s+sp,\s+#(\d+)")
|
|
117
|
+
|
|
118
|
+
# Zero-store patterns
|
|
119
|
+
# str xzr/wzr, [sp, #N] — single 64-bit (xzr) or 32-bit (wzr) zero store to stack
|
|
120
|
+
RE_A64_STR_XZR = re.compile(r"\bstr\s+[xw]zr,\s+\[sp(?:,\s*#-?\d+)?\]")
|
|
121
|
+
# stp xzr, xzr / wzr, wzr, [sp, #N] — paired zero store (most efficient)
|
|
122
|
+
RE_A64_STP_XZR = re.compile(r"\bstp\s+[xw]zr,\s+[xw]zr,\s+\[sp(?:,\s*#-?\d+)?\]")
|
|
123
|
+
# movi vN.*, #0 — SIMD register zeroing (precedes stp qN)
|
|
124
|
+
RE_A64_MOVI_ZERO = re.compile(r"\bmovi\s+v\d+\.\w+,\s+#0\b")
|
|
125
|
+
# bl ...(memset|zeroize) — call to zeroize/memset routine
|
|
126
|
+
RE_A64_MEMSET = re.compile(r"\bbl\s+.*(?:memset|volatile_set_memory|zeroize)")
|
|
127
|
+
|
|
128
|
+
# Register spill patterns
|
|
129
|
+
# str xN/vN/qN, [sp, #offset] — single store to stack
|
|
130
|
+
RE_A64_STR_SPILL = re.compile(r"\bstr\s+(x\d+|v\d+|q\d+),\s+\[sp(?:,\s*#-?\d+)?\]")
|
|
131
|
+
# stp xN, xM / qN, qM, [sp, #offset] — pair store to stack (I31: also covers SIMD q pairs)
|
|
132
|
+
RE_A64_STP_SPILL = re.compile(r"\bstp\s+((?:x|q)\d+),\s+((?:x|q)\d+),\s+\[sp(?:,\s*#-?\d+)?\]")
|
|
133
|
+
|
|
134
|
+
# Return instruction (no suffix on AArch64, unlike x86-64's retq)
|
|
135
|
+
RE_A64_RET = re.compile(r"\bret\b")
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
# ---------------------------------------------------------------------------
|
|
139
|
+
# STACK_RETENTION (AArch64)
|
|
140
|
+
# ---------------------------------------------------------------------------
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def check_stack_retention(
|
|
144
|
+
func_name: str,
|
|
145
|
+
func_lines: list[tuple[int, str]],
|
|
146
|
+
) -> dict | None:
|
|
147
|
+
"""
|
|
148
|
+
Detect AArch64 stack frame allocated but not zeroed before return.
|
|
149
|
+
|
|
150
|
+
[EXPERIMENTAL] Findings require manual verification.
|
|
151
|
+
"""
|
|
152
|
+
frame_alloc_line: tuple[int, str] | None = None
|
|
153
|
+
frame_size = 0
|
|
154
|
+
has_zero_store = False
|
|
155
|
+
ret_line: tuple[int, str] | None = None
|
|
156
|
+
|
|
157
|
+
for lineno, line in func_lines:
|
|
158
|
+
# stp x29, x30, [sp, #-N]! — most common AArch64 prologue (pre-index)
|
|
159
|
+
m = RE_A64_FRAME_STP.search(line)
|
|
160
|
+
if m:
|
|
161
|
+
if frame_alloc_line is None:
|
|
162
|
+
frame_alloc_line = (lineno, line.strip())
|
|
163
|
+
frame_size += int(m.group(1))
|
|
164
|
+
|
|
165
|
+
# sub sp, sp, #N — additional explicit allocation (common with stp prologue)
|
|
166
|
+
# Accumulate rather than taking only the first allocation so that prologues
|
|
167
|
+
# using both stp+sub report the correct total frame size (I28).
|
|
168
|
+
m2 = RE_A64_FRAME_SUB.search(line)
|
|
169
|
+
if m2:
|
|
170
|
+
if frame_alloc_line is None:
|
|
171
|
+
frame_alloc_line = (lineno, line.strip())
|
|
172
|
+
frame_size += int(m2.group(1))
|
|
173
|
+
|
|
174
|
+
# Zero-store detection
|
|
175
|
+
if RE_A64_STR_XZR.search(line) or RE_A64_STP_XZR.search(line):
|
|
176
|
+
has_zero_store = True
|
|
177
|
+
if RE_A64_MOVI_ZERO.search(line) or RE_A64_MEMSET.search(line):
|
|
178
|
+
has_zero_store = True
|
|
179
|
+
|
|
180
|
+
if RE_A64_RET.search(line):
|
|
181
|
+
ret_line = (lineno, line.strip())
|
|
182
|
+
|
|
183
|
+
if frame_alloc_line and ret_line and not has_zero_store and frame_size > 0:
|
|
184
|
+
alloc_lineno, alloc_text = frame_alloc_line
|
|
185
|
+
ret_lineno, _ = ret_line
|
|
186
|
+
return {
|
|
187
|
+
"category": "STACK_RETENTION",
|
|
188
|
+
"severity": "high",
|
|
189
|
+
"symbol": func_name,
|
|
190
|
+
"detail": (
|
|
191
|
+
f"[EXPERIMENTAL] AArch64 stack frame of {frame_size} bytes allocated "
|
|
192
|
+
f"at line {alloc_lineno} ({alloc_text!r}) but no zero-store "
|
|
193
|
+
f"(str xzr / stp xzr,xzr / movi+stp / zeroize call) found "
|
|
194
|
+
f"before return at line {ret_lineno}"
|
|
195
|
+
),
|
|
196
|
+
"evidence_detail": (
|
|
197
|
+
f"{alloc_text} at line {alloc_lineno}; "
|
|
198
|
+
f"no str/stp xzr or zeroize call before ret at line {ret_lineno}"
|
|
199
|
+
),
|
|
200
|
+
}
|
|
201
|
+
return None
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
# ---------------------------------------------------------------------------
|
|
205
|
+
# REGISTER_SPILL (AArch64)
|
|
206
|
+
# ---------------------------------------------------------------------------
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def check_register_spill(
|
|
210
|
+
func_name: str,
|
|
211
|
+
func_lines: list[tuple[int, str]],
|
|
212
|
+
) -> list[dict]:
|
|
213
|
+
"""
|
|
214
|
+
Detect AArch64 registers spilled to the stack.
|
|
215
|
+
|
|
216
|
+
[EXPERIMENTAL] x29/x30 prologue saves will always appear here because both
|
|
217
|
+
are in AARCH64_CALLEE_SAVED. Reviewers should check whether those registers
|
|
218
|
+
actually hold sensitive values in the function under analysis.
|
|
219
|
+
"""
|
|
220
|
+
spills: list[tuple[int, str, str]] = [] # (lineno, reg, line)
|
|
221
|
+
|
|
222
|
+
for lineno, line in func_lines:
|
|
223
|
+
# Single store: str xN/vN/qN, [sp, ...]
|
|
224
|
+
m = RE_A64_STR_SPILL.search(line)
|
|
225
|
+
if m:
|
|
226
|
+
reg = m.group(1)
|
|
227
|
+
if reg in AARCH64_CALLEE_SAVED or reg in AARCH64_CALLER_SAVED:
|
|
228
|
+
spills.append((lineno, reg, line.strip()))
|
|
229
|
+
elif re.match(r"^q\d+$", reg):
|
|
230
|
+
# q registers are the 128-bit view of v registers; q8–q15 are
|
|
231
|
+
# partially callee-saved (lower 64 bits). For simplicity,
|
|
232
|
+
# classify all q-register spills as caller-saved (I31).
|
|
233
|
+
spills.append((lineno, reg, line.strip()))
|
|
234
|
+
|
|
235
|
+
# Pair store: stp xN, xM / qN, qM, [sp, ...]
|
|
236
|
+
m2 = RE_A64_STP_SPILL.search(line)
|
|
237
|
+
if m2:
|
|
238
|
+
for reg in (m2.group(1), m2.group(2)):
|
|
239
|
+
if reg == "xzr":
|
|
240
|
+
continue # zero register — this is a zero-store, not a spill
|
|
241
|
+
if (
|
|
242
|
+
reg in AARCH64_CALLEE_SAVED
|
|
243
|
+
or reg in AARCH64_CALLER_SAVED
|
|
244
|
+
or re.match(r"^q\d+$", reg)
|
|
245
|
+
):
|
|
246
|
+
spills.append((lineno, reg, line.strip()))
|
|
247
|
+
|
|
248
|
+
findings: list[dict] = []
|
|
249
|
+
seen: set[str] = set()
|
|
250
|
+
for lineno, reg, line_text in spills:
|
|
251
|
+
if reg not in seen:
|
|
252
|
+
seen.add(reg)
|
|
253
|
+
if reg in AARCH64_CALLEE_SAVED:
|
|
254
|
+
reg_class, severity = "callee-saved", "high"
|
|
255
|
+
elif (m := re.match(r"^q(\d+)$", reg)) and int(m.group(1)) in range(8, 16):
|
|
256
|
+
# q8–q15: lower 64 bits callee-saved per AAPCS64
|
|
257
|
+
reg_class, severity = "callee-saved (partial)", "high"
|
|
258
|
+
else:
|
|
259
|
+
reg_class, severity = "caller-saved", "medium"
|
|
260
|
+
findings.append(
|
|
261
|
+
{
|
|
262
|
+
"category": "REGISTER_SPILL",
|
|
263
|
+
"severity": severity,
|
|
264
|
+
"symbol": func_name,
|
|
265
|
+
"detail": (
|
|
266
|
+
f"[EXPERIMENTAL] AArch64 register {reg} ({reg_class}) spilled to "
|
|
267
|
+
f"stack at line {lineno} in function '{func_name}' "
|
|
268
|
+
f"— may expose secret value"
|
|
269
|
+
),
|
|
270
|
+
"evidence_detail": f"{line_text} at line {lineno}",
|
|
271
|
+
}
|
|
272
|
+
)
|
|
273
|
+
return findings
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
# ---------------------------------------------------------------------------
|
|
277
|
+
# Public entry point
|
|
278
|
+
# ---------------------------------------------------------------------------
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def analyze_function(
|
|
282
|
+
func_name: str,
|
|
283
|
+
func_lines: list[tuple[int, str]],
|
|
284
|
+
) -> list[dict]:
|
|
285
|
+
"""
|
|
286
|
+
Run all AArch64 checks for one sensitive function.
|
|
287
|
+
Returns a (possibly empty) list of finding dicts.
|
|
288
|
+
|
|
289
|
+
[EXPERIMENTAL] All returned findings carry [EXPERIMENTAL] in their detail
|
|
290
|
+
field and require manual verification.
|
|
291
|
+
"""
|
|
292
|
+
findings: list[dict] = []
|
|
293
|
+
|
|
294
|
+
f = check_stack_retention(func_name, func_lines)
|
|
295
|
+
if f:
|
|
296
|
+
findings.append(f)
|
|
297
|
+
|
|
298
|
+
findings.extend(check_register_spill(func_name, func_lines))
|
|
299
|
+
|
|
300
|
+
return findings
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# /// script
|
|
3
|
+
# requires-python = ">=3.11"
|
|
4
|
+
# dependencies = []
|
|
5
|
+
# ///
|
|
6
|
+
"""
|
|
7
|
+
check_rust_asm_x86.py — x86-64 Rust assembly analysis backend.
|
|
8
|
+
|
|
9
|
+
Called by check_rust_asm.py. Not intended for direct invocation.
|
|
10
|
+
|
|
11
|
+
Detects STACK_RETENTION, REGISTER_SPILL, and red-zone STACK_RETENTION in x86-64
|
|
12
|
+
AT&T-syntax assembly emitted by `cargo +nightly rustc --emit=asm`.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import re
|
|
16
|
+
|
|
17
|
+
# ---------------------------------------------------------------------------
|
|
18
|
+
# x86-64 register sets (System V ABI — identical for C/C++ and Rust)
|
|
19
|
+
# ---------------------------------------------------------------------------
|
|
20
|
+
CALLER_SAVED = {
|
|
21
|
+
"rax",
|
|
22
|
+
"rcx",
|
|
23
|
+
"rdx",
|
|
24
|
+
"rsi",
|
|
25
|
+
"rdi",
|
|
26
|
+
"r8",
|
|
27
|
+
"r9",
|
|
28
|
+
"r10",
|
|
29
|
+
"r11",
|
|
30
|
+
# xmm0-xmm7 are function arguments / scratch; xmm8-xmm15 are also caller-saved
|
|
31
|
+
# (System V AMD64 ABI §3.2.1: XMM registers 0–15 are all caller-saved)
|
|
32
|
+
"xmm0",
|
|
33
|
+
"xmm1",
|
|
34
|
+
"xmm2",
|
|
35
|
+
"xmm3",
|
|
36
|
+
"xmm4",
|
|
37
|
+
"xmm5",
|
|
38
|
+
"xmm6",
|
|
39
|
+
"xmm7",
|
|
40
|
+
"xmm8",
|
|
41
|
+
"xmm9",
|
|
42
|
+
"xmm10",
|
|
43
|
+
"xmm11",
|
|
44
|
+
"xmm12",
|
|
45
|
+
"xmm13",
|
|
46
|
+
"xmm14",
|
|
47
|
+
"xmm15",
|
|
48
|
+
}
|
|
49
|
+
CALLEE_SAVED = {"rbx", "r12", "r13", "r14", "r15", "rbp"}
|
|
50
|
+
|
|
51
|
+
# ---------------------------------------------------------------------------
|
|
52
|
+
# Patterns
|
|
53
|
+
# ---------------------------------------------------------------------------
|
|
54
|
+
|
|
55
|
+
# Frame allocation
|
|
56
|
+
RE_FRAME_ALLOC = re.compile(r"subq\s+\$(\d+),\s+%rsp")
|
|
57
|
+
RE_PUSH = re.compile(r"push[ql]\s+%(\w+)")
|
|
58
|
+
|
|
59
|
+
# Zero-store patterns (volatile wipe) — all widths that can clear secret bytes
|
|
60
|
+
RE_MOVQ_ZERO = re.compile(r"movq\s+\$0,\s+-?\d+\(%r[sb]p\)")
|
|
61
|
+
RE_MOVL_ZERO = re.compile(r"movl\s+\$0,\s+-?\d+\(%r[sb]p\)")
|
|
62
|
+
RE_MOVW_ZERO = re.compile(r"movw\s+\$0,\s+-?\d+\(%r[sb]p\)")
|
|
63
|
+
RE_MOVB_ZERO = re.compile(r"movb\s+\$0,\s+-?\d+\(%r[sb]p\)")
|
|
64
|
+
RE_MEMSET_CALL = re.compile(r"call\s+.*(?:memset|volatile_set_memory|zeroize)")
|
|
65
|
+
# SIMD self-XOR zeroing: xorps/pxor/vpxor %regN, %regN — register is zeroed,
|
|
66
|
+
# typically followed by a store that constitutes the actual wipe.
|
|
67
|
+
RE_SIMD_ZERO = re.compile(r"(?:xorps|xorpd|pxor|vpxor)\s+%(\w+),\s+%(\w+)")
|
|
68
|
+
|
|
69
|
+
# Register spills: movq/movdqa/movups/movaps %reg, N(%rsp|%rbp)
|
|
70
|
+
RE_REG_SPILL = re.compile(r"mov(?:q|dqa|ups|aps)\s+%(\w+),\s+(-?\d+)\(%r[sb]p\)")
|
|
71
|
+
|
|
72
|
+
# Return instruction. Stripping the AT&T comment character (#) before
|
|
73
|
+
# applying this pattern prevents false matches inside assembly comments
|
|
74
|
+
# (e.g. "# retq is the encoding for ...").
|
|
75
|
+
RE_RET = re.compile(r"\bret[ql]?\b")
|
|
76
|
+
|
|
77
|
+
# Red zone: stores to [rsp - N] (N ≤ 128) in leaf functions without subq
|
|
78
|
+
RE_RED_ZONE = re.compile(r"mov(?:q|l|b|w)\s+%\w+,\s+-(\d+)\(%rsp\)")
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# ---------------------------------------------------------------------------
|
|
82
|
+
# STACK_RETENTION
|
|
83
|
+
# ---------------------------------------------------------------------------
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def check_stack_retention(
|
|
87
|
+
func_name: str,
|
|
88
|
+
func_lines: list[tuple[int, str]],
|
|
89
|
+
) -> dict | None:
|
|
90
|
+
"""
|
|
91
|
+
Detect stack frame allocated (subq $N, %rsp) but not zeroed before return.
|
|
92
|
+
"""
|
|
93
|
+
frame_alloc_line: tuple[int, str] | None = None
|
|
94
|
+
frame_size = 0
|
|
95
|
+
has_zero_store = False
|
|
96
|
+
ret_line: tuple[int, str] | None = None
|
|
97
|
+
|
|
98
|
+
for lineno, line in func_lines:
|
|
99
|
+
# Strip trailing AT&T-style comments before pattern matching to avoid
|
|
100
|
+
# false positives from `# retq` or `# movq $0, ...` in comments (I25).
|
|
101
|
+
code = line.split("#", 1)[0]
|
|
102
|
+
|
|
103
|
+
m = RE_FRAME_ALLOC.search(code)
|
|
104
|
+
if m and frame_alloc_line is None:
|
|
105
|
+
frame_alloc_line = (lineno, line.strip())
|
|
106
|
+
frame_size = int(m.group(1))
|
|
107
|
+
|
|
108
|
+
if (
|
|
109
|
+
RE_MOVQ_ZERO.search(code)
|
|
110
|
+
or RE_MOVL_ZERO.search(code)
|
|
111
|
+
or RE_MOVW_ZERO.search(code)
|
|
112
|
+
or RE_MOVB_ZERO.search(code)
|
|
113
|
+
):
|
|
114
|
+
has_zero_store = True
|
|
115
|
+
if RE_MEMSET_CALL.search(code):
|
|
116
|
+
has_zero_store = True
|
|
117
|
+
# SIMD self-XOR (xorps/pxor %xmmN, %xmmN) zeroes a register; treat
|
|
118
|
+
# as a zero-store signal to avoid false-positive STACK_RETENTION when
|
|
119
|
+
# the function wipes data via SIMD before returning (I26).
|
|
120
|
+
m2 = RE_SIMD_ZERO.search(code)
|
|
121
|
+
if m2 and m2.group(1) == m2.group(2):
|
|
122
|
+
has_zero_store = True
|
|
123
|
+
|
|
124
|
+
if RE_RET.search(code):
|
|
125
|
+
ret_line = (lineno, line.strip())
|
|
126
|
+
|
|
127
|
+
if frame_alloc_line and ret_line and not has_zero_store and frame_size > 0:
|
|
128
|
+
alloc_lineno, alloc_text = frame_alloc_line
|
|
129
|
+
ret_lineno, _ = ret_line
|
|
130
|
+
return {
|
|
131
|
+
"category": "STACK_RETENTION",
|
|
132
|
+
"severity": "high",
|
|
133
|
+
"symbol": func_name,
|
|
134
|
+
"detail": (
|
|
135
|
+
f"Stack frame of {frame_size} bytes allocated at line {alloc_lineno} "
|
|
136
|
+
f"({alloc_text!r}) but no zero-store found before return at line {ret_lineno}"
|
|
137
|
+
),
|
|
138
|
+
"evidence_detail": (
|
|
139
|
+
f"{alloc_text} at line {alloc_lineno}; "
|
|
140
|
+
f"no volatile wipe before retq at line {ret_lineno}"
|
|
141
|
+
),
|
|
142
|
+
}
|
|
143
|
+
return None
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
# ---------------------------------------------------------------------------
|
|
147
|
+
# REGISTER_SPILL
|
|
148
|
+
# ---------------------------------------------------------------------------
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def check_register_spill(
|
|
152
|
+
func_name: str,
|
|
153
|
+
func_lines: list[tuple[int, str]],
|
|
154
|
+
) -> list[dict]:
|
|
155
|
+
"""
|
|
156
|
+
Detect registers spilled to the stack (potential secret exposure).
|
|
157
|
+
"""
|
|
158
|
+
spills: list[tuple[int, str, str, str]] = [] # (lineno, reg, line, class)
|
|
159
|
+
|
|
160
|
+
for lineno, line in func_lines:
|
|
161
|
+
m = RE_REG_SPILL.search(line)
|
|
162
|
+
if m:
|
|
163
|
+
reg = m.group(1)
|
|
164
|
+
if reg in CALLER_SAVED:
|
|
165
|
+
spills.append((lineno, reg, line.strip(), "caller-saved"))
|
|
166
|
+
elif reg in CALLEE_SAVED:
|
|
167
|
+
spills.append((lineno, reg, line.strip(), "callee-saved"))
|
|
168
|
+
|
|
169
|
+
findings = []
|
|
170
|
+
seen: set[str] = set()
|
|
171
|
+
for lineno, reg, line_text, reg_class in spills:
|
|
172
|
+
if reg not in seen:
|
|
173
|
+
seen.add(reg)
|
|
174
|
+
severity = "high" if reg_class == "callee-saved" else "medium"
|
|
175
|
+
findings.append(
|
|
176
|
+
{
|
|
177
|
+
"category": "REGISTER_SPILL",
|
|
178
|
+
"severity": severity,
|
|
179
|
+
"symbol": func_name,
|
|
180
|
+
"detail": (
|
|
181
|
+
f"Register %{reg} ({reg_class}) spilled to stack at line {lineno} "
|
|
182
|
+
f"in function '{func_name}' — may expose secret value"
|
|
183
|
+
),
|
|
184
|
+
"evidence_detail": f"{line_text} at line {lineno}",
|
|
185
|
+
}
|
|
186
|
+
)
|
|
187
|
+
return findings
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
# ---------------------------------------------------------------------------
|
|
191
|
+
# RED ZONE (x86-64 specific)
|
|
192
|
+
# ---------------------------------------------------------------------------
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def check_red_zone(
|
|
196
|
+
func_name: str,
|
|
197
|
+
func_lines: list[tuple[int, str]],
|
|
198
|
+
) -> dict | None:
|
|
199
|
+
"""
|
|
200
|
+
Detect x86-64 leaf functions that store data in the red zone without zeroing.
|
|
201
|
+
|
|
202
|
+
The x86-64 System V ABI reserves 128 bytes below %rsp as a "red zone" that
|
|
203
|
+
leaf functions may use as scratch space without adjusting %rsp. Sensitive data
|
|
204
|
+
written to this region is NOT zeroed by the callee and persists after return.
|
|
205
|
+
This check only fires when no subq frame allocation is present (non-leaf
|
|
206
|
+
functions are covered by check_stack_retention).
|
|
207
|
+
"""
|
|
208
|
+
# Only applies to leaf functions (no regular frame allocation)
|
|
209
|
+
if any(RE_FRAME_ALLOC.search(line) for _, line in func_lines):
|
|
210
|
+
return None
|
|
211
|
+
|
|
212
|
+
red_zone_depth = 0
|
|
213
|
+
has_zero_store = False
|
|
214
|
+
has_ret = False
|
|
215
|
+
|
|
216
|
+
for _, line in func_lines:
|
|
217
|
+
code = line.split("#", 1)[0] # strip AT&T comments (I25)
|
|
218
|
+
|
|
219
|
+
m = RE_RED_ZONE.search(code)
|
|
220
|
+
if m:
|
|
221
|
+
offset = int(m.group(1))
|
|
222
|
+
if offset <= 128:
|
|
223
|
+
red_zone_depth = max(red_zone_depth, offset)
|
|
224
|
+
|
|
225
|
+
if (
|
|
226
|
+
RE_MOVQ_ZERO.search(code)
|
|
227
|
+
or RE_MOVL_ZERO.search(code)
|
|
228
|
+
or RE_MOVW_ZERO.search(code)
|
|
229
|
+
or RE_MOVB_ZERO.search(code)
|
|
230
|
+
):
|
|
231
|
+
has_zero_store = True
|
|
232
|
+
if RE_MEMSET_CALL.search(code):
|
|
233
|
+
has_zero_store = True
|
|
234
|
+
m2 = RE_SIMD_ZERO.search(code)
|
|
235
|
+
if m2 and m2.group(1) == m2.group(2):
|
|
236
|
+
has_zero_store = True
|
|
237
|
+
if RE_RET.search(code):
|
|
238
|
+
has_ret = True
|
|
239
|
+
|
|
240
|
+
if red_zone_depth > 0 and has_ret and not has_zero_store:
|
|
241
|
+
return {
|
|
242
|
+
"category": "STACK_RETENTION",
|
|
243
|
+
"severity": "high",
|
|
244
|
+
"symbol": func_name,
|
|
245
|
+
"detail": (
|
|
246
|
+
f"Leaf function '{func_name}' stores {red_zone_depth} bytes in the "
|
|
247
|
+
f"x86-64 red zone (below %rsp) without zeroing before return — "
|
|
248
|
+
f"sensitive data may persist in the 128-byte region below %rsp"
|
|
249
|
+
),
|
|
250
|
+
"evidence_detail": (
|
|
251
|
+
f"red zone depth -{red_zone_depth}(%rsp); "
|
|
252
|
+
f"no mov[qwlb] $0 or memset/zeroize call before retq"
|
|
253
|
+
),
|
|
254
|
+
}
|
|
255
|
+
return None
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
# ---------------------------------------------------------------------------
|
|
259
|
+
# Public entry point
|
|
260
|
+
# ---------------------------------------------------------------------------
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def analyze_function(
|
|
264
|
+
func_name: str,
|
|
265
|
+
func_lines: list[tuple[int, str]],
|
|
266
|
+
) -> list[dict]:
|
|
267
|
+
"""
|
|
268
|
+
Run all x86-64 checks for one sensitive function.
|
|
269
|
+
Returns a (possibly empty) list of finding dicts.
|
|
270
|
+
"""
|
|
271
|
+
findings: list[dict] = []
|
|
272
|
+
|
|
273
|
+
f = check_stack_retention(func_name, func_lines)
|
|
274
|
+
if f:
|
|
275
|
+
findings.append(f)
|
|
276
|
+
|
|
277
|
+
findings.extend(check_register_spill(func_name, func_lines))
|
|
278
|
+
|
|
279
|
+
f = check_red_zone(func_name, func_lines)
|
|
280
|
+
if f:
|
|
281
|
+
findings.append(f)
|
|
282
|
+
|
|
283
|
+
return findings
|