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
Fix out of range enum casts in deSerialize functions #14090
Conversation
Where exactly is the undefined behaviour in using a C-style cast with enums? |
As far as I'm concerned C-style cast are bad practice in C++, so I replaced them on the go, but this has nothing to do with the undefined behavior. As I wrote in the first comment "makes sure the enums stay in scope (to avoid undefined behavior)", casting to them from value that isn't part of them is undefined behavior. https://en.cppreference.com/w/cpp/language/enum At some places this is already checked e.g. minetest/src/util/pointedthing.cpp Lines 95 to 108 in 689aaf5
|
I see. x = static_cast<EnumName>(readU8(is));
switch(x) {
case VALUE1:
// ...
default:
throw SomeException();
} already invokes UB in the first line and isn't safe either. Another point touched by this is what should MT actually do when it sees an unsupported mode used? |
I had not read the cpp reference carefully enough, and assumed that values are out of range if they are not represented by an enum constructor. This is however not the case. To cite the relevant parts of cppreference:
Also:
(Highlights by me.) If I understand it correctly now, if an underlying type is specified ("the underlying type is fixed"), all values of the underlying type are valid enum values. Checking if the resulting enum value after cast is a known value probably still makes sense, because our code likely doesn't handle non-enum-constructor values well. |
Thank you for the clarification, so the casts are mostly fine, but values may still be invalid and this should be addressed.
|
OK, everywhere where SerializationError will not be caught (so basically everywhere) I used a warning, since we already have a version parameter in most deserializing code to break compatibility if necessary, and I guess there is no reason to now introduce future incompatibility if it can be avoided somehow. So my PR does not change any behavior besides
I will also update the PR description to avoid any confusion. |
Sorry for the late response.
(Rebase needed.) |
4409f50
to
7eb12bd
Compare
No problem, I also needed to rethink this a little bit.
OK, I remove the exceptions and defaulted the values.
Done, I guess warnings wouldn't be very helpful anyway, since we already have version fields for most serialized things, which create warnings/errors in case of well behaved servers/clients.
The only reason for this is to get rid a compiler warning, the case will never be executed. At first I just wanted to remove some possible undefined behavior with this PR, but now it also changes some actual behavior, it defaults enum values which are not supported. (as Desour suggested) It's also rebased now, and ready for review again. |
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.
Fine otherwise. 👍
(Haven't checked if there are other occurrences of unchecked integer to enum casts.)
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.
LGTM.
Sends warnings if the received enum values are not supported. (I'm not sure how necessary this is so please tell me if they should be removed.)Note:
This undefined behavior could have been found with sanitizers. E.g. using the
-fsanitize=enum
flag.Does it resolve any reported issue?
I don't think so.
Does this relate to a goal in the roadmap?
Probably 2.2 Internal code refactoring
To do
This PR is Ready for Review.
How to test
-fsanitize=undefined