Dealing with stack is the responsibility of whoever uses it by the rules of whoever manages the stack to the extent of the OS getting out of the way. In regards to Windows, that means: you can override every aspect of the stack behavior completely - just not as efficiently as the OS could.
Slight corrections to what has been said already:
By default on windows a typical stack uses a guard page to trigger stack expansion with the additional requirement that if a function uses more that one page worth of stack then it must call a special function to ensure the guard page is not skipped (this special function call is added automatically by the compiler as part of calling convention / exception handling - in function prolog: "__alloca_probe").
So, besides the overhead of the function call when more than a page is needed - there is no overhead as no checks are done.
Stack underflow is not guarded (just not worth it). IF you are lucky then the memory there is inaccessible and hence causes an immediate access violation crash.
----------------------
Something i use to check the state of address space (VC++):
void dump() {
// dump memory map
intp at = 0;
MEMORY_BASIC_INFORMATION info;
while(true) {
if(VirtualQuery((void*)at, &info, sizeof(info)) != sizeof(info)) break;
LOG(L"%c %p - %p %c%c%c%c%c%c ( %7d KB %s ) %s"
, info.AllocationBase == info.BaseAddress || info.AllocationBase == 0 ? L'>' : L' '
, intp(info.BaseAddress)
, intp(info.BaseAddress) + intp(info.RegionSize) - 1
, info.Protect == 0 ? L'?' : (info.Protect & 0x10 ? L'!' : L' ')
, info.Protect & 0xF0 ? L'x' : '-'
, info.Protect & 0x66 ? L'r' : '-'
, info.Protect & 0xCC ? L'w' : '-'
, info.Protect & 0x88 ? L'c' : '-'
, info.Protect & 0x100 ? L'G' : '-'
, int32u(info.RegionSize >> 10)
, intp(info.BaseAddress) & 65535 ? L"a:???" : L"a:64K"
, info.State == MEM_FREE ? L"¤" : (info.State == MEM_COMMIT ? L"USED" : L"RESERVED")
);
at += info.RegionSize;
}
}
// some quickly made dirty Logger of dubious quality
void logging(wchar_t const *format, ...) {
va_list argptr; va_start(argptr, format);
wchar_t buf[1024];
int wrote = vswprintf(&buf[0], 1024, format, argptr);
if(wrote<0) wrote = 0;
va_end(argptr);
if(wrote) OutputDebugStringW(buf);
}
#define LOG(...) logging(L"\n" __VA_ARGS__);
// intp = unsigned integer fit for pointer (ie. uintptr_t), int32u = unsigned 32 bit integer
which, in my case, gives (some excerpts):
> 00000000 - 0000FFFF ----- ( 64 KB a:64K ) ¤ // a page granularity worth of a general no-go area
...
> 00020000 - 0002EFFF -rw-- ( 60 KB a:64K ) USED // a guarded cyclic buffer of mine - just something the program used for the dump() apparently uses
0002F000 - 0002FFFF -r--G ( 4 KB a:??? ) USED
...
> 00090000 - 00090FFF -r--- ( 4 KB a:64K ) USED // my program (non fixed image address)
00091000 - 000A0FFF xrw-- ( 64 KB a:??? ) USED // ... "x" AND "w" !? ... wut!? What is this!?
000A1000 - 000B5FFF xr--- ( 84 KB a:??? ) USED // ... program code
000B6000 - 000B9FFF -r--- ( 16 KB a:??? ) USED // ... data
000BA000 - 00168FFF -rw-- ( 700 KB a:??? ) USED
00169000 - 0016BFFF -r--- ( 12 KB a:??? ) USED
...
> 00250000 - 00288FFF ?----- ( 228 KB a:64K ) RESERVED // some stack (should not be mine, directly - dunno)
00289000 - 0028BFFF -rw-G ( 12 KB a:??? ) USED
0028C000 - 0028FFFF -rw-- ( 16 KB a:??? ) USED
> 00290000 - 0031FFFF ----- ( 576 KB a:64K ) ¤ // memory following stack is not reserved and is free for grab
...
> 00440000 - 0053BFFF ?----- ( 1008 KB a:64K ) RESERVED // my stack (1MB stack is the the default setting in VC++)
0053C000 - 0053CFFF -rw-G ( 4 KB a:??? ) USED
0053D000 - 0053FFFF -rw-- ( 12 KB a:??? ) USED
> 00540000 - 005CFFFF ----- ( 576 KB a:64K ) ¤ // again - free for grab
...
> 005D0000 - 005D7FFF -rw-- ( 32 KB a:64K ) USED // heap perhaps?
005D8000 - 006CFFFF ?----- ( 992 KB a:??? ) RESERVED
...
> 007D0000 - 207CFFFF -rw-- ( 524288 KB a:64K ) USED // half a GB used for my own - non heap
...
> 207D0000 - 607CFFFF ----- ( 1048576 KB a:64K ) ¤ // first big chunk of unused address space
> 607D0000 - 607D0FFF -r--- ( 4 KB a:64K ) USED // dll/system land begins
607D1000 - 60958FFF xr--- ( 1568 KB a:??? ) USED
60959000 - 6095EFFF -rw-- ( 24 KB a:??? ) USED
6095F000 - 60970FFF -r--- ( 72 KB a:??? ) USED
> 60971000 - 7533FFFF ----- ( 337724 KB a:??? ) ¤ // a stray block of free address space cut short by some dll/system shenanigans
...
dll/system heaven
...
> 7FFF0000 - FFFAFFFF ----- ( 2096896 KB a:64K ) ¤ // some more address space - the benefits of "large address aware" on 64bit windows.
-----------------------------------
"but i heard something that now pages are bigger than 4k ?"
Bigger pages have always been possible (i386+ for the whole x86 line) - just that it is rarely ever used on desktop machines.
Note also: on some hardware (non x86) - 4KB pages are not even an option and 8KB is the smallest possible for example.