Godot: Unhandled division / modulo quotient overflow causes crash.

Created on 21 Feb 2019  路  7Comments  路  Source: godotengine/godot

Godot version:
3.1 (master), and probably any other

OS/device including version:
Any

Issue description:
As mentioned in https://github.com/godotengine/godot/pull/26113#issuecomment-465965854 executing GDScript with division / modulo operation on LLONG_MIN and -1, or simply entering it into script editor (line is executed by autocomplete) causes quotient overflow and crash with SIGFPE (Floating point exception: divide by zero). Division by 0 case is handled correctly.

 -9223372036854775808 / -1
 -9223372036854775808 % -1

IDIV instruction reference

Overflow is indicated with the #DE (divide error) exception rather than with the CF flag.

bug confirmed gdscript

All 7 comments

Oh, that's probably because we disable -ffast-math, now some fp operations can trap.

Oh, that's probably because we disable -ffast-math, now some fp operations can trap.

It's integer division, unlikely -ffast-math is affecting it.

In any case it's UB and should be avoided.

ISO/IEC 9899:201x, 6.5.5.6 Multiplicative operators:

When integers are divided, the result of the/operator is the algebraic quotient with any fractional part discarded. If the quotient a/b is representable, the expression (a/b)*b + a%b shall equal a; otherwise, the behavior of both a/b and a%b is undefined.

Another affected function wrapi(-2147483647, 1, 0) (in addition it should be modified to 64-bit, as well as some other int functions in core/math/math_funcs.h).

Still happens with master with this backtrace:

