// https://syzkaller.appspot.com/bug?id=53599a7fc4882bf655e43ac53edfe43e7740baab
// autogenerated by syzkaller (https://github.com/google/syzkaller)

#define _GNU_SOURCE

#include <endian.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <setjmp.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

#include <linux/futex.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;
  int skip = __atomic_load_n(&skip_segv, __ATOMIC_RELAXED) != 0;
  int valid = addr < prog_start || addr > prog_end;
  if (skip && valid) {
    _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 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 = 0;
  for (; 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_ACQUIRE))
      return 1;
    now = current_time_ms();
    if (now - start > timeout)
      return 0;
  }
}

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);
  }
}

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;
  }
}

#ifndef __NR_memfd_create
#define __NR_memfd_create 319
#endif

uint64_t r[3] = {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff};

void execute_call(int call)
{
  intptr_t res = 0;
  switch (call) {
  case 0:
    NONFAILING(memcpy((void*)0x20000200, "/dev/loop#\000", 11));
    res = -1;
    NONFAILING(res = syz_open_dev(0x20000200, 0, 0));
    if (res != -1)
      r[0] = res;
    break;
  case 1:
    NONFAILING(memcpy((void*)0x20000180, "\020\001\000t\t-\201E\2667\222^"
                                         "\324\201E\263-1\246)\231+/"
                                         "\337\200\000\000\000Hc\232g\217\243"
                                         "\256\016\255\215\314\000\000\000",
                      42));
    res = syscall(__NR_memfd_create, 0x20000180ul, 0ul);
    if (res != -1)
      r[1] = res;
    break;
  case 2:
    NONFAILING(*(uint64_t*)0x200005c0 = 0x20000340);
    NONFAILING(memcpy(
        (void*)0x20000340,
        "\x8d\x57\xec\x69\xb7\x31\x33\x6d\x98\x82\x49\x8f\xea\x0c\x29\x85\x0e"
        "\xaf\xf2\x5b\x88\xf1\xc9\xec\x5e\x79\x7d\xa5\x6c\xcc\xad\x89\x73\x5e"
        "\xf0\xd5\xed\xdb\x92\x69\xc8\x8b\x2c\xaa\x3f\x93\x6c\x2e\xae\x27\xb1"
        "\x7f\xd2\xf6\xa6\xec\x5e\x12\xe5\x55\x86\x73\x89\x3f\x28\x66\xff\xf4"
        "\x5c\x4c\xdf\xef\x85\xf0\xec\xae\xd0\x10\x57\xab\x9f\x5f\x94\x6e\x3b"
        "\xe2\x62\x99\x5c\x39\x24\x42\x73\x93\x75\x50\xd8\x73\x0f\x4c\x5c\xaa"
        "\x42\x2e\xad\x8b\xeb\xda\x85\x48\x68\x2e\xa7\x9a\x3b\x4a\x45\x68\xc1"
        "\x99\x44\x30\x82\xf5\x4a\x54\xe0\x51\x50\xbd\x97\xfb\xee\xed\x93\x6f"
        "\x6d\x01\x7d\x64\xa2\x82\x13",
        143));
    NONFAILING(*(uint64_t*)0x200005c8 = 0x8f);
    NONFAILING(*(uint64_t*)0x200005d0 = 0x20000400);
    NONFAILING(memcpy(
        (void*)0x20000400,
        "\x3d\xb6\x18\x9e\xda\x17\x0a\x92\xfa\xb0\xb0\x95\x2f\x92\x2b\x1e\x28"
        "\xa1\x6d\xd7\xb5\xa0\xa2\x6c\xb9\x9e\x73\x6c\xd1\x75\x56\x8b\xe4\x55"
        "\x8c\xf1\xa6\xc0\x89\x99\x37\x25\x17\x97\x8a\x66\x40\xc7\x2e\xa6\xdc"
        "\x58\x98\x6d\x9e\x59\xe8\xa9\xba\xa2\xf3\x67\xfa\x35\x25\x20\x9d\xee"
        "\xc9\xba\x1f\xb3\x1b\x90\xf2\x3e\x5b\x5e\x11\x36\x8d\x23\x15\xd7\xd3"
        "\x8a\x25\x7b\x54\x6a\xfc\x7a\x33\x7c\x43\x23\xfd\x54\xa2\xf2\x7b\xa8"
        "\x6d\xcd\xc9\xa8\x24\x31\x57\xba\xc7\x48\x10\x60\xe9",
        115));
    NONFAILING(*(uint64_t*)0x200005d8 = 0x73);
    NONFAILING(*(uint64_t*)0x200005e0 = 0x200004c0);
    NONFAILING(memcpy(
        (void*)0x200004c0,
        "\x2c\x8c\xaa\x02\x36\x20\x82\xd4\x0c\xea\x43\xad\x87\xbd\xfe\xae\x15"
        "\x44\x43\x84\x66\x72\x7d\x60\xd1\x96\x31\xfe\xa3\x72\xfc\x6d\x5d\x1b"
        "\x66\xa8\x2d\x18\x01\xb7\x51\x40\x6c\xf8\xe7\x1f\x87\xd4\xc5\x42\x42"
        "\x9b\x3b\xcd\xef\x87\x0b\x6c\x5d\x23\x97\x71\x26\x3b\xdc\xfd\x1d\x81"
        "\x7b\x6c\x0e\xcd\xec\x2e\x95\xdc\x87\x72\xa6\x36\xb2\x33\x05\x96\xb7"
        "\xd2\x9b\xe8\x78\x91\x53\x17\x66\x2a\x58\x5c\xf1\xc7\x89\xc3\x29\x76"
        "\xd1\x11\x11\x41\x28\xe5\x58\xa5\x1f\xea\x8a\xb6\x76\xfd\x4a\xde\x12"
        "\x5a\x21\x78\x2c\xb7\x2b\x95\xf0\x38\xe2\x62\xb7\x3b\xe6\xdc\x70\x2c"
        "\x06\x42\x8d\xa9\xee\x2f\xba\x11\x44\xeb\xb2\x29\xe9\xa2\x6f\x0a\xef"
        "\x18\x82\xeb\xaa\x1e\x85\xe0\x0a\xf5\x28\xb7\x2f\xb2\x13\xa6\xb3\x72"
        "\x00\x01\x4f\xca\x96\x32\x94\x50\x2f\x5c\x8d\xdf\x72\x12\xd5\x30\xd4"
        "\xec\xc2\x43\x4b\x39\x41\x80",
        194));
    NONFAILING(*(uint64_t*)0x200005e8 = 0xc2);
    NONFAILING(*(uint64_t*)0x200005f0 = 0x20000700);
    NONFAILING(memcpy((void*)0x20000700,
                      "\x9a\xd1\xc3\xb5\xef\x2e\xf3\xa5\x86\x72\x33\x69\x27\xf4"
                      "\x65\x0f\x17\x47\x08\xe1\xd0\x0d\xc0\xee\xc5\x6c\x79\x25"
                      "\x3d\x63\x83\x06\xab\xab\x81\x0d\x64\xed\xc9\xda\x7c\xb8"
                      "\x61\x9b\xe3\xb0\x37\x0c\x3b\x3e\xd5\x90\x60\x31\x12\x08"
                      "\xbe\x8a\x2d\x46",
                      60));
    NONFAILING(*(uint64_t*)0x200005f8 = 0x3c);
    syscall(__NR_pwritev, r[1], 0x200005c0ul, 4ul, 0, 0);
    break;
  case 3:
    syscall(__NR_ioctl, r[0], 0x4c00, r[1]);
    break;
  case 4:
    NONFAILING(memcpy((void*)0x20000080, "/dev/loop#\000", 11));
    res = -1;
    NONFAILING(res = syz_open_dev(0x20000080, 0, 0x1cd842));
    if (res != -1)
      r[2] = res;
    break;
  case 5:
    syscall(__NR_mmap, 0x20000000ul, 0x600000ul, 0x3fbcbab16c80fabul, 0x11ul,
            r[2], 0ul);
    break;
  }
}
int main(void)
{
  syscall(__NR_mmap, 0x1ffff000ul, 0x1000ul, 0ul, 0x32ul, -1, 0ul);
  syscall(__NR_mmap, 0x20000000ul, 0x1000000ul, 7ul, 0x32ul, -1, 0ul);
  syscall(__NR_mmap, 0x21000000ul, 0x1000ul, 0ul, 0x32ul, -1, 0ul);
  install_segv_handler();
  loop();
  return 0;
}