Consider the following code for Linux:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int staticvar;
const int constvar = 0;
int main(void)
{
int stackvar;
char buf[200];
int *p;
p = malloc(sizeof(int));
sprintf(buf, "cat /proc/%d/maps", getpid());
system(buf);
printf("&staticvar=%p\n", &staticvar);
printf("&constvar=%p\n", &constvar);
printf("&stackvar=%p\n", &stackvar);
printf("p=%p\n", p);
printf("undefined behaviour: &p[500]=%p\n", &p[500]);
printf("undefined behaviour: &p[50000000]=%p\n", &p[50000000]);
p[500] = 999999; //undefined behaviour
printf("undefined behaviour: p[500]=%d\n", p[500]);
return 0;
}
It prints the memory map of the process and the addresses of some different type of memory.
[osboxes@osboxes ~]$ gcc tmp.c -g -static -Wall -Wextra -m32
[osboxes@osboxes ~]$ ./a.out
08048000-080ef000 r-xp 00000000 fd:00 919429 /home/osboxes/a.out
080ef000-080f2000 rw-p 000a6000 fd:00 919429 /home/osboxes/a.out
080f2000-080f3000 rw-p 00000000 00:00 0
0824d000-0826f000 rw-p 00000000 00:00 0 [heap]
f779c000-f779e000 r--p 00000000 00:00 0 [vvar]
f779e000-f779f000 r-xp 00000000 00:00 0 [vdso]
ffe4a000-ffe6b000 rw-p 00000000 00:00 0 [stack]
&staticvar=0x80f23a0
&constvar=0x80c2fcc
&stackvar=0xffe69b88
p=0x824e2a0
undefined behaviour: &p[500]=0x824ea70
undefined behaviour: &p[50000000]=0x1410a4a0
undefined behaviour: p[500]=999999
Or like why is that address at p[500] even writable?
Heap is from 0824d000-0826f000 and &p[500] is 0x824ea70 by chance, so the memory is writeable and readable, but this memory region may contain real data which will be altered! In the case of the sample program it is most likely that it is unused so the write to this memory is not harmful for the process to work.
&p[50000000] is 0x1410a4a0 by chance, which is not in a page the kernel mapped to the process and therefore is unwriteable and unreadable, hence the seg fault.
If you compile it with -fsanitize=address
memory accesses will be checked and many but not all illegal memory accesses will be reported by AddressSanitizer. Slowdown is about two times slower than without AddressSanitizer.
[osboxes@osboxes ~]$ gcc tmp.c -g -Wall -Wextra -m32 -fsanitize=address
[osboxes@osboxes ~]$ ./a.out
[...]
undefined behaviour: &p[500]=0xf5c00fc0
undefined behaviour: &p[50000000]=0x1abc9f0
=================================================================
==2845==ERROR: AddressSanitizer: heap-buffer-overflow on address 0xf5c00fc0 at pc 0x8048972 bp 0xfff44568 sp 0xfff44558
WRITE of size 4 at 0xf5c00fc0 thread T0
#0 0x8048971 in main /home/osboxes/tmp.c:24
#1 0xf70a4e7d in __libc_start_main (/lib/libc.so.6+0x17e7d)
#2 0x80486f0 (/home/osboxes/a.out+0x80486f0)
AddressSanitizer can not describe address in more detail (wild memory access suspected).
SUMMARY: AddressSanitizer: heap-buffer-overflow /home/osboxes/tmp.c:24 main
[...]
==2845==ABORTING
If so, then does that mean that there are pages that are dedicated to your code/instructions/text segments and marked as unwrite-able completely separate from your pages where your stack/variables are in (where things do change) and marked as writable?
Yes, see the output of the process' memory map above. r-xp
means readable and executable, rw-p
means readable and writeable.