aboutsummaryrefslogtreecommitdiffstats
path: root/fw/decomp/ras_reference_ras2.c (follow)
Commit message (Collapse)AuthorAge
* make the artifacts agree with themselves: 1,018 banner/body name mismatches ↵user2026-10-01
| | | | | | | | | | | | | | | | | | | | | | | | | | down to 7 The thunk naming in the previous commits set row["name"] in this script's metadata only, never in the database. So the emitted banner read j_asd_clear_link_outputs while the signature inside the body still read j_sub_D07E10_16. Checking for it found 1,018 functions disagreeing with their own banner, most introduced by the earlier repair pass rather than the newest one. A reader who sees the two differ cannot tell which to trust, which is worse than leaving the function anonymous -- and I had claimed in the last commit message that no j_sub_ names remained in any artifact, which was false. ta_re.py now pushes every derived name into the database and re-decompiles whatever still disagrees. Two details were needed, both found by measuring the result rather than trusting the step: Names must be uniquified first. Several distinct thunks branch to the same target, so j_<target> is not unique and a colliding rename is silently refused -- on the ASD reference only 55 of 786 reconciled until duplicates were suffixed. Those suffixes are honest, unlike the address-derived _N names dropped earlier: these are genuinely separate functions with a shared destination. And the resolution has to run after address-derived names are dropped, since before that point the rows still carry a stale j_sub_<addr> label and look named, so the pass skipped all of them. Regenerated asd, gcp, powerplay, xgmi, wspe and ras. Mismatches 1,018 -> 7 corpus-wide; the 7 are ASD functions where the decompiler emits no signature line this check can read. Meaningful names hold at 19,038 (65.8%), address-derived 0, field share 75.5%.
* correct an overclaim: the artifacts are not byte-reproducible, and persist ↵user2026-10-01
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | DISPATCH names An earlier pass reported that all 99 artifacts regenerate byte-identically. That was true of that one run and does not hold in general. A full clean regeneration of all sixteen modules changes 220 files, and the cause is IDA's auto-analysis not being identical across fresh databases: AUC's function count comes back 39 instead of 40, and decompiled text varies cosmetically ("(int)nullsub_3" against "nullsub_3"). The claim is corrected here rather than left standing. Semantically the drift is small -- 4 names of 19,353 -- but all four were tier DISPATCH, which exposed a wrong decision. fw/ta_snapshot_names.py excluded DISPATCH on the grounds that fw/ta_re.py re-derives it every run. It does, but not deterministically: dispatcher recovery depends on which function Hex-Rays decompiles cleanly on a given analysis, so xgmi_cmd00, xgmi_cmd04, xgmi_cmd0B and fp_cmd04 simply did not come back. DISPATCH is now persisted, and the exclusion reasoning in that file is rewritten to say why. Persisting it still cannot mask a regression: the dispatcher step runs regardless, so where recovery works the DISPATCH tier wins and where it fails the restored name appears as tier IDA. The tier difference is itself the signal that recovery failed on that build. 7817 names were recovered from the committed artifacts into fw/cgnames so nothing derived is left depending on a database that may not reproduce. XGMI came back whole. The one name that did NOT come back turned out to be a defect being fixed rather than lost. HEAD had TWO functions called fp_cmd04 -- 0x401334 from the dispatcher and 0x402b4c from byte porting -- and the current dispatcher map routes command 4 to 0x402b4c, agreeing with the ported name. So HEAD carried a duplicate, this run does not, and the allow_overwrite=False guard on the re-apply is what refused to restore the bad one. Worth keeping: a name count going DOWN was the correct outcome, and reading it as a regression would have reintroduced the duplicate. Verified state after the clean regeneration: 16/16 gates PASS, 99 builds, 19,349 of 28,945 named (66.8%), field share 66.4%, 21 decompile failures.
* correct two mislabelled GCP handlers, and stop emitting stale pseudocodeuser2026-10-01
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Two GCP handlers were mislabelled in the libdump symbol set and the mislabels had been propagating into every artifact. The duplicate name was the tell: FE0B and FE0D were both called DestroyAuthenticatedSession. The code settles it without needing the documents. gcp_cmdFE0C iterates ctx->crypto, zero-fills a 0x58 gcp_crypto_session and sets crypto->state and crypto->key, so it creates a CRYPTO session. gcp_cmdFE0D computes "(char *)ctx + 88 * HIBYTE(arg10)" -- stride 88, the crypto array at ctx+0x800 -- while gcp_cmdFE0B uses stride 80, the auth array at ctx+0x300. AUDIT-NEW-TAS-RE.md states exactly that split, and the hardware findings agree: FINDING-GCP-ACQUIRE-GATE-BYPASS records 0xFE0C as CreateCryptoSession. Worth distinguishing from the FE07 case: that one claimed the wrong COMMAND and the dispatcher cross-check caught it automatically. These two claim the right command and the wrong SEMANTICS, so no command-id check can see them -- only reading what the code touches. The corrections are applied by name through a RENAME_FIX table so they hold for every build. While verifying them, the dispatcher kept printing the old callee name in four builds after the rename had demonstrably succeeded. Hex-Rays caches pseudocode per function and remote_ops.rename refreshes only the function it renamed, never its callers, so a stale cache was reading as a failed rename. Every function's cached text is now invalidated before the artifacts are emitted. That turned out to matter far beyond the two names. Applied TYPES were also being hidden behind stale text: GCP's field share goes 59% to 83.4%, its typed field accesses 1716 to 3081 and its raw dereferences 1193 to 612, with no new structure added -- the types had been applied all along and the emitted artifacts were showing pre-typing output. WSPE lands at 91%, HDCP 70%, XGMI 69%, POWERPLAY 67%. Corpus field share 65.9% -> 66.4%, held down by ASD, which dominates the corpus and barely moved. This is the sixth consecutive pass whose main finding was that something already done was not visible in the output. The remaining low scores are now informative rather than suspect: merit at 0% and opm at 19% are low because their structures are mostly opaque body arrays, not because typing failed.
* fix phantom commands in the dispatcher map, found by auditing names against ituser2026-10-01
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Rather than chase another percentage, this pass audited whether the names already applied are CORRECT, using the only authority available: the dispatcher. For every build, each handler whose name claims a command id was checked against the command the dispatcher actually routes to it. 257 handlers checked, 246 agreed, and 11 conflicted -- all in GCP, all of the form gcp_cmdFE0A_QueryAccessibilityAttributes__hN being routed from commands 0x8, 0x9, 0xA and 0xF. Those are not GCP commands at all. They are arms of 0xFE0A's own sub-command switch, absorbed as top-level commands because dispatch_map merged EVERY switch-bearing callee at the shallowest depth. The handler names were right; my command map was wrong, which is the opposite of what the conflict report implied. The merge is replaced by taking only the richest switch at the shallowest depth. The union was introduced to allow a TA that splits low ids and 0xFExx ids across sibling switches, but GCP serves both from one function, so nothing is lost: the reference still recovers 14 commands, all 14 still match fw/cmdmaps/gcp_ea.json address for address, and the phantom 0x8/0x9/0xA/0xF are gone. The three still missing (0x3, 0xFE08, 0xFE0E) remain the inlined arms with no callee, as before. Re-running the audit afterwards: 332 handlers checked, 332 agree, zero conflicts. The FE07 case that had been reported since the cross-check was built is also clear, because the pipeline now relabels that handler from gcp_cmdFE03_* to gcp_cmdFE07_* rather than leaving the inherited misnomer in place. Two more corrupted databases were found and rebuilt (hdcp_23.0.8.49, merit_24.0.4.42), with the same "worker exited with status 1" symptom as the gcp one last pass. Rebuilding loses the call-graph names on those builds, so ta_cgname was re-run for both; naming lands at 66.5% rather than 66.9% because of that churn. Also measured and rejected this pass, so they are not retried: cross-module naming by exact body hash yields 77 names, and by mnemonic sequence 170 (against 1496 that match an ambiguous cluster and cannot be resolved). Together under 2% of what remains unnamed, which is not worth the regeneration cost.
* type the TEE runtime's shared formatting state across every module that names ituser2026-10-01
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Rather than pick the next target by hand, the artifacts were measured for where raw dereferences through function ARGUMENTS concentrate. That surfaced something better than another per-module struct: the same offset set (0x08, 0x18, 0x1C, 0x20) appears through a1 in gcp, powerplay, wspe and xgmi, in functions whose names are content-hash clusters -- lib_e53fbaaa_32, lib_7fca7c43_82, lib_8680f995_112, lib_366a1ceb_178. RAS is what identifies it. There the same cluster carries real names -- vsnprintf_fmt_int, vsnprintf_fmt_ll, vsnprintf_pad, vsnprintf_prefix -- and adds 0x10, 0x14 and the tail 0x24 byte, 0x25 byte, 0x26 word, closing the layout at 0x28. It is the runtime's string-formatting state. Declaring ONE shared type here is sound where the powerplay ack-word guess was not, and the difference is worth stating: those "_<hash8>_" names come from ta_libmatch.py, which accepts a cluster only when the function bytes match EXACTLY across modules. The functions are literally the same code, so they necessarily share the structure. The earlier ack-word generalisation rested on a resemblance and was wrong. Applied via the first-parameter rewrite, so each function keeps the rest of its recorded signature: 24 functions in RAS, 6 each in wspe and xgmi, 4 each in gcp and powerplay. HDCP was examined and deliberately NOT given a shared struct. Its three heaviest functions -- hdcp2_rx_AKE_Send_Pairing_Info, hdcp2_rx_RepeaterAuth_ReceiverID_List and hdcp2_rx_AKE_Send_Cert, 590 dereferences between them -- share only two offsets, 0x18 and 0x24. They are distinct HDCP2 message layouts, not one structure, and forcing a common type would have misdescribed all three. Honest limit on the reach of this change: the cluster is only NAMED in five modules. In hdcp, opm, merit, auc, fp, rap, csu, oc, dtm and asd the same helpers exist but are still sub_*, so the fragment-keyed typing cannot attach to them. Running the libnames clustering across those modules first would let the same single declaration cover them too -- the clearest next step. Measured across fw/decomp: typed field accesses 5246 -> 7101, raw pointer-arithmetic dereferences 12701 -> 11849, field share 29.2% -> 37.5%, struct-typed signatures 4.8% -> 5.7%.
* apply the prototypes, types and comments the pass had been droppinguser2026-10-01
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The decompiled artifacts were missing almost all type information. Three things were available in the tree and only one was being used: * declare_type was never called, so no type definitions were parsed at all; * set_type ran only for the four handover types, because fw/libdumps records just {ea,hash,name,size} and the twelve Windows TA types therefore had no signatures to port; * the recorded comments were read into a variable and discarded without ever being applied. All three are now applied, in dependency order, and counted per build so the result is measurable instead of something you have to notice by reading the output. Across 99 builds: 7727 prototypes (was 1232, all of them ASD), 6068 comments (was zero), 309 struct-typed signatures (was zero). The reason prototypes were failing was NOT the missing types, which is what I assumed when I reported "202 of 1434 rejected" without diagnosing it. Declaring the types first changed the rejection count by nothing. 205 of the 239 failures were prototypes recorded in the bare shape 'int()', with no __fastcall for the name-insertion to latch onto, so they were emitted as the unparsable 'int();'. The name now goes before the first parenthesis whatever the shape, and prototypes land 100%: 1434/1434 on the ASD reference, 99/99 HDCP, 9/9 DTM. Worth recording so nobody repeats the detour: the 43 'local_types' in each handover symbol set are ARM NEON and SVE vector typedefs plus va_list and BOOL -- compiler boilerplate with no domain structs in it. Declaring them is harmless and buys nothing. The only real PSP structs in this tree are the seven hand-written ones in fw/ta_ida.py (gcp_ctx, ta_params, gcp_drm_session, gcp_auth_session, gcp_crypto_session, merit_slot, merit_table), which were sitting unused and are now applied: GCP command handlers finally read as '(struct gcp_ctx *ctx, struct ta_params *params)' rather than '(int, int)'. So the remaining type gap is a data gap, not a pipeline one. Thirteen of the sixteen components have no struct definitions anywhere in the repo to apply, and ASD's 6800 prototypes are almost entirely primitive-typed, which is why it shows only 30 struct-typed signatures against 1439 named functions. Closing that needs structs to be defined by hand, component by component.
* extend the decompilation to ASD and the three handover-symbol typesuser2026-10-01
The twelve Windows TA types take their symbols from fw/libdumps; ASD, HDCP, RAS and DTM have theirs in psp_out/handover, in a different shape that also carries prototypes and comments. This adds that reference path, so every component the corpus holds is covered by one pipeline rather than twelve of fifteen. ASD needed its own ELFs. It is driver-class, not a TA: base 0xD00100, and no TA entry trampoline, so dispatcher-anchored naming does not apply to it at all -- its RPC dispatcher hangs off trampoline+0x1C instead. The base is not guessed; it matches psp_out/handover/elf/asd2.elf, whose LOAD segment contains 1562/1562 of the recorded ASD symbols. The frame discriminator used for the TAs is inapplicable here for the same reason the trampoline is: there is none, and it reports "neither" for all 17 builds. Prototypes are applied where recorded. Without them Hex-Rays guesses argument counts and types, which is most of the distance between readable C and a wall of _DWORD; 1232 of 1434 land on the ASD reference and the rest are rejected by IDA, reported rather than hidden. One more false-positive class in the dispatcher cross-check, found by running it on HDCP: the agreement test assumed a ported name encodes its command number, which is GCP's convention and not HDCP's, so every one of HDCP's 16 commands was reported as conflicting. A name can only contradict the dispatcher if it claims a command itself, so the check now requires the ported name to match <ta>_cmd<hex> before judging it. HDCP drops to zero conflicts while GCP's single real one -- the 0xFE07 handler carrying the inherited name gcp_cmdFE03_CreateAuthenticatedSession -- is still reported. Coverage is honest rather than flattering. ASD lands 162-425 named of ~1100-1400 functions per build, and the dominant limiter is ambiguity, not similarity: 677-908 functions per build share a byte signature with a differently-named reference function and are skipped instead of being assigned arbitrarily. ASD is full of identical small stubs, so this is expected, but it means the ASD figures are a floor and a smarter tie-break would raise them. RAS is weaker still at 13-20 named per build.