aboutsummaryrefslogtreecommitdiffstats
path: root/fw/audits (follow)
Commit message (Collapse)AuthorAge
* tenth pass: field-flow v2 on IDA boundaries with verified writers; TAs and ↵user2026-09-29
| | | | | | | | | | | | | | | | | | | | | | | | | | | | ASD clean Rebuilds the retracted v1 without its cause. Function boundaries come from IDA and are cached, so every instruction decoded starts at a real function entry rather than from a resynchronising sweep; every writer is re-verified to disassemble inside its own function to a genuine store of the [base,#imm] form, with reject counts printed so the filtering is visible; stores through sp and fp are excluded as stack slots rather than fields; and a source register assigned an immediate now counts as bounded, which v1 missed and which had it reporting a constant-zero store as an unbounded writer. The positive control now passes for the right reason: HDCP's site is reported with two writers that were hand-verified to disassemble to strb.w sb,[r5,#0x38], the very field whose value the bug loads as its length, with the same base register. The negative control still holds. Results: zero sites across all twelve TAs and zero in ASD, with SYS_DRV at 42 still dominated by offset-keyed conflation. States the blind spot in that zero. v2 tracks struct-field stores, while the XGMI finding keeps its length in a stack slot written by a callee through a pointer, so no store of the tracked form writes it and v2 cannot see it, which is why xgmi reports zero here though v1 reported that site. The zero therefore means no copy length comes from a struct field with an unbounded writer, not that no unbounded length exists, and callee-written in/out length slots remain outside its scope despite being the class that produced the one real finding in this hunt.
* fw: restore AMD's own function names and apply prototypes/types across the TAsuser2026-09-29
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Names alone left Hex-Rays emitting *(_DWORD *)(a1 + 7680) for the most important expression in this audit, so this adds function prototypes and structure types, and in the process corrects names that had drifted. GCP logs AMD_TA_GCP: <Name> ENTER on entry to most handlers, which is ground truth. fw/ta_name_audit.py checks every derived symbol against it and found 18 mismatches in GCP, plus two false positives in WSPE where the logged words are ordinary prose. Combined with a fresh decode of the dispatcher's two tbb tables, giving command to handler address rather than the stale name-keyed map, this corrects: cmd 3 is ActivateDRMCertAuthSession and was not defined as a function at all; FE03 to FE06 are unimplemented and share the error arm; FE07 is CreateAuthenticatedSession; FE0C and FE0D are Create and DestroyCryptoSession; FE0A is QueryAuthenticatedSession rather than the invented QueryAccessibilityAttributes; FE0E and FE0F likewise. The hardware sweep independently agrees, since FE03 to FE06 all returned an identical 0x102 while FE07 alone returned 0, which had been sitting in the output mislabelled. This also corrects a finding's wording: FE0B manages the stride-80 auth array and FE0D the stride-88 crypto array, so they are two destroy paths over different arrays rather than a same-type sibling pair. The in-database comment is updated, including replacing its now-stale 'not yet shown' caveat with the hardware result. Five structures declared and verified by reading the layout back from IDA and comparing against the addressing the code performs; the three session arrays tile the context exactly, which is the check. 179 prototypes applied across all twelve TAs with no failures. Measured against a pristine database rather than one that already carries the work: raw casts 81 to 7, anonymous parameters 113 to 0. Method notes recorded: do not drive the TUI struct editor, since a focus change types into the filter box and a half-applied redeclare silently truncated three structs; declare interdependent structs in one parse unit; build prototypes from the database rather than the stale dump; a type that misrepresents addressing is worse than none, which is why the lvar retype was reverted; and two metric bugs were caught only by disbelieving a number that flattered or unfairly punished the work. Adds fw/ta_ida.py, fw/ta_name_audit.py, fw/ta_rename_from_logstrings.py, fw/cmdmaps/gcp_ea.json, fw/audits/name_vs_logstring.json and fw/pseudo/ artifacts.
* fw: fix the opcode rule (req[0] = store at [SP]); AUC/FP/XGMI reach was ↵user2026-09-29
| | | | | | | | | | | | | | | | | | | | | | | | misread as empty The opcode recovery was taking the first stack store in each thunk, which is a neighbouring field rather than req[0]. XGMI stores 2 to var_78 before storing the real opcode 0x1067 to var_7C, so every XGMI, AUC and FP opcode came out as 0x2 and their privilege reach looked empty. The rule is now the immediate stored at [SP], the store whose var_ offset equals the frame size. Re-ran all twelve TAs. AUC and FP each reach twelve SYS_DRV opcodes, XGMI thirteen including the 0x104x and 0x106x families, OC ten. GCP's ASD set is unchanged, so the 0x202 finding is unaffected. That also retracts my own claim from two commits ago that AUC, FP and XGMI use a different kernel ABI whose reach is not enumerable. They use the same svc 0xF2 ABI; my rule was misreading it. The SVC 0xF3 stub is real but is a lazy import resolver -- XGMI's sub_403CA0 caches a resolved pointer in a global and tail-calls it, and its callers build an ordinary request struct with the opcode at offset 0 -- so it is another route to the same service RPC rather than a separate privilege channel. This supersedes both earlier SVC tables in the document.
* fw: corrected IDA-derived SVC tables for all twelve TAs; retract the shifted ↵user2026-09-29
| | | | | | | | | | | | | | | | | | | | | | | | | | | | sweep table Rewrote fw/ta_rpc_names.py so the linear sweep only contributes "a service call happens near here" and IDA resolves the containing function, service id and opcode from its own disassembly. The previous table, published one commit ago, had a shifted opcode column and is now explicitly superseded. Site confirmation is 100% for every TA except MERIT, where one of thirteen candidates is rejected as not being an SVC at all -- the sweep misaligning and the cross-check catching it, which is the reason the tool reports agreement counts. Both ASD-reachability findings survive the correction and now rest on IDA's own analysis rather than my sweep: GCP reaches ASD 0x202, the canary disclosure, and OPM reaches ASD 0x301, the content-controlled write of up to 5120 bytes. Both had been dismissed only because no then-known TA emitted them. Recorded honestly: AUC, FP and XGMI resolve almost nothing because they do not use the req[0]-opcode form. XGMI caches a handle via SVC 0xF3 in a global and calls indirectly through it, so their privilege reach is not enumerable by scanning for opcode immediates. That is flagged as an open item rather than reported as "no calls". Every thunk in every TA is now named from IDA-derived values, or reset to sub_<addr> where unresolved, so no function carries a guessed name.
* fw: IDA-authoritative RPC thunk naming; GCP 0x202 -> ASD verified three waysuser2026-09-29
| | | | | | | | | | | | | | | | | | | | | | | | | Added fw/ta_rpc_names.py, which treats the linear sweep as a candidate generator and IDA as the arbiter: for every svc site it asks IDA what instruction is really there and which function contains it, and it reports the disagreement count so a silently misaligned scan cannot pass. On GCP, 25 of 25 candidates are confirmed by IDA as real SVC instructions. That check caught a real error. The thunk names first applied from the sweep were off by one thunk in GCP's table, each function receiving the previous one's opcode, because the resumable sweep is misaligned in that region. Re-deriving every opcode from IDA's own disassembly lines gives the corrected service-2 table, with 0x405446 carrying opcode 0x202. The headline finding is now verified three independent ways: aligned capstone disassembly from a function boundary, IDA's disassembly of the thunk, and IDA xrefs for the callers, which are gcp_cmd04_GetContentKey and gcp_cmdFE02_GetObfKeyCryptoSession. As a control the neighbouring 0x305 thunk resolves to five different GCP commands, so the caller query discriminates rather than returning everything. Also fixed the tooling to use one persistent RPC connection. The first version spawned a subprocess per call, and since the socket is single-driver a hung client locked out every later call.
* fw: SVC surface of the new TAs -- GCP forwards ASD 0x202, overturning a ↵user2026-09-29
standing negative Added fw/ta_svc_scan.py, which enumerates every SVC site per TA and recovers service-2/service-1 RPC opcodes from the store into req[0], per the ABI in GUARD-INIT-CHAIN.md. Validated against HDCP first: 49 svc 0xF2 sites, matching the count in the original 89-site audit, with service-1 opcodes matching its documented list. The result that matters: VULN-ASD-202-CANARY-LEAK.md rates the ASD canary disclosure not host-reachable on three axes, the load-bearing one being that no host-reachable TA forwards opcode 0x202. That scan covered ASD, HDCP, DTM and RAS, the only TAs known at the time. GCP contains a thunk at 0x405446 that sets req[0]=0x202 and service 2 and issues svc 0xF2, and it is called from two host-invokable GCP commands, cmd04 GetContentKey at 0x4037e8 and cmdFE02 at 0x403c42. OPM likewise emits ASD 0x301, the 5120-byte content-controlled write that was dismissed only because nothing emitted it. So that axis is now false: GCP and OPM are measured host-invokable on this card and they reach ASD opcodes the four-TA scan concluded nobody reaches. Deliberately not claimed: a host-visible leak. ASD 0x202 copies its sixteen uninitialised stack bytes to a caller-supplied pointer, and the caller here is GCP, so the canary lands in GCP's buffer; host visibility requires GCP to copy that region into its response, which is the next step. Also recorded as a warning attached to the result: the scan's resumable sweep can fabricate instructions, and an earlier pass reported this very movw from a misaligned window. Every opcode quoted for GCP and OPM was re-verified by disassembling from a function boundary, and the call sites by disassembling around them; the rest of the table is leads until checked the same way.