Skip to main content
28 events
when toggle format what by license comment
Jul 4, 2022 at 16:51 comment added A P I just use addi with -4 because the sub vsrient is pseudo code
Feb 17, 2022 at 18:09 comment added Sebastian Redl Downward growing stack makes it slightly easier to align the stack pointer to power-of-2.
Feb 17, 2022 at 2:23 history edited Erik Eidt CC BY-SA 4.0
added 399 characters in body
Feb 22, 2019 at 22:26 audit First posts
Feb 22, 2019 at 22:27
S Jan 29, 2019 at 20:21 history suggested Ghost4Man CC BY-SA 4.0
corrected spelling (poping → popping); improved formatting (instructions)
Jan 29, 2019 at 20:21 review Suggested edits
S Jan 29, 2019 at 20:21
Jan 29, 2019 at 1:23 comment added Erik Eidt @user207421, PDP-1 from the late '50s did not have a stack pointer; the call instruction stored the return address in the Accumulator. The PDP-8 (1965); also had no stack pointer: its JSR instruction stored the return address into the first word of the function. (Thus, functions started with a NOP instruction to reserve storage for the return address right there in the code! Nested subroutines but no recursion.) I'm sure there are other examples, both older and more modern than these. Such machines are somewhat hostile to modern programming languages, like Pascal and C.
Jan 29, 2019 at 0:46 comment added user207421 'Originally processors had no stack handling instructions': originally when? The Burroughs B5500 had them in 1960, the PDP-11 in 1970, ...
Jan 28, 2019 at 19:59 comment added Peter Cordes @R..: oh good point, yes I was thinking x86 because of the code in the question. But of course only ISAs like MIPS which don't impose a stack-growth direction and leave that purely to software could practically do this now. And that means call will use a link-register, not a baked-in stack push. (Might be possible on ARM32 outside of Thumb mode; stm is available in 4 modes, and decrement-before (push) is only one possibility.) I'm not sure if ARM interrupt handling ever uses the stack in HW, though.
Jan 28, 2019 at 19:13 comment added supercat Some applications and programming languages (e.g.early versions of Turbo Pascal as well as PostScript) supported a mark/release allocation strategy where the only way to free anything is to free everything allocated after a particular object. Such a design is very convenient with an upward-growing heap, since one can free an object and everything allocated after it by setting the "new object location" pointer to the address of an object without having to know the size of it or any other allocated object.
Jan 28, 2019 at 14:40 comment added R.. GitHub STOP HELPING ICE @PeterCordes: Indeed my comment noted that same-level or more recent stack frames than the overflowed buffer are still potentially clobberable, but that's a lot less. In the case where the clobbering function is a leaf function directly called by the function whose buffer it is (e.g. strcpy), on an arch where return address is kept in a register unless it needs to be spilled, there is no access to clobber the return address.
Jan 28, 2019 at 13:12 comment added Holger @PeterCordes ok, now I understood.
Jan 28, 2019 at 11:42 comment added Peter Cordes @Holger: The usual buffer overruns happen when calling library functions that copy a string into a buffer, not in open-coded loops inside the function itself. I don't think you understood my previous comments. A library strcpy function doesn't know where the buffer ends and its parent's return address begins. It only knows where its own return address is stored. (It could unwind the stack, but with -fomit-frame-pointer being the default, that's very expensive to look up metadata in .eh_frame, or whatever Microsoft does. It can't follow a saved-RBP frame pointer chain.)
Jan 28, 2019 at 11:13 comment added Holger @PeterCordes the knowledge of the return address does not depend on the stack’s run direction. When you’re talking about target buffers within the current stack frame, it would be possible to detect when the copying would cross the return address, if the particular language/compiler was willing to let the application spend CPU cycles for that. But when changing the stack’s direction, the writing into the target buffer would run into the same direction, hence, could never cross the return address.
Jan 28, 2019 at 6:05 comment added Peter Cordes Upward stacks would make it possible for a defensive strcpy to detect some errors, though: it knows where its own return address is stored, and could make that an upper limit for the destination pointer. e.g. abort() if passed an input that would result in overwriting the return address. It can't catch the general case of a function allocating a buffer and passing it to another function, which only then calls strcpy, though. But I think many vulnerabilities allocate and strcpy in the same function.
Jan 28, 2019 at 6:00 comment added Peter Cordes @R..: growing up doesn't eliminate buffer overrun exploits, because vulnerable functions are usually not leaf functions: they call other functions, placing a return address above the buffer. Leaf functions that get a pointer from their caller could become vulernable to overwriting their own return address. e.g. If a function allocates a buffer on the stack and passes it to gets(), or does a strcpy() that doesn't get inlined, then the return in those library functions will use the overwritten return address. Currently with downward-growing stacks, it's when their caller returns.
Jan 28, 2019 at 3:38 comment added Peter Cordes @EdgarBonet: Linux on x86 / x86-64 still follows the same layout, too for user-space processes. The stack is near the very top of usable user-mode virtual address space (e.g. RSP = 0x7ffff7f85578 on entry to main, near the top of the lower-47-bit canonical range that Linux reserves for user-space), and text/data/bss segments are near the bottom (in a position-dependent executable. Otherwise in a PIE, text/data/bss are ASLRed somewhere around 0x555555555000 (that address is with ASLR disabled by GDB). Or notes.shichao.io/lkd/ch15 shows some 32-bit memory map examples.
Jan 28, 2019 at 2:30 comment added R.. GitHub STOP HELPING ICE For what it's worth, I don't think "the growth direction is arbitrary" in the sense that either choice is equally good. Growing down has the property that overflowing the end of a buffer clobbers earlier stack frames, including saved return addresses. Growing up has the property that overflowing the end of a buffer clobbers only storage in the same or later (if the buffer is not in the latest, there may be later ones) call frame, and possibly even only unused space (all assuming a guard page after the stack). So from a safety perspective, growing up seems highly preferable
Jan 28, 2019 at 0:49 history edited Erik Eidt CC BY-SA 4.0
added 42 characters in body
Jan 27, 2019 at 20:28 comment added Margaret Bloom ARM can have all all four stack flavours.
Jan 27, 2019 at 20:04 comment added Edgar Bonet Some microcontrollers (e.g. AVR) still follow this historical model, with the stack at the top of the RAM and .data + .bss + heap at the bottom, with only one gap of free memory in between (.text is not in RAM: it is a Harvard architecture).
Jan 27, 2019 at 16:46 comment added Deduplicator It's not just push and pop on most architectures, but also the far more important interrupt-handling, call, ret, and whatever else has baked-in interaction with the stack.
Jan 27, 2019 at 16:36 history edited Erik Eidt CC BY-SA 4.0
added 102 characters in body
Jan 27, 2019 at 16:28 history edited Erik Eidt CC BY-SA 4.0
added 303 characters in body
Jan 27, 2019 at 16:20 history edited Erik Eidt CC BY-SA 4.0
deleted 6 characters in body
Jan 27, 2019 at 16:14 history edited Erik Eidt CC BY-SA 4.0
added 293 characters in body
Jan 27, 2019 at 16:06 history edited Erik Eidt CC BY-SA 4.0
added 293 characters in body
Jan 27, 2019 at 16:01 history answered Erik Eidt CC BY-SA 4.0