forked from Manuel/meeting-assistant
Fix native CI permit handoff and bounded acknowledgement
This commit is contained in:
1 parent
ea545bf719
commit
399f77a0f1
4 files changed
+172
-8
No files matched your search
@@ -31,6 +31,7 @@ static class NativeDiagnostic
|
||||
const string SdkVersion = "10.0.401";
|
||||
const string SdkSha512 = "33401b4a2da8554e3306db6072ea8569d9fcc608509c271e0aa4b39e7cc432da3631f14e7e1e2445d67d72550d18ce44a8bbd2382a756867ad2edab6b1c963c0";
|
||||
const string FullState = "/storage/13/ci-state";
|
||||
const string PermitPublication = "test -f \"$1\" && test ! -L \"$1\" && dd if=\"$1.tmp\" of=\"$1\" bs=256 count=1 conv=notrunc status=none && cmp -s \"$1.tmp\" \"$1\"";
|
||||
static readonly JsonSerializerOptions JsonOptions = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = true };
|
||||
const string OriginalBootstrap = "[ ! -e /tmp/m ]&&{ /sbin/mount_9p installstate >/dev/null 2>&1;exec /Volumes/installstate/launch.sh;};: >/tmp/m\n";
|
||||
const string MountOnlyBootstrap = "[ ! -e /tmp/m ]&& /sbin/mount_9p installstate >/dev/null 2>&1; : >/tmp/m\n";
|
||||
@@ -75,11 +76,15 @@ static class NativeDiagnostic
|
||||
if (recoveryFormat is not ("dmg" or "raw")) throw new ArgumentException("Recovery format must be dmg or raw.");
|
||||
if (args.Contains("--validate"))
|
||||
{
|
||||
if (full) await ValidatePermitHandoff(output);
|
||||
await ValidateRuntimeObservation(output);
|
||||
ValidateRunnerMemoryGate();
|
||||
ValidateGuestProgress(output);
|
||||
ValidateContracts();
|
||||
if (full) ValidateFullContracts();
|
||||
if (full)
|
||||
{
|
||||
ValidateFullContracts();
|
||||
}
|
||||
if (Option(args, "--source") is { } source)
|
||||
{
|
||||
await PrepareSource(Path.GetFullPath(source), output, full ? new string('0', 32) : "validation", false, CancellationToken.None, full, Option(args, "--cryptex-archive"), Option(args, "--noavx-archive"), recoveryFormat);
|
||||
@@ -182,8 +187,8 @@ static class NativeDiagnostic
|
||||
{
|
||||
await PermitInstallation(id, output, token, sourceCommit, result, deadline.Token);
|
||||
permitted = true;
|
||||
phase = "installation";
|
||||
phaseBudget = TimeSpan.FromMinutes(80);
|
||||
phase = "permit-handoff";
|
||||
phaseBudget = TimeSpan.FromMinutes(5);
|
||||
phaseStarted.Restart();
|
||||
}
|
||||
else
|
||||
@@ -195,18 +200,28 @@ static class NativeDiagnostic
|
||||
}
|
||||
if (full)
|
||||
{
|
||||
var permitReceiptPath = Path.Combine(output, "install-permit-received.json");
|
||||
if (permitted && phase == "permit-handoff" && File.Exists(permitReceiptPath))
|
||||
{
|
||||
using var acknowledged = JsonDocument.Parse(File.ReadAllText(permitReceiptPath));
|
||||
using var readiness = JsonDocument.Parse(File.ReadAllText(resultPath));
|
||||
if (acknowledged.RootElement.GetProperty("token").GetString() != token || acknowledged.RootElement.GetProperty("sourceCommit").GetString() != sourceCommit || acknowledged.RootElement.GetProperty("disk").GetString() != readiness.RootElement.GetProperty("disk").GetString())
|
||||
throw new InvalidOperationException("Guest permit receipt does not match the authorized run, source and disk.");
|
||||
phase = "installation"; phaseBudget = TimeSpan.FromMinutes(80); phaseStarted.Restart();
|
||||
Console.WriteLine("[native-diagnostic] phase: installation (guest permit receipt verified)");
|
||||
}
|
||||
var phasePath = Path.Combine(output, "guest-phase.json");
|
||||
if (File.Exists(phasePath))
|
||||
{
|
||||
using var nativePhase = JsonDocument.Parse(File.ReadAllText(phasePath));
|
||||
if (nativePhase.RootElement.GetProperty("token").GetString() != token) throw new InvalidOperationException("Stale native phase receipt.");
|
||||
var current = nativePhase.RootElement.GetProperty("phase").GetString();
|
||||
var next = !permitted ? phase : current == "toolchain-installing" ? "toolchain" : current is "tests-running" or "tests-passed" ? "tests" : phase;
|
||||
var next = !permitted || phase == "permit-handoff" ? phase : current == "toolchain-installing" ? "toolchain" : current is "tests-running" or "tests-passed" ? "tests" : phase;
|
||||
if (next != phase) { phase = next; phaseBudget = TimeSpan.FromMinutes(next == "toolchain" ? 30 : 25); phaseStarted.Restart(); Console.WriteLine("[native-diagnostic] phase: " + phase); }
|
||||
if (current is "tests-failed" or "bootstrap-failed" or "installation-failed") throw new InvalidOperationException("Guest phase failed: " + current);
|
||||
}
|
||||
var fullResult = Path.Combine(output, "full-result.json");
|
||||
if (permitted && File.Exists(fullResult))
|
||||
if (permitted && phase != "permit-handoff" && File.Exists(fullResult))
|
||||
{
|
||||
ValidateFullResult(File.ReadAllText(fullResult), token, sourceCommit, File.ReadAllText(Path.Combine(output, "archive.sha256")).Trim());
|
||||
ValidateTrx(File.ReadAllBytes(Path.Combine(output, "native.trx")), File.ReadAllBytes(Path.Combine(output, "discovery.txt")), File.ReadAllText(fullResult));
|
||||
@@ -877,19 +892,150 @@ static class NativeDiagnostic
|
||||
return Encoding.UTF8.GetString(bytes);
|
||||
}
|
||||
|
||||
static string CreateFullReadiness(string hook) => ReplaceOnce(hook,
|
||||
static async Task ValidatePermitHandoff(string output)
|
||||
{
|
||||
const string token = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
|
||||
const string commit = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
|
||||
var complete = token + "\n/dev/disk1\n" + commit + "\n";
|
||||
var cases = new[]
|
||||
{
|
||||
(Name: "stale-stat", Record: (string?)complete, Accepted: true),
|
||||
(Name: "timeout", Record: (string?)null, Accepted: false),
|
||||
(Name: "foreign-token", Record: complete.Replace(token, new string('f', 32)), Accepted: false),
|
||||
(Name: "foreign-disk", Record: complete.Replace("/dev/disk1", "/dev/disk2"), Accepted: false),
|
||||
(Name: "foreign-commit", Record: complete.Replace(commit, new string('f', 40)), Accepted: false),
|
||||
(Name: "foreign-owner", Record: (string?)complete, Accepted: false),
|
||||
(Name: "partial", Record: token + "\n/dev/disk1\n", Accepted: false),
|
||||
(Name: "unterminated", Record: complete.TrimEnd('\n'), Accepted: false),
|
||||
(Name: "extra-empty-line", Record: complete + "\n", Accepted: false),
|
||||
(Name: "extra-unterminated", Record: complete + "extra", Accepted: false)
|
||||
};
|
||||
var hook = CreateFullReadiness(File.ReadAllText("tools/ci/macos-native-readiness.sh").Replace("\r\n", "\n", StringComparison.Ordinal));
|
||||
var start = hook.IndexOf("finish() {", StringComparison.Ordinal);
|
||||
var end = hook.IndexOf("\ninit_timer_fifo()", start, StringComparison.Ordinal);
|
||||
if (start < 0 || end < 0) throw new InvalidOperationException("Recovery finish fixture boundary changed.");
|
||||
foreach (var test in cases)
|
||||
{
|
||||
var state = Path.Combine(output, "permit-" + test.Name + "-fixture");
|
||||
if (Directory.Exists(state)) throw new InvalidOperationException("Permit fixtures require a fresh validation output.");
|
||||
Directory.CreateDirectory(state);
|
||||
File.WriteAllText(Path.Combine(state, "run.owner"), token + "\n");
|
||||
File.WriteAllText(Path.Combine(state, "source.commit"), commit + "\n");
|
||||
if (test.Record is not null) File.WriteAllText(Path.Combine(state, "install.permit.tmp"), test.Record);
|
||||
var script = """
|
||||
set -u
|
||||
STATE_DIR="$1"; PROOF_TOKEN="$2"; COMMIT="$3"; PUBLICATION="$4"; CASE="$5"
|
||||
PROOF_LOG="$STATE_DIR/proof.log"; RESULT="$STATE_DIR/result.json"
|
||||
TIMER_FIFO="$STATE_DIR/no-timer"; selected_disk=/dev/disk1
|
||||
os_version=13.6; architecture=x86_64; uid=0
|
||||
system_exit=0; arbitration_exit=0; recovery_exit=0; disk_list_exit=0; disk_bytes=68719476736
|
||||
exec 3>> "$PROOF_LOG"; exec 9<> /dev/null
|
||||
flush_outputs() { return 0; }
|
||||
# External filesystem metadata seam: the share still reports an empty permit.
|
||||
[() {
|
||||
if [[ "$1" == -s && "$2" == "$STATE_DIR/install.permit" ]]; then return 1; fi
|
||||
builtin [ "$@"
|
||||
}
|
||||
sleep() {
|
||||
[ "$1" != 60 ] || exit 0
|
||||
if [ ! -e "$STATE_DIR/published" ]; then
|
||||
stat -c '%i:%s' "$STATE_DIR/install.permit" > "$STATE_DIR/mailbox-before"
|
||||
if [ -f "$STATE_DIR/install.permit.tmp" ]; then
|
||||
if [ "$(wc -c < "$STATE_DIR/install.permit.tmp")" -eq "$(wc -c < "$STATE_DIR/install.permit")" ]; then
|
||||
/bin/sh -c "$PUBLICATION" owned-permit-publication "$STATE_DIR/install.permit" || exit 1
|
||||
else
|
||||
# Inject malformed share contents at the external filesystem boundary.
|
||||
cat "$STATE_DIR/install.permit.tmp" > "$STATE_DIR/install.permit"
|
||||
fi
|
||||
fi
|
||||
stat -c '%i:%s' "$STATE_DIR/install.permit" > "$STATE_DIR/mailbox-after"
|
||||
if [ "$CASE" = foreign-owner ]; then printf 'foreign\n' > "$STATE_DIR/run.owner"; fi
|
||||
: > "$STATE_DIR/published"
|
||||
fi
|
||||
SECONDS=$((SECONDS + 60))
|
||||
}
|
||||
exec() {
|
||||
if (( $# == 0 )); then builtin exec; return; fi
|
||||
printf '%s\n' "$*" > "$STATE_DIR/installer-exec.log"; exit 0
|
||||
}
|
||||
""" + "\n" + hook[start..end] + "\nfinish true ready\n";
|
||||
var command = await Command("bash", ["-c", script, "permit-handoff-contract", state.Replace('\\', '/'), token, commit, PermitPublication, test.Name], output, "permit-" + test.Name, CancellationToken.None, requireSuccess: false);
|
||||
if (command.ExitCode != (test.Name == "foreign-owner" ? 1 : 0)) throw new InvalidOperationException("Unexpected permit fixture exit: " + test.Name);
|
||||
var executed = File.Exists(Path.Combine(state, "installer-exec.log"));
|
||||
var receiptPath = Path.Combine(state, "install-permit-received.json");
|
||||
if (executed != test.Accepted || File.Exists(receiptPath) != test.Accepted)
|
||||
throw new InvalidOperationException("Owned permit acceptance mismatch with stale shared metadata: " + test.Name);
|
||||
if (test.Accepted)
|
||||
{
|
||||
using var receipt = JsonDocument.Parse(File.ReadAllText(receiptPath));
|
||||
if (receipt.RootElement.GetProperty("token").GetString() != token || receipt.RootElement.GetProperty("sourceCommit").GetString() != commit || receipt.RootElement.GetProperty("disk").GetString() != "/dev/disk1" || File.ReadAllText(Path.Combine(state, "mailbox-before")) != File.ReadAllText(Path.Combine(state, "mailbox-after")))
|
||||
throw new InvalidOperationException("Permit acknowledgement or stable mailbox identity changed.");
|
||||
}
|
||||
else if (test.Name == "foreign-owner")
|
||||
{
|
||||
if (File.Exists(Path.Combine(state, "guest-phase.json"))) throw new InvalidOperationException("Permit handoff overwrote foreign-owned state.");
|
||||
}
|
||||
else
|
||||
{
|
||||
using var failure = JsonDocument.Parse(File.ReadAllText(Path.Combine(state, "guest-phase.json")));
|
||||
if (failure.RootElement.GetProperty("token").GetString() != token || failure.RootElement.GetProperty("phase").GetString() != "installation-failed" || failure.RootElement.GetProperty("reason").GetString() != "permit-timeout")
|
||||
throw new InvalidOperationException("Invalid or missing permit did not publish its owned terminal failure: " + test.Name);
|
||||
}
|
||||
}
|
||||
Save(Path.Combine(output, "permit-handoff-validation.json"), new { success = true, cases = cases.Length, actualGeneratedRecoveryShell = true, actualHostPublicationCommand = true, staleStatFixture = true, sameMailboxInodeAndSize = true, invalidPermitsNeverExecuteInstaller = true, guestExecuted = false });
|
||||
}
|
||||
|
||||
static string CreateFullReadiness(string hook) => ReplaceOnce(ReplaceOnce(hook,
|
||||
" printf '[proof-result] %s: %s\\n' \"$success\" \"$reason\" >&3",
|
||||
"""
|
||||
if [ "$success" = true ]; then
|
||||
local source_commit
|
||||
source_commit=$(cat "$STATE_DIR/source.commit")
|
||||
if [ "$(cat "$STATE_DIR/run.owner")" != "$PROOF_TOKEN" ] ||
|
||||
! [[ "$source_commit" =~ ^[0-9a-f]{40}$ && "$selected_disk" =~ ^/dev/disk[0-9]+$ ]] ||
|
||||
[ -e "$STATE_DIR/install.permit" ] || [ -L "$STATE_DIR/install.permit" ]; then
|
||||
success=false; reason=invalid_permit_mailbox
|
||||
else
|
||||
# The guest creates a fixed-size mailbox before publishing readiness.
|
||||
# The host updates this same inode without truncating or renaming it.
|
||||
printf '%32s\n%s\n%s\n' pending "$selected_disk" "$source_commit" > "$STATE_DIR/install.permit" || {
|
||||
success=false; reason=permit_mailbox_write_failed;
|
||||
}
|
||||
fi
|
||||
fi
|
||||
printf '[proof-result] %s: %s\n' "$success" "$reason" >&3
|
||||
"""),
|
||||
" # Keep the service alive for the bounded host diagnostic to capture evidence.\n while :; do sleep 60; done",
|
||||
"""
|
||||
# Full mode waits for the host's independently validated fresh owned-disk permit.
|
||||
if [ "$success" = true ]; then
|
||||
local permit_token permit_disk permit_commit extra received
|
||||
permit_start=$SECONDS
|
||||
while (( SECONDS - permit_start < 300 )); do
|
||||
if [ -s "$STATE_DIR/install.permit" ]; then
|
||||
permit_token=; permit_disk=; permit_commit=; extra=; received=false
|
||||
# Read the record, not cached share size/entry metadata. Require three
|
||||
# terminated lines and EOF: partial or foreign permits never authorize.
|
||||
{
|
||||
if IFS= read -r permit_token && IFS= read -r permit_disk && IFS= read -r permit_commit; then
|
||||
if ! IFS= read -r extra && [ -z "$extra" ]; then received=true; fi
|
||||
fi
|
||||
} < "$STATE_DIR/install.permit"
|
||||
if [ "$received" = true ] && [ "$permit_token" = "$PROOF_TOKEN" ] &&
|
||||
[ "$permit_disk" = "$selected_disk" ] && [ "$permit_commit" = "$source_commit" ]; then
|
||||
[ "$(cat "$STATE_DIR/run.owner")" = "$PROOF_TOKEN" ] || exit 1
|
||||
printf '{"token":"%s","sourceCommit":"%s","disk":"%s"}\n' \
|
||||
"$PROOF_TOKEN" "$permit_commit" "$permit_disk" > "$STATE_DIR/guest-phase.permit.$$.tmp" &&
|
||||
/bin/mv -f "$STATE_DIR/guest-phase.permit.$$.tmp" "$STATE_DIR/install-permit-received.json" || exit 1
|
||||
printf '[full-install] owned permit received and validated\n' >> "$PROOF_LOG"
|
||||
exec /bin/bash "$STATE_DIR/full-install.sh"
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
printf '[full-install] host permit was not received in five minutes\n' >> "$PROOF_LOG"
|
||||
[ "$(cat "$STATE_DIR/run.owner")" = "$PROOF_TOKEN" ] || exit 1
|
||||
printf '{"token":"%s","phase":"installation-failed","reason":"permit-timeout"}\n' "$PROOF_TOKEN" \
|
||||
> "$STATE_DIR/guest-phase.permit.$$.tmp" &&
|
||||
/bin/mv -f "$STATE_DIR/guest-phase.permit.$$.tmp" "$STATE_DIR/guest-phase.json" || exit 1
|
||||
fi
|
||||
while :; do sleep 60; done
|
||||
""");
|
||||
@@ -1126,10 +1272,12 @@ static class NativeDiagnostic
|
||||
if (owner.Output.Trim() != token) throw new InvalidOperationException("Native state owner mismatch.");
|
||||
using var receipt = JsonDocument.Parse(readiness);
|
||||
var disk = receipt.RootElement.GetProperty("disk").GetString();
|
||||
var mailbox = await Command("docker", ["exec", id, "sh", "-c", "test -f \"$1\" && test ! -L \"$1\" && cat \"$1\"", "owned-permit-mailbox", FullState + "/install.permit"], output, "owned-permit-mailbox", cancellation);
|
||||
if (mailbox.Output != "pending".PadLeft(32) + "\n" + disk + "\n" + commit + "\n") throw new InvalidOperationException("Guest permit mailbox does not match fresh readiness.");
|
||||
var permit = Path.Combine(output, "install.permit");
|
||||
File.WriteAllText(permit, token + "\n" + disk + "\n" + commit + "\n");
|
||||
await Command("docker", ["cp", permit, id + ":" + FullState + "/install.permit.tmp"], output, "stage-owned-install-permit", cancellation);
|
||||
await Command("docker", ["exec", id, "mv", FullState + "/install.permit.tmp", FullState + "/install.permit"], output, "authorize-owned-guest-installation", cancellation);
|
||||
await Command("docker", ["exec", id, "sh", "-c", PermitPublication, "owned-permit-publication", FullState + "/install.permit"], output, "authorize-owned-guest-installation", cancellation);
|
||||
}
|
||||
|
||||
static readonly string[] NativeFacts = [
|
||||
@@ -1322,6 +1470,7 @@ static class NativeDiagnostic
|
||||
var files = new List<(string, string)> { ("proof.log", "guest-proof.log"), ("result.json", "guest-result.json") };
|
||||
if (full) files.AddRange([("guest-phase.json", "guest-phase.json"), ("full-result.json", "full-result.json"), ("firstboot.log", "firstboot.log"), ("install.log", "install.log"), ("apple.log", "apple.log"), ("disk-ownership-ioreg.log", "disk-ownership-ioreg.log"), ("installed-root.plist", "installed-root.plist"), ("apfs-containers.plist", "apfs-containers.plist"), ("physical-store.plist", "physical-store.plist"), ("clt-catalog.log", "clt-catalog.log"), ("clt-install.log", "clt-install.log")]);
|
||||
if (full) files.Add(("clt-sdk.log", "clt-sdk.log"));
|
||||
if (full) files.Add(("install-permit-received.json", "install-permit-received.json"));
|
||||
foreach (var file in files)
|
||||
{
|
||||
var result = await Command("docker", ["exec", id, "cat", (full ? FullState : "/dev/shm/installstate") + "/" + file.Item1], output, "capture-" + file.Item1.Replace('/', '-'), cancellation, requireSuccess: false);
|
||||
|
||||
Reference in new issue
Block a user