-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Copy channelhandle by value instead of pointers #4672
Conversation
hm ok I guess this doesn't work because now the struct is non-trivial:
it builds on my machine though. |
This failure was expected. There is a reason why pointers are used. |
the commit that made the change doesn't give a lot of hints: b2a28bd |
probably we used memset because: https://stackoverflow.com/questions/11812555/how-to-zero-initialize-an-union so therefore we just need to figure out which is the biggest struct item and zero that out. |
@@ -97,10 +95,10 @@ struct EffectsRequest { | |||
} RemoveEffectChain; | |||
struct { | |||
EffectStatesMapArray* pEffectStatesMapArray; | |||
const ChannelHandle* pChannelHandle; | |||
ChannelHandle channelHandle; |
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.
I was expecting that ChannelHandle with its custom constructor is not usable in this union. Removing the memset seems to work, though.
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.
yes. Unfortunately that means the data isn't guaranteed to be zeroed out. According to what I've read online, as long as we memset that largest possible struct in the union, that will zero out all the data. I believe that SetEffectChainParameters is the largest one since it has three items, one of which is a double.
Another option would be to remove the default constructor and force callers to construct messages with specific types in mind -- then we'd know for sure nobody's accessing uninitialized data
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.
That seems really brittle and easy to break when refactoring.
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.
agree -- for now we think it's just better to state that we don't guarantee initialization and callers are responsible for it. We can clean this up with the followup rewrite using variant or whatever
I believe as long as we call memset on the largest struct item, that will be sufficient for zeroing out the data |
I'm going to squash this down since there are some bad intermediate commits |
…ixes issues with pointers to deleted channelhandles being accessed, causing crashes, mostly with QT6. Converting to values means we can no longer trivially initialize the union/struct by memsetting all of the subtypes, because two of the struts are no longer trivial. Instead, we memset what we think is the largest datatype. SetEffectChainParameters includes a bool, an enum, and a double, so it's the largest one.
4def2d5
to
d3603b8
Compare
While this is already a good way forward which is already mergable, I have not yet understand how it avoids the bad_allic exception. Do we have an evidence that this fixes the issue? |
src/engine/effects/message.h
Outdated
#undef CLEAR_STRUCT | ||
// zero out the struct with the largest size to ensure the entire union memory is set to | ||
// zero. | ||
memset(&SetEffectChainParameters, 0, sizeof(SetEffectChainParameters)); |
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.
Can we name the anonymous union here and then clear the memory by sizeof(union Payload)?
All other ideas I have in mind are breaking during refectoring.
However, I actually think that a zeroed memory is in the same way dangerous as a ununutalized memory.
We must not use it in both cases. It looks like the rest of the control structures take already care of it. So if zeroing has an effect, it will only obfusticate an issue we have anyway.
Thats why I would prefere to not initialize the memory. It makes more sense to assert, the validity of the control structures and introduce constructors, that take care of them in one call and cannot fail. We should verify the code that the union is never accessed without checking the type before.
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.
no that's not how it works unfortunately. the union itself does not have a sizeof -- only the members therein.
If I rewrite the constructor to enforce a type being specified at construction time, will this PR get merged? I don't want to do any more work on this if it's going to be controversial -- not everyone agrees we should go with this fix. (@Be-ing ?)
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.
If we give the union a name, you can access the sizeof() it. But I still believether is no need to zero out the memory.
I also don't mind to commit that as an intermediate fix.
Yes. I traced the bad alloc to the following:
We don't know exactly why the channelhandle pointer has gone invalid, but I don't think we need to know that to fix the bug. After this change, even running the UnEjectTest many times does not crash, whereas it crashed about 50% of the time before |
How about just removing the initializing? |
I think you're right, zeroing out data won't help if we're not initializing correctly anyway so it doesn't really buy us anything. |
Add comment to make clear callers are responsible for initializing the values correctly. Double-checked call points to make sure all creations were correct. Removed unused struct parameter that I found during this process.
hah! found an unused parameter by checking the call sites. |
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.
Thank you. Merge?
I'd like to give @Be-ing a chance to chime in -- we plan to rewrite this subsystem so this is just a stop-gap fix |
Let's not let the perfect be the enemy of the good. Getting rid of the horribly unsafe union would be a good next step. The current situation is that we have a crash, so going from crash to no crash is good even if the code isn't perfect. |
Store channelhandles in messages by value instead of pointers. This fixes issues with pointers to deleted channelhandles being accessed, causing crashes, mostly with QT6.
Converting to values means we can no longer trivially initialize the union/struct by memsetting all of the subtypes, because two of the struts are no longer trivial.
Instead, we memset what we think is the largest datatype. SetEffectChainParameters includes a bool, an enum, and a double, so it's the largest one.