How can a stack underflow happen in C++?
Asked Answered
S

4

19

What is a simple example in C++ that causes a stack underflow in the case of invoking and returning from method calls?

I am familiar with the calling convention, i.e thiscall, stdcall and the cdecl and way they would clean the stack. Wouldn't a stack underflow automatically be taken care of by the code generated by the compiler?

What are the situations that can get me into trouble with stack underflow?

Spinneret answered 1/7, 2011 at 18:56 Comment(10)
Are you sure you're not talking about stack over flow?Quilting
I think you have the wrong forum. This is stack OVERflow. If you want to find out about stack UNDERflow, you should visit that site. ;) sorry, had to do it...Kristiekristien
@Martinho and @Kristiekristien - umm.. a stack underflow is a actual issue as well, although not something you would run into often.Largent
I almost answered the question reading stackoverflow. May be because of my time spent on this forum made me to read stack-underflow as stackoverflow :)Kittykitwe
Yes.. an experienced programmer eye can catch a stack-overflow. What hacky cases could create a stack-underflow ?? Messing with the calling conventions ??Spinneret
@Eric: you can cause a stack underflow in the call stack? Without invoking UB? Can you please elaborate? Maybe make that an answer?Quilting
@de costo: I think while (1) { __asm pop EAX } would do it (it's not standard C++ of course).Behemoth
@Eric Petroelje yes I know, I was jokingKristiekristien
I like how there's "C++" in the title twice, even though you don't put the language in the title on SO :)Renwick
I should have authored this question... :)Attaboy
H
13

The only way I can see this actually happening would be if you declared a function to use the stdcall (or any other calling convention that specifies the callee clean the stack) and then invoke the function through a function pointer that was specified as a cdecl (or any other calling convention where the stack is cleaned by the caller). If you do that, the called function will pop the stack before returning and then the caller would also pop the stack leading to underflow and terrible things.

In the specific case of member functions, the calling convention is usually referred to as thiscall and whether the caller or the callee cleans the stack depends on the compiler.

See here for details of calling conventions.

Hydrology answered 1/7, 2011 at 20:58 Comment(6)
@Sean: could this also be caused by an incorrect forward declaration of a function? like void foobar(int a); and in another place void foobar(int a, int b);?Eustis
@Alex If you use stack framing then correctly poping the stack is not a problem.Draftee
@Jean-BaptisteYunès what does stack framing prevent? The OP asks on scenarios where this could go wrong, so are you suggesting that turning off stack framing would allow this?Eustis
@alex Stack framing would prevent bad arg-by-arg poping in the case of incoherent prototyping, isn't it?Draftee
@Jean-BaptisteYunès do you have a reference to what Stack framing is? If you are just referring to calling conventions and frame sizes, then it would not prevent anything, because it's about mismatching contracts.Eustis
@Hydrology And here I am thinking that this wasn't a real thing... interesting!Attaboy
C
5

I am not sure if you are talking of the data structure stack and the underflow problem in it or something else. As far as the stack(data structure) underflow problem is concerned here is a explanation.

stack is a last in, first out (LIFO) abstract data type and data structure. A stack can have any abstract data type as an element, but is characterized by only three fundamental operations: push, pop and stack top.

The push operation adds a new item to the top of the stack, or initializes the stack if it is empty. If the stack is full and does not contain enough space to accept the given item, the stack is then considered to be in an overflow state. The pop operation removes an item from the top of the stack.

A pop either reveals previously concealed items, or results in an empty stack, but if the stack is empty then it goes into underflow state (It means no items are present in stack to be removed).

The stack top operation gets the data from the top-most position and returns it to the user without deleting it. The same underflow state can also occur in stack top operation if stack is empty.

Consider a stack implementation example:

template <class Item> class Stack 
{
public:
    bool isEmpty() const;
    size_t size() const;
    Item pop();
    void push(const Item& it);
private:

};

Now consider the following operations being performed on this stack.

C++ command                      resulting stack
------------------------------------------------
Stack<int> S;
                                  _____ (empty stack of ints)



S.push(7);                            
                                  | 7 |  <-- top
                                  -----

S.push(2);                            
                                  | 2 |  <-- top 
                                  | 7 |
                                  -----

S.push(73);                           
                                  |73 |  <-- top 
                                  | 2 |
                                  | 7 |
                                  -----

S.pop();                           
                                  | 2 |  <-- top
                                  | 7 |                    -----
S.pop();      
                                  -----
S.pop();                           
                                  | 7 |  <-- top
                                  -----
S.pop();                           
                                  -----  (empty)

S.pop();                           
                    ERROR "stack underflow"
Crosslink answered 1/7, 2011 at 19:1 Comment(0)
L
1

Normally this would be taken care of by the compiler. In reality, the only way I can think of that you could accidentally cause a stack underflow would be by calling a method implemented with one calling convention as if it was using another calling convention.

Largent answered 1/7, 2011 at 19:3 Comment(2)
As in calling a invoking a method call defined with a different calling convention ??Spinneret
Yep, if the compiler didn't catch it then simply ending a loop that didn't start would give underflow.Calumniate
E
1

If you are ever in a position where a call-stack underflow could happen, chances are your program is going to die a violent death before it happens. At least, if I understand the way function calls work is accurate.

Basically, the only way for it to happen is if you call a function where the callee cleans up the stack, and it pops off too many values... if the caller believes a function accepts two parameters, but the callee actually takes three, this might happen. A variant would be a function where the callee cleans up the stack, and then the caller cleans up the stack again, as might happen if you get your calling convention wrong. In both situations, you will probably get a problem when you go to link and the mangled names are wrong, but perhaps you just get really unlucky.

Either way, the important thing is that after the function call, the stack is one or more bytes shorter than it is supposed to be. Theoretically the program will then continue on, popping the correct amount of data off, but remaining one or more bytes short of what is expected. Eventually, there is no more data to pop off, and you have a stack underflow.

However, when refering to the stack, addresses are relative to the top. So the compiler will look for a particular object at [top of stack + 3] if it is three bytes from the top of the stack. It will still do that if the stack ends up shorter than expected, and look for that object in the wrong location. Assuming that object is even still there... it might have accidently gotten popped off already. When you get to the end of whatever function you are in, it might not be able to find the correct return address for this same reason, but even if it does, having all of your objects suddenly corrupted is a pretty dire situation to be in.

Caveat: this is all based on the assumption that modern systems behave the same as the old microcontrollers I used to work on a decade ago. Maybe they're smarter than that now.

Epinasty answered 1/7, 2011 at 20:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.