Files
cls 9dfa06ffee
Docker image / Build (linux/amd64) (push) Has been cancelled
Docker image / Build (linux/arm64) (push) Has been cancelled
Docker image / Merge release multi-arch manifest (push) Has been cancelled
Docker image / Merge debug multi-arch manifest (push) Has been cancelled
Docker image / Build public push gateway (linux/amd64) (push) Has been cancelled
Docker image / Build public push gateway (linux/arm64) (push) Has been cancelled
Docker image / Publish public push gateway image (push) Has been cancelled
Sprig image / Build (linux/amd64) (push) Has been cancelled
Sprig image / Build (linux/arm64) (push) Has been cancelled
Sprig image / Merge multi-arch manifest (push) Has been cancelled
Harbor Buzz Orchestra / Python tests and lint (push) Has been cancelled
CI / Detect Changed Paths (push) Has been cancelled
CI / Rust Lint (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Desktop Core (push) Has been cancelled
CI / Desktop Smoke E2E (1) (push) Has been cancelled
CI / Desktop Smoke E2E (2) (push) Has been cancelled
CI / Desktop Smoke E2E (3) (push) Has been cancelled
CI / Desktop Smoke E2E (4) (push) Has been cancelled
CI / Desktop (push) Has been cancelled
CI / Desktop E2E Relay (push) Has been cancelled
CI / Desktop E2E Integration (1/2) (push) Has been cancelled
CI / Desktop E2E Integration (2/2) (push) Has been cancelled
CI / Desktop E2E Integration (push) Has been cancelled
CI / Backend Integration (relay e2e) (push) Has been cancelled
CI / Relay E2E (push) Has been cancelled
CI / Web (push) Has been cancelled
CI / Mobile (push) Has been cancelled
CI / Security (push) Has been cancelled
CI / Dead Token Reference Guard (push) Has been cancelled
CI / Server Cross-Compile (aarch64-unknown-linux-musl) (push) Has been cancelled
CI / Server Cross-Compile (x86_64-unknown-linux-musl) (push) Has been cancelled
CI / Windows Rust (x86_64-pc-windows-msvc) (push) Has been cancelled
CI / Desktop Build (macOS) (push) Has been cancelled
helm chart / lint + unittest + render matrix (push) Has been cancelled
helm chart / install on kind (gated) (push) Has been cancelled
helm chart / publish chart to GHCR (push) Has been cancelled
Mesh Lifecycle / Relay-Driven Mesh Lifecycle Smoke (push) Has been cancelled
Sprig / Build (aarch64-unknown-linux-musl) (push) Has been cancelled
Sprig / Build (x86_64-unknown-linux-musl) (push) Has been cancelled
Sprig / Publish rolling release (push) Has been cancelled
Sprig / Publish tagged release (push) Has been cancelled
feat: import Chinese-localized Buzz source snapshot
Signed-off-by: cls_宁波本机 <908705107@qq.com>
2026-08-13 18:34:25 +08:00

520 lines
22 KiB
Python

"""Mutation harness for candidate B (two-counter) model.
Each mutant: subclass DeviceB with a weakened rule, run the BFS explorer,
require a recorded counterexample. A model that stays green under a real
weakening is worthless.
Mutants:
M1: drop baseline dominance (frontier > B no longer clears stale set)
M2: drop max(S,C)+1 bump (use S+1 or C+1 — counter can regress)
M3: flip tie policy (verify the model distinguishes them)
M4: revert to delete-on-dominance compaction (drops the tombstone floor
entirely instead of zeroing S and keeping max(S,C) as C) — reproduces
Thufir's pass-3 CRITICAL: stale-replay resurrection after counter reuse
M5: uint32 overflow bypass (legacy sanitization disabled)
M6: componentwise-max -> last-write-wins merge (convergence breaks)
M7: publish without canonicalization (serialize raw registers instead
of the compact-at-publish canonical form) — reproduces Thufir's
pass-1/2 CRITICAL: dead+dead merge resurrection
M8: revert split_blob_into_slots to per-entry splitting (violates the
atomic-grouping rule) — reproduces Thufir's pass-2/2 CRITICAL:
partial-slot reconstruction of a live RegB creates a false tombstone
that permanently suppresses the override after eventual full delivery
M9: revert split_blob_into_slots to escaped-key grouping (groups frontier
by wire key instead of unescaped logical ID) — reproduces Thufir's
round-2 CRITICAL: for a context whose raw ID starts with a reserved
prefix (e.g. "ov_s:evil"), the frontier's escaped wire key
("esc:ov_s:evil") and the ov_* siblings (keyed by raw suffix "ov_s:evil")
resolve to different groups → register split across slots →
old/new slot-coordinate mixture produces partial reconstruction →
false tombstone → permanent false clear across publication cycles
Each mutant is injected into the model via DeviceB subclass, then the
explorer or invariant suite is rerun. The counterexample (first violation)
is recorded and printed.
"""
from copy import deepcopy
from model import (
RegB, merge_reg_b, override_set_b, compact_b,
DeviceB, legacy_sanitize_blob,
escape_context_key,
SET, CLEAR,
)
from exhaustive import (
explore_b, test_concurrent_stability,
test_compaction_register_exhaustive, test_deep_history_compaction,
test_published_merge_closure, test_interleaved_delivery_grouping,
test_escaped_context_slot_grouping,
CONTEXTS,
)
# ---------------------------------------------------------------------------
# M1: drop baseline dominance
# ---------------------------------------------------------------------------
class M1_NoBaselineDominance(DeviceB):
def _override_set(self, reg, frontier_val, tie_policy):
if reg is None:
return False
if reg.s > reg.c:
return True
if reg.s == reg.c and reg.s > 0:
return tie_policy == SET
return False
def _compact(self, reg, frontier_val, tie_policy):
if reg.s == 0 and reg.c == 0:
return None
if self._override_set(reg, frontier_val, tie_policy):
return reg
if reg.c > reg.s:
return RegB(s=0, c=reg.c, b=0)
if reg.c == reg.s and tie_policy == CLEAR:
return RegB(s=0, c=reg.c, b=0)
return reg
def mutant_m1():
"""M1: without baseline dominance, a stale set persists after frontier
advance past baseline. Verify by constructing the scenario directly:
mark-unread at frontier=10, then advance frontier to 100. The correct
model clears the override; the mutant keeps it live."""
violations = []
for ctx in CONTEXTS:
dev = M1_NoBaselineDominance("d0")
dev.frontier[ctx] = 10
dev.do_mark_unread(ctx)
dev.do_advance_frontier(ctx, 100)
correct = override_set_b(dev.overrides[ctx], 100, CLEAR)
mutant_result = dev.override_is_set(ctx, CLEAR)
if correct != mutant_result:
violations.append((
"baseline-dominance-missing", ctx,
dev.overrides[ctx], 100,
f"correct={correct}", f"mutant={mutant_result}",
))
if not violations:
_, violations = explore_b(max_depth=3, tie_policy=CLEAR,
device_cls=M1_NoBaselineDominance)
return violations
# ---------------------------------------------------------------------------
# M2: drop max(S,C)+1 bump
# ---------------------------------------------------------------------------
class M2_NoBump(DeviceB):
"""Each counter bumps only itself: mark_unread does S := S+1,
mark_read does C := C+1. When S > C from a prior set, a clear
at C+1 can produce C < S even though the clear is causally later."""
def do_mark_unread(self, ctx):
if self.is_legacy:
return
cur = self.overrides.get(ctx, RegB())
self.overrides[ctx] = RegB(s=cur.s + 1, c=cur.c,
b=self.effective_frontier(ctx))
def do_mark_read(self, ctx, frontier_ts):
self.frontier[ctx] = max(self.frontier.get(ctx, 0), frontier_ts)
if not self.is_legacy:
cur = self.overrides.get(ctx, RegB())
self.overrides[ctx] = RegB(s=cur.s, c=cur.c + 1, b=cur.b)
def mutant_m2():
"""M2: each counter bumps independently. After set→set→clear at
the SAME frontier (no advance past baseline): correct clear has
C=3 > S=2, mutant clear has C=1 < S=2 — a causally later clear
fails to dominate.
Use mark_read at the current frontier (not advancing past baseline)
so baseline dominance doesn't mask the counter discrepancy.
"""
violations = []
for ctx in CONTEXTS:
front = 10
dev_correct = DeviceB("d0")
dev_correct.frontier[ctx] = front
dev_correct.do_mark_unread(ctx)
dev_correct.do_mark_unread(ctx)
dev_correct.do_mark_read(ctx, front)
dev_mutant = M2_NoBump("d0")
dev_mutant.frontier[ctx] = front
dev_mutant.do_mark_unread(ctx)
dev_mutant.do_mark_unread(ctx)
dev_mutant.do_mark_read(ctx, front)
correct_set = dev_correct.override_is_set(ctx, CLEAR)
mutant_set = dev_mutant.override_is_set(ctx, CLEAR)
if correct_set != mutant_set:
violations.append((
"bump-independent", ctx,
f"correct={dev_correct.overrides[ctx]}",
f"mutant={dev_mutant.overrides[ctx]}",
f"correct_set={correct_set}", f"mutant_set={mutant_set}",
))
if not violations:
_, violations = explore_b(max_depth=4, tie_policy=CLEAR,
device_cls=M2_NoBump)
return violations
# ---------------------------------------------------------------------------
# M3: tie policy distinguishable
# ---------------------------------------------------------------------------
def mutant_m3():
"""M3: tie policy is load-bearing — S==C must produce different verdicts.
Not a DeviceB mutation; tests the model function directly."""
reg = RegB(s=1, c=1, b=10)
frontier = 10
v_clear = override_set_b(reg, frontier, CLEAR)
v_set = override_set_b(reg, frontier, SET)
if v_clear == v_set:
return []
return [("tie-distinguishable", v_clear, v_set, reg, frontier)]
# ---------------------------------------------------------------------------
# M4: revert to delete-on-dominance compaction (drops the tombstone floor)
# ---------------------------------------------------------------------------
class M4_DeleteOnDominance(DeviceB):
"""The pre-fix compaction rule: any dead/dominated register is deleted
entirely rather than reduced to the tombstone floor RegB(0, max(S,C), 0).
This makes counters reusable — a later local set/clear pair restarts
from S=0/C=0, so a delayed stale peer snapshot can dominate it on
replay. This is exactly the rule Thufir's pass-3 CRITICAL found live
at e453b3945."""
def _compact(self, reg, frontier_val, tie_policy):
if reg.s == 0 and reg.c == 0:
return None
if self._override_set(reg, frontier_val, tie_policy):
return reg
if frontier_val > reg.b:
return None
if reg.c > reg.s:
return RegB(s=0, c=reg.c, b=0)
if reg.c == reg.s and tie_policy == CLEAR:
return RegB(s=0, c=reg.c, b=0)
return reg
def mutant_m4():
"""M4: without the tombstone floor, compaction deletes the counter
ceiling instead of preserving it. Reproduce Thufir's exact witness
directly: RegB(3,0,10) at frontier=20 compacts to None under the old
rule (vs. RegB(0,3,0) under the fix); a subsequent local set+clear
reuses counters from zero; the stale ancestor then replays and
resurrects (S>C) under both tie policies.
Then confirm the explorer/deep-history suite also catches it (defense
in depth — a mutant that only fails a hand-built scenario would still
be a real bug, but the directed check is what's supposed to catch this
class per T2/T3)."""
violations = []
stale = RegB(s=3, c=0, b=10)
frontier_after = 20
for tie_policy in (CLEAR, SET):
dev = M4_DeleteOnDominance("d0")
dev.frontier["c0"] = 10
dev.overrides["c0"] = stale
dev.do_advance_frontier("c0", frontier_after)
dev.do_compact("c0", tie_policy)
if "c0" in dev.overrides:
continue # old rule didn't drop it here; not the witness shape
dev.do_mark_unread("c0") # S := 1, B := 20
dev.do_mark_read("c0", frontier_after) # C := 2
stale_blob = {"contexts": {"ov_s:c0": stale.s, "ov_c:c0": stale.c, "ov_b:c0": stale.b}}
dev.receive_merge(stale_blob)
resurrected = dev.override_is_set("c0", tie_policy)
if resurrected:
violations.append((
"M4-delete-on-dominance-resurrection", tie_policy,
f"stale_ancestor={stale}", f"post_compact_reuse=(set,clear)",
f"final_reg={dev.overrides['c0']}", f"override_is_set={resurrected}",
))
if not violations:
_, violations = test_deep_history_compaction(device_cls=M4_DeleteOnDominance)
return violations
# ---------------------------------------------------------------------------
# M5: uint32 overflow bypass
# ---------------------------------------------------------------------------
def mutant_m5():
"""M5: values outside uint32 range must fail legacy sanitization."""
blob = {"v": 1, "client_id": "x", "contexts": {
"ov_s:c0": 4294967296,
"ov_c:c0": 0,
"ov_b:c0": 10,
}}
sanitized = legacy_sanitize_blob(blob)
if "ov_s:c0" in sanitized["contexts"]:
return []
return [("overflow-rejected", blob["contexts"]["ov_s:c0"],
sanitized["contexts"])]
# ---------------------------------------------------------------------------
# M6: last-write-wins merge (breaks convergence)
# ---------------------------------------------------------------------------
class M6_LastWriteWins(DeviceB):
def _merge_reg(self, a, b):
if a is None:
return b
if b is None:
return a
return b
def mutant_m6():
"""M6: replace componentwise max with last-write-wins. Convergence must
break — different delivery orders produce different final states."""
_, violations = explore_b(max_depth=3, tie_policy=CLEAR,
device_cls=M6_LastWriteWins)
return violations
# ---------------------------------------------------------------------------
# M7: publish without canonicalization (reproduces Thufir's pass-1/2
# CRITICAL — dead+dead merge resurrection)
# ---------------------------------------------------------------------------
class M7_PublishWithoutCanonicalization(DeviceB):
"""Reverts `publish_blob` to serialize raw, uncompacted registers —
the exact pre-fix behavior Thufir's pass-1/2 CRITICAL exploited:
a dead register's baseline-relative death (or clear-count-relative
death) never gets folded into a globally-comparable ceiling before
hitting the wire, so two individually-dead registers can
componentwise-max-merge into a live join."""
def publish_blob(self, tie_policy=CLEAR):
blob_ctx = {escape_context_key(k): v for k, v in self.frontier.items()}
if not self.is_legacy:
for k, reg in self.overrides.items():
blob_ctx[f"ov_s:{k}"] = reg.s
blob_ctx[f"ov_c:{k}"] = reg.c
blob_ctx[f"ov_b:{k}"] = reg.b
return {"v": 1, "client_id": self.client_id, "contexts": blob_ctx}
def mutant_m7():
"""M7: publish-without-canonicalization must be caught by the
published-state merge-closure invariant — proving that invariant
has teeth. Reproduce Thufir's exact witness directly first (fast,
deterministic); fall back to the full search if the hand-built
scenario doesn't trigger under a given tie policy."""
violations = []
for tie_policy in (CLEAR, SET):
dev_a = M7_PublishWithoutCanonicalization("a")
dev_a.frontier["c0"] = 50
dev_a.overrides["c0"] = RegB(s=3, c=2, b=0)
dev_b = M7_PublishWithoutCanonicalization("b")
dev_b.frontier["c0"] = 100
dev_b.overrides["c0"] = RegB(s=1, c=2, b=100)
blob_a = dev_a.publish_blob(tie_policy)
blob_b = dev_b.publish_blob(tie_policy)
for first, second in [(blob_a, blob_b), (blob_b, blob_a)]:
recv = M7_PublishWithoutCanonicalization("recv")
recv.receive_merge(first)
recv.receive_merge(second)
if recv.override_is_set("c0", tie_policy):
violations.append((
"M7-publish-without-canonicalization-resurrection",
tie_policy, blob_a, blob_b, recv.overrides["c0"],
))
if not violations:
_, violations = test_published_merge_closure(
device_cls=M7_PublishWithoutCanonicalization
)
return violations
# ---------------------------------------------------------------------------
# M8: revert split_blob_into_slots to per-entry splitting
# (violates the atomic-grouping rule — reproduces Thufir's pass-2/2 CRITICAL)
# ---------------------------------------------------------------------------
class M8_PerEntrySplit(DeviceB):
"""Reverts `split_blob_into_slots` to a per-entry split that violates the
atomic-grouping rule by separating `ov_s:` + frontier from `ov_b:` + `ov_c:`.
This reproduces Thufir's exact transport witness:
- Slot 0: frontier key + `ov_s:` entry (the "partial set" slot)
- Slot 1: `ov_c:` + `ov_b:` entries
An observer receiving only slot 0 reconstructs `RegB(s=1, c=0, b=0)` at
`frontier=10`. Because `frontier(10) > b(0)`, the override is baseline-dead.
Canonical re-publication emits tombstone `RegB(0, 1, 0)`. After full
eventual delivery (both original slots + transient tombstone), the merged
result is `RegB(s=1, c=1, b=10)` — dead under clear-wins — permanently
suppressing a live override.
"""
def split_blob_into_slots(self, tie_policy=CLEAR, n_slots=2):
"""Split by key type: frontier + ov_s: in slot 0, ov_b: + ov_c: in slot 1.
Violates the atomic-grouping rule by separating ov_s: from ov_b:."""
blob = self.publish_blob(tie_policy)
slots = [{"v": blob["v"], "client_id": blob["client_id"], "contexts": {}}
for _ in range(n_slots)]
for wire_key, value in blob["contexts"].items():
if wire_key.startswith("ov_b:") or wire_key.startswith("ov_c:"):
# ov_b and ov_c go to slot 1 — separated from their ov_s: sibling
slots[1]["contexts"][wire_key] = value
else:
# frontier keys and ov_s: go to slot 0
slots[0]["contexts"][wire_key] = value
return slots
def mutant_m8():
"""M8: per-entry splitting must be caught by test_interleaved_delivery_grouping —
proving that the new interleaved-delivery test has teeth.
Reproduce Thufir's exact transport witness directly: source live
`RegB(1,0,10)` at frontier=10. Per-entry split puts frontier+`ov_s:c0`
in slot 0 and `ov_c:c0`+`ov_b:c0` in slot 1. An observer receiving only
slot 0 reconstructs `RegB(1,0,0)`, re-publishes tombstone `RegB(0,1,0)`.
Full merge including the transient: `RegB(1,1,10)` → inactive.
Confirmed by running test_interleaved_delivery_grouping with M8_PerEntrySplit;
the witness must be caught before resorting to the full suite."""
return test_interleaved_delivery_grouping(device_cls=M8_PerEntrySplit)
# ---------------------------------------------------------------------------
# M9: revert split_blob_into_slots to escaped-key grouping
# (groups frontier by wire key instead of unescaped logical ID —
# reproduces Thufir's round-2 CRITICAL)
# ---------------------------------------------------------------------------
class M9_EscapedKeyGrouping(DeviceB):
"""Reverts `split_blob_into_slots` to group the frontier key by its
ESCAPED wire key rather than the unescaped logical context ID.
For a normal context like "c0", this is a no-op (escape_context_key("c0")
== "c0"), so M9 is identical to the correct model on normal contexts.
The defect only manifests when the raw context ID starts with a reserved
prefix — e.g. raw "ov_s:evil" escapes to frontier wire key "esc:ov_s:evil".
The ov_* sibling keys are keyed by the RAW suffix ("ov_s:evil"), while
the frontier is keyed by the escaped wire key ("esc:ov_s:evil") — two
identities for one logical context, so they land in different slots.
This reproduces Thufir's round-2 CRITICAL: across publication cycles an
observer can receive the new frontier slot (esc:ov_s:evil=10) plus the
stale old-cycle override slot (ov_s/ov_c/ov_b at b=0), reconstructing
RegB(s=1,c=0,b=0) at frontier=10 — baseline-dead — and emitting tombstone
RegB(0,1,0). Full eventual delivery merges to RegB(1,1,10) — dead under
clear-wins — permanently suppressing a live override.
"""
def split_blob_into_slots(self, tie_policy=CLEAR, n_slots=2):
"""Split by original (escaped) wire key identity — does not unescape
frontier keys before grouping, so escaped contexts split incorrectly."""
blob = self.publish_blob(tie_policy)
contexts = blob["contexts"]
groups = {} # wire_key -> list of (wire_key, value)
for wire_key, value in contexts.items():
if wire_key.startswith("ov_s:"):
ctx = wire_key[5:]
elif wire_key.startswith("ov_c:"):
ctx = wire_key[5:]
elif wire_key.startswith("ov_b:"):
ctx = wire_key[5:]
else:
ctx = wire_key # frontier: use escaped wire key as group ID (BUG)
groups.setdefault(ctx, []).append((wire_key, value))
slots = [{"v": blob["v"], "client_id": blob["client_id"], "contexts": {}}
for _ in range(n_slots)]
for i, (_ctx, pairs) in enumerate(sorted(groups.items())):
slot = slots[i % n_slots]
for wire_key, value in pairs:
slot["contexts"][wire_key] = value
return slots
def mutant_m9():
"""M9: escaped-key grouping must be caught by test_escaped_context_slot_grouping —
proving that the escaped-context regression test has teeth.
For a context whose raw ID starts with a reserved prefix ("ov_s:evil"),
the frontier wire key is "esc:ov_s:evil" and the ov_* sibling keys are
"ov_s:ov_s:evil", "ov_c:ov_s:evil", "ov_b:ov_s:evil". The escaped-key
grouping treats "esc:ov_s:evil" (frontier) and "ov_s:evil" (ov_* suffix)
as different groups, splitting the register across slots.
Old/new slot-coordinate mixture across publication cycles then reproduces
the round-1 transport poison: partial reconstruction → false tombstone →
permanent false clear of a live override.
The test is parameterized to route through the "mismatched grouping" path
(else branch) when the M9 split puts frontier and siblings in different slots,
and the witness must be caught."""
return test_escaped_context_slot_grouping(device_cls=M9_EscapedKeyGrouping)
# ---------------------------------------------------------------------------
# Runner
# ---------------------------------------------------------------------------
def run_mutations():
mutants = [
("M1: drop baseline dominance", mutant_m1),
("M2: drop max(S,C)+1 bump", mutant_m2),
("M3: tie policy distinguishable", mutant_m3),
("M4: revert to delete-on-dominance compaction (reproduces pass-3 CRITICAL)", mutant_m4),
("M5: uint32 overflow bypass", mutant_m5),
("M6: last-write-wins merge", mutant_m6),
("M7: publish without canonicalization (reproduces pass-1/2 CRITICAL)", mutant_m7),
("M8: per-entry split violates atomic-grouping rule (reproduces pass-2/2 CRITICAL)", mutant_m8),
("M9: escaped-key grouping splits escaped-ctx register across slots (reproduces round-2 CRITICAL)", mutant_m9),
]
print("=" * 60)
print("Mutation harness — candidate B")
print("=" * 60)
caught = []
missed = []
for name, fn in mutants:
violations = fn()
if violations:
caught.append(name)
v = violations[0]
detail = str(v)[:200]
print(f" CAUGHT: {name}")
print(f" counterexample: {detail}")
else:
missed.append(name)
print(f" MISSED: {name}")
print(f"\nCaught {len(caught)}/{len(mutants)} mutants")
if missed:
print(f"MISSED: {missed}")
print("=" * 60)
return len(missed) == 0
if __name__ == "__main__":
import sys
sys.exit(0 if run_mutations() else 1)