text: // inconsistent lock state in sp_get
  // https://syzkaller.appspot.com/bug?id=c02cf77a89e9160538695c809d592d1d4ad2fcf6
  // status:fixed
  // autogenerated by syzkaller (https://github.com/google/syzkaller)
  
  #define _GNU_SOURCE
  
  #include <arpa/inet.h>
  #include <dirent.h>
  #include <endian.h>
  #include <errno.h>
  #include <fcntl.h>
  #include <net/if.h>
  #include <netinet/in.h>
  #include <signal.h>
  #include <stdarg.h>
  #include <stdbool.h>
  #include <stdint.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <sys/prctl.h>
  #include <sys/socket.h>
  #include <sys/stat.h>
  #include <sys/syscall.h>
  #include <sys/types.h>
  #include <sys/wait.h>
  #include <time.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>
  
  unsigned long long procid;
  
  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 bool write_file(const char* file, const char* what, ...)
  {
    char buf[1024];
    va_list args;
    va_start(args, what);
    vsnprintf(buf, sizeof(buf), what, args);
    va_end(args);
    buf[sizeof(buf) - 1] = 0;
    int len = strlen(buf);
    int fd = open(file, O_WRONLY | O_CLOEXEC);
    if (fd == -1)
      return false;
    if (write(fd, buf, len) != len) {
      int err = errno;
      close(fd);
      errno = err;
      return false;
    }
    close(fd);
    return true;
  }
  
  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 void kill_and_wait(int pid, int* status)
  {
    kill(-pid, SIGKILL);
    kill(pid, SIGKILL);
    int i;
    for (i = 0; i < 100; i++) {
      if (waitpid(-1, status, WNOHANG | __WALL) == pid)
        return;
      usleep(1000);
    }
    DIR* dir = opendir("/sys/fs/fuse/connections");
    if (dir) {
      for (;;) {
        struct dirent* ent = readdir(dir);
        if (!ent)
          break;
        if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0)
          continue;
        char abort[300];
        snprintf(abort, sizeof(abort), "/sys/fs/fuse/connections/%s/abort",
                 ent->d_name);
        int fd = open(abort, O_WRONLY);
        if (fd == -1) {
          continue;
        }
        if (write(fd, abort, 1) < 0) {
        }
        close(fd);
      }
      closedir(dir);
    } else {
    }
    while (waitpid(-1, status, __WALL) != pid) {
    }
  }
  
  static void setup_test()
  {
    prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
    setpgrp();
    write_file("/proc/self/oom_score_adj", "1000");
  }
  
  static void execute_one(void);
  
  #define WAIT_FLAGS __WALL
  
  static void loop(void)
  {
    int iter;
    for (iter = 0;; iter++) {
      int pid = fork();
      if (pid < 0)
        exit(1);
      if (pid == 0) {
        setup_test();
        execute_one();
        exit(0);
      }
      int status = 0;
      uint64_t start = current_time_ms();
      for (;;) {
        if (waitpid(-1, &status, WNOHANG | WAIT_FLAGS) == pid)
          break;
        sleep_ms(1);
        if (current_time_ms() - start < 5 * 1000)
          continue;
        kill_and_wait(pid, &status);
        break;
      }
    }
  }
  
  uint64_t r[1] = {0xffffffffffffffff};
  
  void execute_one(void)
  {
    intptr_t res = 0;
    memcpy((void*)0x20000040, "/dev/ttyS3\000", 11);
    res = syscall(__NR_openat, 0xffffffffffffff9cul, 0x20000040ul, 0ul, 0ul);
    if (res != -1)
      r[0] = res;
    *(uint32_t*)0x20000080 = 7;
    syscall(__NR_ioctl, r[0], 0x5423ul, 0x20000080ul);
    *(uint32_t*)0x20000080 = 0x14;
    syscall(__NR_ioctl, r[0], 0x5423ul, 0x20000080ul);
  }
  int main(void)
  {
    syscall(__NR_mmap, 0x20000000ul, 0x1000000ul, 3ul, 0x32ul, -1, 0);
    for (procid = 0; procid < 6; procid++) {
      if (fork() == 0) {
        loop();
      }
    }
    sleep(1000000);
    return 0;
  }
  