⬢ DragonFlyBSD Kernel Audit
DF-5498 / c5498_user.c
← back to finding ↓ download raw
/*
 * DF-5498 userspace model: production ROM *builder* off-by-one.
 *
 * crom_add_simple_text() computes the text-leaf length including the
 * 12-byte struct csrtext header:
 *
 *	sys/bus/firewire/fwcrom.c:459
 *		tl->crc_len = howmany(sizeof(struct csrtext) + len, 4);
 *
 * But crc_len counts the quadlets FOLLOWING the crc quadlet (compare the
 * directory builder: crom_add_quad() counts entries only), and
 * crom_load() adds the crc quadlet back:
 *
 *	sys/bus/firewire/fwcrom.c:520-522
 *		len = 1 + chunk->data.crc_len;
 *		crom_copy(..., len, ...)	-> bcopy of len*4 bytes
 *	sys/bus/firewire/fwcrom.c:517-518
 *		chunk->data.crc = crom_crc(&chunk->data.buf[0],
 *					   chunk->data.crc_len);
 *
 * => the copy/CRC always consume ONE quadlet past the text; for text
 * length 69..72 (MAX_TEXT == 72, fwcrom.c:442) that quadlet lies past
 * chunk->data[] (84 bytes = 21 quadlets): a sub-object OOB read whose
 * bytes are published verbatim in the host's Config ROM.
 *
 * Model: struct crom_chunk-sized buffer; bytes [0..116) (the struct up
 * to the end of data[]) are zeroed, the 4 tail bytes past data[]
 * (offset 116..120, in-kernel: zeroed tail padding of the kmalloc'd
 * object / adjacent memory) are pre-filled with 0xCAFEBABE.  Run the
 * PRODUCTION crom_add_simple_text()/crom_load() and scan the generated
 * ROM: 0xCAFEBABE present == the quadlet past data[] was read and
 * broadcast.  68-char control: exact fit, no pattern.
 */
#include <sys/types.h>
#include <sys/queue.h>		/* STAILQ_*: needed before iec13213.h's -DTEST structs */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>

#include <bus/firewire/firewire.h>
#include <bus/firewire/iec13213.h>

#define PATTERN 0xCAFEBABEu

static int
run_case(int tlen, const char *label)
{
	struct crom_src src;
	struct crom_chunk root;
	char raw[sizeof(struct crom_chunk) + 16];
	struct crom_chunk *chunk = (struct crom_chunk *)(void *)raw;
	char text[80];
	u_int32_t out[256];
	size_t used = offsetof(struct crom_chunk, data) +
	    sizeof(chunk->data);			/* == 116 */
	int i, n, found = -1, leafhdr = -1;

	/* pattern everywhere, then zero the meaningful prefix so ONLY the
	 * bytes past data[] keep the pattern */
	memset(raw, 0xBB, sizeof(raw));
	memset(raw, 0, used);
	*(u_int32_t *)(raw + used) = PATTERN;

	memset(&src, 0, sizeof(src));
	memset(&root, 0, sizeof(root));
	src.hdr.info_len = 4;
	STAILQ_INIT(&src.chunk_list);
	crom_add_chunk(&src, NULL, &root, 0);

	memset(text, 'A', sizeof(text));
	text[tlen] = '\0';
	crom_add_simple_text(&src, &root, chunk, text);

	printf("%s: strlen=%d builder crc_len=%d (data holds 21 quads; "
	    "1+crc_len=%d quads will be copied/CRC'd)\n",
	    label, tlen, chunk->data.crc_len, 1 + chunk->data.crc_len);

	memset(out, 0, sizeof(out));
	n = crom_load(&src, out, 256);
	if (n < 0) {
		printf("%s: crom_load failed\n", label);
		return (1);
	}
	/* text chunk starts after 5 bus quads + 1+root quads (root has 1
	 * entry): out[7] is the leaf header. */
	leafhdr = out[7];
	for (i = 0; i < n; i++)
		if (out[i] == PATTERN)
			found = i;
	printf("%s: ROM quadlets=%d, published leaf header=0x%08x "
	    "(crc_len field=%d, IEEE-1212-correct=%d), PATTERN %s\n",
	    label, n, leafhdr, (leafhdr >> 16) & 0xffff,
	    2 + (tlen + 3) / 4,
	    found >= 0 ? "FOUND in generated ROM (OOB quadlet published)"
		       : "absent");
	return (found >= 0);
}

int
main(void)
{
	int r1, r2;

	puts("DF-5498 userspace model: production fwcrom.c builder");
	r1 = run_case(72, "case1 len=72 (max)");
	r2 = run_case(68, "case2 len=68 (control, exact fit)");
	printf("RESULT: %s\n", (r1 && !r2) ?
	    "REPRODUCED: the quadlet past chunk->data[] was read by "
	    "crom_load/crom_crc and published in the Config ROM" :
	    "not reproduced");
	return (r1 && !r2) ? 42 : 1;
}