core/ustring.cpp:1818:12: runtime error: signed integer overflow: 9223372036854775800 + 8 cannot be represented in type 'long int'
core/variant_op.cpp:1019:4: runtime error: negation of -9223372036854775808 cannot be represented in type 'long int'; cast to an unsigned type to negate this value to itself
core/variant_op.cpp:956:4: runtime error: division of -9223372036854775808 by -1 cannot be represented in type 'long int'
handle_crash: Program crashed with signal 8
Dumping the backtrace. Please include this when reporting the bug on https://github.com/godotengine/godot/issues
[1] /home/rafal/Pulpit/mojgodot/bin/godot.x11.tools.64s() [0x1406fde] (/home/rafal/Pulpit/mojgodot/platform/x11/crash_handler_x11.cpp:54)
[2] /lib/x86_64-linux-gnu/libc.so.6(+0x46470) [0x7fb05e069470] (??:0)
[3] Variant::evaluate(Variant::Operator const&, Variant const&, Variant const&, Variant&, bool&) (/home/rafal/Pulpit/mojgodot/core/variant_op.cpp:956 (discriminator 9))
[4] GDScriptParser::_reduce_expression(GDScriptParser::Node*, bool) (/home/rafal/Pulpit/mojgodot/modules/gdscript/gdscript_parser.cpp:1971 (discriminator 14))
[5] GDScriptParser::_parse_and_reduce_expression(GDScriptParser::Node*, bool, bool, bool) (/home/rafal/Pulpit/mojgodot/modules/gdscript/gdscript_parser.cpp:2015)
[6] GDScriptParser::_parse_block(GDScriptParser::BlockNode*, bool) (/home/rafal/Pulpit/mojgodot/modules/gdscript/gdscript_parser.cpp:3343)
[7] GDScriptParser::_parse_class(GDScriptParser::ClassNode*) (/home/rafal/Pulpit/mojgodot/modules/gdscript/gdscript_parser.cpp:3978)
[8] GDScriptParser::_parse(String const&) (/home/rafal/Pulpit/mojgodot/modules/gdscript/gdscript_parser.cpp:8444 (discriminator 1))
[9] GDScriptParser::parse(String const&, String const&, bool, String const&, bool, Set<int, Comparator<int>, DefaultAllocator>*, bool) (/home/rafal/Pulpit/mojgodot/modules/gdscript/gdscript_parser.cpp:8550)
[10] GDScriptLanguage::validate(String const&, int&, int&, String&, String const&, List<String, DefaultAllocator>*, List<ScriptLanguage::Warning, DefaultAllocator>*, Set<int, Comparator<int>, DefaultAllocator>*) const (/home/rafal/Pulpit/mojgodot/modules/gdscript/gdscript_editor.cpp:123 (discriminator 1))
[11] ScriptTextEditor::_validate_script() (/home/rafal/Pulpit/mojgodot/editor/plugins/script_text_editor.cpp:570 (discriminator 2))
[12] MethodBind0::call(Object*, Variant const**, int, Variant::CallError&) (/home/rafal/Pulpit/mojgodot/./core/method_bind.gen.inc:61 (discriminator 4))
[13] Object::call(StringName const&, Variant const**, int, Variant::CallError&) (/home/rafal/Pulpit/mojgodot/core/object.cpp:921 (discriminator 1))
[14] Object::emit_signal(StringName const&, Variant const**, int) (/home/rafal/Pulpit/mojgodot/core/object.cpp:1218 (discriminator 1))
[15] Object::emit_signal(StringName const&, Variant const&, Variant const&, Variant const&, Variant const&, Variant const&) (/home/rafal/Pulpit/mojgodot/core/object.cpp:1261)
[16] CodeTextEditor::_text_changed_idle_timeout() (/home/rafal/Pulpit/mojgodot/editor/code_editor.cpp:1484 (discriminator 2))
[17] MethodBind0::call(Object*, Variant const**, int, Variant::CallError&) (/home/rafal/Pulpit/mojgodot/./core/method_bind.gen.inc:61 (discriminator 4))
[18] Object::call(StringName const&, Variant const**, int, Variant::CallError&) (/home/rafal/Pulpit/mojgodot/core/object.cpp:921 (discriminator 1))
[19] Object::emit_signal(StringName const&, Variant const**, int) (/home/rafal/Pulpit/mojgodot/core/object.cpp:1218 (discriminator 1))
[20] Object::emit_signal(StringName const&, Variant const&, Variant const&, Variant const&, Variant const&, Variant const&) (/home/rafal/Pulpit/mojgodot/core/object.cpp:1261)
[21] Timer::_notification(int) (/home/rafal/Pulpit/mojgodot/scene/main/timer.cpp:61 (discriminator 2))
[22] Timer::_notificationv(int, bool) (/home/rafal/Pulpit/mojgodot/scene/main/timer.h:38 (discriminator 14))
[23] Object::notification(int, bool) (/home/rafal/Pulpit/mojgodot/core/object.cpp:933)
[24] SceneTree::_notify_group_pause(StringName const&, int) (/home/rafal/Pulpit/mojgodot/scene/main/scene_tree.cpp:981)
[25] SceneTree::idle(float) (/home/rafal/Pulpit/mojgodot/scene/main/scene_tree.cpp:526 (discriminator 3))
[26] Main::iteration() (/home/rafal/Pulpit/mojgodot/main/main.cpp:1980)
[27] OS_X11::run() (/home/rafal/Pulpit/mojgodot/platform/x11/os_x11.cpp:3260)
[28] /home/rafal/Pulpit/mojgodot/bin/godot.x11.tools.64s(main+0x324) [0x13ff2ea] (/home/rafal/Pulpit/mojgodot/platform/x11/godot_x11.cpp:57)
[29] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf3) [0x7fb05e04a1e3] (??:0)
[30] /home/rafal/Pulpit/mojgodot/bin/godot.x11.tools.64s(_start+0x2e) [0x13fef0e] (??:?)

Testing on linux mint 18.3
Can reproduce crash in 3.0.stable thru 3.2.2.stable
Can't reproduce crash in 3.2.3.beta1 thru 3.2.3.rc3

In 3.2.3, this script:

func _ready():
    print(-9223372036854775808 / -1)
    print(-9223372036854775808 % -1)

produces this output:

 Cannot represent 9223372036854775808 as 64-bit integer, provided value is too big.
 Cannot represent 9223372036854775808 as 64-bit integer, provided value is too big.
--- Debugging process started ---
Godot Engine v3.2.3.rc3.official - https://godotengine.org
OpenGL ES 3.0 Renderer: GeForce GTX 750 Ti/PCIe/SSE2

9223372036854775807
0
--- Debugging process stopped ---

The "cannot represent .. as 64-bit integer" line repeats several times in the editor each time it is handled, eg. when the scene is loaded or switched to, when the script is edited and autocomplete/syntax checking kicks in, etc.

EDIT: Can still crash in 3.2.3, see bruvg's post below.

cannot represent .. as 64-bit integer"

Probably GDScript parser has changed and read int before sign now (9223372036854775808 is too big for positive int but OK for negative):

But this should still have the same effect:

(-9223372036854775807 - 1) % -1
(-9223372036854775807 - 1) / -1
Was this page helpful?
0 / 5 - 0 ratings