diff --git a/MobileGL/MG_Remote/Wire/PipeWireCodec.cpp b/MobileGL/MG_Remote/Wire/PipeWireCodec.cpp index d79af8ff..f6c0b846 100644 --- a/MobileGL/MG_Remote/Wire/PipeWireCodec.cpp +++ b/MobileGL/MG_Remote/Wire/PipeWireCodec.cpp @@ -31,6 +31,9 @@ #include #include #include +// 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 #include #include #include @@ -1379,6 +1382,19 @@ namespace MobileGL::MG_Remote::Wire { // // DECLINED is a real answer, not a failure: the three frontend sites already // 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); return true; diff --git a/MobileGL/MG_Test/Wire/PipeWireCodecTest.cpp b/MobileGL/MG_Test/Wire/PipeWireCodecTest.cpp index d6a7878a..e61acbdb 100644 --- a/MobileGL/MG_Test/Wire/PipeWireCodecTest.cpp +++ b/MobileGL/MG_Test/Wire/PipeWireCodecTest.cpp @@ -34,6 +34,8 @@ #include "Includes.h" +// MG_Config::Transport and MG_Config::Ipc.AdoptTier: the two knobs R-6's tier gate reads. +#include #include #include #include @@ -516,6 +518,37 @@ TEST_F(PipeWireCodecTest, KReplySlotMapPersistentIsAConstantDecline) { 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) { // 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 @@ -1818,6 +1851,59 @@ TEST_F(PipeWireCodecTest, ASecondProcessResolverIsFatalRatherThanASilentRace) { 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 TEST_F(PipeWireCodecTest, TheFatalArmsNeedFork) {