8

Consider the following code (for demonstration purposes only):

#include <iostream>

int main()
{
    char pixels[4][1280][720]; // Big enough to cause a stack overflow on my machine
    for (unsigned int i = 0; i < 4; i++)
    {
        for (unsigned int j = 0; j < 1280; j++)
        {
            for (unsigned int k = 0; k < 720; k++)
            {
                pixels[i][j][k] = i + j + k;
            }
        }
    }

    std::cout << pixels[2][640][360];
    return 0;
}

According to answers on this question, the maximum stack size is set by visual studio.

Am I correct in assuming it could warn users about a potential stack overflow? (I tried this myself and didn't get a warning)

P.S: The only reason I'm asking is because I see a lot of questions on SO that could be prevented by such a warning (Yes I know not everyone SO user uses VS).

Community
  • 1
  • 1
Borgleader
  • 15,826
  • 5
  • 46
  • 62
  • 4
    Evaluating complex code to know exactly what it will do without executing it is one of the holy grails. Don't get your hopes up. – Jeroen Vannevel Dec 04 '14 at 18:06
  • 1
    Do you mean something akin to GCC `-Wstack-usage=1234`? –  Dec 04 '14 at 18:08
  • 2
    @Fanael I mean if the size of the array in bytes is bigger than the stack limit, at which point a stack overflow is bound to happen. So it could warn on that. And yes that seems to be it. – Borgleader Dec 04 '14 at 18:09
  • ​+1, I run into this all the time when I turn on aggressive inlining with lots of SIMD objects. I'd rather the compiler warn me at compile-time than to crash at run-time. – Mysticial Dec 04 '14 at 21:04
  • In theory it could be done. I did it for a Pascal implementation on IBM System/38. The problem is that you effectively must throw an exception, and that requires stack space to handle, so you must "reserve" a substantial amount of stack for this. I found that it really only worked in a halfway worthwhile fashion if you reserved half the stack for exception handling, and, in the balance, it made more sense to give the application that much more stack before throwing an exception. – Hot Licks Dec 04 '14 at 23:37

2 Answers2

11

It already does, in the editions that have the /analyze flag available:

C:\>cl /EHsc /analyze stack.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 18.00.21005.1 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

stack.cpp
c:\stack.cpp(3) : warning C6262: Function uses '3686412' bytes of stack:  exceeds /analyze:stacksize '16384'.  Consider moving some data to heap.

The prefast tool shipped with the DDK/WDK produces similar warnings.

Of course, this is a very simple static check (if the function's stack usage is above a certain threshold). It does not attempt to detect recursive calls or add up total static usage through call chains.

nobody
  • 19,814
  • 17
  • 56
  • 77
  • Isn't analyze only available in the >$2,000 versions? – Zan Lynx Dec 05 '14 at 00:43
  • 3
    I believe that was the case for previous releases, but as of VS 2013 [all editions have it](http://blogs.msdn.com/b/hkamel/archive/2013/10/24/visual-studio-2013-static-code-analysis-in-depth-what-when-and-how.aspx). – nobody Dec 05 '14 at 00:44
6

The compiler could give a warning about almost anything, but in this case it would be pretty difficult for the compiler to give a truly meaningful warning.

In particular, the stack size isn't really selected until link time. The compiler runs before the linker, so it's essentially impossible for it to know what stack size you might select when you run the linker. As such, if the compiler were to issue a warning, about the best it could do would be to assume some "reasonable" stack size was going to be selected, and base the warning on violating that.

A sufficiently intelligent linker could presumably issue such a warning, but it would require quite a bit of intelligence. In particular, by the time the linker sees it, the stack allocation looks something like (the machine code representation of):

sub esp, 123456 ; or sub rsp, 123456 in 64-bit code.

The linker would have to find each place the stack pointer was being manipulated, and examine the size of the number involved to issue warnings meaningfully. To do that, it would basically have to figure out exactly what was code and what was data, and disassemble and examine the code (but not the data, which might disassemble to nonsensical code). That's all probably possible, but probably somewhat non-trivial, and certainly well outside the purview of the kinds of things linkers normally do or work with.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • I wonder is there's a compiler option that will warn when a single function uses an excessive amount of stack. Since I regularly blow the stack when a few levels of inlining makes the compiler decide to put like 1000 SIMD values on the stack. – Mysticial Dec 04 '14 at 19:51
  • @Mysticial: As Andrew Medico pointed out, the `/analyze` flag basically does what my second paragraph suggests: chooses a "reasonable" size, and warns you if that's exceeded. The size defaults to 16K, but you can set it to some other size if you wish, such as with: `/analyze:stacksize 65535`, which would (obviously enough) look for 64K instead. – Jerry Coffin Dec 04 '14 at 20:02
  • I'll probably try something like 1k and see what it complains about. The biggest annoyance is when the compiler inlines a ton of stuff into a recursive function without me knowing about it until run-time. – Mysticial Dec 04 '14 at 20:04
  • This analysis is flawed because it ignores the possibility of cooperation. The compiler could actual emit stack usage data, say in a separate side file. The linker, reading in this file, could create a partial call graph and determine if any path on the known call graph exceeds the stack limit. Challenge: cycles - but that's a case which we understand. Unbounded recursion indeed causes a stack overflow even with just a single variable. – MSalters Dec 04 '14 at 23:34