Godot: Infinite recursive func crashes when running out of stack (stack overflow)

Created on 6 Feb 2018  路  18Comments  路  Source: godotengine/godot

Godot version:
Godot_v3.0-stable_win64

OS/device including version:
Windows 10 64-bit Pro / Intel(R) UHD Graphics 620
Windows 10 64-bit Home / GeForce GTX 970/PCIe/SSE2
Windows 10 64-bit Education / Intel(R) HD Graphics 620

Issue description:
When running a recursive function 345 times the engine crashes without an in-editor error message. Windows shows the message "Godot Engine Editor has stopped working". This only seems to happen with the 64-bit version. 32-bit overflows at 1024 as expected.

Steps to reproduce:
Run a scene with the following code attached to a Label node:

extends Label

var ceiling = 1

func r(count = 0):
    count += 1
    if count == ceiling:
        text = str(count)
        return
    r(count)

func _process(delta):
    r()
    ceiling += 1

Minimal reproduction project:
example.zip
.

bug crash discussion gdscript

Most helpful comment

I'm not sure if this is a bug, gdscript doesn't have tail-end recursion so we just run out of stack. There are two ways to fix this I suppose:

a) implement tail-end recursion for gdscript
b) implement a check in a tools=yes build that can error out cleanly when a script runs out of stack. This will be quite slow.

thoughts anyone?

All 18 comments

I'm not sure if this is a bug, gdscript doesn't have tail-end recursion so we just run out of stack. There are two ways to fix this I suppose:

a) implement tail-end recursion for gdscript
b) implement a check in a tools=yes build that can error out cleanly when a script runs out of stack. This will be quite slow.

thoughts anyone?

Stack overflow (Stack Size: 1024) message on the debugger at 1024 on linux 64bit, editor does not crash, only the running window.

I'm not sure if this is a bug, gdscript doesn't have tail-end recursion so we just run out of stack.

It does this as expected on the 32-bit version with a stack overflow message at 1024. On the 64-bit version, I see no message and it crashes at 345.

In my system 64 bit crash at recursion 342 and 32 bit version crash at recursion 555
Win 7 64 bit 16 gb ram.

32 bit version don麓t overflow as expected.

Official builds are using msvc or mingw?

I'm using a self compiled version in msvc.

In release i get more that 800 recursions but not go up to 1024. I try debug-tools 32 and 64, debug_release_tools 32 and 64 and release without tools... all of this have different ceiling to the number of reachable recursions, any of this go up to 1024.

Error message came from: drivers\windows\stream_peer_tcp_winsock.cpp:203
StreamPeerTCPWinsock::read: Served disconnected!

I feel this like an issue if linux-windows or mingw-msvc compilation behaviour is different for posible bugs in release, but if behavior is the same i don麓t see this like a bug. Recursion in not very needed in game play code, and if it is, and is very deep, is better investigate other secure methods with while (that you can push million times)

Ran on that problem while making celullar automata caves. Flood fill always crashed. I made a workaround, but would be nice to have proper code. Btw, does this happen in C# too?

Edit:
I used a queue-based flood fill.

var map = create_blank_array(width, height)

func flood_fill(y, x):
    var verified = create_blank_array(width, height)

    var q = [[y, x]]

    while q.size() > 0:
        var nx = q[0][1]
        var ny = q[0][0]
                q.pop_front()

        if is_occupied(ny, nx, verified):
            continue

        if is_occupied(ny, nx, map):
            continue

        verified[ny][nx] = 1

        q.append([ny + 1, nx])
        q.append([ny, nx + 1])
        q.append([ny - 1, nx])
        q.append([ny, nx - 1])

As @hpvb said, implement tail-end recursion for GDscript could be a very cool feature. GDscript is easy, powerfull and made for fast prototyping so tail-end recursion could really improve readability for dynamic algorithms used to solve certain mechanics.

not quite sure if this is worth fixing, maybe the debugger recursion check limit should be in kb instead of calls.

An easier demo is just using this script:

func _ready():
    _ready()

not quite sure if this is worth fixing, maybe the debugger recursion check limit should be in kb instead of calls.

There should definitly be an error message of some sorts, I was working on a procedurall level generator and It crashed without any signs of why. A simple message like too many recursive calls could really save a lot of debugging and figuring-out-why-it-wont-work time .

This just happened to me in 3.1, as @Jummit said a simple message stating that you ran out of calls and an infinite loop might be happening would just of saved from 2 hours of debuging something very simple.

I have the same problem with 3.1.1 on Linux 64 bit when in a different thread and I'm close to 100 calls on stack, not 1024. I've changed the Max Call Stack to 16000 and it's the same. If I run it from the main thread it works fine.

I wanted to test whether or not #37115 resolved this issue, but I noticed that it already doesn't happen in 3.2.x; at least not with the minimal reproduction project provided.

For completeness with:
Godot_v3.1.2-stable_win32.exe: Crash at (904)
Godot_v3.1.2-stable_win64.exe: Crash at (535)
Godot_v3.2-stable_win32.exe: Debugger message: Stack overflow (Stack Size: 1024)
Godot_v3.2-stable_win64.exe: Debugger message: Stack overflow (Stack Size: 1024)
Godot_v3.2.1-stable_win32.exe: Debugger message: Stack overflow (Stack Size: 1024)
Godot_v3.2.1-stable_win64.exe: Debugger message: Stack overflow (Stack Size: 1024)

Thanks for testing, I'll close as fixed in 3.2 then.

If anyone can still reproduce it on 3.2.1 or later, please add a comment with a reproduction project and we'll reopen the issue.

The issue did result in two good feature proposals that should probably not be lost:

  • Implement tail-end recursion for GDScript by @hpvb
  • Make the debugger recursion check limit in kB instead of calls by @reduz

Both would need to be developed further in proper proposals on https://github.com/godotengine/godot-proposals, as is they're just suggestions given here to solve an issue which no longer seems to happen.

I thought I was experiencing this issue but after troubleshooting, it turned out to be an unexpected difference between the behaviour of array slicing in gdscript and python. I'm not sure if it's intentional behaviour but the gdscript slice method will only return an empty array when the array was empty to begin with.

python:

a = [1,2,3] 
a[4:] -> []

gdscript
```
var a = [1,2,3]
a.slice(4, 10) -> [3]

@Nattie-G It looks like you've discovered a new bug. You should open a new Issue, because it's unrelated to this Issue.

EDIT: This is a known issue: #34284

Was this page helpful?
0 / 5 - 0 ratings