-
Notifications
You must be signed in to change notification settings - Fork 25
Patch undefined behavior stuff #243
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
base: development
Are you sure you want to change the base?
Patch undefined behavior stuff #243
Conversation
Undefined sanitizer's complaint: ../external/sources/SDL3-3.2.10/src/events/SDL_eventwatch.c:72:17: runtime error: call to function RTE::WindowMan::HandleWindowExposedEvent(void*, SDL_Event*) through pointer to incorrect function type 'bool (*)(void *, union SDL_Event *)' /media/ext_hdd/nobackup/architector4/Downloads/Cortex-Command-Community-Project/builddebug/../Source/Managers/WindowMan.cpp:678: note: RTE::WindowMan::HandleWindowExposedEvent(void*, SDL_Event*) defined here SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior ../external/sources/SDL3-3.2.10/src/events/SDL_eventwatch.c:72:17
Undefined sanitizer's complaint: ../Source/Menus/ScenarioActivityConfigGUI.cpp:101:59: runtime error: load of value 190, which is not a valid value for type 'bool' SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior ../Source/Menus/ScenarioActivityConfigGUI.cpp:101:59 ...While working on this one, I discovered that C++ "default-initializes" class members, which leaves primitives and pointers with undefined garbage values, but properly initializes classes. And also the [Most vexing parse](https://en.wikipedia.org/wiki/Most_vexing_parse). Wow.
Undefined sanitizer's complaint: ../Source/System/PathFinder.cpp:176:54: runtime error: 3.40282e+38 is outside the range of representable values of type 'int' SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior ../Source/System/PathFinder.cpp:176:54 ...Also change a comment slightly lol
The door hinge still spins up and explodes just fine with this patch applied, as code below lerps it downward properly anyway. Undefined sanitizer complaint: ../Source/Entities/ADoor.cpp:470:24: runtime error: signed integer overflow: 671088640 * 4 cannot be represented in type 'int' SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior ../Source/Entities/ADoor.cpp:470:24
HeliumAnt
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice catches!
You can come by the discord if you need help/feedback/etc. btw. (If you don't use discord that's totally fine)
I don't use Discord much at all (only as a browser tab lmao), but decided to join anyway. Immediately after joining it locked my account out entirely asking for phone number verification and insists that my real correctly typed phone number is invalid. Well, I tried. edit: Turns out, installing Discord then doing verification with exact same phone number then deleting discord immediately after helped. Guess I'm there now lol |
|
I don't think this makes sense. FLT_MAX is used as a sentinel value within the pathfinder itself to do infinite jump height. This is treated completely separately within the pathfinder, we can't just set it to an arbitrarily high value. |
|
Also- not too convinced about the bracket initialisation for bools and other simple types. I understand the desire for consistency, but it's less readable and obvious than the typical "= false". The concept of default initialisation is just language lawyer rules for simple types as opposed to being directly defined within a visible constructor ala complex types, which feels like "consistency" is outweighing actual readability. |
|
It's not really all about consistency, it's the modern c++ recommendation. I would especially prefer it for member init since it enforces strict value typing. We can say to have explicit defaults, but we need fixed defaults, in the place the member is defined (also default empty constructors exist for complex types, we literally use that everywhere, poorly, wayoa???). I've had enough of unitialized value problems. |
I'm sorry, I don't understand. How does "bool m_x {}" enforce anything or resolve uninitialised values in any way that "bool m_x = false;" does not? As far as I can see, the former is exactly equivalent to the latter except for being implicit in relying on the spec to define the default value. Edit: I mean within this very PR's earlier comments there was already confusion about the behaviour here. Which further solidifies my opinion that explicit is better than implicit. |
|
|
|
That argument makes sense. Although, taken to its extreme, it would imply that usage of "bool b = false;" in ANY context would be inappropriate and incorrect; even on the stack. I guess this is really a case of the standard being too concerned with backwards compatibility to introduce fix things properly. Anyways, that's fair, I suppose (although surely setting a bool to 0.0 is identical to false?). But under the circumstances I would prefer explicit initialisation to false under this situation. I'd prefer that empty implicit initialisation is withheld for situations where it may communicate something else (i.e, this value is meant to be properly initialised later but we're default-initialising as a precaution). |
That is in fact the recommendation for modern c++ |
|
Using awkward syntax to work around the refusal of the standard committee to fix its broken standard syntax doesn't feel like a good solution. It seems very fragile to have code quietly break depending on the preferred syntactic style. Most other major codebases I've worked on enables compiler warnings-treated-as-errors for implicit or narrowing conversions- can we use that instead? Would require a few up-front fixes but would make things safe by default (including in non-initialisation contexts). |
|
Also- @Architector4
Can we fix these too? We're compiling Allegro from source as it has been modified. |
Oh, wasn't aware. It was causing undefined behavior due to getting casted to int. I guess I should instead just add an explicit check at that spot in code for ...Also, since we're on the topic of code style, do I use these C style |
Yeah, maybe sometime perhaps If I feel like it lmao |
Search for s_JumpHeight, that's where it's checked. I guess INT_MAX should work for the vertical/diagonal checks, but then any value should as it's a separate code path entirely. On the FLT_MAX, I don't care much as long as the identifiers are consistent. Personally I both love and hate std::numeric_limits. It's nice to have a "consistent" templated method, but it's also shit because it's not actually consistent and min() has the exact opposite behaviour for floats vs signed integers (which I've personally seen has caused bugs in the past). In any case I don't care all that much, as long as we choose one and stick to it. |
Worth noting that this is the same behavior for |
Wow. Apparently GitHub just lets me edit other people's comments. So apologies for that- meant to quote reply. Otherwise; yes, this is true. It's just that INT_MIN doesn't pretend to be consistent via templates, and you can also trivially go-to-definition to see what its value is (unlike with numeric limits). Though I'm not really making much argument one way or another moreso than just bitching about the standard. |

I ran the game with the undefined behavior sanitizer and played for an hour or two and found a few small undefined behavior things happening.
Some of them come from Allegro's
_parallelogram_mapfunction which apparently does a divide-by-zero and then multiplying withINT_MAX, and lot come from LuaJIT. A few came from the game code itself, which is what I patched up here.