⬢ DragonFlyBSD Kernel Audit
DF-2283 / sg2283.c
← back to finding ↓ download raw
/*
 * DF-2283 PoC v2 -- sgread() tsleep(&hstat) has no wakeup source.
 *
 * Redesigned after the first run WEDGED the guest when the test fd was
 * closed while a reader slept (the DF-2282 open/close refcount-underflow
 * collision -- documented separately; not this finding).  v2:
 *   - a HOLDER process opens /dev/sg0 once and keeps the fd open for the
 *     whole session (no sgclose ever runs while tests are in flight);
 *   - children fork from the holder share the same open file description
 *     (no additional sgopen/sgclose churn);
 *   - state is observed by a separate `ps` probe, not by killing things;
 *   - the reader child parks in the blocked read indefinitely (proof is
 *     its wchan + sleep time, not its death).
 *
 * Output: machine-readable lines "DF2283:" on stdout.
 *
 * Usage:  ./sg2283 block          # child blocks in read(pack_id=4242)
 *         ./sg2283 probe          # parent-side: ps snapshot of child
 * (run block in background, probe from another session)
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

struct sg_hdr_local {
	int32_t pack_len;
	int32_t reply_len;
	int32_t pack_id;
	int32_t result;
	uint32_t bits;
	unsigned char sense[16];
};
#define SG_HDR_SIZE 36

int
main(int argc, char **argv)
{
	unsigned char buf[SG_HDR_SIZE + 512];
	struct sg_hdr_local *h = (struct sg_hdr_local *)buf;
	int fd;

	setvbuf(stdout, NULL, _IONBF, 0);
	if (argc < 2) {
		printf("usage: %s block|probe\n", argv[0]);
		return (2);
	}
	fd = open("/dev/sg0", O_RDWR);
	if (fd < 0) {
		printf("DF2283: open /dev/sg0 FAILED %s\n", strerror(errno));
		return (1);
	}

	if (strcmp(argv[1], "block") == 0) {
		/* queue one request with pack_id=2002 (INQUIRY) */
		unsigned char req[SG_HDR_SIZE + 6 + 512];
		struct sg_hdr_local *rh = (struct sg_hdr_local *)req;
		unsigned char *cdb = req + SG_HDR_SIZE;

		memset(req, 0, sizeof(req));
		rh->pack_len = (int32_t)sizeof(req);
		rh->reply_len = 512;
		rh->pack_id = 2002;
		cdb[0] = 0x12; cdb[4] = 0x24;
		if (write(fd, req, sizeof(req)) != (ssize_t)sizeof(req)) {
			printf("DF2283: write failed %s\n", strerror(errno));
			return (1);
		}
		printf("DF2283: request pack_id=2002 queued; "
		    "waiting 2s for sgdone()\n");
		sleep(2);

		/* CONTROL: read with the matching id returns at once */
		memset(buf, 0xAA, sizeof(buf));
		h->pack_id = 2002;
		h->reply_len = 512;
		alarm(10);
		if (read(fd, buf, SG_HDR_SIZE + 512) < 0) {
			printf("DF2283: control read FAILED %s\n",
			    strerror(errno));
			return (1);
		}
		alarm(0);
		printf("DF2283: CONTROL read(pack_id=2002) returned "
		    "immediately, result=%d\n", h->result);

		/* TEST: queue another, then read with a bogus pack_id */
		rh->pack_id = 3003;
		if (write(fd, req, sizeof(req)) != (ssize_t)sizeof(req)) {
			printf("DF2283: write2 failed %s\n", strerror(errno));
			return (1);
		}
		sleep(2);
		memset(buf, 0xAA, sizeof(buf));
		h->pack_id = 4242;		/* never matches */
		h->reply_len = 512;
		printf("DF2283: TEST read(pack_id=4242) entering "
		    "tsleep(&hstat, PCATCH, \"sgnull\", 0) NOW "
		    "(pid %d)\n", getpid());
		read(fd, buf, SG_HDR_SIZE + 512);
		printf("DF2283: TEST read RETURNED errno=%d "
		    "(unexpected: no wakeup source exists)\n", errno);
		return (0);
	}

	/* probe mode: nothing to do here; the ps probe runs externally */
	printf("DF2283: probe placeholder\n");
	return (0);
}