I'm assuming there's a history to it, but why does the stack grow downward?
It seems to me like buffer overflows would be a lot harder to exploit if the stack grew upward...
I believe it comes from the very early days of computing, when memory was very limited, and it was not wise to pre-allocate a large chunk of memory for exclusive use by the stack. So, by allocating heap memory from address zero upwards, and stack memory from the end of the memory downwards, you could have both the heap and the stack share the same area of memory.
If you needed a bit more heap, you could be careful with your stack usage; if you needed more stack, you could try to free some heap memory. The result was, of course, mostly, spectacular crashes, as the stack would occasionally overwrite the heap and vice versa.
Back in those days there were no interwebz, so there was no issue of buffer overrun exploitations. (Or at least to the extent that the interwebz existed, it was all within high security facilities of the united states department of defense, so the possibility of malicious data did not need to be given much thought.)
After that, with most architectures it was all a matter of maintaining compatibility with previous versions of the same architecture. That's why upside-down stacks are still with us today.
program memory is traditionally set up as
heap (growing up)
stack (growing down)
heap and stack can be exchanged
but buffer overflows can still be exploited if the stack went the other way
taking the classic
strcpy as an example
with stack memory as
this would mean that when the copying is done the return address for
strcpy is after the buffer (instead of
foo's return address) and can be overwritten by whatever is in
Some hardware has the heap starting at high memory, growing down, while the stack starts at low memory growing up.
HP's PA-RISC hardware, among others, does this: http://www.embeddedrelated.com/usenet/embedded/show/68749-1.php
The venerable Multics Operating System ran on hardware that had (one of possibly many) stacks growing up: see http://www.acsac.org/2002/papers/classic-multics.pdf, end of section 2.3.2:
Third, stacks on the Multics processors grew in the positive direction, rather than the negative direction. This meant that if you actually accomplished a buffer overflow, you would be overwriting unused stack frames, rather than your own return pointer, making exploitation much more difficult.
That's a rather interesting statement. Did buffer overflows become such a huge problem only because of the "customary" procedure-call-stack-frame arrangement? Also, how much of Multics' reputation as Totally Invulnerable was just a fluke of hardware design?