/*
 * DF-2896 holder/recycler + misdirection detector (UNPRIVILEGED uid).
 *
 * Pinning to cpu0 (arranged by the root harness via usched_set on our pid)
 * is a *scheduling aid only*: it makes the unprivileged alloc/free churn
 * share the per-cpu objcache magazine with the devfs thread that frees the
 * constty cdevs, so a freed slot is re-initialized quickly (its si_ops is
 * transiently NULL and its si_tty re-targeted) while a racing /dev/console
 * write still holds the old pointer.
 *
 * DETECTOR: every pty opened here is NEVER TIOCCONS'd, so no console data
 * may ever legitimately appear on its master.  Finding the writer's fill
 * byte 'W' here proves the kernel dispatched a /dev/console write through
 * a stale cdev pointer that was recycled into an unrelated object.
 */
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/syscall.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define NPAIR		192
#define WBYTE		'W'

struct slot {
	int m, s;
	char name[64];
};

static struct slot slots[NPAIR];
static unsigned long hits;

int
main(void)
{
	int i, n, pin = 0;
	char buf[8192];
	int cpu = 0;

	setvbuf(stdout, NULL, _IONBF, 0);

	if (syscall(481, 0, 1 /*USCHED_SET_CPU*/, &cpu, sizeof(cpu)) == 0)
		pin = 1;
	printf("holder: uid=%d pinned_cpu0=%d\n", getuid(), pin);

	for (;;) {
		n = 0;
		while (n < NPAIR) {
			int m = open("/dev/ptmx", O_RDWR);
			char *pn;
			struct slot *sl = &slots[n];

			if (m < 0) { usleep(1000); continue; }
			if (grantpt(m) || unlockpt(m)) { close(m); continue; }
			pn = ptsname(m);
			if (!pn) { close(m); continue; }
			snprintf(sl->name, sizeof(sl->name), "%s", pn);
			sl->m = m;
			sl->s = open(sl->name, O_RDWR | O_NONBLOCK);
			if (sl->s < 0) { close(m); continue; }
			n++;
		}

		/* watch every master for misdirected console bytes */
		for (i = 0; i < n; i++) {
			int r = read(slots[i].m, buf, sizeof(buf));
			if (r > 0) {
				int j, w = 0;
				for (j = 0; j < r; j++)
					if (buf[j] == WBYTE)
						w++;
				if (w) {
					hits += w;
					printf("HIT: %ld W-bytes on %s "
					       "(never TIOCCONS'd)\n",
					       hits, slots[i].name);
				}
			}
		}

		/* release this generation so slots cycle again */
		for (i = 0; i < n; i++) {
			close(slots[i].s);
			close(slots[i].m);
		}
	}
}
