diff options
author | Kevin McCarthy <kevin@8t8.us> | 2017-04-27 21:22:08 -0700 |
---|---|---|
committer | Kevin McCarthy <kevin@8t8.us> | 2017-04-27 21:22:08 -0700 |
commit | 3fc8a24abfd9553f7ac2f37693079509947bc6e3 (patch) | |
tree | 5e01aebaad8959450c4aa2d392da7f21aefd23a1 /curs_lib.c | |
parent | 77c149f966340898b9d176ca441da5e51084413f (diff) |
Fix km_error_key() infinite loop and unget buffer pollution.
'bind pager \Ch help' produces an infinite loop when an unbound key is
pressed in the pager. The reason is because km_error_key() tries to
verify that the key sequence is really bound to the OP_HELP operation.
It does this by using km_expand_key(), tokenize_unget_string() on the
resulting buffer, then checking if the next km_dokey() returns OP_HELP.
The problem is that km_expand_key() does not always produce a string
that is properly reparsed by tokenize_unget_string(). Control-h
sequences are expanded to ^H. tokenize_unget_string() recognizes this
as two characters '^' and 'H'. km_error_key() checks the OP returned,
which is OP_PAGER_TOP for the '^'. This is not OP_HELP, so it prints
a generic error and returns. This leaves the 'H' in the input buffer!
Since 'H' (by default) is unbound in the pager, it retriggers
km_error_key(), resulting in an infinite loop.
The same issues can occur without control sequences:
bind generic ? noop
bind generic dq help
In the index, hitting an unbound key will end up leaving 'q' in the unget
buffer, because 'd' is bound in the index menu and will be read by km_dokey().
A simple approach to fix this would be to just use the same code as in
mutt_make_help(), which has no double-check. This would be no worse
than the help menu, but can generate an inaccurate error message (e.g
if '?' were bound to noop)
This patch instead uses OP_END_COND as a barrier in the unget buffer.
It directly inserts the keys in the OP_HELP keymap, instead of using
km_expand_key() + tokenize_unget_string(). After calling km_dokey()
it flushes the unget buffer to the OP_END_COND barrier.
Thanks to Walter Alejandro Iglesias for reporting the bug.
Diffstat (limited to 'curs_lib.c')
-rw-r--r-- | curs_lib.c | 12 |
1 files changed, 12 insertions, 0 deletions
@@ -826,6 +826,18 @@ void mutt_flush_macro_to_endcond (void) } } +/* Normally, OP_END_COND should only be in the MacroEvent buffer. + * km_error_key() (ab)uses OP_END_COND as a barrier in the unget + * buffer, and calls this function to flush. */ +void mutt_flush_unget_to_endcond (void) +{ + while (UngetCount > 0) + { + if (UngetKeyEvents[--UngetCount].op == OP_END_COND) + return; + } +} + void mutt_flushinp (void) { UngetCount = 0; |