/* -*- Mode: C; tab-width: 8; c-basic-offset: 2; indent-tabs-mode: nil; -*- */

#include "rrutil.h"

static char* p;

static void sighandler(__attribute__((unused)) int sig) {
  p[1] = 78;
  signal(SIGSEGV, SIG_DFL);
}

int main(void) {
  pid_t child;
  int status;
  int pipe_fds[2];

  p = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED,
           -1, 0);
  test_assert(MAP_FAILED != p);
  p[0] = 0;
  p[1] = 0;

  test_assert(0 == pipe(pipe_fds));

  if (0 == (child = fork())) {
    char ch;
    read(pipe_fds[0], &ch, 1);
    signal(SIGSEGV, sighandler);
    p[0] = 77;
    /* trigger SIGSEGV */
    crash_null_deref();
    return 77;
  }

  test_assert(0 == ptrace(PTRACE_ATTACH, child, NULL, NULL));
  test_assert(child == waitpid(child, &status, 0));
  test_assert(status == ((SIGSTOP << 8) | 0x7f));

  test_assert(1 == write(pipe_fds[1], "x", 1));

  test_assert(0 == ptrace(PTRACE_CONT, child, NULL, (void*)0));
  test_assert(child == waitpid(child, &status, 0));
  test_assert(status == ((SIGSEGV << 8) | 0x7f));
  /* Check that the child actually executed forwards to the SIGSEGV */
  test_assert(p[0] == 77);
  test_assert(p[1] == 0);

  /* Progress to second (fatal) SIGSEGV */
  test_assert(0 == ptrace(PTRACE_CONT, child, NULL, (void*)SIGSEGV));
  test_assert(child == waitpid(child, &status, 0));
  test_assert(status == ((SIGSEGV << 8) | 0x7f));
  test_assert(p[0] == 77);
  /* Check that code actually ran */
  test_assert(p[1] == 78);

  /* Continue with the signal again. This should be fatal. */
  test_assert(0 == ptrace(PTRACE_CONT, child, NULL, (void*)SIGSEGV));
  test_assert(child == waitpid(child, &status, 0));
  test_assert(WIFSIGNALED(status));
  test_assert(WTERMSIG(status) == SIGSEGV);

  atomic_puts("EXIT-SUCCESS");
  return 0;
}
