foo()
{
  char *buf;

  buf = malloc(10);
  // check if malloc succeeded
  strcpy(buf, "hello world"); // 12 bytes long (incl. \0 terminating string)
  // by copying 12 bytes into a 10 byte buf, I've overflown the buffer 'buf' by 2 bytes.

  // why did the above buf overflow succeed?  why didn't we get a SEGV?
  // malloc(3) is a library call?  How does malloc actually ask the OS for
  // more space in  the HEAP segment? A: it calls special system calls like
  // brk(2) or sbrk(2).  These syscalls ask the OS to change  the "break
  // point" of where the HEAP segment ends, and can ask the OS to make it
  // larger or smaller, namely move the end of the HEAP segment closer or
  // farther away from the STACK.  HEAP must be contiguous: you cannot ask
  // s/brk to create "holes" in the HEAP, but only move it's end point.
  // s/brk can ONLY change the HEAP seg by whole 4KB pages, so the user
  // process can get more memory in PAGE_SIZE units.

  // first time you call malloc, internally it'll call sbrk and ask to
  // enlarge the HEAP segment by 1 page (HEAP starts empty).  Once the OS
  // gives malloc 1 page, malloc can then internally dole out parts of that
  // page to the program that's calling malloc.  Usually, malloc will give
  // you space in the newly extended HEAP segment, the new page just mapped
  // by the OS to your program's virt addr space.

  // assume malloc gives you the first 10 bytes in that page.
  // the new HEAP page given to your program byte the OS is 4096 bytes
  // malloc now has 4096-10=4086 bytes "free".
  // note, only the first 10 bytes in that page "belong" to the str buf
  // above.  The rest, is MAPPED to your addr space with R+W protections.

  // Therefore, when you overflew your 10-byte buf, you went into the
  // remaining bytes in the 4KB page, which belong to your process, and you
  // have both R+W permission.  IOW, the OS happily lets your program do a
  // buffer overflow!

  // if you keep writing bytes after 'buf', as long as you stay inside that
  // 4KB HEAP page, you're allowed to access that memory (i.e., trash your
  // own program's state). Once you've tried to access a SINGLE bytes after
  // that 4KB page, that's when  you'll get a SEGV, b/c you'll try to access
  // a page that's not mapped to you, right after the current end of HEAP
  // segment.  e.g.,

  memset(buf, 0, 4096); // reset the HEAP segment first page, "allowed"
  memset(buf, 0, 4097); // reset the HEAP segment first page, then try to
			// access the first byte of the NEXT page (get a SEGV).

  // IOW, small buf overflows cannot often be caught by the OS, b/c you're
  // not going outside legally mapped pages to your virt addr space.  Larger
  // buf overflows, are more likely to cause a SEGV.
}

// how can we catch even 1 byte buf overflows?
foo2()
{
  // 1. use a special version of malloc that does this
  // 2. you asked to allocate N bytes (assume less than 4KB)
  // 3. the special malloc will allocate 2 * 4KB or two whole pages (8KB).
  // 4. the special malloc will protect the first page with
  // PROT_READ+PROT_WRITE
  // 5. the special malloc will protect the second page with
  // PROT_NONE
  // Note: the second page is right after the first page, in virt. mem
  // 6. the special malloc will return to the user, the start addr that is
  // the end of page 1 minus N bytes.
  // Meaning: if you overflow the N bytes by even 1 byte, you'll have tried
  // to access  the PROT_NONE page -> OS will core dump with SEGV and you
  // can debug your code at the very moment you went outside the buffer's
  // bounds.

  // this technique is implemented in a Linux library called ElectricFence,
  // which can use PROT_NONE pages to catch buffer overflows, underflows,
  // and even both.  But: it wastes memory by protecting every small malloc
  // with at least 2 x 4KB pages.
}
