-
Notifications
You must be signed in to change notification settings - Fork 323
FIX: Reenabling the VirtualMouseInput component may lead to NullReferenceException #2175
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: develop
Are you sure you want to change the base?
Conversation
@@ -872,7 +872,8 @@ public void ResetActionState(int actionIndex, InputActionPhase toPhase = InputAc | |||
// Wipe state. | |||
actionState->phase = toPhase; | |||
actionState->controlIndex = kInvalidIndex; | |||
actionState->bindingIndex = memory.actionBindingIndices[memory.actionBindingIndicesAndCounts[actionIndex]]; | |||
var idx = memory.actionBindingIndicesAndCounts[actionIndex]; | |||
actionState->bindingIndex = memory.actionBindingIndices != null ? memory.actionBindingIndices[idx] : 0; |
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.
Would it even be smart to set bindingIndex to -1 to indicate it's not a valid index?
It would allow doing (actionState->bindingIndex >= 0) as an indirect null-check, but if other indices are zero it makes sense to keep it at zero for consistency
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.
This is a very good question to be honest. I'm surprised it's not been set to -1 before. I should see if that's safe to change in order not to break some stuff somewhere, this code is kinda interconnected quite a bit.
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.
Awesome, it was just a thought and not something I consider a must-have if it adds complexity
1e134e8
to
b893d99
Compare
Ok we have some failing tests apparently - Actions_CanAddCompositeBindingsToActions_AfterActionHasAlreadyResolvedControls and Actions_WhenShortcutsEnabled_CanConsumeInput, maybe a few others as well. Investigating. |
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.
Looks like you will consider the index (not set value), but changes LGTM to me already now so approving.
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.
Just saw the failing tests so updating this to "Request Changes" since clearly more work is needed on figuring out why
Description
Fixed an exception that happens when you toggle enable/disable a few times on the VirtualMouseInput component.
This exception is occasionally raised from within
InputActionState.ApplyProcessors
as it tries to read from the processors array that is null in this case. By the normal flow of things, we shouldn't have tried to index into a null array since just a few lines above there is a check that ensures there are more than zero processors before doing this. However, when no bindings are present ,processorCount
will actually be a random value. Let me explain how.So in
InputActionState.ApplyProcessors
, we readprocessorCount
from thebindingStates
array usingbindingIndex
. However, when there are no bindings present,bindingIndex
has a chance to be a fairly large random value if you disable and enable theVirtualMouseInput
component. Note thatbindingStates
is an unsafe array so nothing prevents from reading out of bounds. How can thisbindingIndex
go wrong?Moving a bit up the call stack, in
InputAction.ReadValue
we see thatbindingIndex
is taken fromInputActionState.TriggerState
's bindingIndex field.This field is normally correct, however when we disable the
VirtualMouseInput
component there is some interesting logic that tries to restore the binding index to what is stored in the corresponding arrays. It is part ofInputActionState.ResetActionState
method, and the problem here arises frommemory.actionBindingIndices
being an invalid pointer when no bindings are present. This is an unsafe pointer, and it is not null in case the array contains no elements by design of theUnmanagedMemory
class. Reading from this pointer will get random data that is used after you Enable theVirtualMouseInput
component.Let me pull up some code from
UnmanagedMemory
that I find a bit problematic at the moment:So essentially this thing makes one allocation to host all the smaller bits of data including the aggregated arrays. However, the problem is that if some array has zero length, we don't set its pointer to null. Instead, we just shift the current pointer in the blob by how much memory we needed for the previous array. Because of that, some array pointers can be equal (imagine a few empty arrays going in a sequence). Also, especially problematic are the last pointers in the blob -- because if they are zero sized, then they are set to invalid pointer, pointing to the first byte right after the blob. That's precisely the reason why we had random bindingIndex - note how
actionBindingindices
will be set to point to the first byte after the blob in case bindingCount is zero.I believe there are a few ways we could attack this problem.
One, that would aim for minimal footprint is to change just one line in
InputActionState.ApplyProcessors
to also include a check for an empty processors array, like this:However, in my mind that is slightly unfortunate because it leaves the core issue of having bad bindingIndices around, and it also still leaves us with invalid pointers being around. It's only until next time someone doesn't check for an empty array in the right way. When debugging, it's somewhat uneasy to understand the pointers that look somewhat reasonable are actually pointers to unallocated space.
Because of that, I thought to propose a change that assigns null to any pointers in the
UnmanagedMemory
class that reflect zero-sized arrays. That way, if someone tries to access them, we get an instant exception that is easy to diagnose. Please advise what you think.Testing status & QA
Local testing
Overall Product Risks
Comments to reviewers
Please describe any additional information such as what to focus on, or historical info for the reviewers.
Checklist
Before review:
Changed
,Fixed
,Added
sections.Area_CanDoX
,Area_CanDoX_EvenIfYIsTheCase
,Area_WhenIDoX_AndYHappens_ThisIsTheResult
.During merge:
NEW: ___
.FIX: ___
.DOCS: ___
.CHANGE: ___
.RELEASE: 1.1.0-preview.3
.After merge: