Closed qarmin closed 4 years ago
So, if I get this right, OBJ_DEBUG_LOCK
is referencing the object that is emitting the signal (by pointer, so it's generic enough).
Given the peer object is a reference, if the object loses the last Ref (via multiplayer.network_peer = null
) during the signal, then _ObjectDebugLock
tries to access it right after the signal emission (via obj->_lock_index
in ~_ObjectDebugLock
), and we get the crash.
Am I getting this right? Tagging people with a bit more knowledge on the internals (@reduz @hpvb ?).
Should we just use an extra reference to it while polling? Sounds more like an hack then a fix :(
Well, that seems to do it, at least according to valgrind... @qarmin can you try this patch (again, pretty hacky, we'll need to likely discuss something better):
diff --git a/core/io/multiplayer_api.cpp b/core/io/multiplayer_api.cpp
index 1426dbbd4d..d12b5c52a5 100644
--- a/core/io/multiplayer_api.cpp
+++ b/core/io/multiplayer_api.cpp
@@ -97,7 +97,10 @@ void MultiplayerAPI::poll() {
if (!network_peer.is_valid() || network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED)
return;
- network_peer->poll();
+ // Avoid the network peer going out of scope during polling (when signals are emitted)
+ Ref<NetworkedMultiplayerPeer> safe_peer = network_peer;
+ safe_peer->poll();
+ safe_peer = Ref<NetworkedMultiplayerPeer>();
if (!network_peer.is_valid()) // It's possible that polling might have resulted in a disconnection, so check here.
return;
A more "appropriate" fix might be to prevent references from going out of scope when the object->_lock_index
is > 0
, but then we'll have to make sure to properly free them when _lock_index
goes back to 0, and no reference is left.
EDIT: assuming of course, I got the culprit right
This patch fixed invalid write
Errors from Godot 3.2 beta 4
ERROR: _disconnect: Attempt to disconnect signal 'connection_failed' while in emission callback. Use CONNECT_DEFERRED (to be able to safely disconnect) or CONNECT_ONESHOT (for automatic disconnection) as connection flags.
At: core/object.cpp:1522.
ERROR: _disconnect: Attempt to disconnect signal 'connection_failed' while in emission callback. Use CONNECT_DEFERRED (to be able to safely disconnect) or CONNECT_ONESHOT (for automatic disconnection) as connection flags.
At: core/object.cpp:1522.
ERROR: ~Object: Object was freed or unreferenced while signal 'connection_failed' is being emitted from it. Try connecting to the signal using 'CONNECT_DEFERRED' flag, or use queue_free() to free the object (if this object is a Node) to avoid this error and potential crashes.
At: core/object.cpp:1955.
Still happens with Godot 3.2.3 rc4 with a little different backtrace
drivers/unix/net_socket_posix.cpp:714:9: runtime error: implicit conversion from type 'unsigned long' of value 4294969390 (64-bit, unsigned) to type 'int' changed the value to 2094 (32-bit, signed)
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior drivers/unix/net_socket_posix.cpp:714:9 in
ERROR: _verify_headers: Invalid protocol or status code.
At: modules/websocket/wsl_client.cpp:112.
ERROR: ~Object: Object [Object:1954] was freed or unreferenced while a signal is being emitted from it. Try connecting to the signal using 'CONNECT_DEFERRED' flag, or use queue_free() to free the object (if this object is a Node) to avoid this error and potential crashes.
At: core/object.cpp:2012.
=================================================================
==45707==ERROR: AddressSanitizer: heap-use-after-free on address 0x621000070969 at pc 0x0000122c8170 bp 0x7fff4295b350 sp 0x7fff4295b348
WRITE of size 1 at 0x621000070969 thread T0
#0 0x122c816f in Object::emit_signal(StringName const&, Variant const**, int) /mnt/Miecz/godot3.2/core/object.cpp:1250:14
#1 0x122c12c2 in Object::emit_signal(StringName const&, Variant const&, Variant const&, Variant const&, Variant const&, Variant const&) /mnt/Miecz/godot3.2/core/object.cpp:1306:9
#2 0x349db0f in WebSocketClient::_on_error() /mnt/Miecz/godot3.2/modules/websocket/websocket_client.cpp:139:3
#3 0x352ae92 in WSLClient::_do_handshake() /mnt/Miecz/godot3.2/modules/websocket/wsl_client.cpp:82:6
#4 0x3535067 in WSLClient::poll() /mnt/Miecz/godot3.2/modules/websocket/wsl_client.cpp:269:4
#5 0x12e152f8 in MultiplayerAPI::poll() /mnt/Miecz/godot3.2/core/io/multiplayer_api.cpp:100:16
#6 0xbde6d0e in SceneTree::idle(float) /mnt/Miecz/godot3.2/scene/main/scene_tree.cpp:515:16
#7 0x1f97b4b in Main::iteration() /mnt/Miecz/godot3.2/main/main.cpp:2107:44
#8 0x1e5f130 in OS_X11::run() /mnt/Miecz/godot3.2/platform/x11/os_x11.cpp:3233:7
#9 0x1dd853d in main /mnt/Miecz/godot3.2/platform/x11/godot_x11.cpp:56:6
#10 0x7f805f3030b2 in __libc_start_main /build/glibc-YYA7BZ/glibc-2.31/csu/../csu/libc-start.c:308:16
#11 0x1d2d84d in _start (/usr/bin/godots+0x1d2d84d)
@qarmin this can't be fixed in godot, must be fixed in demos
For me it is strange that GDscript code(even invalid) may crash project in editor due invalid using of memory.
@qarmin but there isn't much we can do, it's like calling free
in the script of a Node
during processing.
The only thing we can do, is warn the user not to do that.
Godot version: 3.2 alpha 3 compiled with sanitizers support OS/device including version: Ubuntu 19.10
Issue describtion:
Steps to reproduce:
Minimal reproduction project: https://github.com/godotengine/godot-demo-projects/tree/master/networking/websocket_multiplayer