fix(kernel): a GPT disk with a bad header, or an unreadable first block, is not used

The increment 2 final review's minors 1-4 (Captain Bob, 2026-10-27: "fix 1-4"):
1. A protective MBR with no "EFI PART" header is GPT_BAD, not GPT_NONE: the
   MBR says it is a GPT disk, and a GPT disk is never attached whole.
2. A disk whose block 0 cannot be read is GPT_EIO: "GPT: a USB drive could
   not be read -- not used", nothing attached (it was read raw, attached
   whole, and a raw Zuse drive behind it logged her in).
3. The entry array's LBA is checked against the disk's size directly.
4. gpt_bad_entries: the entry array changed, the header valid -- refused.
Harness modes gpt_no_header, gpt_read_error (blkdebug EIO on sector 0),
gpt_bad_entries. The plan carries the amendment.

Red (amd64): gpt_no_header 070335, gpt_read_error 070717 FAIL;
gpt_bad_entries 070048 PASS (existing check, now pinned). Green: every GPT
mode on amd64 071113-072933; gpt_zuse, gpt_read_error, gpt_no_header on
aarch64 073222-074047 and riscv64 074526-075535; the identity gate's and
increment 1's sessions 080036-082345, emergency 083130 (082635 was run
without EMERGENCY_CONSOLE_ENABLED=1 -- a harness invocation error).
Accepted: suite EXIT 0; 093121, 093352, 093750, POST 538/538, dict_hash
0xfec8c47cfc6f437f; test-32, 0 failures.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rajames
2026-10-27 12:17:00 -04:00
co-authored by Claude Opus 5.5
parent f612bb37a9
commit 2e71f7aa0d
65 changed files with 4910 additions and 18 deletions
+37 -10
View File
@@ -39,6 +39,10 @@ the stick's, plan 2026-10-26-stick-1-screen-and-keyboard.md):
gpt_hotplug ... pulled: session ended; added again: prompt
gpt_foreign a GPT drive with only a Linux partition: no identity, not attached (info level)
gpt_bad_crc the gpt_zuse image with its header CRC broken: not used, said so, not attached
gpt_bad_entries ... its entry array changed (the header intact): the same
gpt_no_header ... its "EFI PART" signature gone, the protective MBR kept: the same
gpt_read_error the raw Zuse drive with reads of its sector 0 failing (blkdebug): not used,
said so, not attached
virtio_kbd aarch64/riscv64: QEMU's virtio keyboard types 7 ok
Needs only Python 3. One QEMU at a time (.claude/CLAUDE.md).
@@ -57,7 +61,8 @@ ap.add_argument("--make", default="")
ap.add_argument("--no-zuse", action="store_true")
a = ap.parse_args()
NO_ZUSE_MODES = {"gpt_zuse", "gpt_hotplug", "gpt_foreign", "gpt_bad_crc", "headless", "hotplug", "midpull", "waitpull", "typeahead", "emergency",
NO_ZUSE_MODES = {"gpt_zuse", "gpt_hotplug", "gpt_foreign", "gpt_bad_crc", "gpt_bad_entries", "gpt_no_header",
"gpt_read_error", "headless", "hotplug", "midpull", "waitpull", "typeahead", "emergency",
"alttab_after_logout", "alttab_before_pull", "emergency_pull",
"screen_dark_until_login", "screen_console_after_login", "screen_dark_after_logout",
"kbd_typeahead"}
@@ -191,9 +196,11 @@ GPT_IDENTITY = "23BEBFE1-4817-4CF3-A537-9069BF159788"
GPT_LINUX = "0FC63DAF-8483-4772-8E79-3D69D8477DE4"
def make_gpt_image(path, ptype, payload, mib=72, break_crc=False):
def make_gpt_image(path, ptype, payload, mib=72, break_crc=False, break_entries=False, break_sig=False):
"""A GPT disk image: one partition at LBA 2048, 131072 sectors (64 MiB), of type
`ptype`, holding `payload` from its start; optionally the primary header's CRC broken."""
`ptype`, holding `payload` from its start; optionally the primary header's CRC broken,
a byte of the entry array changed (the header left valid), or the header's "EFI PART"
signature zeroed (the protective MBR left in place)."""
subprocess.run(["truncate", "-s", f"{mib}M", path], check=True)
sf = shutil.which("sfdisk") or "/usr/sbin/sfdisk"
subprocess.run([sf, "--quiet", path], input=f"label: gpt\nstart=2048, size=131072, type={ptype}\n",
@@ -206,6 +213,16 @@ def make_gpt_image(path, ptype, payload, mib=72, break_crc=False):
b = f.read(1)
f.seek(512 + 16)
f.write(bytes([b[0] ^ 0xFF]))
if break_entries:
with open(path, "r+b") as f:
f.seek(1024 + 56) # entry 0's name, inside the CRC'd entry array
b = f.read(1)
f.seek(1024 + 56)
f.write(bytes([b[0] ^ 0xFF]))
if break_sig:
with open(path, "r+b") as f:
f.seek(512) # the primary header's Signature
f.write(bytes(8))
def blk_lines():
@@ -239,9 +256,12 @@ def screen():
return w, h, lit, px
def add_zuse(n, img=None):
qcmd("blockdev-add", driver="raw", **{"node-name": f"zuse{n}"},
file={"driver": "file", "filename": img or zimg})
def add_zuse(n, img=None, fail_sector0=False):
f = {"driver": "file", "filename": img or zimg}
if fail_sector0: # every read touching sector 0 fails, EIO
f = {"driver": "blkdebug", "image": f,
"inject-error": [{"event": "none", "iotype": "read", "sector": 0, "errno": 5}]}
qcmd("blockdev-add", driver="raw", **{"node-name": f"zuse{n}"}, file=f)
qcmd("device_add", driver="usb-storage", bus="xhci0.0", port="1",
drive=f"zuse{n}", id=f"zusedev{n}")
@@ -437,13 +457,18 @@ elif m == "emergency_pull":
_, _, _, px2 = screen()
check("the screen changed with what was typed", px2 != px1)
elif m in ("gpt_zuse", "gpt_hotplug", "gpt_foreign", "gpt_bad_crc"):
elif m in ("gpt_zuse", "gpt_hotplug", "gpt_foreign", "gpt_bad_crc", "gpt_bad_entries", "gpt_no_header",
"gpt_read_error"):
gimg = os.path.join(work, "gpt.img")
make_gpt_image(gimg, GPT_LINUX if m == "gpt_foreign" else GPT_IDENTITY, zimg,
break_crc=(m == "gpt_bad_crc"))
break_crc=(m == "gpt_bad_crc"), break_entries=(m == "gpt_bad_entries"),
break_sig=(m == "gpt_no_header"))
check("no ok> for 10 s with no drive", not wait_for(ANYPROMPT, 10))
b0 = blk_lines()
add_zuse(1, gimg)
if m == "gpt_read_error":
add_zuse(1, zimg, fail_sector0=True) # the raw Zuse drive: no GPT, but block 0 unreadable
else:
add_zuse(1, gimg)
if m in ("gpt_zuse", "gpt_hotplug"):
check("Zuse: identity confirmed", wait_for(r"Zuse: identity confirmed from attached thumbdrive", 120))
check("[zuse@Hera] ok>", wait_for(PROMPT, 60))
@@ -463,8 +488,10 @@ elif m in ("gpt_zuse", "gpt_hotplug", "gpt_foreign", "gpt_bad_crc"):
add_zuse(2, gimg)
check("[zuse@Hera] ok> again", wait_for(PROMPT, 120, p))
else:
if m == "gpt_bad_crc":
if m in ("gpt_bad_crc", "gpt_bad_entries", "gpt_no_header"):
check("the damaged table is said so", wait_for(r"GPT: a USB drive has a damaged partition table -- not used", 60))
if m == "gpt_read_error":
check("the unreadable drive is said so", wait_for(r"GPT: a USB drive could not be read -- not used", 60))
check("no ok> for 30 s", not wait_for(ANYPROMPT, 30))
check("no identity", count(r"Zuse: identity confirmed") == 0)
check(f"not attached (blk: lines {b0} -> {blk_lines()})", blk_lines() == b0)