fix: prevent cartridges hijacking keyboard events #1787
Merged
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Resolves #1785.
This fix captures the current (
input.ram.keyboard
) state intick_start
and immediately restores it intick_end
. Thisprevents cartridges from injecting corrupt "previous" states
into the keyboard state engine that could be used to hijack
keyboard input.
When we trust
input.ram.keyboard
after user code has runwe open ourselves up to a cartridge:
(which is still held by the user, then registering as
separate repeating keypress/releases until the user
finally releases the key)
(this prevents the state engine from ever seeing the
key release).
Originally this was a lot larger, but I figured it was going to be too hard to review and certify with just a glance. This takes your "don't let someone modify KEYBOARD" idea and simplifies it to preserving rather than protecting. We preserve the "now" state during a tick and then restore it immediately afterwards, vs trusting whatever (potentially incorrect) state could be present in the RAM.
Since the real problem is making sure the integrity of KEYBOARD state is guaranteed for "system" bits, this does so by making sure that immediate after
pre-tick
the "now" state is restored, guaranteeing that:I hope this is clear. If you have any questions, ask.
I'm still open to doing a larger refactor here, but I thought this was pretty clean. The only gotcha is that code like
handleShortcuts()
must be run before the user code, and I've added a comment to that effect: