// RUN: %clang_dfsan -mllvm -dfsan-combine-pointer-labels-on-load=false %s -o %t && %run %t
// RUN: %clang_dfsan -DORIGIN_TRACKING -mllvm -dfsan-track-origins=1 -mllvm -dfsan-combine-pointer-labels-on-load=false %s -o %t && %run %t
//
// Tests custom implementations of various glibc functions.

#include <sanitizer/dfsan_interface.h>

#include <assert.h>
#include <malloc.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>

#define ASSERT_ZERO_LABEL(data) \
  assert(0 == dfsan_get_label((long) (data)))

#define ASSERT_READ_ZERO_LABEL(ptr, size) \
  assert(0 == dfsan_read_label(ptr, size))

const int kAlignment = 8;
const int kSize = 16;

void test_aligned_alloc() {
  char *p = (char *) aligned_alloc(kAlignment, kSize);
  ASSERT_ZERO_LABEL(p);
  ASSERT_READ_ZERO_LABEL(p, kSize);
  free(p);
}

void test_calloc() {
  char *p = (char *) calloc(kSize, 1);
  ASSERT_ZERO_LABEL(p);
  ASSERT_READ_ZERO_LABEL(p, kSize);
  free(p);
}

void test_cfree() {
  // The current glibc does not support cfree.
}

void test_free() {
  char *p = (char *) malloc(kSize);
  dfsan_set_label(1, p, kSize);
  free(p);
  ASSERT_READ_ZERO_LABEL(p, kSize);
}

void test_mallinfo() {
  // The mallinfo interceptor takes an argument instead of returning a struct.
  // This doesn't work on AArch64 which uses different registers for the two
  // function types.
#if defined(__GLIBC__) && !defined(__aarch64__)
  struct mallinfo mi = mallinfo();
  for (int i = 0; i < sizeof(struct mallinfo); ++i) {
    char c = ((char *)(&mi))[i];
    assert(!c);
    ASSERT_ZERO_LABEL(c);
  }
#endif
}

void test_malloc() {
  char *p = (char *) malloc(kSize);
  ASSERT_ZERO_LABEL(p);
  ASSERT_READ_ZERO_LABEL(p, kSize);
  free(p);
}

void test_malloc_stats() {
  // Only ensures it does not crash. Our interceptor of malloc_stats is empty.
  malloc_stats();
}

void test_malloc_usable_size() {
  char *p = (char *) malloc(kSize);
  size_t s = malloc_usable_size(p);
  assert(s == kSize);
  ASSERT_ZERO_LABEL(s);
  free(p);
}

void test_mallopt() {
  int r = mallopt(0, 0);
  assert(!r);
  ASSERT_ZERO_LABEL(r);
}

void test_memalign() {
  char *p = (char *) memalign(kAlignment, kSize);
  ASSERT_ZERO_LABEL(p);
  ASSERT_READ_ZERO_LABEL(p, kSize);
  free(p);
}

void test_mmap() {
  char *p = mmap(NULL, kSize, PROT_READ | PROT_WRITE,
                 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  ASSERT_READ_ZERO_LABEL(p, kSize);
  char val = 0xff;
  dfsan_set_label(1, &val, sizeof(val));
  memset(p, val, kSize);
  p = mmap(p, kSize, PROT_READ | PROT_WRITE,
           MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  ASSERT_READ_ZERO_LABEL(p, kSize);
  munmap(p, kSize);
}

void test_mmap64() {
  // The current glibc does not support mmap64.
}

void test_unmmap() {
  char *p = mmap(NULL, kSize, PROT_READ | PROT_WRITE,
                 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  char val = 0xff;
  dfsan_set_label(1, &val, sizeof(val));
  memset(p, val, kSize);
  munmap(p, kSize);
  ASSERT_READ_ZERO_LABEL(p, kSize);
}

void test_posix_memalign() {
  char *p;
  dfsan_set_label(1, &p, sizeof(p));
  int r = posix_memalign((void **)&p, kAlignment, kSize);
  assert(!r);
  ASSERT_ZERO_LABEL(p);
  ASSERT_READ_ZERO_LABEL(p, kSize);
  free(p);
}

void test_pvalloc() {
  char *p = (char *) pvalloc(kSize);
  ASSERT_ZERO_LABEL(p);
  ASSERT_READ_ZERO_LABEL(p, kSize);
  free(p);
}

void test_realloc() {
  char *p = (char *) malloc(kSize);

  char *q = (char *) realloc(p, kSize * 2);
  ASSERT_ZERO_LABEL(q);
  ASSERT_READ_ZERO_LABEL(q, kSize * 2);

  char *x = (char *) realloc(q, kSize);
  ASSERT_ZERO_LABEL(x);
  ASSERT_READ_ZERO_LABEL(x, kSize);

  free(x);
}

void test_reallocarray() {
  // The current glibc does not support reallocarray.
}

void test_valloc() {
  char *p = (char *) valloc(kSize);
  ASSERT_ZERO_LABEL(p);
  ASSERT_READ_ZERO_LABEL(p, kSize);
  free(p);
}

void test___libc_memalign() {
  // The current glibc does not support __libc_memalign.
}

void test___tls_get_addr() {
  // The current glibc does not support __tls_get_addr.
}

int main(void) {
  // With any luck this sequence of calls will cause allocators to return the
  // same pointer. This is probably the best we can do to test these functions.
  test_aligned_alloc();
  test_calloc();
  test_cfree();
  test_free();
  test_mallinfo();
  test_malloc();
  test_malloc_stats();
  test_malloc_usable_size();
  test_mallopt();
  test_memalign();
  test_mmap();
  test_mmap64();
  test_unmmap();
  test_posix_memalign();
  test_pvalloc();
  test_realloc();
  test_reallocarray();
  test_valloc();
  test___libc_memalign();
  test___tls_get_addr();
}
