In order to implement memory protection and virtual memory, modern operating systems are organized around memory pages. Protection is implemented by marking these pages with an access flag, e.g. PAGEREADONLY or PAGEEXECUTE. Whenever the protection is violated, the program will halt with an access violation.
The page size is the same for all pages, and is typically of a size between 4-32 kb.
When you make an allocation of 1 byte, a 4kb page will be allocated. Subsequent allocation will reuse the same memory page until the page is full, in that case a new page will be allocated. Memory protection is only between pages. If you do overwrites within a page, that will silently go unnoticed, but it you write into an un allocated memory space, that memory “doesn’t exist” any doesn’t have any memory page, and will consequently result in an access violation. If you end up with a pointer to some executable code, and you try to write to it, the result is also an access violation, since those pages don’t have the PAGE_WRITE flag set.
This protection mechanism is used by many memory corruption detectors. They typically override the default memory allocation routines, and allocates a new page for every allocation, even if the allocation is for only 1 byte. If buffer underrun wants to be tested, a pointer to the start of the page is returned. An underrun will read/write into a protected page. For testing buffer overruns, a pointer relative to the end is returned. Writing passed the boundary of the page results in an access violation.
If you want to write self modifying code you should add write permissions to the memory pages, then you can write to the memory and jump to it, or patch binary code on the fly.
This is exactly how some software protectors work. Some of them encrypt the .exe binary, and then they decrypt it at start, some are more sophisticated and decrypt on a need basis, and then restores the encryption.