DF-4855 / agp_orphan.c
/* * 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); } |