// { dg-do run }

#include <assert.h>
#include <signal.h>
#include <setjmp.h>
#include <stdio.h>

#include <iostream>
#include <fstream>

using std::ofstream;
using std::ifstream;
using std::ios;

struct A {
  A():value(123) {}
    int value;
    virtual int access() { return this->value; }
};
struct B {
  B():value(456) {}
    int value;
    virtual int access() { return this->value; }
};
struct C : public A, public B {
  C():better_value(789) {}
    int better_value;
    virtual int access() { return this->better_value; }
};
struct D: public C {
  D():other_value(987) {}
  int other_value;
  virtual int access() { return this->other_value; }
};

volatile static int signal_count = 0;

sigjmp_buf before_segv;

static void
handler(int sig, siginfo_t *si, void *unused)
{
  /*
  printf("Got SIGSEGV at address: 0x%lx\n",
         (long) si->si_addr);
  */

  signal_count++;
  /* You are not supposed to longjmp out of a signal handler but it seems
     to work for this test case and it simplifies it */
  siglongjmp(before_segv, 1);
  /* exit(1); */
}

/* Access one of the vtable_map variables generated by this .o */
extern void * _ZN4_VTVI1BE12__vtable_mapE;

/* Access one of the vtable_map variables generated by libstdc++ */
extern void * _ZN4_VTVISt8ios_baseE12__vtable_mapE;

int use(B *b)
{
  int ret;

  ret = sigsetjmp(before_segv, 1);
  if (ret == 0)
    {
      /* This should generate a segmentation violation. ie: at this point it should 
	 be protected */
      _ZN4_VTVI1BE12__vtable_mapE = 0;
    }
  assert(ret == 1 && signal_count == 1);

  ret = sigsetjmp(before_segv, 1);
  if (ret == 0)
    {
      /* Try to modify one of the vtable_map variables in the stdc++ library.
	 This should generate a segmentation violation. ie: at this point it
	 should be protected */
      _ZN4_VTVISt8ios_baseE12__vtable_mapE = 0;
    }
  assert(ret == 1 && signal_count == 2);

  return b->access();
}

void myread(std::istream * in)
{
  char input_str[50] = "\0";
  if (in->good())
    (*in) >> input_str;
  std::cout << input_str << std::endl;
  delete in;
}

int main()
{
  ifstream * infile = new ifstream("./thunk_vtable_map_attack.cpp");
  myread(infile);

  /* Set up handler for SIGSEGV. */
  struct sigaction sa;
  sa.sa_flags = SA_SIGINFO;
  sigemptyset(&sa.sa_mask);
  sa.sa_sigaction = handler;
  if (sigaction(SIGSEGV, &sa, NULL) == -1)
    assert(0);

  C c;
  assert(use(&c) == 789);

  return 0;
}
