summaryrefslogtreecommitdiffstats
path: root/curs_lib.c
diff options
context:
space:
mode:
authorKevin McCarthy <kevin@8t8.us>2017-04-27 21:22:08 -0700
committerKevin McCarthy <kevin@8t8.us>2017-04-27 21:22:08 -0700
commit3fc8a24abfd9553f7ac2f37693079509947bc6e3 (patch)
tree5e01aebaad8959450c4aa2d392da7f21aefd23a1 /curs_lib.c
parent77c149f966340898b9d176ca441da5e51084413f (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.c12
1 files changed, 12 insertions, 0 deletions
diff --git a/curs_lib.c b/curs_lib.c
index c4c42b42..9d108755 100644
--- a/curs_lib.c
+++ b/curs_lib.c
@@ -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;