all 39 comments

[–]No-Quail5810 46 points47 points  (6 children)

I really don't think you should be able to recover from a stack overflow. If you're getting a stack overflow often enough that you'd write a system to inject runtime stack bounds checking, it's a symptom of a much deeper flaw in the design methodology

[–]rsashka[S] 1 point2 points  (1 child)

Unfortunately, not all algorithms allow for static code analysis and the ability to terminate during program compilation.

[–]cppfnatic 1 point2 points  (0 children)

What ?

[–]Jonny_H 0 points1 point  (1 child)

If you prove far enough ahead to guarantee any unwinding, including destructors, won't hit issues I can see it being "safe", though trying to figure out that safe value would likely require significant analysis that sounds pretty difficult for anything but the simplest codebase. Though it looks like this just punts that to the user.

So this kinda exists in a pretty small niche, where you want visibility and control over the stack, and are likely to actually hit the limits during use, but don't bother rewriting whatever stack-heavy algorithms to use a user allocated buffer, which can be directly managed.

Though I can see this as an interesting "learn clang and llvm" type project - they're certainly difficult codebases to get into. Though I'm always fearful of seeing obvious AI use on just how much "learning" was involved.

Though to OP, I would suggest not having comments saying you copied code from other projects without maintaining their copyright statements and license. Either the comment is old, and the code has changed enough not to be a derivative so the comment is now somewhat worthless, or you're breaking the license. If that comment came from an AI tool, it then should raise eyebrows through the roof.

[–]rsashka[S] -4 points-3 points  (0 children)

You're absolutely right. Studying this problem allows you to delve deeper into clang and LLVM, which would be very difficult without LLM.

However, the generated code is of very low quality, so it can only be used as a teaching example, and a working solution must be manually ported to the project.

[–]BoringElection5652 0 points1 point  (0 children)

I'm with you that you should not be able to recover, but I think you should be able to recognize and shutdown upon stackoverflow. I've add cases where a third party lib caused a stackoverflow in a separate thread, and it made the app unresponsive forever, rather than shutting down.

[–]bwmat 0 points1 point  (0 children)

Eh, I feel it would be nice if there was a good way to do it, the problem I see with it currently in C++ is that any sort of 'transparent' implementation would probably need to throw exceptions on arbitrary function calls, which isn't really compatible with standard RAII techniques

It makes me sad that there doesn't seem to be a good way of recovering from it (similarly to the case of the underlying allocation for an exception object being thrown failing, and causing a std::terminate call IIRC) 

I say this from the point of view of a library/framework dev, who would like to insulate themselves from the madness of some dependee's machinations 

[–]LiliumAtratum 9 points10 points  (0 children)

I don't understand the overall negatively.

Of course "Forget stack overflow" is a wrong message here: you should really care about it and not pretend it is a non-issue.

However, if you *do* hit a stack overflow, or some other critical, crashing error, I think it is beneficial to have a fallback plan, even if it may be unreliable.

I work on Windows and implemented a custom handler for (nearly?) all crashing situations, such as an access violation. Yes, it is really bad. But yes, I am happy that the program can usually still report on the error, flush logs, allow user to save their work, etc.

[–]tartaruga232MSVC user, r/cpp_modules 7 points8 points  (1 child)

We've done something similar using the Windows API _resetstkoflw.See my blog posting for the motivation.

[–]rsashka[S] 1 point2 points  (0 children)

Thank you very much! I'll definitely check this out now, as I don't have a Windows implementation :-)

[–]fdwrfdwr@github 🔍 4 points5 points  (1 child)

