| // BUG: unable to handle kernel paging request in do_con_trol |
| // https://syzkaller.appspot.com/bug?id=04775771a119c1ba4974a07c365475ada756a40d |
| // 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 <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> |
| |
| 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"); |
| } |
| |
| static long syz_open_dev(volatile long a0, volatile long a1, volatile long a2) |
| { |
| if (a0 == 0xc || a0 == 0xb) { |
| char buf[128]; |
| sprintf(buf, "/dev/%s/%d:%d", a0 == 0xc ? "char" : "block", (uint8_t)a1, |
| (uint8_t)a2); |
| return open(buf, O_RDWR, 0); |
| } else { |
| char buf[1024]; |
| char* hash; |
| strncpy(buf, (char*)a0, sizeof(buf) - 1); |
| buf[sizeof(buf) - 1] = 0; |
| while ((hash = strchr(buf, '#'))) { |
| *hash = '0' + (char)(a1 % 10); |
| a1 /= 10; |
| } |
| return open(buf, a2, 0); |
| } |
| } |
| |
| uint64_t r[2] = {0xffffffffffffffff, 0xffffffffffffffff}; |
| |
| int main(void) |
| { |
| syscall(__NR_mmap, 0x20000000ul, 0x1000000ul, 3ul, 0x32ul, -1, 0); |
| intptr_t res = 0; |
| memcpy((void*)0x20000100, "/dev/fb0\000", 9); |
| res = syscall(__NR_openat, 0xffffffffffffff9cul, 0x20000100ul, 0ul, 0ul); |
| if (res != -1) |
| r[0] = res; |
| *(uint32_t*)0x20000140 = 0; |
| *(uint32_t*)0x20000144 = 0; |
| *(uint32_t*)0x20000148 = 0; |
| *(uint32_t*)0x2000014c = 0; |
| *(uint32_t*)0x20000150 = 0; |
| *(uint32_t*)0x20000154 = 0; |
| *(uint32_t*)0x20000158 = 8; |
| *(uint32_t*)0x2000015c = 0; |
| *(uint32_t*)0x20000160 = 0; |
| *(uint32_t*)0x20000164 = 0; |
| *(uint32_t*)0x20000168 = 0; |
| *(uint32_t*)0x2000016c = 0; |
| *(uint32_t*)0x20000170 = 0; |
| *(uint32_t*)0x20000174 = 0; |
| *(uint32_t*)0x20000178 = 0; |
| *(uint32_t*)0x2000017c = 0; |
| *(uint32_t*)0x20000180 = 0; |
| *(uint32_t*)0x20000184 = 0; |
| *(uint32_t*)0x20000188 = 0; |
| *(uint32_t*)0x2000018c = 0; |
| *(uint32_t*)0x20000190 = 0; |
| *(uint32_t*)0x20000194 = 0; |
| *(uint32_t*)0x20000198 = 0; |
| *(uint32_t*)0x2000019c = 0; |
| *(uint32_t*)0x200001a0 = 0; |
| *(uint32_t*)0x200001a4 = 0; |
| *(uint32_t*)0x200001a8 = 0; |
| *(uint32_t*)0x200001ac = 0; |
| *(uint32_t*)0x200001b0 = 0; |
| *(uint32_t*)0x200001b4 = 0; |
| *(uint32_t*)0x200001b8 = 0; |
| *(uint32_t*)0x200001bc = 0; |
| *(uint32_t*)0x200001c0 = 0; |
| *(uint32_t*)0x200001c4 = 3; |
| *(uint32_t*)0x200001c8 = 0; |
| *(uint32_t*)0x200001cc = 0; |
| *(uint32_t*)0x200001d0 = 0; |
| *(uint32_t*)0x200001d4 = 0; |
| *(uint32_t*)0x200001d8 = 0; |
| *(uint32_t*)0x200001dc = 0; |
| syscall(__NR_ioctl, r[0], 0x4601ul, 0x20000140ul); |
| res = syz_open_dev(0xc, 4, 0x14); |
| if (res != -1) |
| r[1] = res; |
| *(uint8_t*)0x20000200 = 0x1b; |
| *(uint8_t*)0x20000201 = 0x5b; |
| *(uint8_t*)0x20000202 = 0x4b; |
| *(uint8_t*)0x20000203 = 0x9b; |
| *(uint8_t*)0x20000204 = 0; |
| *(uint8_t*)0x20000205 = 0; |
| *(uint8_t*)0x20000206 = 0; |
| *(uint8_t*)0x20000207 = 0; |
| *(uint64_t*)0x20000208 = 0; |
| *(uint16_t*)0x20000210 = 0; |
| *(uint16_t*)0x20000212 = 0; |
| *(uint32_t*)0x20000214 = 0; |
| *(uint64_t*)0x20000218 = 0; |
| *(uint64_t*)0x20000220 = 0x40; |
| *(uint64_t*)0x20000228 = 0; |
| *(uint32_t*)0x20000230 = 0; |
| *(uint16_t*)0x20000234 = 0; |
| *(uint16_t*)0x20000236 = 0x38; |
| *(uint16_t*)0x20000238 = 0; |
| *(uint16_t*)0x2000023a = 0; |
| *(uint16_t*)0x2000023c = 0; |
| *(uint16_t*)0x2000023e = 0; |
| *(uint32_t*)0x20000240 = 0; |
| *(uint32_t*)0x20000244 = 0; |
| *(uint64_t*)0x20000248 = 0; |
| *(uint64_t*)0x20000250 = 0; |
| *(uint64_t*)0x20000258 = 0; |
| *(uint64_t*)0x20000260 = 0; |
| *(uint64_t*)0x20000268 = 0; |
| *(uint64_t*)0x20000270 = 0; |
| syscall(__NR_write, r[1], 0x20000200ul, 0x78ul); |
| return 0; |
| } |