โฌข DragonFlyBSD Kernel Audit
DF-4280 / trigger.c
โ† back to finding โ†“ download raw
/* DF-4280 trigger โ€” ccd.c:1434 dev_drefs() guard can never be satisfied.
 *
 * disk_create() (subr_disk.c:699) creates the raw device with
 * make_only_dev(), which takes a PERMANENT reference (kern_conf.c:294
 * reference_dev(); destroy_only_dev releases twice).  cs->sc_dev IS
 * that raw device, so dev_drefs(cs->sc_dev) >= 2 even with zero open
 * descriptors, and the CCDIOCCLR guard
 *
 *     if (dev_drefs(cs->sc_dev) > 1)   (ccd.c:1434)
 *         return (EBUSY);
 *
 * is ALWAYS true.  Once a unit is configured it can never be
 * unconfigured: CCDF_INITED stays set forever, every component vnode
 * stays exclusively open (ccdlookup's v_opencount>1 check), the
 * interleave tables stay allocated, and `kldunload ccd' fails with
 * "In use, cannot detach" forever.
 *
 * Demonstration: configure ccdN (any ileave), then attempt
 * CCDIOCCLR through the SAME still-open descriptor โ€” EBUSY.
 */
#define _KERNEL_STRUCTURES
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/ccdvar.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

int
main(int argc, char **argv)
{
	struct ccd_ioctl ccio;
	char *disks[1];
	int fd;

	if (argc < 3) {
		fprintf(stderr, "usage: %s <ccddev> <component>\n", argv[0]);
		return (2);
	}

	fd = open(argv[1], O_RDWR);
	if (fd < 0) { perror("open ccd"); return (1); }

	disks[0] = argv[2];
	bzero(&ccio, sizeof(ccio));
	ccio.ccio_disks = disks;
	ccio.ccio_ndisks = 1;
	ccio.ccio_ileave = 32;
	ccio.ccio_flags = 0;
	if (ioctl(fd, CCDIOCSET, &ccio) < 0) { perror("CCDIOCSET"); return (1); }
	printf("[+] CCDIOCSET ok size=%llu\n",
	    (unsigned long long)ccio.ccio_size);
	fflush(stdout);

	/* clear through the SAME fd, still open โ€” nothing else holds the
	 * device; this must succeed but returns EBUSY */
	bzero(&ccio, sizeof(ccio));
	if (ioctl(fd, CCDIOCCLR, &ccio) < 0) {
		printf("[-] CCDIOCCLR(same fd, no other users): %s\n",
		    strerror(errno));
		printf("[-] BUG: unit can never be unconfigured this boot\n");
		return (1);
	}
	printf("[+] CCDIOCCLR ok\n");
	return (0);
}