| // autogenerated by syzkaller (https://github.com/google/syzkaller) |
| |
| #define _GNU_SOURCE |
| |
| #include <arpa/inet.h> |
| #include <endian.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <net/if.h> |
| #include <netinet/in.h> |
| #include <pthread.h> |
| #include <stdbool.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/socket.h> |
| #include <sys/syscall.h> |
| #include <sys/types.h> |
| #include <time.h> |
| #include <unistd.h> |
| |
| #include <linux/futex.h> |
| #include <linux/genetlink.h> |
| #include <linux/if_addr.h> |
| #include <linux/if_link.h> |
| #include <linux/in6.h> |
| #include <linux/neighbour.h> |
| #include <linux/net.h> |
| #include <linux/netlink.h> |
| #include <linux/rtnetlink.h> |
| #include <linux/veth.h> |
| |
| static void sleep_ms(uint64_t ms) |
| { |
| usleep(ms * 1000); |
| } |
| |
| static uint64_t current_time_ms(void) |
| { |
| struct timespec ts; |
| if (clock_gettime(CLOCK_MONOTONIC, &ts)) |
| exit(1); |
| return (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000; |
| } |
| |
| static void thread_start(void* (*fn)(void*), void* arg) |
| { |
| pthread_t th; |
| pthread_attr_t attr; |
| pthread_attr_init(&attr); |
| pthread_attr_setstacksize(&attr, 128 << 10); |
| int i; |
| for (i = 0; i < 100; i++) { |
| if (pthread_create(&th, &attr, fn, arg) == 0) { |
| pthread_attr_destroy(&attr); |
| return; |
| } |
| if (errno == EAGAIN) { |
| usleep(50); |
| continue; |
| } |
| break; |
| } |
| exit(1); |
| } |
| |
| typedef struct { |
| int state; |
| } event_t; |
| |
| static void event_init(event_t* ev) |
| { |
| ev->state = 0; |
| } |
| |
| static void event_reset(event_t* ev) |
| { |
| ev->state = 0; |
| } |
| |
| static void event_set(event_t* ev) |
| { |
| if (ev->state) |
| exit(1); |
| __atomic_store_n(&ev->state, 1, __ATOMIC_RELEASE); |
| syscall(SYS_futex, &ev->state, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, 1000000); |
| } |
| |
| static void event_wait(event_t* ev) |
| { |
| while (!__atomic_load_n(&ev->state, __ATOMIC_ACQUIRE)) |
| syscall(SYS_futex, &ev->state, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, 0, 0); |
| } |
| |
| static int event_isset(event_t* ev) |
| { |
| return __atomic_load_n(&ev->state, __ATOMIC_ACQUIRE); |
| } |
| |
| static int event_timedwait(event_t* ev, uint64_t timeout) |
| { |
| uint64_t start = current_time_ms(); |
| uint64_t now = start; |
| for (;;) { |
| uint64_t remain = timeout - (now - start); |
| struct timespec ts; |
| ts.tv_sec = remain / 1000; |
| ts.tv_nsec = (remain % 1000) * 1000 * 1000; |
| syscall(SYS_futex, &ev->state, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, 0, &ts); |
| if (__atomic_load_n(&ev->state, __ATOMIC_RELAXED)) |
| return 1; |
| now = current_time_ms(); |
| if (now - start > timeout) |
| return 0; |
| } |
| } |
| |
| struct nlmsg { |
| char* pos; |
| int nesting; |
| struct nlattr* nested[8]; |
| char buf[1024]; |
| }; |
| |
| static struct nlmsg nlmsg; |
| |
| static void netlink_init(struct nlmsg* nlmsg, int typ, int flags, |
| const void* data, int size) |
| { |
| memset(nlmsg, 0, sizeof(*nlmsg)); |
| struct nlmsghdr* hdr = (struct nlmsghdr*)nlmsg->buf; |
| hdr->nlmsg_type = typ; |
| hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags; |
| memcpy(hdr + 1, data, size); |
| nlmsg->pos = (char*)(hdr + 1) + NLMSG_ALIGN(size); |
| } |
| |
| static void netlink_attr(struct nlmsg* nlmsg, int typ, const void* data, |
| int size) |
| { |
| struct nlattr* attr = (struct nlattr*)nlmsg->pos; |
| attr->nla_len = sizeof(*attr) + size; |
| attr->nla_type = typ; |
| memcpy(attr + 1, data, size); |
| nlmsg->pos += NLMSG_ALIGN(attr->nla_len); |
| } |
| |
| static int netlink_send_ext(struct nlmsg* nlmsg, int sock, uint16_t reply_type, |
| int* reply_len) |
| { |
| if (nlmsg->pos > nlmsg->buf + sizeof(nlmsg->buf) || nlmsg->nesting) |
| exit(1); |
| struct nlmsghdr* hdr = (struct nlmsghdr*)nlmsg->buf; |
| hdr->nlmsg_len = nlmsg->pos - nlmsg->buf; |
| struct sockaddr_nl addr; |
| memset(&addr, 0, sizeof(addr)); |
| addr.nl_family = AF_NETLINK; |
| unsigned n = sendto(sock, nlmsg->buf, hdr->nlmsg_len, 0, |
| (struct sockaddr*)&addr, sizeof(addr)); |
| if (n != hdr->nlmsg_len) |
| exit(1); |
| n = recv(sock, nlmsg->buf, sizeof(nlmsg->buf), 0); |
| if (hdr->nlmsg_type == NLMSG_DONE) { |
| *reply_len = 0; |
| return 0; |
| } |
| if (n < sizeof(struct nlmsghdr)) |
| exit(1); |
| if (reply_len && hdr->nlmsg_type == reply_type) { |
| *reply_len = n; |
| return 0; |
| } |
| if (n < sizeof(struct nlmsghdr) + sizeof(struct nlmsgerr)) |
| exit(1); |
| if (hdr->nlmsg_type != NLMSG_ERROR) |
| exit(1); |
| return -((struct nlmsgerr*)(hdr + 1))->error; |
| } |
| |
| static int netlink_send(struct nlmsg* nlmsg, int sock) |
| { |
| return netlink_send_ext(nlmsg, sock, 0, NULL); |
| } |
| |
| static int netlink_next_msg(struct nlmsg* nlmsg, unsigned int offset, |
| unsigned int total_len) |
| { |
| struct nlmsghdr* hdr = (struct nlmsghdr*)(nlmsg->buf + offset); |
| if (offset == total_len || offset + hdr->nlmsg_len > total_len) |
| return -1; |
| return hdr->nlmsg_len; |
| } |
| |
| static void netlink_device_change(struct nlmsg* nlmsg, int sock, |
| const char* name, bool up, const char* master, |
| const void* mac, int macsize, |
| const char* new_name) |
| { |
| struct ifinfomsg hdr; |
| memset(&hdr, 0, sizeof(hdr)); |
| if (up) |
| hdr.ifi_flags = hdr.ifi_change = IFF_UP; |
| hdr.ifi_index = if_nametoindex(name); |
| netlink_init(nlmsg, RTM_NEWLINK, 0, &hdr, sizeof(hdr)); |
| if (new_name) |
| netlink_attr(nlmsg, IFLA_IFNAME, new_name, strlen(new_name)); |
| if (master) { |
| int ifindex = if_nametoindex(master); |
| netlink_attr(nlmsg, IFLA_MASTER, &ifindex, sizeof(ifindex)); |
| } |
| if (macsize) |
| netlink_attr(nlmsg, IFLA_ADDRESS, mac, macsize); |
| int err = netlink_send(nlmsg, sock); |
| (void)err; |
| } |
| |
| const int kInitNetNsFd = 239; |
| |
| #define DEVLINK_FAMILY_NAME "devlink" |
| |
| #define DEVLINK_CMD_PORT_GET 5 |
| #define DEVLINK_CMD_RELOAD 37 |
| #define DEVLINK_ATTR_BUS_NAME 1 |
| #define DEVLINK_ATTR_DEV_NAME 2 |
| #define DEVLINK_ATTR_NETDEV_NAME 7 |
| #define DEVLINK_ATTR_NETNS_FD 138 |
| |
| static int netlink_devlink_id_get(struct nlmsg* nlmsg, int sock) |
| { |
| struct genlmsghdr genlhdr; |
| struct nlattr* attr; |
| int err, n; |
| uint16_t id = 0; |
| memset(&genlhdr, 0, sizeof(genlhdr)); |
| genlhdr.cmd = CTRL_CMD_GETFAMILY; |
| netlink_init(nlmsg, GENL_ID_CTRL, 0, &genlhdr, sizeof(genlhdr)); |
| netlink_attr(nlmsg, CTRL_ATTR_FAMILY_NAME, DEVLINK_FAMILY_NAME, |
| strlen(DEVLINK_FAMILY_NAME) + 1); |
| err = netlink_send_ext(nlmsg, sock, GENL_ID_CTRL, &n); |
| if (err) { |
| return -1; |
| } |
| attr = (struct nlattr*)(nlmsg->buf + NLMSG_HDRLEN + |
| NLMSG_ALIGN(sizeof(genlhdr))); |
| for (; (char*)attr < nlmsg->buf + n; |
| attr = (struct nlattr*)((char*)attr + NLMSG_ALIGN(attr->nla_len))) { |
| if (attr->nla_type == CTRL_ATTR_FAMILY_ID) { |
| id = *(uint16_t*)(attr + 1); |
| break; |
| } |
| } |
| if (!id) { |
| return -1; |
| } |
| recv(sock, nlmsg->buf, sizeof(nlmsg->buf), 0); /* recv ack */ |
| return id; |
| } |
| |
| static void netlink_devlink_netns_move(const char* bus_name, |
| const char* dev_name, int netns_fd) |
| { |
| struct genlmsghdr genlhdr; |
| int sock; |
| int id, err; |
| sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); |
| if (sock == -1) |
| exit(1); |
| id = netlink_devlink_id_get(&nlmsg, sock); |
| if (id == -1) |
| goto error; |
| memset(&genlhdr, 0, sizeof(genlhdr)); |
| genlhdr.cmd = DEVLINK_CMD_RELOAD; |
| netlink_init(&nlmsg, id, 0, &genlhdr, sizeof(genlhdr)); |
| netlink_attr(&nlmsg, DEVLINK_ATTR_BUS_NAME, bus_name, strlen(bus_name) + 1); |
| netlink_attr(&nlmsg, DEVLINK_ATTR_DEV_NAME, dev_name, strlen(dev_name) + 1); |
| netlink_attr(&nlmsg, DEVLINK_ATTR_NETNS_FD, &netns_fd, sizeof(netns_fd)); |
| err = netlink_send(&nlmsg, sock); |
| if (err) { |
| } |
| error: |
| close(sock); |
| } |
| |
| static struct nlmsg nlmsg2; |
| |
| static void initialize_devlink_ports(const char* bus_name, const char* dev_name, |
| const char* netdev_prefix) |
| { |
| struct genlmsghdr genlhdr; |
| int len, total_len, id, err, offset; |
| uint16_t netdev_index; |
| int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); |
| if (sock == -1) |
| exit(1); |
| int rtsock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); |
| if (rtsock == -1) |
| exit(1); |
| id = netlink_devlink_id_get(&nlmsg, sock); |
| if (id == -1) |
| goto error; |
| memset(&genlhdr, 0, sizeof(genlhdr)); |
| genlhdr.cmd = DEVLINK_CMD_PORT_GET; |
| netlink_init(&nlmsg, id, NLM_F_DUMP, &genlhdr, sizeof(genlhdr)); |
| netlink_attr(&nlmsg, DEVLINK_ATTR_BUS_NAME, bus_name, strlen(bus_name) + 1); |
| netlink_attr(&nlmsg, DEVLINK_ATTR_DEV_NAME, dev_name, strlen(dev_name) + 1); |
| err = netlink_send_ext(&nlmsg, sock, id, &total_len); |
| if (err) { |
| goto error; |
| } |
| offset = 0; |
| netdev_index = 0; |
| while ((len = netlink_next_msg(&nlmsg, offset, total_len)) != -1) { |
| struct nlattr* attr = (struct nlattr*)(nlmsg.buf + offset + NLMSG_HDRLEN + |
| NLMSG_ALIGN(sizeof(genlhdr))); |
| for (; (char*)attr < nlmsg.buf + offset + len; |
| attr = (struct nlattr*)((char*)attr + NLMSG_ALIGN(attr->nla_len))) { |
| if (attr->nla_type == DEVLINK_ATTR_NETDEV_NAME) { |
| char* port_name; |
| char netdev_name[IFNAMSIZ]; |
| port_name = (char*)(attr + 1); |
| snprintf(netdev_name, sizeof(netdev_name), "%s%d", netdev_prefix, |
| netdev_index); |
| netlink_device_change(&nlmsg2, rtsock, port_name, true, 0, 0, 0, |
| netdev_name); |
| break; |
| } |
| } |
| offset += len; |
| netdev_index++; |
| } |
| error: |
| close(rtsock); |
| close(sock); |
| } |
| |
| static void initialize_devlink_pci(void) |
| { |
| int netns = open("/proc/self/ns/net", O_RDONLY); |
| if (netns == -1) |
| exit(1); |
| int ret = setns(kInitNetNsFd, 0); |
| if (ret == -1) |
| exit(1); |
| netlink_devlink_netns_move("pci", "0000:00:10.0", netns); |
| ret = setns(netns, 0); |
| if (ret == -1) |
| exit(1); |
| close(netns); |
| initialize_devlink_ports("pci", "0000:00:10.0", "netpci"); |
| } |
| |
| struct thread_t { |
| int created, call; |
| event_t ready, done; |
| }; |
| |
| static struct thread_t threads[16]; |
| static void execute_call(int call); |
| static int running; |
| |
| static void* thr(void* arg) |
| { |
| struct thread_t* th = (struct thread_t*)arg; |
| for (;;) { |
| event_wait(&th->ready); |
| event_reset(&th->ready); |
| execute_call(th->call); |
| __atomic_fetch_sub(&running, 1, __ATOMIC_RELAXED); |
| event_set(&th->done); |
| } |
| return 0; |
| } |
| |
| static void loop(void) |
| { |
| int i, call, thread; |
| int collide = 0; |
| again: |
| for (call = 0; call < 6; call++) { |
| for (thread = 0; thread < (int)(sizeof(threads) / sizeof(threads[0])); |
| thread++) { |
| struct thread_t* th = &threads[thread]; |
| if (!th->created) { |
| th->created = 1; |
| event_init(&th->ready); |
| event_init(&th->done); |
| event_set(&th->done); |
| thread_start(thr, th); |
| } |
| if (!event_isset(&th->done)) |
| continue; |
| event_reset(&th->done); |
| th->call = call; |
| __atomic_fetch_add(&running, 1, __ATOMIC_RELAXED); |
| event_set(&th->ready); |
| if (collide && (call % 2) == 0) |
| break; |
| event_timedwait(&th->done, 45); |
| break; |
| } |
| } |
| for (i = 0; i < 100 && __atomic_load_n(&running, __ATOMIC_RELAXED); i++) |
| sleep_ms(1); |
| if (!collide) { |
| collide = 1; |
| goto again; |
| } |
| } |
| |
| uint64_t r[3] = {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff}; |
| |
| void execute_call(int call) |
| { |
| intptr_t res; |
| switch (call) { |
| case 0: |
| res = syscall(__NR_eventfd2, 0, 0ul); |
| if (res != -1) |
| r[0] = res; |
| break; |
| case 1: |
| res = syscall(__NR_dup, r[0]); |
| if (res != -1) |
| r[1] = res; |
| break; |
| case 2: |
| *(uint32_t*)0x20000100 = 0; |
| *(uint16_t*)0x20000104 = 0x29da; |
| *(uint16_t*)0x20000106 = 0xfa00; |
| *(uint64_t*)0x20000108 = 0; |
| *(uint64_t*)0x20000110 = 0; |
| *(uint16_t*)0x20000118 = 0; |
| *(uint8_t*)0x2000011a = 0; |
| *(uint8_t*)0x2000011b = 0; |
| *(uint8_t*)0x2000011c = 0; |
| *(uint8_t*)0x2000011d = 0; |
| *(uint8_t*)0x2000011e = 0; |
| *(uint8_t*)0x2000011f = 0; |
| syscall(__NR_write, r[1], 0x20000100ul, 0x20ul); |
| break; |
| case 3: |
| memcpy((void*)0x20000080, "./bus\000", 6); |
| res = syscall(__NR_open, 0x20000080ul, 0x141042ul, 0ul); |
| if (res != -1) |
| r[2] = res; |
| break; |
| case 4: |
| memcpy((void*)0x20000000, "./file0\000", 8); |
| memcpy((void*)0x20000040, "9p\000", 3); |
| memcpy((void*)0x200001c0, "trans=fd,", 9); |
| memcpy((void*)0x200001c9, "rfdno", 5); |
| *(uint8_t*)0x200001ce = 0x3d; |
| sprintf((char*)0x200001cf, "0x%016llx", (long long)r[2]); |
| *(uint8_t*)0x200001e1 = 0x2c; |
| memcpy((void*)0x200001e2, "wfdno", 5); |
| *(uint8_t*)0x200001e7 = 0x3d; |
| sprintf((char*)0x200001e8, "0x%016llx", (long long)r[0]); |
| *(uint8_t*)0x200001fa = 0x2c; |
| memcpy((void*)0x200001fb, "cache=none", 10); |
| *(uint8_t*)0x20000205 = 0x2c; |
| memcpy((void*)0x20000206, "access=client", 13); |
| *(uint8_t*)0x20000213 = 0x2c; |
| memcpy((void*)0x20000214, "debug", 5); |
| *(uint8_t*)0x20000219 = 0x3d; |
| sprintf((char*)0x2000021a, "0x%016llx", (long long)0xce); |
| *(uint8_t*)0x2000022c = 0x2c; |
| memcpy((void*)0x2000022d, "nodevmap", 8); |
| *(uint8_t*)0x20000235 = 0x2c; |
| memcpy((void*)0x20000236, "subj_role", 9); |
| *(uint8_t*)0x2000023f = 0x3d; |
| memcpy((void*)0x20000240, "eth1", 4); |
| *(uint8_t*)0x20000244 = 0x2c; |
| memcpy((void*)0x20000245, "subj_user", 9); |
| *(uint8_t*)0x2000024e = 0x3d; |
| memcpy((void*)0x2000024f, "%(+/(em0!", 9); |
| *(uint8_t*)0x20000258 = 0x2c; |
| memcpy((void*)0x20000259, "fsname", 6); |
| *(uint8_t*)0x2000025f = 0x3d; |
| memcpy((void*)0x20000260, "vmnet0cgroup)++-($", 18); |
| *(uint8_t*)0x20000272 = 0x2c; |
| memcpy((void*)0x20000273, "fsuuid", 6); |
| *(uint8_t*)0x20000279 = 0x3d; |
| *(uint8_t*)0x2000027a = 0x36; |
| *(uint8_t*)0x2000027b = 0xc6; |
| *(uint8_t*)0x2000027c = 0x37; |
| *(uint8_t*)0x2000027d = 0x30; |
| *(uint8_t*)0x2000027e = 0x36; |
| *(uint8_t*)0x2000027f = 0x38; |
| *(uint8_t*)0x20000280 = 0x61; |
| *(uint8_t*)0x20000281 = 0x33; |
| *(uint8_t*)0x20000282 = 0x2d; |
| *(uint8_t*)0x20000283 = 0x31; |
| *(uint8_t*)0x20000284 = 4; |
| *(uint8_t*)0x20000285 = 0x38; |
| *(uint8_t*)0x20000286 = 0x32; |
| *(uint8_t*)0x20000287 = 0x2d; |
| *(uint8_t*)0x20000288 = 0x31; |
| *(uint8_t*)0x20000289 = 0x39; |
| *(uint8_t*)0x2000028a = 0x37; |
| *(uint8_t*)0x2000028b = 0x32; |
| *(uint8_t*)0x2000028c = 0x2d; |
| *(uint8_t*)0x2000028d = 0x65; |
| *(uint8_t*)0x2000028e = 0x30; |
| *(uint8_t*)0x2000028f = 0x66; |
| *(uint8_t*)0x20000290 = 0x78; |
| *(uint8_t*)0x20000291 = 0x2d; |
| *(uint8_t*)0x20000292 = 0x62; |
| *(uint8_t*)0x20000293 = 0x38; |
| *(uint8_t*)0x20000294 = 0x39; |
| *(uint8_t*)0x20000295 = 0x64; |
| *(uint8_t*)0x20000296 = 0x48; |
| *(uint8_t*)0x20000297 = 0x34; |
| *(uint8_t*)0x20000298 = 0x38; |
| *(uint8_t*)0x20000299 = 0x31; |
| *(uint8_t*)0x2000029a = 0x2c; |
| memcpy((void*)0x2000029b, "pcr", 3); |
| *(uint8_t*)0x2000029e = 0x3d; |
| sprintf((char*)0x2000029f, "%020llu", (long long)0x32); |
| *(uint8_t*)0x200002b3 = 0x2c; |
| memcpy((void*)0x200002b4, "appraise", 8); |
| *(uint8_t*)0x200002bc = 0x2c; |
| memcpy((void*)0x200002bd, "defcontext", 10); |
| *(uint8_t*)0x200002c7 = 0x3d; |
| memcpy((void*)0x200002c8, "root", 4); |
| *(uint8_t*)0x200002cc = 0x2c; |
| memcpy((void*)0x200002cd, "fsmagic", 7); |
| *(uint8_t*)0x200002d4 = 0x3d; |
| sprintf((char*)0x200002d5, "0x%016llx", (long long)1); |
| *(uint8_t*)0x200002e7 = 0x2c; |
| *(uint8_t*)0x200002e8 = 0; |
| syscall(__NR_mount, 0ul, 0x20000000ul, 0x20000040ul, 0x2000000ul, |
| 0x200001c0ul); |
| break; |
| case 5: |
| memcpy((void*)0x200000c0, "./file0\000", 8); |
| syscall(__NR_open, 0x200000c0ul, 0x220141042ul, 0ul); |
| break; |
| } |
| } |
| int main(void) |
| { |
| syscall(__NR_mmap, 0x20000000ul, 0x1000000ul, 3ul, 0x32ul, -1, 0); |
| loop(); |
| return 0; |
| } |