Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Crash when lambda connected to signal attempts to use freed instance #79707

Closed
romlok opened this issue Jul 20, 2023 · 1 comment · Fixed by #81605
Closed

Crash when lambda connected to signal attempts to use freed instance #79707

romlok opened this issue Jul 20, 2023 · 1 comment · Fixed by #81605

Comments

@romlok
Copy link
Contributor

romlok commented Jul 20, 2023

Godot version

v4.1.2.rc.custom_build [6884be6]

System information

Godot v4.1.2.rc (6884be6) - Debian GNU/Linux trixie/sid trixie - Vulkan (Mobile) - dedicated NVIDIA GeForce GTX 1060 (nvidia) - Intel(R) Core(TM) i5-8400 CPU @ 2.80GHz (6 Threads)

Issue description

When:

  1. A lambda is connected to a signal
  2. The lambda references some node
  3. That node gets freed
  4. The signal is then triggered

The game will crash. If running in the editor, no feedback is produced in the output or debugger.

I initially saw this on v4.1.stable.official [970459615], so compiled the engine from the current 4.1 tag to get a backtrace with debug symbols:

Godot Engine v4.1.2.rc.custom_build.6884be6b1 - https://godotengine.org
Vulkan API 1.3.224 - Forward+ - Using Vulkan Device #1: NVIDIA - NVIDIA GeForce GTX 1060
 
Connecting
Freeing
Emitting
ERROR: Can't get method on CallableCustom "<anonymous lambda>(lambda)".
   at: get_method (core/variant/callable.cpp:386)

================================================================
handle_crash: Program crashed with signal 11
Engine version: Godot Engine v4.1.2.rc.custom_build (6884be6b173d22c758806e2207d64f080e9ea7c1)
Dumping the backtrace. Please include this when reporting the bug to the project developer.
[1] /lib/x86_64-linux-gnu/libc.so.6(+0x3bf90) [0x7fb6926c7f90] (??:0)
[2] Variant::get_type() const (/data/src/godot/engine/./core/variant/variant.h:330)
[3] Variant::get_call_error_text(Object*, StringName const&, Variant const**, int, Callable::CallError const&) (/data/src/godot/engine/core/variant/variant.cpp:3657 (discriminator 4))
[4] Variant::get_callable_error_text(Callable const&, Variant const**, int, Callable::CallError const&) (/data/src/godot/engine/core/variant/variant.cpp:3692)
[5] Object::emit_signalp(StringName const&, Variant const**, int) (/data/src/godot/engine/core/object/object.cpp:1082 (discriminator 4))
[6] Node::emit_signalp(StringName const&, Variant const**, int) (/data/src/godot/engine/scene/main/node.cpp:3571)
[7] Signal::emit(Variant const**, int) const (/data/src/godot/engine/core/variant/callable.cpp:463)
[8] _VariantCall::func_Signal_emit(Variant*, Variant const**, int, Variant&, Callable::CallError&) (/data/src/godot/engine/core/variant/variant_call.cpp:1061)
[9] bin/godot.linuxbsd.editor.dev.x86_64(+0x724f64a) [0x55e9222aa64a] (/data/src/godot/engine/core/variant/variant_call.cpp:2061)
[10] Variant::callp(StringName const&, Variant const**, int, Variant&, Callable::CallError&) (/data/src/godot/engine/core/variant/variant_call.cpp:1188)
[11] GDScriptFunction::call(GDScriptInstance*, Variant const**, int, Callable::CallError&, GDScriptFunction::CallState*) (/data/src/godot/engine/modules/gdscript/gdscript_vm.cpp:1690)
[12] GDScriptFunctionState::resume(Variant const&) (/data/src/godot/engine/modules/gdscript/gdscript_function.cpp:252)
[13] GDScriptFunctionState::_signal_callback(Variant const**, int, Callable::CallError&) (/data/src/godot/engine/modules/gdscript/gdscript_function.cpp:203)
[14] MethodBindVarArgTR<GDScriptFunctionState, Variant>::call(Object*, Variant const**, int, Callable::CallError&) const (/data/src/godot/engine/./core/object/method_bind.h:261 (discriminator 4))
[15] Object::callp(StringName const&, Variant const**, int, Callable::CallError&) (/data/src/godot/engine/core/object/object.cpp:739 (discriminator 2))
[16] Callable::callp(Variant const**, int, Variant&, Callable::CallError&) const (/data/src/godot/engine/core/variant/callable.cpp:62)
[17] CallableCustomBind::call(Variant const**, int, Variant&, Callable::CallError&) const (/data/src/godot/engine/core/variant/callable_bind.cpp:145)
[18] Callable::callp(Variant const**, int, Variant&, Callable::CallError&) const (/data/src/godot/engine/core/variant/callable.cpp:64)
[19] Object::emit_signalp(StringName const&, Variant const**, int) (/data/src/godot/engine/core/object/object.cpp:1071)
[20] Error Object::emit_signal<>(StringName const&) (/data/src/godot/engine/./core/object/object.h:881)
[21] SceneTree::process_timers(double, bool) (/data/src/godot/engine/scene/main/scene_tree.cpp:580 (discriminator 4))
[22] SceneTree::process(double) (/data/src/godot/engine/scene/main/scene_tree.cpp:520)
[23] Main::iteration() (/data/src/godot/engine/main/main.cpp:3425)
[24] OS_LinuxBSD::run() (/data/src/godot/engine/platform/linuxbsd/os_linuxbsd.cpp:912)
[25] bin/godot.linuxbsd.editor.dev.x86_64(main+0x16e) [0x55e91dae0f27] (/data/src/godot/engine/platform/linuxbsd/godot_linuxbsd.cpp:76)
[26] /lib/x86_64-linux-gnu/libc.so.6(+0x2718a) [0x7fb6926b318a] (??:0)
[27] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x85) [0x7fb6926b3245] (??:0)
[28] bin/godot.linuxbsd.editor.dev.x86_64(_start+0x21) [0x55e91dae0cf1] (??:?)
-- END OF BACKTRACE --
================================================================

Expectation

If the lambda is replaced by a regular callable (eg. do_boop.bind(...)), then the editor will provide helpful error messages and debugging information:

Screenshot_20230720_143236

I would expect something similar when a lambda is being used.

Steps to reproduce

This is the script from the attached MRP. The key points appear to be:

  1. The signal is defined in a persistent location (ie. doesn't itself get free'd)
  2. A lambda is used in the connect call
  3. The crash is triggered on the emit() call
extends Node2D

signal boop

func _ready():
	var level : Node2D = $Level
	print("Connecting")
	
	# Will show runtime errors in editor
	#boop.connect(do_boop.bind(level))
	
	# Will crash with no editor feedback
	boop.connect(func(): do_boop(level))
	
	print("Freeing")
	level.queue_free()
	await get_tree().create_timer(0.1).timeout
	print("Emitting")
	boop.emit()
	print("Done")
	

func do_boop(level):
	level.position = Vector2.ZERO

(Strangely, if running in the editor, the Emitting text does not appear in the output tab, though it does appear in STDERR)

Minimal reproduction project

Self-contained scene which reliably reproduces the crash:

lambda_crash.zip

@AThousandShips
Copy link
Member

AThousandShips commented Jul 20, 2023

Strangely, if running in the editor, the Emitting text does not appear in the output tab, though it does appear in STDERR

This might be threading related and to the problems with printing from threads

This seems related to #79591 (though I'm not sure the same guarantee to error instead of crash when accessing a property applies)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants