[
  {
    "path": "Makefile",
    "content": "CC = clang\nFLAGS = -O0 -W -Wall -Wextra -g\n\nall: malloc.so test-0 test-1 test-2 test-3 test-4 wrapper\n\nmalloc.so: malloc.c\n\t$(CC) $^ $(FLAGS) -o $@ -shared -fPIC\n\ntest-0: test/test-0.c\n\t$(CC) $^ $(FLAGS) -o $@\n\ntest-1: test/test-1.c\n\t$(CC) $^ $(FLAGS) -o $@\n\ntest-2: test/test-2.c\n\t$(CC) $^ $(FLAGS) -o $@\n\ntest-3: test/test-3.c\n\t$(CC) $^ $(FLAGS) -o $@\n\ntest-4: test/test-4.c\n\t$(CC) $^ $(FLAGS) -o $@\n\nwrapper: wrapper.c\n\t$(CC) $^ $(FLAGS) -o $@\n"
  },
  {
    "path": "README.md",
    "content": "See [danluu.com/malloc-tutorial](https://danluu.com/malloc-tutorial/) :-).\n\nTests and wrapper borrowed from [Andrew Roth](https://github.com/ps2dude756).\n"
  },
  {
    "path": "malloc.c",
    "content": "#include <assert.h>\n#include <string.h>\n#include <sys/types.h>\n#include <unistd.h>\n// Don't include stdlb since the names will conflict?\n\n// TODO: align\n\n// sbrk some extra space every time we need it.\n// This does no bookkeeping and therefore has no ability to free, realloc, etc.\nvoid *nofree_malloc(size_t size) {\n  void *p = sbrk(0);\n  void *request = sbrk(size);\n  if (request == (void*) -1) { \n    return NULL; // sbrk failed\n  } else {\n    assert(p == request); // Not thread safe.\n    return p;\n  }\n}\n\nstruct block_meta {\n  size_t size;\n  struct block_meta *next;\n  int free;\n  int magic;    // For debugging only. TODO: remove this in non-debug mode.\n};\n\n#define META_SIZE sizeof(struct block_meta)\n\nvoid *global_base = NULL;\n\n// Iterate through blocks until we find one that's large enough.\n// TODO: split block up if it's larger than necessary\nstruct block_meta *find_free_block(struct block_meta **last, size_t size) {\n  struct block_meta *current = global_base;\n  while (current && !(current->free && current->size >= size)) {\n    *last = current;\n    current = current->next;\n  }\n  return current;\n}\n\nstruct block_meta *request_space(struct block_meta* last, size_t size) {\n  struct block_meta *block;\n  block = sbrk(0);\n  void *request = sbrk(size + META_SIZE);\n  assert((void*)block == request); // Not thread safe.\n  if (request == (void*) -1) {\n    return NULL; // sbrk failed.\n  }\n  \n  if (last) { // NULL on first request.\n    last->next = block;\n  }\n  block->size = size;\n  block->next = NULL;\n  block->free = 0;\n  block->magic = 0x12345678;\n  return block;\n}\n\n// If it's the first ever call, i.e., global_base == NULL, request_space and set global_base.\n// Otherwise, if we can find a free block, use it.\n// If not, request_space.\nvoid *malloc(size_t size) {\n  struct block_meta *block;\n  // TODO: align size?\n\n  if (size <= 0) {\n    return NULL;\n  }\n\n  if (!global_base) { // First call.\n    block = request_space(NULL, size);\n    if (!block) {\n      return NULL;\n    }\n    global_base = block;\n  } else {\n    struct block_meta *last = global_base;\n    block = find_free_block(&last, size);\n    if (!block) { // Failed to find free block.\n      block = request_space(last, size);\n      if (!block) {\n\treturn NULL;\n      }\n    } else {      // Found free block\n      // TODO: consider splitting block here.\n      block->free = 0;\n      block->magic = 0x77777777;\n    }\n  }\n  \n  return(block+1);\n}\n\nvoid *calloc(size_t nelem, size_t elsize) {\n  size_t size = nelem * elsize;\n  void *ptr = malloc(size);\n  memset(ptr, 0, size);\n  return ptr;\n}\n\n// TODO: maybe do some validation here.\nstruct block_meta *get_block_ptr(void *ptr) {\n  return (struct block_meta*)ptr - 1;\n}\n\nvoid free(void *ptr) {\n  if (!ptr) {\n    return;\n  }\n\n  // TODO: consider merging blocks once splitting blocks is implemented.\n  struct block_meta* block_ptr = get_block_ptr(ptr);\n  assert(block_ptr->free == 0);\n  assert(block_ptr->magic == 0x77777777 || block_ptr->magic == 0x12345678);\n  block_ptr->free = 1;\n  block_ptr->magic = 0x55555555;  \n}\n\nvoid *realloc(void *ptr, size_t size) {\n  if (!ptr) { \n    // NULL ptr. realloc should act like malloc.\n    return malloc(size);\n  }\n\n  struct block_meta* block_ptr = get_block_ptr(ptr);\n  if (block_ptr->size >= size) {\n    // We have enough space. Could free some once we implement split.\n    return ptr;\n  }\n\n  // Need to really realloc. Malloc new space and free old space.\n  // Then copy old data to new space.\n  void *new_ptr;\n  new_ptr = malloc(size);\n  if (!new_ptr) {\n    return NULL; // TODO: set errno on failure.\n  }\n  memcpy(new_ptr, ptr, block_ptr->size);\n  free(ptr);  \n  return new_ptr;\n}\n"
  },
  {
    "path": "test/test-0.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n\nint main() {\n  int *ptr = malloc(sizeof(int));\n  if (ptr == NULL) { \n    printf(\"Failed to malloc a single int\\n\");\n    return 1;\n  }\n\n  *ptr = 1;\n  *ptr = 100;\n\n  free(ptr);\n\n  printf(\"malloc'd an int, assigned to it, and free'd it\\n\");\n\n  int *ptr2 = malloc(sizeof(int));\n  if (ptr2 == NULL) { \n    printf(\"Failed to malloc a single int\\n\");\n    return 1;\n  }\n\n  *ptr2 = 2;\n  *ptr2 = 200;\n\n  free(ptr2);\n  printf(\"malloc'd an int, assigned to it, and free'd it #2\\n\");\n\n  malloc(1); // Screw up alignment.\n\n  int *ptr3 = malloc(sizeof(int));\n  if (ptr3 == NULL) { \n    printf(\"Failed to malloc a single int\\n\");\n    return 1;\n  }\n\n  *ptr3 = 3;\n  *ptr3 = 300;\n\n  free(ptr3);\n  printf(\"malloc'd an int, assigned to it, and free'd it #3\\n\");\n\n  return 0;\n}\n"
  },
  {
    "path": "test/test-1.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n\n#define RUNS 10000\n\nint main() {\n  malloc(1);\n\n  int i;\n  int **arr = malloc(RUNS * sizeof(int *));\n\n  if (arr == NULL) {\n    printf(\"Memory failed to allocate!\\n\");\n    return 1;\n  }\n\n  for (i = 0; i < RUNS; i++) {\n    arr[i] = malloc(sizeof(int));\n    if (arr[i] == NULL) {\n      printf(\"Memory failed to allocate!\\n\");\n      return 1;\n    }\n    \n    *(arr[i]) = i+1;\n  }\n\n  for (i = 0; i < RUNS; i++) {\n    if (*(arr[i]) != i+1) {\n      printf(\"Memory failed to contain correct data after many allocations!\\n\");\n      return 2;\n    }\n  }\n\n  for (i = 0; i < RUNS; i++) {\n    free(arr[i]);\n  }\n  \n  free(arr);\n  printf(\"Memory was allocated, used, and freed!\\n\");\t\n  return 0;\n}\n"
  },
  {
    "path": "test/test-2.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n\n#define TOTAL_ALLOCS 200000\n#define ALLOC_SIZE 1024*1024\n\nint main() {\n  malloc(1);\n\t\n  int i;\n  void *ptr = NULL;\n\n  for (i = 0; i < TOTAL_ALLOCS; i++) {\n    ptr = malloc(ALLOC_SIZE);\n    if (ptr == NULL) {\n      printf(\"Memory failed to allocate!\\n\");\n      return 1;\n    }\n\t\t\n    free(ptr);\n  }\n\n  printf(\"Memory was allocated and freed!\\n\");\t\n  return 0;\n}\n"
  },
  {
    "path": "test/test-3.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n\n#define START_MALLOC_SIZE 1024*1024*128\n#define STOP_MALLOC_SIZE  1024\n\nvoid dummy() { return; }\n\nvoid *reduce(void *ptr, int size) {\n  if (size > STOP_MALLOC_SIZE) {\n    void *ptr1 = realloc(ptr, size / 2);\n    void *ptr2 = malloc(size / 2);\n\n    if (ptr1 == NULL || ptr2 == NULL) {\n      printf(\"Memory failed to allocate!\\n\");\n      exit(1);\n    }\n\n    ptr1 = reduce(ptr1, size / 2);\n    ptr2 = reduce(ptr2, size / 2);\n\n    if (*((int *)ptr1) != size / 2 || *((int *)ptr2) != size / 2) {\n      printf(\"Memory failed to contain correct data after many allocations!\\n\");\n      exit(2);\n    }\n\n    void *old_ptr1 = ptr1;\n    ptr1 = realloc(ptr1, size);\n    free(ptr2);\n\n    if (*((int *)ptr1) != size / 2) {\n      printf(\"Memory failed to contain correct data after realloc()!\\n\");\n      printf(\"Expected %i found %i (old %i)\\n\", (size/2), *((int *)ptr1), *((int *)old_ptr1));\n      dummy();\n      exit(3);\n    }\n\n    *((int *)ptr1) = size;\n    return ptr1;\n  } else {\n    *((int *)ptr) = size;\n    return ptr;\n  }\n}\n\nint main() {\n  malloc(1);\n\t\n  int size = START_MALLOC_SIZE;\n  while (size > STOP_MALLOC_SIZE)\t{\n    void *ptr = malloc(size);\n    ptr = reduce(ptr, size / 2);\n    free(ptr);\n\t\t\n    size /= 2;\n  }\n\n  printf(\"Memory was allocated, used, and freed!\\n\");\t\n  return 0;\n}\n"
  },
  {
    "path": "test/test-4.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n\n#define MIN_ALLOC_SIZE 24\n#define MAX_ALLOC_SIZE 1024 * 100\n#define CHANCE_OF_FREE 95\n#define CHANCE_OF_REALLOC 50\n#define TOTAL_ALLOCS 400000\n\nint main() {\n  malloc(1);\n\t\n  int i;\n  void *realloc_ptr = NULL;\n  void **dictionary = malloc(TOTAL_ALLOCS * sizeof(void *));\n  int *dictionary_elem_size = malloc(TOTAL_ALLOCS * sizeof(int));\n  int dictionary_ct = 0;\n  int data_written = 0;\n\n  for (i = 0; i < TOTAL_ALLOCS; i++) {\n    int size = (rand() % (MAX_ALLOC_SIZE - MIN_ALLOC_SIZE + 1)) + MIN_ALLOC_SIZE;\n    void *ptr;\n\t\t\n    if (realloc_ptr == NULL) {\n      ptr = malloc(size);\n      data_written = 0;\n    } else {\n      ptr = realloc(realloc_ptr, size);\n      realloc_ptr = NULL;\n    }\n\n\n    if (ptr == NULL) {\n      printf(\"Memory failed to allocate!\\n\");\n      return 1;\n    }\n\n\n    if (rand() % 100 < CHANCE_OF_FREE) {\n      free(ptr);\n    } else {\n      if (!data_written) {\n\t*((void **)ptr) = &dictionary[dictionary_ct];\n\tdata_written = 1;\n      }\n\t\t\t\n      if (rand() % 100 < CHANCE_OF_REALLOC) {\n\trealloc_ptr = ptr;\n      } else {\n\t*((void **)(ptr + size - sizeof(void *))) = &dictionary[dictionary_ct];\n\tdictionary[dictionary_ct] = ptr;\n\tdictionary_elem_size[dictionary_ct] = size;\n\tdictionary_ct++;\n      }\n    }\n  }\n\n  for (i = dictionary_ct - 1; i >= 0; i--) {\n    if ( *((void **)dictionary[i]) != &dictionary[i] ) {\n      printf(\"Memory failed to contain correct data after many allocations (beginning of segment)!\\n\");\n      return 100;\n    }\n\t\t\n    if ( *((void **)(dictionary[i] + dictionary_elem_size[i] - sizeof(void *))) != &dictionary[i] ) {\n      printf(\"Memory failed to contain correct data after many allocations (end of segment)!\\n\");\n      return 101;\n    }\n\t\t\n\n    char *memory_check = dictionary[i] + sizeof(void *);\n    char *memory_check_end = dictionary[i] + dictionary_elem_size[i] - sizeof(void *) - 1;\n\t\t\n    while (memory_check > memory_check_end) {\n      if (*memory_check != 0x00) {\n\tprintf(\"Memory failed to contain correct data after many allocations (mid segment or reused segment)!\\n\");\n\treturn 102;\n      }\n      \n      *memory_check = 0x01;\n      memory_check++;\n    }\n    \n    free(dictionary[i]);\n  }\n\n  printf(\"Memory was allocated and freed!\\n\");\t\n  return 0;\n}\n\n"
  },
  {
    "path": "test/test-5.c",
    "content": ""
  },
  {
    "path": "wrapper.c",
    "content": "// Wrapper does LD_PRELOAD of our malloc.\n// Using this because if we LD_PRELOAD our buggy malloc, gdb segfaults\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n\nint main(int argc, char **argv) {\n  // Ccheck that we have at least one arg.\n  if (argc == 1) {\n    printf(\"You must supply a program to be invoked to use your replacement malloc() script.\\n\");\n    printf(\"...you may use any program, even system programs, such as `ls`.\\n\");\n    printf(\"\\n\");\n    printf(\"Example: %s /bin/ls\\n\", argv[0]);\n    return 1;\n  }\n\n  /*\n   * Set up the environment to pre-load our 'malloc.so' shared library, which\n   * will replace the malloc(), calloc(), realloc(), and free() that is defined\n   * by standard libc.\n   */\n  char **env = malloc(2 * sizeof(char *));\n  env[0] = malloc(100 * sizeof(char));\n  sprintf(env[0], \"LD_PRELOAD=./malloc.so\");\n\n  env[1] = NULL;\n\n\n  /*\n   * Replace the current running process with the process specified by the command\n   * line options.  If exec() fails, we won't even try and recover as there's likely\n   * nothing we could really do; however, we do our best to provide useful output\n   * with a call to perror().\n   */\n  execve(argv[1], argv + 1, env);    /* Note that exec() will not return on success. */\n  perror(\"exec() failed\");\n\n  free(env[0]);\n  free(env);\n\n  return 2;\n}\n"
  }
]