โฌข DragonFlyBSD Kernel Audit
DF-4855 / agp_orphan.c
โ† back to finding โ†“ download raw
/*
 * DF-4855 trigger โ€” orphaned /dev/agpgart on failed agp_amd64_attach.
 *
 * agp_amd64_attach() (sys/dev/agp/agp_amd64.c:159) calls agp_generic_attach()
 * at :183, which make_dev()s /dev/agpgart (agp.c:240-242). If a vendor
 * set_aperture then fails (ULi >=512MB / nForce3 >=1GB BIOS aperture:
 * agp_amd64.c:207-208, :213-214, :219-223), attach returns ENXIO WITHOUT
 * agp_generic_detach() โ€” newbus never calls detach for a failed attach, so:
 *
 *   - the GATT contigmalloc block (up to 2MiB) leaks           (agp.c:146)
 *   - the aperture bus resource leaks                          (agp.c:209)
 *   - /dev/agpgart stays live, si_drv1 -> unattached device    (agp.c:242)
 *
 * This program is the user-side probe of the orphaned node: it opens
 * /dev/agpgart and drives AGPIOC_ALLOCATE/AGPIOC_BIND. On real affected
 * hardware (AMD 8151/SiS 755/SiS 760/ULi M1689/nForce3/VIA K8 host bridge
 * with a too-large BIOS-set AGP aperture at kldload/boot), the bind lands in
 * a GATT that was never installed in the hardware (the ATTBASE/GARTEN loop
 * at agp_amd64.c:229-238 never ran) while sc->gatt is set โ€” plus, per
 * kldunload, state is uncleanable.
 *
 * Expected here (QEMU/i440FX, no AMD64 AGP bridge): open() -> ENOENT, i.e.
 * the driver never attached โ€” the failure path cannot be exercised on this
 * guest.
 *
 * build: cc -O -o agp_orphan agp_orphan.c
 * run:   ./agp_orphan
 */
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/agpio.h>

int
main(void)
{
	agp_allocate alloc;
	agp_bind bind;
	int fd, id;

	fd = open("/dev/agpgart", O_RDWR);
	if (fd < 0) {
		printf("open(/dev/agpgart) failed: %s\n", strerror(errno));
		printf("(no agp device attached on this machine)\n");
		return (1);
	}

	memset(&alloc, 0, sizeof(alloc));
	alloc.pg_count = 4;		/* 16KiB, one page-group */
	alloc.type = 0;		/* must be 0 for generic alloc */
	if (ioctl(fd, AGPIOC_ACQUIRE, 0) < 0)
		printf("ACQUIRE: %s\n", strerror(errno));
	if (ioctl(fd, AGPIOC_ALLOCATE, &alloc) < 0)
		printf("ALLOCATE: %s\n", strerror(errno));

	id = alloc.key;
	memset(&bind, 0, sizeof(bind));
	bind.key = id;
	bind.pg_start = 0;		/* this walks agp_amd64_bind_page */
	if (ioctl(fd, AGPIOC_BIND, &bind) < 0)
		printf("BIND: %s\n", strerror(errno));
	else
		printf("BIND: ok (bound into orphaned, never-installed GATT)\n");

	close(fd);
	return (0);
}