[Fix] (MG_Remote, Wire): send the codec's MapPersistent arm through the same adoption-tier gate the applier uses, so MOBILEGL_IPC_ADOPT_TIER=0/1 die on their named diagnostic

This commit is contained in:
2026-09-16 06:41:01 -04:00
parent ba6dc4f7f1
commit bb6ba28241
2 changed files with 102 additions and 0 deletions
+16
View File
@@ -31,6 +31,9 @@
#include <Config.h> #include <Config.h>
#include <MG_Pipe/MGPipeRenderStateSpans.h> #include <MG_Pipe/MGPipeRenderStateSpans.h>
#include <MG_Pipe/PipeApply.h> #include <MG_Pipe/PipeApply.h>
// R-6's tier gate, and the ONE spelling of it (b1's file, unchanged by this package): the
// MapPersistent arm below asks it the same question MGPipeApplyMapPersistent asks.
#include <MG_Remote/Client/PersistentMapTracker.h>
#include <MG_Remote/Protocol/generated/protocol_generated.h> #include <MG_Remote/Protocol/generated/protocol_generated.h>
#include <MG_State/GLState/ProgramState/ProgramArtifactsCodec.h> #include <MG_State/GLState/ProgramState/ProgramArtifactsCodec.h>
#include <MG_Util/Debug/Log.h> #include <MG_Util/Debug/Log.h>
@@ -1379,6 +1382,19 @@ namespace MobileGL::MG_Remote::Wire {
// //
// DECLINED is a real answer, not a failure: the three frontend sites already // DECLINED is a real answer, not a failure: the three frontend sites already
// tolerate it (BufferObject.cpp:238, :603-606, :657-660). // tolerate it (BufferObject.cpp:238, :603-606, :657-660).
//
// THE TIER IS CONSULTED HERE, AND IT IS THE SAME CONJUNCTION THE MONOLITH APPLIER
// USES (PipeApply.cpp's `Transport != Monolith && AdoptTierIsEmulate()`). The arm
// used to decline UNCONDITIONALLY and AdoptTier had no reference anywhere on the
// codec path, so MOBILEGL_IPC_ADOPT_TIER=0 and =1 - which contract §5 promises
// "parse and are Fatal at use, naming P11" - decoded as an ordinary DECLINED and
// the operator got a run that looked like a working T0. AdoptTierIsEmulate returns
// true at T2 and ABORTS at T0/T1 on its own named diagnostic, so the return value
// is deliberately not a branch: P5 declines at every tier it survives (R-6), and
// the two forbidden ones never get this far.
if (MG_Config::Transport != MG_Config::TransportMode::Monolith) {
(void)MG_Remote::Client::AdoptTierIsEmulate();
}
PostReply(op, seq, ReplySink::kStatusDeclined, nullptr, 0); PostReply(op, seq, ReplySink::kStatusDeclined, nullptr, 0);
return true; return true;
@@ -34,6 +34,8 @@
#include "Includes.h" #include "Includes.h"
// MG_Config::Transport and MG_Config::Ipc.AdoptTier: the two knobs R-6's tier gate reads.
#include <Config.h>
#include <MG_Pipe/MGPipe.h> #include <MG_Pipe/MGPipe.h>
#include <MG_Pipe/MGPipeRenderStateSpans.h> #include <MG_Pipe/MGPipeRenderStateSpans.h>
#include <MG_Pipe/PipeApply.h> #include <MG_Pipe/PipeApply.h>
@@ -516,6 +518,37 @@ TEST_F(PipeWireCodecTest, KReplySlotMapPersistentIsAConstantDecline) {
EXPECT_TRUE(wire.Answers().All[0].Bytes.empty()); EXPECT_TRUE(wire.Answers().All[0].Bytes.empty());
} }
TEST_F(PipeWireCodecTest, TierTwoUnderSplitTransportStillDeclinesRatherThanRefusing) {
// The POSITIVE half of the two AdoptTier death cases below. Without it, those two could
// be satisfied by an arm that aborted on every tier, which is the opposite mistake to the
// one wave1-codex-verify.md §4 found. T2 is the only tier P5 implements and R-6 says the
// answer there is DECLINED - a real answer, not a failure - even when the transport is
// the split one that makes the tier question live at all.
const MG_Config::TransportMode savedTransport = MG_Config::Transport;
const Uint32 savedTier = MG_Config::Ipc.AdoptTier;
struct Restore {
MG_Config::TransportMode T;
Uint32 A;
~Restore() {
MG_Config::Transport = T;
MG_Config::Ipc.AdoptTier = A;
}
} restore{savedTransport, savedTier};
MG_Config::Transport = MG_Config::TransportMode::InProcess;
MG_Config::Ipc.AdoptTier = 2u;
Wire2 wire;
const MGPHandleOnly handle = HandleOnly(5, MGPipeKind::Buffer);
ASSERT_NE(wire.Encoder().EncodeRecord(MGPWireOp::MapPersistent, &handle, sizeof(handle)),
kInvalidSeq);
bool applied = false;
ASSERT_TRUE(wire.PumpOne(&applied));
EXPECT_TRUE(applied);
ASSERT_EQ(wire.Answers().All.size(), 1u);
EXPECT_EQ(wire.Answers().All[0].Status, ReplySink::kStatusDeclined);
EXPECT_TRUE(wire.Answers().All[0].Bytes.empty());
}
TEST_F(PipeWireCodecTest, KNeedsAckRespecifyCarriesItsRedefinitionScope) { TEST_F(PipeWireCodecTest, KNeedsAckRespecifyCarriesItsRedefinitionScope) {
// Contract table 1 row 19b. Without the carrier every per-level glTexImage*D would take // Contract table 1 row 19b. Without the carrier every per-level glTexImage*D would take
// the whole-resource arm on the far side and eat the other levels' pending uploads, so // the whole-resource arm on the far side and eat the other levels' pending uploads, so
@@ -1818,6 +1851,59 @@ TEST_F(PipeWireCodecTest, ASecondProcessResolverIsFatalRatherThanASilentRace) {
EXPECT_NE(r.Log.find("already installed"), std::string::npos) << r.Log; EXPECT_NE(r.Log.find("already installed"), std::string::npos) << r.Log;
} }
// ---- R-6 / contract §5: the two forbidden adoption tiers die ON THE WIRE PATH TOO --------
//
// wave1-codex-verify.md §4: `AdoptTier` had ZERO references anywhere on the codec path, so
// MOBILEGL_IPC_ADOPT_TIER=0 and =1 - which contract §5 promises "parse and are Fatal at use,
// naming P11" - decoded as an ordinary DECLINED. The verifier set each forbidden tier inside
// KReplySlotMapPersistentIsAConstantDecline and watched its successful-decline assertions
// still pass, on BOTH tiers.
//
// THESE ARE FORKED, NOT EXPECT_DEATH, for the reason at the top of this file - and forking is
// what lets the case REQUIRE THE DIAGNOSTIC rather than any abort: r.Log is searched for the
// exact sentence AdoptTierIsEmulate prints. ID-46 finding 10 is an empty death regex; the
// EXPECT_NE lines below are the opposite of that, and a crash for any other reason fails the
// case on the log it prints.
//
// I made both red once, by doing X: X = restoring the unconditional decline in
// PipeWireCodec.cpp's MapPersistent arm (deleting the AdoptTierIsEmulate call). Both children
// then exit 0 having posted a clean DECLINED, and both cases fail on DiedOfAbort.
TEST_F(PipeWireCodecTest, AdoptTierZeroIsFatalOnTheWirePathAndNamesP11) {
const ChildResult r = RunInChild([] {
// The child dies; nothing needs restoring. The transport half is the same conjunction
// MGPipeApplyMapPersistent uses - a monolith TRANSPORT mints like push (ID-42) and is
// not the arm this record can arrive on.
MG_Config::Transport = MG_Config::TransportMode::InProcess;
MG_Config::Ipc.AdoptTier = 0u;
Wire2 wire;
const MGPHandleOnly handle = HandleOnly(5, MGPipeKind::Buffer);
(void)wire.Encoder().EncodeRecord(MGPWireOp::MapPersistent, &handle, sizeof(handle));
bool applied = false;
(void)wire.PumpOne(&applied);
});
ASSERT_TRUE(DiedOfAbort(r)) << DescribeStatus(r) << "\n" << r.Log;
EXPECT_NE(r.Log.find("MOBILEGL_IPC_ADOPT_TIER=0 names adoption tier T0, which P11 implements"),
std::string::npos)
<< r.Log;
}
TEST_F(PipeWireCodecTest, AdoptTierOneIsFatalOnTheWirePathAndNamesP11) {
const ChildResult r = RunInChild([] {
MG_Config::Transport = MG_Config::TransportMode::InProcess;
MG_Config::Ipc.AdoptTier = 1u;
Wire2 wire;
const MGPHandleOnly handle = HandleOnly(5, MGPipeKind::Buffer);
(void)wire.Encoder().EncodeRecord(MGPWireOp::MapPersistent, &handle, sizeof(handle));
bool applied = false;
(void)wire.PumpOne(&applied);
});
ASSERT_TRUE(DiedOfAbort(r)) << DescribeStatus(r) << "\n" << r.Log;
EXPECT_NE(r.Log.find("MOBILEGL_IPC_ADOPT_TIER=1 names adoption tier T1, which P11 implements"),
std::string::npos)
<< r.Log;
}
#else #else
TEST_F(PipeWireCodecTest, TheFatalArmsNeedFork) { TEST_F(PipeWireCodecTest, TheFatalArmsNeedFork) {