I guess the clang AddressSanitizer is failing because there is a legit leak. So my answer ignores that:
Alternatives:
- Disable the overcommit behaviour, as you have already figured out: that is going to affect other processes and requires root.
- run you app in a docker image with the oom killer disabled: that doesn't affect other processes but requires root to install docker (this is my favourite solution though).
- write after malloc: may take long to alloc a huge chunk of memory and your process can still get killed because of other running process but doesn't require root.
- use ulimit -v to limit the amount of memory depending on the machine: that also doesn't require root but your process might be killed anyway. 
Code for the third alternative (for linux):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <setjmp.h>
jmp_buf resume_malloc;
void handle_malloc_error(int sig)
{
   longjmp(resume_malloc, sig);
}
void *memalloc(size_t sz) {
   void *p = 0;
   int sig = setjmp(resume_malloc);
   if ( sig == 0 ) {
      p = malloc(sz);
      signal(SIGSEGV, &handle_malloc_error);
      memset(p, 0, sz);
   } else {
      p = 0;
   }
   signal(SIGSEGV, SIG_DFL);
   return p;
}
int main(int argc, char *argv[])
{
   size_t sz = 160L * 1024 * 1024 * 1024L;
   void *p;
   for (int i=0; i < 100; i++) {
      printf("size: %lu\n", sz);
      p = memalloc(sz);
      if ( p == 0 ) {
         printf("out of memory\n");
         break;
      }
      sz *= 2;
   }
}