From 5505620e8c64ebea78a7f80f4e26dcd004f80754 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Tue, 22 Sep 2026 20:56:57 -0400 Subject: [PATCH] =?UTF-8?q?[docs]=20(P7):=20ph-f.md=20records=20wave=202-F?= =?UTF-8?q?=20-=20PH-7=20(1)-(4)=20and=20PH-8=20landed=20with=20their=20re?= =?UTF-8?q?d-once=20lines=20and=20the=20fingerprint=20change,=20D11=20/=20?= =?UTF-8?q?PH-2=20/=20PH-6=20/=20PH-1=20(3)(4)=20not=20started=20-=20and?= =?UTF-8?q?=20the=20PLAN=20rows=20and=20CONTRACT-P7=20=C2=A710=20item=203?= =?UTF-8?q?=20say=20so?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MobileGL/MG_Remote/CONTRACT-P7.md | 2 +- .../Disaggregated/notes/p7/PLAN-PH-P34B-P7.md | 10 +- docs/Disaggregated/notes/p7/ph-f.md | 191 ++++++++++++++++++ 3 files changed, 197 insertions(+), 6 deletions(-) create mode 100644 docs/Disaggregated/notes/p7/ph-f.md diff --git a/MobileGL/MG_Remote/CONTRACT-P7.md b/MobileGL/MG_Remote/CONTRACT-P7.md index 983b6f03..dcde4632 100644 --- a/MobileGL/MG_Remote/CONTRACT-P7.md +++ b/MobileGL/MG_Remote/CONTRACT-P7.md @@ -139,7 +139,7 @@ verify 今天只在 monolith 注册(`MG_IntegrationTest/CMakeLists.txt:1684-17 1. `CONTRACT-P6.md:666`(§10.3)把 `DynamicBackendParameters` 定宽记在 P7 名下——已由 P6.5 wf 落地,P7 不再持有。 2. `CONTRACT-P6.md` §12.1「棘轮在 CI 里重算 184 并断言只降」——由 wave 0 包 R 落地为 `scripts/link_ratchet.py`;「移交 P6.5」从未被 P6.5 接收,归 P7。 -3. `CONTRACT-P6.md` §12.2 D11 把五行中的三行分给 P6 `dl`——树上五处全部仍在原状态(`PipeApply.cpp:1575`、`PipeApplier.cpp:444`、`ProgramArtifactsCodec.cpp:309`、`StagedTextureStore.h:440`、`SlotTables.h:347/:371`);归 Ph(计划 §1.1 D11),wave 2-F 做。 +3. `CONTRACT-P6.md` §12.2 D11 把五行中的三行分给 P6 `dl`——树上五处全部仍在原状态(`PipeApply.cpp:1575`、`PipeApplier.cpp:444`、`ProgramArtifactsCodec.cpp:309`、`StagedTextureStore.h:440`、`SlotTables.h:347/:371`);归 Ph(计划 §1.1 D11),wave 2-F 做。**wave 2-F 实况(`notes/p7/ph-f.md`)**:落地的是 PH-7 (1)–(4)(常量时间令牌合一、≥16 字节、无令牌只 loopback、`Welcome.dataNonce` 绑定数据连接——`protocol.fbs` 变、`wireFingerprint` 随 `MOBILEGL_PROTOCOL_CONTROL_REVISION` 变)与 PH-8 单测;**D11 五处与 PH-2 仍未落地**:每处都要一条字节来自对端的两进程负控,而树上没有能越过 client 自身守卫伪造记录的对端驱动(= fuzz 臂 2 的骨架),按计划停止规则未开工。 4. `CONTRACT-P6.md` §5.2「`Session::Fail` 在 P6 保持 `[[noreturn]]`」——Ph 的翻转按 ID-P7-1 重定界,不做 98 站点的字面翻转;`Server/StagedShadow.h:115`、`Server/StagedTextureStore.h:344` 两处旁路由 wave 0 包 C 收进漏斗。 5. `ARCHITECTURE.md` §8.5「XFB scatter 在 client」——树上取的是 server staged shadow + `OnBufferWriteback`(P5c/P5f);`OnXfbScatterReady` 是死声明,删除。 diff --git a/docs/Disaggregated/notes/p7/PLAN-PH-P34B-P7.md b/docs/Disaggregated/notes/p7/PLAN-PH-P34B-P7.md index f51d016c..e552a60b 100644 --- a/docs/Disaggregated/notes/p7/PLAN-PH-P34B-P7.md +++ b/docs/Disaggregated/notes/p7/PLAN-PH-P34B-P7.md @@ -23,20 +23,20 @@ P7 收官的关键路径是:**先造仪器(wave 0)→ 真机诊断(wave | 项 | 判定 | 必要性 | 剩余工作(可调度) | 规模 | 波次 | |---|---|---|---|---|---| | PH-1 `Session::Fail` 翻成每会话闩 | partial | **必须,但重新定界** | 路线图写的「每个 `[[noreturn]]` 站点造真实返回路径」(98 处 `SessionFail` + 6 处 `WireLogFatal`)**拒做**:≥9 处返回引用(`ServerSession&`、`RingControl&`、`Doorbell&`、`ClientSession&`,`ServerSession.cpp:270/283/740/765/774/784/790`、`WireTables.cpp:118`、`EmitTables.cpp:212`)没有诚实返回值;且 §12.3 的进程级 `eglTerminate` 使「闩住但活着的 server 进程」也服务不了第二个会话。P6.5 的 `--serve` fork-per-session(`ServerMain.cpp:302`)**已经**给出「下一连接照常服务」。真正欠的四片:(1) supervisor **观测并命名**子进程死亡——三处 `waitpid(active, nullptr, WNOHANG)`(`:279/:289/:294`)换成读 status、记 `pid exit=/signal=`、计 `sessionsFaulted`;(2) 两处绕过漏斗的裸 `abort`(`Server/StagedShadow.h:115`、`Server/StagedTextureStore.h:344`)改走 `SessionFail`;(3) **只对对端字节可达的站点**做 latch-and-decline(`PipeWireCodec.cpp` R-2 诚实臂 + 解码边界、`PipeApplier.cpp` 七处、`ServerLoop.cpp` 四处、`SurfaceOpCodec.cpp` 三处——它们都已在 `Bool`/`MobileGLResult` 返回的函数里),`DrainRing` 顶部加闩检查;(4) 负控:喂畸形帧 → 会话闩住 → 下一连接得到 Welcome。 | (1)+(2) 1 天;(3)+(4) 2–2.5 天 | (1)(2) 与 P7 wave 0 同一提交(费用极低);(3)(4) 在 P7 之后 | -| PH-2 slot 预算(`SlotTables.h:347/:371` 两条 `MOBILEGL_ASSERT`) | partial | 必须 | 提升为 release 下具名拒绝(先经 `SessionFail`,闩落地后改闩) | 0.5 天 | 与 D11 五处一起 | +| PH-2 slot 预算(`SlotTables.h:347/:371` 两条 `MOBILEGL_ASSERT`) | partial(wave 2-F 未做,见 `ph-f.md` §4) | 必须 | 提升为 release 下具名拒绝(先经 `SessionFail`,闩落地后改闩) | 0.5 天 | 与 D11 五处一起 | | PH-3 readback 尺寸门(`PipeApplier.cpp:444`) | not_started | 必须 | 绝对上限取 P6.5 `LinkTerms.maxReplyBytes`(server 自陈),在 resize **之前**比较,超限回 `PostReply(kStatusError)` 而非 Fatal | 4–6 h | D11 | | PH-4 `StagedTextureStore` 按 level 范围设界(`:440`) | not_started | 必须 | `CopyRunInto` 传入 level 宣告 extent,`imageOffset+byteSize` 越界即拒绝 | 1 天 | D11 | | PH-5 `ArchiveVector` 上限(`ProgramArtifactsCodec.cpp:309`) | not_started | 必须 | `TakeCount` 带元素大小,`raw*sizeof(T)` 对 `Remaining()` 设界 | 4–8 h | D11 | | D11 第一行 `create_render_state` 的 `Cso.Slot+1` 无界 resize(`PipeApply.cpp:1575`) | not_started | 必须 | 加 `kMGPipeMaxRenderStateCsoSlots`,走同族 `RecordAt(table, slot, limit)` | 2–3 h | D11 | | PH-6 `SEG_EVENT` 不排空对端 | not_started | 必须 | 取 **drop-with-latch** 形状(不依赖 PH-1):`ReserveEventOrBlock`(`ServerSession.cpp:215-263`)两处 BUSY 拒绝改为置 `m_reverseChannelForfeit` + `CountDrop()` + 返回 scratch,`ServerLoop` park 谓词见闩走正常 Stop;`kEventBacklogWaitMs=30000` 改成 `MOBILEGL_IPC_EVENT_WAIT_MS`(默认 2000);先判 `Dead()` 免把对端死亡误报成 30 s 超时。两条单测(stonewall / trickle)。路线图「60 秒」实为 30 s(两轮)且涓流对端毫秒级触发 `:259` | 1.5–2 天 | P7 之后 | -| PH-7 配对 / 认证 | partial ⚠ | 必须 | 已有:非 loopback 无令牌拒绝监听(`SocketTransport.cpp:160`)、Hello 带 token、Refuse。欠:(1) **常量时间比较**且两处策略(`ServerSession.cpp:498` / `ServerMain.cpp:93`)合一;(2) 名字 `Refuse{AuthenticationRequired}` 不在 `RefuseCode` 枚举里——用 `Authentication` 或追加枚举,并给 `fatal_census.py` 加 `Refuse{Word}` 词表门;(3) 令牌最小长度 ≥16 字节;(4) **数据面未认证**:`AcceptPair` 按到达顺序配对、从不查 peer(`SocketTransport.cpp:371/:391`)——`Welcome` 带 `dataNonce`,第二连接首帧必须回它;认证挪到 fork 之前;(5) 有界的预认证工作 + 失败退避;(6) OQ-21 裁定(见 §5 ID-P7-3) | (1)(2)(3) 1 天;(4)(5) 2 天 | (1)(2)(3) 与 wave 0 同期;(4)(5) P7 之后 | -| PH-8 尺寸由 server 钳制 | partial | 必须 | 已陈述;补「client Hello 要 64 GiB 时 server 钳到上限而非分配」的单测 | 0.5 天 | P7 之后 | +| PH-7 配对 / 认证 | partial ⚠ → **(1)–(4) done**(wave 2-F,`ph-f.md`);(5) 未做 | 必须 | 已有:非 loopback 无令牌拒绝监听(`SocketTransport.cpp:160`)、Hello 带 token、Refuse。欠:(1) **常量时间比较**且两处策略(`ServerSession.cpp:498` / `ServerMain.cpp:93`)合一;(2) 名字 `Refuse{AuthenticationRequired}` 不在 `RefuseCode` 枚举里——用 `Authentication` 或追加枚举,并给 `fatal_census.py` 加 `Refuse{Word}` 词表门;(3) 令牌最小长度 ≥16 字节;(4) **数据面未认证**:`AcceptPair` 按到达顺序配对、从不查 peer(`SocketTransport.cpp:371/:391`)——`Welcome` 带 `dataNonce`,第二连接首帧必须回它;认证挪到 fork 之前;(5) 有界的预认证工作 + 失败退避;(6) OQ-21 裁定(见 §5 ID-P7-3) | (1)(2)(3) 1 天;(4)(5) 2 天 | (1)(2)(3) 与 wave 0 同期;(4)(5) P7 之后 | +| PH-8 尺寸由 server 钳制 | **done**(wave 2-F:单测 + 两进程 smoke `hello_asks_64_gib`) | 必须 | 已陈述;补「client Hello 要 64 GiB 时 server 钳到上限而非分配」的单测 | 0.5 天 | P7 之后 | | 门:`Session::Fail` 站点普查零新增 | partial ⚠ | 必须 | `scripts/ci/fatal_census.py` 存在且在 CI(`test.yml:2011`),但 (a) `SCAN_ROOT` 只有 `MG_Remote`,server 进程里 ~79 处 abort(DirectGLES.cpp 16、Managers.cpp 12、VkBufferManager.cpp 11、PipeRoute.cpp 7……)看不见;(b) `abort_sites` 记了从不比较;(c) 12 个 family 词无 `.def` 行。改:SCAN_ROOT 扩到 server 镜像(MG_Remote / MG_Pipe / MG_Backend / MG_Impl/Pipe / MG_State),`abort_sites` 变棘轮,补 `.def` 行 | 1 天 | **wave 0**(P7 需要它看见 `@P7` abort) | | 门:fuzz 臂 1 畸形控制帧 | partial | 必须 | 机制已在(`Framing.h:168` latch、`ControlInbox`),欠端到端脚本 `scripts/ci/ph_fuzz_control_frames.py`(五种畸形)+ `ServerMain.cpp:193` 补 `CtrlEnvelopeBufferHasIdentifier` + ctest 车道 | 1.5 天 | P7 之后 | | 门:fuzz 臂 2 超界计数 | not_started | 必须 | = PH-1 (3)(4) + D11 五处 | 见上 | P7 之后 | | 门:fuzz 臂 3 不排空对端 | not_started | 必须 | = PH-6 + 场景 | 见上 | P7 之后 | -| 门:非 loopback 无令牌具名拒绝 | done(机制)⚠ | 必须 | 见 PH-7 (2):名字不在词表;`tcp_supervisor_smoke.py` **未接 CI**(全仓无引用) | 含在 PH-7 | 同期 | -| OQ-21 令牌够不够 / TLS | 未答 | 必须裁定 | 见 ID-P7-3 | 0 | 本文 | +| 门:非 loopback 无令牌具名拒绝 | **done**(wave 2-F:smoke 入 CI,SKIP 即红) | 必须 | 见 PH-7 (2):名字不在词表;`tcp_supervisor_smoke.py` **未接 CI**(全仓无引用) | 含在 PH-7 | 同期 | +| OQ-21 令牌够不够 / TLS | 已答(ID-P7-3;wave 2-F 落地) | 必须裁定 | 见 ID-P7-3 | 0 | 本文 | | OQ-24 E1 对照重定义(ID-122) | 无主 | 可选 | 挂到普查门的 S8 (`SessionFaultCount()==0`) 负控设计上 | 0.5 天 | P7 之后 | ### 1.2 P3b / P4b 深化(Espryt) diff --git a/docs/Disaggregated/notes/p7/ph-f.md b/docs/Disaggregated/notes/p7/ph-f.md new file mode 100644 index 00000000..a8588b78 --- /dev/null +++ b/docs/Disaggregated/notes/p7/ph-f.md @@ -0,0 +1,191 @@ +# P7 wave 2-F:Ph 小件(分支 `p7/ph-f`) + +> 计划见 [`PLAN-PH-P34B-P7.md`](PLAN-PH-P34B-P7.md) §1.1(PH-7、PH-8、D11、门行)与 §3 wave 2-F; +> 裁定见 [`INTEGRATOR-DECISIONS-P7.md`](INTEGRATOR-DECISIONS-P7.md) ID-P7-3(令牌足够、不做 TLS、 +> `Welcome.dataNonce`);规范见 [`CONTRACT-P7.md`](../../../../MobileGL/MG_Remote/CONTRACT-P7.md) §0 +> 规则 I / J、§9 簇 F、§10 第 3 条。基线 `ec46a550`。 +> +> 主机口径:WSL Arch + lavapipe,`build-split` 与集成者同配置(integration tests ON)。flatc 取 +> `3rdparty/flatbuffers` 子模块构建的钉版本(`scripts/gen_protocol.py`)。 +> +> 基线门读数(`~/w7/logs/p7-gate-f-base.log`):G1 `.text` `0xa52203`、符号 0/0;普查 79 处 abort、 +> 0 未标记;棘轮 173;车道 unit 2429 / split 303 / spawn 219 / tcp 222 / magma-tcp 74。 + +## 0. 每片一条提交,每片一次 red-once + +| 片 | 项 | 提交 | 状态 | +|---|---|---|---| +| 1 | PH-7 (1)(2)(3):常量时间令牌合一、≥16 字节、无令牌只 loopback、smoke 入 CI | `2975a337` | 落地 | +| 2 | PH-7 (4):`Welcome.dataNonce` 把数据连接绑定到已认证的控制连接 | `596b34fd` | 落地 | +| 3 | PH-8:Hello 要 64 GiB → server 钳到自己的条款且不分配 | `a746417a` | 落地 | +| 4 | D11 五处上限 + PH-2 | — | **未做**,见 §4 | +| 5 | PH-6 drop-with-latch | — | **未做**(顺序在 4 之后) | +| 6 | PH-1 (3)(4) | — | **未做**(顺序在 4 之后) | + +red-once 原始行汇总在 `~/w7/logs/p7-ph-f-redonce.txt`。 + +--- + +## 1. 片 1:令牌策略合一(PH-7 (1)(2)(3),ID-P7-3) + +### 1.1 机制 + +`MOBILEGL_IPC_TOKEN` 原来有三种读法:`SocketTransport.cpp` 的 `ListenTcp` 只问「非空」,所以一个字节 +的令牌也能打开 `tcp://0.0.0.0`;`ServerSession::Accept` 用 `std::strcmp`;`ServerMain::RunSession` 用 +`std::string::operator==`,且在 server 没配令牌时反而要求对端送空令牌。两处比较都在第一个不同字节处返回。 + +- 新文件 `MG_Remote/Transport/AuthToken.h`:`ConfiguredAuthToken()`(空值 = 未配置)、 + `kMinimumAuthTokenBytes = 16`、`ConstantTimeTokenMatch`(循环长度只取本进程自己的令牌,长度差并入累加器, + 嵌入的 NUL 参与比较)。 +- `Handshake.h` `AuthenticatePeerToken`:**唯一**的策略函数,`ServerSession::Accept` 与 supervisor 子进程 + (`ServerMain::RunSession`)都调它。不配置令牌时不因对端送了令牌而拒绝——策略由监听侧的 loopback 限制负责。 +- `ListenTcp`:配置了但不足 16 字节的令牌**让监听失败**(具名 `Refuse{Authentication} ... minimum is 16`, + 进程退出 72),而不是悄悄退化成 loopback server。令牌值从不进日志。 +- `Refuse{Authentication}` 是 `protocol.fbs` `RefuseCode` 的枚举值,`fatal_census.py` 规则 4 的词表门 + 直接认它;普查前后都是 3 个 refusal 词,无需新增 `.def` 行(Refuse 词不是 Fatal 家族词)。 +- CI:`TcpLane.SupervisorProtocolControls` 从 wave 0 起挂在 `integration-tcp` 上却因 CI 没有 flatc 而恒为 + SKIP。`test.yml` 的 integration-split 作业现在从子模块构建钉版 flatc(`gen_protocol.py --check`, + 不改已提交头文件),导出 `MOBILEGL_FLATC_EXECUTABLE`,并用 `junit_tally.py --require-entry-passed + TcpLane.SupervisorProtocolControls` 断言它**跑了且过了**——SKIP 在这里等于红(`tcp_lane_tools_test.py` + 覆盖 passed / skipped / notrun / failed / absent 五态)。 +- smoke 新增两条监听控制:无令牌 + 通配地址 → 具名拒绝;13 字节令牌 + loopback → 具名拒绝、行里不含令牌。 + +### 1.2 red-once(两进程,`TcpLane.SupervisorProtocolControls`) + +把 `SocketTransport.cpp` 还原到 `ec46a550`、只重链 `libMobileGL.so`: + +``` +RED AssertionError: (None, '... MG_Remote server: pid=42256 listening on tcp://127.0.0.1:46447') +GREEN short_token_listen exit 72: MG_Remote: Refuse{Authentication} MOBILEGL_IPC_TOKEN is 13 bytes and the + minimum is 16 (PH-7 (3)); refusing to listen on tcp://127.0.0.1:46545 rather than authenticating with + a guessable secret +``` + +即:改前 13 字节令牌起了一个活的监听者;改后进程以名字拒绝。单测 `SocketTransportTest.TcpRefusesToListen +WithATokenShorterThanTheMinimum` 与 `TheTokenComparisonAnswersLengthAndPrefixTheSameWay` 是廉价的那一半。 + +--- + +## 2. 片 2:`Welcome.dataNonce`(PH-7 (4),ID-P7-3) + +### 2.1 现场故障 + +设备窗口 #1:经 Windows `adb forward` 时一个会话的两条连接**乱序**到达;`AcceptPair` 规定「先到的是控制、 +后到的是数据」,于是数据连接被当成控制读 Hello,每个子进程等 10 s 后以 67 退出(日志里伴随 +`only 1 of 2 connections arrived within 250 ms`)。同一条规则也意味着任何能连到端口的人都能把一条连接 +插进窗口、成为别人会话的数据面。 + +### 2.2 机制 + +- `protocol.fbs`:`Welcome` 末尾追加 `dataNonce: [ubyte]`;新表 `DataBind { nonce: [ubyte]; }` 追加到 + `CtrlMsg` 末尾。头文件由钉版 flatc 重新生成。 +- **wire 指纹随之改变。** 原指纹的输入全是数据面的结构尺寸与编解码摘要,控制 schema 改形状不会动它。新增 + `MOBILEGL_PROTOCOL_CONTROL_REVISION`(`mg_protocol_base.h`,本次 = 1)作为 `AbiFingerprintInputs. + ControlSchemaRevision` 混入;`SessionHandshakeTest.TheAbiFingerprintChangesWhenAnyOfItsInputsDoes` 钉住 + 该输入并加扰动行。实测 `wireFingerprint` 由 `0x7d4470f241d23539` 变为 `0x03c4c293a54b3e08`。 + **设备侧后果**:旧 server 与新 client(或反之)现在以 `Refuse{WireFingerprint}` 拒绝,设备上的 + server 需要随本提交重新部署。 +- 流程(仅 `tcp://`;unix 端点的第二条连接是 SCM_RIGHTS 套接字,保持 `AcceptPair`): + 1. client `SocketTransport::ConnectControl` 只连控制连接,发 Hello; + 2. server 子进程在令牌通过后用内核 CSPRNG(`getrandom`,退回 `/dev/urandom`,绝不退回 PRNG)铸 128 位 + nonce,`StreamLink::AttachOwnedDeferred` 先建好私有内存(Welcome 要宣告其尺寸)但**不带 fd、不起 + reader 线程**,发 Welcome(带 nonce); + 3. client 收到 Welcome 后 `ConnectDataConnection` 开数据连接,第一帧是 `DataBind{nonce}`,随后才挂 + StreamLink; + 4. supervisor 一次只 `AcceptOne`:无活会话时新连接是控制连接、fork 子进程(带一对 hand-off + socketpair);有活会话时读新连接的**恰好一帧**(`ReceiveOneFrame` 不多读一个字节——DataBind 后面 + 紧跟 StreamLink 自己的帧),是 DataBind 就把 fd 连同所呈 nonce(sideband)经 `FdPassing::SendFd` + 交给子进程,否则照旧 `Refuse{Busy}`; + 5. 子进程里 `ServerSession::BindDataConnection` 是**唯一**比较点(`ConstantTimeNonceMatch`):匹配者 + `StreamLink::BindDataFd` 起 reader;不匹配者在**那条连接上**回 `Refuse{Authentication} data + connection nonce mismatch` 并关闭,等待继续——陈旧或抢插的连接结束不了它没能加入的会话。截止时间 + (5 s)到则在控制连接上回 `Refuse{Authentication} no data connection presented the session nonce`; + 控制对端先走则立即结束等待(子进程按 0 退出,不计入 `sessionsFaulted`)。 + 6. 无活会话时到达的 DataBind 由子进程具名拒绝:`Refuse{Authentication} data connection names no live + session`,干净退出 0。 +- 250 ms / 2000 ms 的「两连接到达窗口」在 TCP 上不复存在;取而代之的是 nonce 匹配。非 `--serve` 的单会话 + TCP 模式保留监听者,由会话自己 accept 并读 DataBind(`ListenerSource`),比较仍在同一处。 +- 仍然**没有**做的:ID-P7-3 说「认证在 fork 之前完成」——本片的令牌检查与 nonce 铸造仍在 fork 出的会话子 + 进程里(每会话一进程,不影响绑定的正确性);把 Hello 读取与认证移进 supervisor 属于 PH-7 (5)「有界的预 + 认证工作 + 失败退避」,未做。 + +### 2.3 red-once(两进程) + +探针只跑「数据连接先到、控制连接后到」这一条(新 smoke 的 (a)),server 分别是片 1 的头与本片: + +``` +RED (片 1 server) stray: RuntimeError: control channel closed before a complete frame + control: ConnectionResetError: [Errno 104] Connection reset by peer + supervisor: session pid=1705895 reaped exit=67 sessionsFaulted=1 ← 现场故障原样 +GREEN stray: Refuse{Authentication} 'data connection names no live session'(子进程 exit=0) + control: Welcome,nonce 16 字节 +``` + +smoke 常驻控制(`TcpLane.SupervisorProtocolControls`):`data_connection_first`(上面这条)与 +`stale_nonce`(会话活着时先呈一个取反的 nonce → 那条连接收到 `Refuse{Authentication} data connection +nonce mismatch`,日志 `MGPipe: Refuse{Authentication} data connection nonce mismatch`,随后正确的 nonce +绑定、子进程打出 `ready`)。单测 `SocketTransportTest.TheDataBindIsReadExactlyAndTheStreamBehindItIsLeft +OnTheSocket` 钉住「恰好一帧」与 16 字节宽度。 + +`integration-tcp`(真 GL client 走新流程)222/222、`integration-magma-tcp` 74/74 保持全绿。 + +--- + +## 3. 片 3:PH-8(server 钳制尺寸) + +### 3.1 机制 + +没有改代码:`ServerSession::Accept` 只看 Hello `linkTerms` 的 `dataPlane` / `wireForm`,段按 server 自己的 +`SetSegmentSizes` 建,Welcome 宣告的也是这组。本片把它钉住: + +- 单测 `SessionHandshakeTest.AHelloAskingFor64GiBIsClampedToTheServersTermsAndAllocatesNothingOfIt`: + Hello 四个窗口各要 64 GiB,Welcome 必须回 server 的 4 KiB 条款,四个 SegmentRef 各 ≤ 64 KiB。 +- smoke `hello_asks_64_gib`:两进程下同样的请求,Welcome 的条款 < 1 GiB,且回答它的会话子进程(Welcome + `serverPid`)在数据连接绑定后(即全部段已存在)`VmPeak` < 64 GiB。实测:maxReply 2097136、cmd 8 MiB、 + stage 32 MiB、event 256 KiB,子进程 VmPeak 237264896。 + +### 3.2 red-once(变异:`Accept` 改为按 Hello 的条款定尺寸,已还原) + +``` +RED unit SessionHandshakeTest.cpp:294 Accept rc 2 != 0(64 GiB 私有段建不出来) +RED smoke hello_asks_64_gib: RuntimeError: control channel closed before a complete frame(子进程死于该请求) +GREEN 如 3.1 +``` + +--- + +## 4. 片 4–6 未做,以及为什么停在这里 + +计划的停止规则:「某片超过一天就停在上一片」。片 4(D11 五处 + PH-2)的代码改动本身不大,但每一处都要求 +**一条在两进程臂上、字节来自对端的负控**,而树上没有任何车道能把一条**越过 client 自身守卫**的畸形记录 +送进一个 spawn / TCP server: + +- `create_render_state` 的 `Cso.Slot`、`read_pixels` 的 `W×H`(client 在发射前按回复槽尺寸自检)、程序归档 + 里的 `ArchiveVector` 计数、`StagedTextureStore` 的 run 与 level extent、`SlotTables` 的 handle slot/gen—— + 全都由 client 的编码器生成且被它自己的检查挡住;要到达 server 必须有一个能按需伪造记录的对端(fuzz 臂 2 + 「超界计数」一行正是这件事,计划里记在 P7 之后)。 +- 另外两处落在簇 F 之外、且会碰 pull 构建:`ProgramArtifactsCodec.cpp` 编进 pull 构建(G1 `.text` 要求新界限 + 只能在 `MOBILEGL_BUILD_DISAGGREGATED` 内),`SlotTables.h` 在簇 D 的 `MG_Backend/DirectGLES/` 下,且 + `MGPipeFatalFamily` 目前只有 `UnmigratedVerb` / `RoleViolation`,PH-2 的具名拒绝需要先加一个 + `ProtocolCorruption` 族并在 hook 映射里接上。 +- 已核实的一点备查:`MG_Pipe/PipeApply.cpp` **不在** pull 构建里(`build-linux/compile_commands.json` + 无该文件),D11 第一行在那里改不影响 G1。 + +所以片 4 连同其后的片 5(PH-6)、片 6(PH-1 (3)(4))按规则未开工。建议下一包先落「对端伪造记录」的两进程 +驱动(它同时是 fuzz 臂 2 的骨架),再逐处上界限,每处一条经它红一次的负控。 + +--- + +## 5. 门(头 `a746417a`) + +| 门 | 基线 `ec46a550` | 头 | +|---|---|---| +| G1 pull `.text` / 符号增删 | `0xa52203` / 0/0 | `0xa52203` / 0/0(`build-linux` 无需重编:簇 F 文件不在 pull 构建) | +| `fatal_census.py` | 79 abort、44 家族词、3 refusal 词、0 未标记 | 同左 | +| `link_ratchet.py --assert-monotone` | 173 | 173(不变) | +| `spawn_lane_parity.py` | 0 错 | 0 错 | +| unit | 2429 | 2433(+4:片 1 两条、片 2 一条、片 3 一条) | +| integration-split | 303 | 303 | +| integration-spawn | 219 | 219 | +| integration-tcp | 222 | 222 | +| integration-magma-tcp | 74 | 74 |