godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
89.06k stars 20.19k forks source link

Integer underflows throughout the code when trying to round up #80358

Closed darksylinc closed 8 months ago

darksylinc commented 1 year ago

Godot version

4.2.x master [16a93563bfd3b02ca0a8f6df2026f3a3217f5571]

System information

Godot v4.2.dev (262d1eaa6) - Ubuntu 20.04.6 LTS (Focal Fossa) - X11 - Vulkan (Forward+) - dedicated AMD Radeon RX 6800 XT - AMD Ryzen 9 5900X 12-Core Processor (24 Threads)

Issue description

Based on my findings on #80356 I noticed this pattern repeats throghout the code in order to perform integer division that rounds up:

Whenever the code wants to round up, it performs x = (x - 1) / y + 1 when it should be doing x = (x + y - 1) / y

The original code when the input is 0 is wrong for both unsigned (produces a very large value) and signed (produces 1 instead of 0)

This version handles 0 properly. (x / y = 0).

e.g.

Mathematically they're the same:

= (x - 1) / y + 1
= (x - 1) / y + y / y
= (x + y - 1) / y

However in terms of computer science, they're not

If no one objects I'll submit a PR next weekend.

Steps to reproduce

None, this is a chronic problem spread out throghout the code.

See #80286 for an example of a repro.

Minimal reproduction project

See #80286

reduz commented 1 year ago

This is my fault, just for reference, I never expected it to be used with zero. I eventually put the code in: RenderingDevice::compute_list_dispatch_threads And it validates zero arguments as error, but it seems I left early pieces of the code floating around that were not moved to use this function and forgot about them.

reduz commented 1 year ago

PR fixing this would be very welcome!

EddieBreeg commented 1 year ago

I have some time to spare right now, I can take care of it if that makes everyone happy

darksylinc commented 1 year ago

I have some time to spare right now, I can take care of it if that makes everyone happy

Fine by me.

@lawnjelly has suggested to use a function which I agree it is much more obvious what the code is trying to do (rather than hope the reader is familiar with the formula):

#define GODOT_ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))

My only remark to that is that perhaps a function would be better, because a macro would accept floats with no warnings and it won't produce the right result (since it's meant for unsigned/signed integers).

EddieBreeg commented 1 year ago

I assume this function would be added somewhere in math_funcs.h?

akien-mga commented 1 year ago

How should this function be named? divide_round_up(int numerator, int denominator)?

EddieBreeg commented 1 year ago

Yeah that does seem reasonable

darksylinc commented 1 year ago

In floating point math this operation is usually known as ceil after division, but I personally prefer round_up

miv391 commented 1 year ago

Whenever the code wants to round up, it performs x = (x - 1) / y + 1 when it should be doing x = (x + y - 1) / y

Wont x = (x + y - 1) / y cause overflow when for example both x and y are somewhere between max_int / 2 and max_int?

lawnjelly commented 1 year ago

Wont x = (x + y - 1) / y cause overflow when for example both x and y are somewhere between max_int / 2 and max_int?

Yes, but this is an implementation detail better discussed on the PR. Integer math is nearly always susceptible to overflow.

EddieBreeg commented 1 year ago

Well of course you can always find values for which it overflows, but we can at least mitigate the issue in the typical case where we have small unsigned values for example. Besides, having a function makes the code way more readable anyway so in my opinion there's no reason not to.

darksylinc commented 1 year ago

I just submitted PR #81277 for this.

Regarding:

Possibly count = ((to - from - 1) / incr) + 1; & count = ((from - to - 1) / -incr) + 1 in gdscript_utility_functions.cpp

I analyzed what's happening and decided to leave that as is.

The current code deals with a lot of nuances specific to range( from, to, incr ) and modifying it would not only potentially create possible bugs; it would probably also made it harder to understand what's going on.

The PR & this bug report is about having positive numbers that require divide & round up. range( from, to, incr ) deals with a lot more than that.

Update: I totally missed PR #80390 despite yesterday I went to see if it had been handled already :cry: