| // KASAN: use-after-free Write in __alloc_skb (2) |
| // https://syzkaller.appspot.com/bug?id=24e7fdb26d37ba956d9b7bec2663bfbbdda0930d |
| // status:open |
| // autogenerated by syzkaller (https://github.com/google/syzkaller) |
| |
| #define _GNU_SOURCE |
| |
| #include <arpa/inet.h> |
| #include <endian.h> |
| #include <fcntl.h> |
| #include <net/if.h> |
| #include <netinet/in.h> |
| #include <setjmp.h> |
| #include <signal.h> |
| #include <stdbool.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/socket.h> |
| #include <sys/stat.h> |
| #include <sys/syscall.h> |
| #include <sys/types.h> |
| #include <unistd.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 __thread int skip_segv; |
| static __thread jmp_buf segv_env; |
| |
| static void segv_handler(int sig, siginfo_t* info, void* ctx) |
| { |
| uintptr_t addr = (uintptr_t)info->si_addr; |
| const uintptr_t prog_start = 1 << 20; |
| const uintptr_t prog_end = 100 << 20; |
| if (__atomic_load_n(&skip_segv, __ATOMIC_RELAXED) && |
| (addr < prog_start || addr > prog_end)) { |
| _longjmp(segv_env, 1); |
| } |
| exit(sig); |
| } |
| |
| static void install_segv_handler(void) |
| { |
| struct sigaction sa; |
| memset(&sa, 0, sizeof(sa)); |
| sa.sa_handler = SIG_IGN; |
| syscall(SYS_rt_sigaction, 0x20, &sa, NULL, 8); |
| syscall(SYS_rt_sigaction, 0x21, &sa, NULL, 8); |
| memset(&sa, 0, sizeof(sa)); |
| sa.sa_sigaction = segv_handler; |
| sa.sa_flags = SA_NODEFER | SA_SIGINFO; |
| sigaction(SIGSEGV, &sa, NULL); |
| sigaction(SIGBUS, &sa, NULL); |
| } |
| |
| #define NONFAILING(...) \ |
| { \ |
| __atomic_fetch_add(&skip_segv, 1, __ATOMIC_SEQ_CST); \ |
| if (_setjmp(segv_env) == 0) { \ |
| __VA_ARGS__; \ |
| } \ |
| __atomic_fetch_sub(&skip_segv, 1, __ATOMIC_SEQ_CST); \ |
| } |
| |
| static void use_temporary_dir(void) |
| { |
| char tmpdir_template[] = "./syzkaller.XXXXXX"; |
| char* tmpdir = mkdtemp(tmpdir_template); |
| if (!tmpdir) |
| exit(1); |
| if (chmod(tmpdir, 0777)) |
| exit(1); |
| if (chdir(tmpdir)) |
| exit(1); |
| } |
| |
| 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"); |
| } |
| |
| uint64_t r[1] = {0xffffffffffffffff}; |
| |
| int main(void) |
| { |
| syscall(__NR_mmap, 0x20000000ul, 0x1000000ul, 3ul, 0x32ul, -1, 0); |
| install_segv_handler(); |
| use_temporary_dir(); |
| intptr_t res = 0; |
| res = syscall(__NR_socket, 0x10ul, 3ul, 0ul); |
| if (res != -1) |
| r[0] = res; |
| NONFAILING(*(uint64_t*)0x20000180 = 0); |
| NONFAILING(*(uint32_t*)0x20000188 = 0); |
| NONFAILING(*(uint64_t*)0x20000190 = 0x200000c0); |
| NONFAILING(*(uint64_t*)0x200000c0 = 0x20000000); |
| NONFAILING(*(uint32_t*)0x20000000 = 0x3c); |
| NONFAILING(*(uint16_t*)0x20000004 = 0x10); |
| NONFAILING(*(uint16_t*)0x20000006 = 0x401); |
| NONFAILING(*(uint32_t*)0x20000008 = 0); |
| NONFAILING(*(uint32_t*)0x2000000c = 0); |
| NONFAILING(*(uint8_t*)0x20000010 = 0); |
| NONFAILING(*(uint8_t*)0x20000011 = 0); |
| NONFAILING(*(uint16_t*)0x20000012 = 0); |
| NONFAILING(*(uint32_t*)0x20000014 = 0); |
| NONFAILING(*(uint32_t*)0x20000018 = 0x801); |
| NONFAILING(*(uint32_t*)0x2000001c = 0); |
| NONFAILING(*(uint16_t*)0x20000020 = 0x12); |
| NONFAILING(*(uint16_t*)0x20000022 = 0x12); |
| NONFAILING(*(uint16_t*)0x20000024 = 0xc); |
| NONFAILING(*(uint16_t*)0x20000026 = 1); |
| NONFAILING(memcpy((void*)0x20000028, "bridge\000", 7)); |
| NONFAILING(*(uint16_t*)0x20000030 = 0xc); |
| NONFAILING(*(uint16_t*)0x20000032 = 2); |
| NONFAILING(*(uint16_t*)0x20000034 = 8); |
| NONFAILING(*(uint16_t*)0x20000036 = 4); |
| NONFAILING(*(uint32_t*)0x20000038 = 0xffffff9e); |
| NONFAILING(*(uint64_t*)0x200000c8 = 0x3c); |
| NONFAILING(*(uint64_t*)0x20000198 = 1); |
| NONFAILING(*(uint64_t*)0x200001a0 = 0); |
| NONFAILING(*(uint64_t*)0x200001a8 = 0); |
| NONFAILING(*(uint32_t*)0x200001b0 = 0); |
| syscall(__NR_sendmsg, r[0], 0x20000180ul, 0ul); |
| return 0; |
| } |