aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoruser <user@clank>2026-09-29 23:32:02 +0200
committeruser <user@clank>2026-09-29 23:32:02 +0200
commitbb2c2e466e991f15ff278a54fdfbca17b59be18b (patch)
tree4e00575502cb2d98877be9dcb033051d05c12099
parenttwenty-seventh pass: resolve the lead's gate and move it one level down (diff)
downloadamd-psp-re-bb2c2e466e991f15ff278a54fdfbca17b59be18b.tar.gz
amd-psp-re-bb2c2e466e991f15ff278a54fdfbca17b59be18b.tar.xz
amd-psp-re-bb2c2e466e991f15ff278a54fdfbca17b59be18b.zip
twenty-eighth pass: gate resolves into a split verdict, one writer bounded and one not
Pass twenty-seven reduced the lead to whether a single count field can exceed 1188. It cannot: its only writer sets it from a five-arm dispatch to one of five constants with a maximum of 64, and the consumer calls that writer immediately before reading the field, so the two are paired and that path is safe by a wide margin. The length field has a second writer that is not bounded. After a properly bounded copy of message payload into the slot's buffer, a dword is read back out of the just-copied content and stored into the length field with no validation between load and store. The two writers also disagree about units, since the first stores a count pre-scaled by eight so the consumer's shift recovers it while the second stores a raw dword, meaning the consumer's shift is only correct for the first. Through the second writer a message dword becomes the shifted length at the copy site, where the only test is a lower bound and the write ends past a frame that has the link register pushed. This is deliberately not called a finding. Four things remain unestablished and are recorded in the lead file: whether the payload offset is host-supplied, whether the relevant arm is reachable and under which opcode, whether a poisoned field survives to the consumer, and whether the consumer is reachable from a host RPC. Any one failing closes it, which is what happened to both previous leads of this shape.
-rw-r--r--fw/AUDIT-TA-MEMSAFETY-HUNT.md39
-rw-r--r--fw/LEAD-SYSDRV-E17334-STACKED-LENGTH-NO-UPPER-BOUND.md54
2 files changed, 88 insertions, 5 deletions
diff --git a/fw/AUDIT-TA-MEMSAFETY-HUNT.md b/fw/AUDIT-TA-MEMSAFETY-HUNT.md
index 4280ab5..bd45f7c 100644
--- a/fw/AUDIT-TA-MEMSAFETY-HUNT.md
+++ b/fw/AUDIT-TA-MEMSAFETY-HUNT.md
@@ -1488,3 +1488,42 @@ count. The lead now turns on a single sharper question: **can `[r6+0x144]` excee
Still **no confirmed primitive**. The open chain is now three links shorter and its gate is one
concrete field, recorded in `fw/LEAD-SYSDRV-E17334-STACKED-LENGTH-NO-UPPER-BOUND.md`.
+
+================================================================================
+# Twenty-eighth pass: the gate resolves into a split verdict — one writer safe, one not
+
+Pass twenty-seven reduced the lead to a single question: can `[r6+0x144]` exceed `0x4a4`?
+
+**Answer: no.** That field's only writer in SYS_DRV is `sub_E18A74 @0xe18b06`, and the value
+comes from a 5-arm `tbb` setting it to one of `0x14, 0x1c, 0x20, 0x30, 0x40` — **max 64**
+against the 1188 needed. Through that writer the chain is safe by a wide margin, and notably
+`sub_E1D6BC` calls `sub_E18A74` immediately before reading the field, so the two are paired.
+
+**But `slot[0x14]` has a second writer, and it is not bounded.** `sub_E1D6BC @0xe1d8f6`:
+
+```asm
+0xe1d8e8 cmp r2, #0xd0 ; bhi bail ; the copy length IS bounded
+0xe1d8f0 blx #0xe0192c ; memcpy(slot->buf, msg+0x70, <= 0xd0)
+0xe1d8f4 ldr r0, [r6, #4] ; a dword of the just-copied CONTENT
+0xe1d8f6 str r0, [r4, #0x14] ; stored unvalidated
+```
+
+The copy is fine; the **value** read back out of it is not checked. And the two writers disagree
+about the field's units — writer 1 stores a count pre-scaled by 8 so the consumer's `>> 3`
+recovers it, writer 2 stores a raw dword. The consumer's shift is correct only for writer 1.
+
+Via writer 2, a message dword `V` produces `r8 = V >> 3` at `sub_E17334`, where the only test is
+`r8 >= 0xb` and the write ends at `sp+0x210+r8` in a `0x6b4` frame that has `lr` pushed.
+
+## Deliberately not called a finding yet
+
+Four things remain unestablished, recorded in the lead file: whether `msg+0x70` is host-supplied;
+whether the `0xe1d8e6` arm is reachable and under which opcode; whether a poisoned `slot[0x14]`
+survives to the consumer; and whether `sub_E1CDEC` is reachable from a host RPC at all. Any one
+failing closes it — which is what happened to both previous leads of this shape.
+
+## Objective status after twenty-eight passes
+
+No confirmed primitive, but for the first time in this hunt there is a **specific unvalidated
+attacker-influenced value** with a concrete path to an out-of-frame write, rather than a bound
+that turned out to be present.
diff --git a/fw/LEAD-SYSDRV-E17334-STACKED-LENGTH-NO-UPPER-BOUND.md b/fw/LEAD-SYSDRV-E17334-STACKED-LENGTH-NO-UPPER-BOUND.md
index 6e95618..7daa9be 100644
--- a/fw/LEAD-SYSDRV-E17334-STACKED-LENGTH-NO-UPPER-BOUND.md
+++ b/fw/LEAD-SYSDRV-E17334-STACKED-LENGTH-NO-UPPER-BOUND.md
@@ -102,13 +102,57 @@ The first writer is decisive for this chain: `sub_E1CDEC` computes `r5 = slot[0x
exactly undoes the `<< 3`. **So `r8` is literally `[r6+0x144]`** — an element count, stored
scaled and recovered unscaled.
-**The remaining question is therefore: what is the maximum of `[r6+0x144]`?**
+**Writer 1 (`0xe1d8d0`) is BOUNDED.** `[r6+0x144]`'s only writer anywhere in SYS_DRV is
+`sub_E18A74 @0xe18b06` (`strd r8, r6, [sb, #0x144]`, with `sb` = the same object), and the
+stored value `r8 = r4` is set in a 5-arm `tbb` to one of the constants
+**`0x14, 0x1c, 0x20, 0x30, 0x40`**. Maximum **64**, versus the 1188 needed to overflow. Via
+this writer the chain is safe by a factor of ~18.
-* If it can exceed `0x4a4` (1188), `sub_E17334` overflows its frame and the saved `lr`.
-* If it is bounded at or below that, the lead closes.
+## Writer 2 is the problem: `slot[0x14]` takes a dword of message content
-This is a strictly sharper question than the one this file opened with, and it is one level from
-the bottom: `r6` in `sub_E1D6BC` is the object whose `+0x144` holds the count.
+`slot[+0x14]`'s other writer is `sub_E1D6BC @0xe1d8f6`:
+
+```asm
+0xe1d8e6 ldr r6, [r4, #0x4c] ; r6 = the slot's buffer pointer
+0xe1d8e8 cmp r2, #0xd0
+0xe1d8ea bhi #0xe1d82c ; copy length bounded to 0xd0 -- the COPY is fine
+0xe1d8ec adds r1, #0x70 ; source = message payload + 0x70
+0xe1d8f0 blx #0xe0192c ; memcpy(r6, msg+0x70, <=0xd0)
+0xe1d8f4 ldr r0, [r6, #4] ; <-- a dword of the JUST-COPIED CONTENT
+0xe1d8f6 str r0, [r4, #0x14] ; slot[0x14] = that dword, UNVALIDATED
+```
+
+The copy itself is properly bounded. What is not validated is the **value** subsequently read
+out of the copied buffer and stored into `slot[0x14]`. There is no range test between the load
+and the store, and no `<< 3` scaling here — unlike writer 1, which stores a count pre-scaled so
+that the consumer's `>> 3` recovers it.
+
+Combined with the consumer:
+
+```
+sub_E1CDEC: r5 = slot[0x14] >> 3 ; then used as a CAPACITY, and passed on
+sub_E17334: r8 = r5, only r8 >= 0xb checked, write ends at sp+0x210+r8
+```
+
+a message dword `V` yields `r8 = V >> 3`, which exceeds the `0x4a4` limit for any
+`V > 0x2527`. The two writers disagree about the field's units (writer 1 scaled, writer 2
+raw), which is itself the smell: the consumer's `>> 3` is correct only for writer 1.
+
+## What must still be established before this is a finding
+
+1. **Is `msg+0x70` host-supplied?** The source derives from `sub_E0B0F4(...)`'s object at `+4`,
+ offset `0x70` — consistent with an RPC payload, but its provenance was not traced to a
+ host-writable buffer.
+2. **Is the `0xe1d8e6` arm reachable?** `sub_E1D6BC` selects arms through a chain of comparisons
+ against `dword_E1D900`/`dword_E1D918`/`dword_E1D91C`; which opcode reaches this arm is not
+ yet decoded.
+3. **Can the same slot then reach `sub_E1CDEC` -> `sub_E17334`?** Both use the accessor, but no
+ ordering/lifetime argument has been made that the poisoned `slot[0x14]` survives to the
+ consumer.
+4. **`sub_E1CDEC`'s own callers** remain untraced, so host reachability of the consumer is
+ equally unestablished.
+
+Any one of these failing closes the lead, exactly as happened to the XGMI and `0x109a` leads.
## Honest caveats