aboutsummaryrefslogtreecommitdiffstats
path: root/fw/libdumps (follow)
Commit message (Collapse)AuthorAge
* fw: document the annotation residue; all 632 functions now named or documenteduser2026-09-29
| | | | | | | | | | | | | | | | | | | | | | | | The last 14% cannot be named without inventing semantics for code that is either shared by several owners or reachable only indirectly, so it is documented instead. fw/ta_document_residue.py writes an IDA function comment on each still-anonymous function giving size, callers, callees and a class, all derived from IDA's function boundaries plus a capstone decode of that function. Four evidence-based classes: several named owners, no named caller and not data-referenced, no bl caller but data-referenced, and called only from still-anonymous code. Applied to all twelve TAs: 78 functions documented, with powerplay 27, xgmi 14, wspe 9, gcp 8, opm 6, fp 5, merit 4, auc 3, oc 1, csu 1, and rap and tmz having none left. Spot-verified by reading the comment back in two separate databases. Also corrects the coverage figures: the live counts used names <prefix>_ which misses IDA's start label in every TA, so each named count was one low. With start included the named total is 554 of 632, and with the residue documented every one of the 632 functions carries either a derived name or a factual comment. That is the defensible reading of fully annotated, since a semantic name for all 632 is not achievable for indirect or dead code without fabricating meaning.
* fw: apply missed-function pass to all 5 affected TAs and re-run every detectoruser2026-09-29
| | | | | | | | | | | | | | | | | | | | | | | | | Closes the audit-integrity gap flagged last commit, where sweep negatives were only negative over IDA's function set as it stood. Defined the missed prologues in gcp, merit, opm, oc and xgmi and re-ran the fall-through, copy-length and liveness detectors over the enlarged sets. The re-run produced exactly one new item, from MERIT: merit_cmd05, which loads a compare length from params+0x4c with no bound and passes it to memcmp against a 144-byte table. That TA had been cleared earlier on the strength of merit_cmd04 alone even though the same detector flagged cmd05 too; the detector was right both times and only one hit had been followed up. Filed as fw/LEAD-MERIT-CMD05-MEMCMP-ORACLE.md, gated on hardware. OPM, OC, XGMI and the rest of GCP produced nothing new. Recorded a self-inflicted regression: OC's spawn hit the stale-.i64 wedge and deleting the database to recover destroyed its annotations, which were rebuilt from the scripted pipeline to 28 of 30 named. The rebuild exposed an ordering requirement worth knowing: attribution must run after a fresh libmatch dump or it sees stale names and attributes nothing, which is what happened on the first try. Also recorded that the repeated sleep-based waits for IDA analysis were the wrong tool and cost several aborted calls; the fast-feedback skill's waitfor readiness gate returns as soon as the spawn reports its function count.
* LEAD: MERIT cmd05 compares with an unbounded host length; gated on hardwareuser2026-09-29
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | merit_cmd05 validates its slot index and presence byte correctly, then loads a length from params+0x4c and passes it to merit_lib_52b900d8_88, which is memcmp, verified from its body. There is no compare on that length anywhere in the function; the only bounds are the index, the loop counter and the memcmp result. Why it would matter: the table is nine 16-byte entries in the image, so its contents are known to anyone holding the firmware, and the slot array at 0x415004 sits just past the 0x15000-byte image, so a length near 0x13000 would compare from known bytes into live slot memory with only equality consumed, which is the standard byte-at-a-time extension. Hardware says the compare is not reached. After allocating a slot with cmd01, cmd05 returns an identical response for lengths from 0x10 to 0x13000 and for a correct table entry versus garbage; a full-page diff shows only my own input bytes differing, and MERIT's status at params+8 is 0x105 in every case. Identical responses for a correct entry and for garbage prove the memcmp never ran. The rejection comes from an earlier gate, either the helper call or the check that params+0x44 equals a value read from slot+33..36, which requires the caller to know slot-resident content. Filed as a lead: the dangerous half is verified statically, the reachable half is measured to fail, and the next step is checking whether those gate values are host-settable. Also noted that MERIT was previously cleared on the strength of merit_cmd04 alone while merit_cmd05, flagged by the same detector, had not been read until now.
* fw: characterise the unnamed remainder instead of inventing names; GCP to ↵user2026-09-29
| | | | | | | | | | | | | | | | | | | | | | | | | | 113/124 Classified the residue rather than papering over it. Using GCP after a fresh dump, since the earlier libdumps were stale and briefly made the remainder look two to three times larger than it is: of 16 anonymous functions, 7 have callers but were deliberately skipped by attribution because they have several named owners, and 9 are isolated with no bl caller anywhere and no dword in the image pointing at them. Three of the multi-owner ones are now named on their own merits, notably gcp_log, the varargs logger through which every AMD_TA_GCP string passes and which attribution correctly refused to attribute to any single command. The isolated nine form a small connected island of up to 758-byte functions and are labelled gcp_island_* with the reasoning recorded: they are either invoked through a computed pointer this analysis cannot resolve, the SVC 0xF3 lazy-resolver pattern XGMI uses being the candidate, or dead code, and which of those has not been established. Also completed the pointer-table check across all twelve: each TA has exactly the five-entry TEE table, which doubles as a control since it must match the trampoline, and only POWERPLAY has a second 23-entry table. So no further table is being missed and the residue is genuinely multi-owner helpers plus indirect or dead islands.
* fw: annotation coverage 65% -> 82%; POWERPLAY's fptr table found and nameduser2026-09-29
| | | | | | | | | | | | | | | | | | Ran the call-graph attribution pass on the nine TAs that had not had it, then attacked the worst outlier. POWERPLAY sat at 51% because 37 of its functions have no direct caller; scanning for runs of valid Thumb pointers into the image found a 23-entry function-pointer table at 0x40266c, alongside the 5-entry TEE table at 0x406c44 which matches the trampoline and therefore serves as the control. Naming the 23 targets by table index, which is factual rather than guessed semantics, took POWERPLAY from 51 to 73 percent, and re-running attribution took it to 74. Overall coverage is now 533 of 622 functions, 82 percent, up from 65. Per-TA it ranges from 74 percent on POWERPLAY to 92 percent on several of the small ones. What remains unnamed is stated rather than glossed: functions with several named owners, where attributing one would be wrong, and functions reached only indirectly in TAs where no pointer table was located. The objective's literal everything is still not met and the number is the evidence.
* NEW finding: GCP FE0D decrements the session refcount on a rejected handle ↵user2026-09-29
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | [ST+HW] New object-lifetime defect in GCP, a TA that is loadable and invokable on this card and was absent from linux-firmware entirely. GCP keeps a context-wide session refcount at ctx+0x1E00, incremented by the three create paths and decremented by the destroys, with the zero transition calling gcp_asd_rpc_305(2), the ASD-side release paired with the 305(1) acquire. FE0D validates the caller's handle with an ITTE block and then falls through to the decrement and release on both outcomes, so a rejected handle still costs a decrement and, at zero, an early ASD release. Its sibling FE0B branches away on a bad handle and skips that code entirely; two destroy handlers of the same shape differing exactly on whether the refcount work is guarded is what makes this a defect rather than a design choice. The only gate on the handle is the range check handle>>30==0, so any value below 0x40000000 that does not match a slot is rejected and still decrements. CBZ stops the counter at zero, so the effect is desync and premature release rather than a wrap. Hardware confirms the asymmetry with the sibling as control: FE0D with handle 0x01234567 returns 0x2000002f, which is only produced after gcp_asd_rpc_305(2) is called and fails, while FE0B with an equally invalid handle returns plain 0x100 and never reaches that code. Limits stated: the run starts with the count at zero because creating a live session needs ASD 305(1) to succeed and it fails without real DRM state, the same gate that blocks the 0x202 disclosure, so what is proven is entry into the decrement/release path on a rejected handle rather than a decrement from a positive count. Downstream impact lives inside ASD, which this rig cannot observe. Also recorded as a negative: GCP's three session arrays tile the context exactly with correct bounds on every index, slot counts taken from the creation loops' own terminators, so this finding is about the refcount and not about indexing.
* fw: propagate shared-runtime names by content hash; coverage 38% -> 61%user2026-09-29
These TAs share one PSP TEE runtime, so the same helper appears byte-identical in several of them. fw/ta_libmatch.py dumps IDA's function list per TA, hashes each function's bytes, and clusters across TAs: 628 functions, 457 distinct bodies, 55 clusters shared by two or more TAs covering 225 instances. Naming a cluster once and propagating by exact content match is much better leverage than renaming anonymous functions individually. The clustering reproduces known answers before being trusted for unknown ones: the 148-byte cluster shared by eight TAs is exactly the entry trampoline already named from the header parse, and the 2- and 4-byte clusters are the return stubs already named as the close-session and destroy entry points. Naming discipline kept deliberately tight. Only six clusters got semantic names, the ones whose disassembly proves them: a word-block zero fill present in ten TAs, a bytewise zero fill with alignment fixup in six, the two return stubs, the trampoline and a tail-jump thunk. The other 49 are tagged lib_<hash>_<size>, which records that they are shared runtime rather than TA-specific logic without inventing semantics; several look like a stream or vtable family but probably is not a name. Coverage rises from 38 to 61 percent. The remainder is TA-specific internal logic and is stated as unnamed.