Am 18.05.16 um 19:15 schrieb Steven D'Aprano:
Not being a C programmer, I don't really understand this.

The idea I have in mind is a model of program memory I learned[1] way back
in the 80s. I don't know if it's still valid. Your application has a bunch
of memory available, which broadly speaking can be divided into three
chunks: globals, the stack, and the heap.

The application knows what globals exist, and can allocate a fixed block of
memory big enough for them, so that's not very interesting. It just sits
there, holding space for the globals, and doesn't grow or shrink.

Then there's the stack, which holds local variables whenever a function is
called, the return address of the caller, and other stuff. There's a fixed
bottom to the stack, but the top can grown and shrink depending on how many
functions you call and how many local variables they use.

Until here, it is reasonably accurate. On some machines, the stack does grow in the other direction, but that does not matter either. ON x86, it grows from top to bottom

Then there's everything else, which is the heap, and it can grown and shrink
in both directions (up to some maximum, of course):

bottom [ globals | stack ----->      <----- heap -----> ] top

If the stack grows into the heap, or vice versa, Bad Things happen. At best
you get a crash. At worst you get arbitrary code execution.

No, you have virtual memory management in effect in the OS which maps the real memory addresses into your address space. On 64 bit, a collision between stack and heap is practically impossible.

I don't really understand why the system can't track the current top of the
stack and bottom of the heap, and if they're going to collide, halt the
process.

It does. But in a different way. For the heap, you need to call a function which asks for more memory. It returns an error code, if the memory can't be supplied. The problem is that often in this case, the program needs more memory to handle that, e.g. to format an error message. If you allocate memory in small pieces until it is exhausted, the program will die in unforeseen ways. If you try to alloc 1TB on the heap and it fails, there is enough room for a clean shutdown. Unless the C program is buggy and does not check the error.

On the stack, you don't allocate by telling the OS. You simply increase the stack pointer register. This is a single machine instruction, very fast, and unfeasible to trap by the OS and intercept. Instead, the stack is framed by pages which are non-writeable. As soon as the program tries to write there, it segfaults (SIGSEGV or SIGBUS). At this point there is no way to cleanly exit the program, therefore you see the crash/segfault. It might happen that you overrun the stack so much as to reach writeable memory again. But not under normal circumstances, where only a few bytes are pushed/popped.

That would still be kinda awful, in a sudden "your application
just died" kind of way, but it would be better than "your computer is now
owned by some hacker in Hong Kong, who is now renting it by the hour to
some spammer in Texas".

Stack overflow does not usually lead to security risks. A buffer overflow is different: It means that the program allocates a fixed-size buffer on the stack, which overflows and writes into the return addresses / local variables of functions higher up the callchain. The basic problem here is, that the C programmer was too lazy to get the memory from the heap.

        Christian
--
https://mail.python.org/mailman/listinfo/python-list

Reply via email to