Curiously, I found that if I had a buffer size of even 1 byte over (8 MB - 8 KB), I still got the segfault. I’m not yet sure what’s going on there!
This is because of gcc padding. Programs have to allocate whole page from OS. So if you want just 1 int, you have to get whole page for it (compilers can optimize it in some conditions).
This is result of MMU that works for memory block and not for single bytes (performance issue I think)
But as I know by default page size have 4KB.
Another reason may be that compiler tries to allocate 2^n bytes because of performance. and 8KB is close enough I think.
The main problem there is that local non-static variables get placed onto stack. Stack space is allocated by generated code by just decrementing stack pointer without any explicit calls to OS. On typical modern unix, only few pages of stack are actually mapped and kernel handles page faults on neighboring pages by allocating more stack pages. Because it is possible that function needs more than one page of stack space, there is more than one such "magic" stack page, but still there is some finite number of them (othervise there would be no way to distinguish between accesses beyond the end of stack that should grow stack and accesses to random unmapped memory). Thus if you allocate some ridiculously large things on stack and access them in the direction opposite of stack growth, you may get segfault (accessing these structures in "the right order" is by no means sufficient for this to be safe because there are things like red-zones, signals, other local variables...).
This is true only for first thread, other threads have fixed stack size specified on thread creation (on Linux it's 8MB by default), but usually even thread's stack pages are really allocated only on first access.
On UNIX if you really want to bump stack by arbitrary amounts the most portable way is to preallocate your own stack of sufficient size and then use that (either by abusing sigaltstack() or via makecontext()/setcontext() or possibly by creating new thread). But generally, having large local variables is not exactly good idea.
push %rbp
mov %rsp, %rbp
sub $something, %rsp
... actual code ...
mov %rbp, %rsp
ret
The sub $something, %rsp instruction is everything that user-space does to allocate stack memory. Actual allocation of stack pages happens in kernel in a way that is completely transparent as long as the $something does not get unreasonably large.
Ah so the idea is that there's already 8 KB of actual pages set for stack by this point. So when I try to get another (8 MB - 8 KB + 1 B), that blows things up? I wonder if I can watch this happen in /proc/$pid/maps or somewhere else around there.
Comments
This is because of gcc padding. Programs have to allocate whole page from OS. So if you want just 1 int, you have to get whole page for it (compilers can optimize it in some conditions). This is result of MMU that works for memory block and not for single bytes (performance issue I think) But as I know by default page size have 4KB.
Another reason may be that compiler tries to allocate 2^n bytes because of performance. and 8KB is close enough I think.
The main problem there is that local non-static variables get placed onto stack. Stack space is allocated by generated code by just decrementing stack pointer without any explicit calls to OS. On typical modern unix, only few pages of stack are actually mapped and kernel handles page faults on neighboring pages by allocating more stack pages. Because it is possible that function needs more than one page of stack space, there is more than one such "magic" stack page, but still there is some finite number of them (othervise there would be no way to distinguish between accesses beyond the end of stack that should grow stack and accesses to random unmapped memory). Thus if you allocate some ridiculously large things on stack and access them in the direction opposite of stack growth, you may get segfault (accessing these structures in "the right order" is by no means sufficient for this to be safe because there are things like red-zones, signals, other local variables...).
This is true only for first thread, other threads have fixed stack size specified on thread creation (on Linux it's 8MB by default), but usually even thread's stack pages are really allocated only on first access.
On UNIX if you really want to bump stack by arbitrary amounts the most portable way is to preallocate your own stack of sufficient size and then use that (either by abusing sigaltstack() or via makecontext()/setcontext() or possibly by creating new thread). But generally, having large local variables is not exactly good idea.
Could you elaborate on "Stack space is allocated by generated code" Are you talking about the C runtime in the binary? Thanks.
No. Typical C function looks something like this:
The sub $something, %rsp instruction is everything that user-space does to allocate stack memory. Actual allocation of stack pages happens in kernel in a way that is completely transparent as long as the $something does not get unreasonably large.Ah so the idea is that there's already 8 KB of actual pages set for stack by this point. So when I try to get another (8 MB - 8 KB + 1 B), that blows things up? I wonder if I can watch this happen in /proc/$pid/maps or somewhere else around there.