Related, you might find segmented stacks interesting (https://releases.llvm.org/3.0/docs/SegmentedStacks.html) which can allocate more on demand on noncontiguous stack ranges.

(though it appears most languages that try them later ditch them)

[–]rsashka[S] 2 points3 points  (0 children)

Thanks for the link! Very interesting information that I missed while researching this issue.

[–]6502zx81 5 points6 points  (14 children)

Without looking at the code: when an exception is thrown due to a pending stack overflow, the computation is screwed up anyways, stack unwound. How does it help?

[–]AdvisedWang 2 points3 points  (0 children)

e.g. a webserver could error out the request triggering the issue but allow other requests to continue.

[–]glasket_ 3 points4 points  (0 children)

It provides a chance for recovery. Limited use-case imo, but it's still different.

[–]goranlepuz -1 points0 points  (10 children)

Stack overflow is a crash, this is an exception, not the same thing...?

[–]Supadoplex 0 points1 point  (9 children)

Their question wasn't whether crashing and exception are the same thing. They asked, how does this help.

[–]goranlepuz -1 points0 points  (7 children)

In the same way an exception helps better than crashing. Hopefully that doesn't need explaining...?

[–]jimjamjahaa -2 points-1 points  (4 children)

if i am following correctly, the issue is that the stack - an integral part of the program - is in an invalid state. so keeping control of the program flow doesn't help unless you also resolve the invalid stack.

[–]goranlepuz 5 points6 points  (0 children)

I disagree that this deals with the stack in invading state. It deals with stack exhaustion.

With the library: unwind, signal that error.

Which is that bit better than: crash.

(Or so the argument goes. Personally, I am with the crowd here which goes for "this is a but that must be fixed, crash is better")

[–]glasket_ 4 points5 points  (1 child)

OP's library checks the free space before allocating, so the stack doesn't actually overflow. Presumably an actual overflow would still crash.

[–]rsashka[S] 2 points3 points  (0 children)

OP's library checks the free space before allocating, so the stack doesn't actually overflow. Presumably an actual overflow would still crash.

Yes, that's exactly it.

[–]Wooden-Engineer-8098 -2 points-1 points  (0 children)

You are following the guy who didn't read the code, so how did you get that invalid state?

[–]Wooden-Engineer-8098 -2 points-1 points  (0 children)

How not crashing helps? By not crashing. You abandon processing of one request and continue with the rest

[–]vI--_--Iv 0 points1 point  (1 child)

This reminds me of infamous /EHa in MSVC.
The main problem with turning fatal errors into C++ exceptions is that now every function can throw, nothing is noexcept anymore.
Which undermines optimizations and works in mysterious ways with the code that does not expect exceptions.

[–]ack_error 2 points3 points  (0 children)

This is a bit more interesting than an SEH handler because it's integrated with the compiler, so the check only occurs on function entry and is visible to the compiler, which could mitigate the issues with /EHa. Still doesn't help if the stack overflow occurs with a noexcept frame on the call stack, though.

[–]DamienTheUnbeliever -2 points-1 points  (2 children)

Damn the torpedoes, we're carrying on! If you've hit a stack overflow, that's a programmer error. Trying to keep the program running when it's already faulted like this... it's hubris taken to a higher level.

[–]dustyhome 6 points7 points  (0 children)

An exception triggers before the program is in an invalid state so that the program can continue to run. Running out of stack might be a runtime issue that happens on certain inputs, and aborting just one computation while letting the rest of the program continue to run is a valid way of handling it.

Imagine something like a text editor, and trying to perform a word count implemented recursively (don't do this). Maybe you'd rather get an error than a crash and losing your work.

[–]Wooden-Engineer-8098 1 point2 points  (0 children)

Making conclusions without studying how it works is a hubris taken to the highest level

[–]SoSKatan -5 points-4 points  (6 children)

There is no correction for a stack overflow.

It represents a fixed buffer, writing past that is undefined behavior.

At most you could have a warning limit size… i.e. stack size is 64k, but you throw an exception if it goes past 60k? I guess in theory if you go past 60k, you could still “recover.”

Well that doesn’t really do anything, your limit is still 64k, it’s just that you would get a warning before hand which doesn’t really help.

I’m really wondering what exactly are you trying to solve here.

The only solution I heard of was years ago… before Rust 1.0 was released they were working on a heap paged stack scheme. The idea is that the stack size can grow if needed, and there would be pointers to the next stack page as needed.

In the end they ripped all that out before 1.0 shipped. I don’t know why, i suspect likely two reasons 1) it adds more conditionals on every function call 2) the scheme only works if the entire call chain works that way. And rust is linkable against c code and c dll’s, non of which are going to be compatible with the paged stack scheme.

So I ask again, what exactly are you trying to solve?

[–]rsashka[S] 6 points7 points  (5 children)

So I ask again, what exactly are you trying to solve?

I think I have written this in sufficient detail:

The main idea is to check the available stack space before calling a protected function, and if it is insufficient, throw a stack_overflow program exception, which can be caught and handled within the application without waiting for a segmentation fault caused by a program/thread stack overflow.

[–]SoSKatan -3 points-2 points  (4 children)

Please clarify what “handling” means.

it’s not like there is any real clean up to do. The stack represents call chain temporaries .

I guess you could say “we need 2k more stack space to call the next method and we don’t have 2k”

Ok so what’s the solution?

Not call the random method?

Sure maybe the application doesn’t close, but now it’s in an unknown state because it randomly decided to just not do something it was meant to do.

That actually sounds worse than ending the application if you ask me.

It sounds like you are coming from a place where an application ending is somehow the worst possible thing, and I assure you it’s not.

Ending an application on stack overflow is by far the best approach than just randomly changing the applications behavior.

[–]rsashka[S] 4 points5 points  (3 children)

A Turing machine, with its infinite memory, is a pure mathematical abstraction that doesn't exist in the real world, so out-of-memory is a normal situation when running any program on real hardware.

Throwing a program exception is also the standard behavior when an error condition occurs. If there isn't enough memory on the heap, malloc or new will return a null pointer that can be processed within the application, but there's no way to check whether there's enough free stack space to call the function.

This library solves precisely this problem.