fix escape code leak

This commit is contained in:
2026-04-11 00:39:21 +02:00
parent 350be6611c
commit d2939de936
4 changed files with 147 additions and 16 deletions
+66 -16
View File
@@ -482,7 +482,9 @@ impl SharedParser {
// Like Kitty line 1516: consume_input(self, ...)
let made_progress = self.consume_input(handler);
parsed_any = true;
if made_progress {
parsed_any = true;
}
// Re-acquire lock
state = self.state.lock().unwrap();
@@ -635,14 +637,14 @@ impl SharedParser {
true
}
State::Csi => {
// Like Kitty lines 1465-1466:
// if (consume_csi(self)) { self->read.consumed = self->read.pos; if (self->csi.is_valid) dispatch_csi(self); SET_STATE(NORMAL); }
let state_before = *vte_state;
if Self::consume_csi_impl(
handler,
buf,
parse_pos,
parse_sz,
*parse_consumed,
parse_consumed,
vte_state,
csi,
escape_len,
) {
@@ -650,30 +652,41 @@ impl SharedParser {
if csi.is_valid {
handler.csi(csi);
}
*vte_state = State::Normal;
if *vte_state == state_before {
*vte_state = State::Normal;
}
true
} else {
false
*vte_state != state_before
}
}
State::Osc => {
let state_before = *vte_state;
if Self::consume_osc_impl(
handler, buf, parse_pos, parse_sz, vte_state, osc_buffer,
handler,
buf,
parse_pos,
parse_sz,
parse_consumed,
vte_state,
osc_buffer,
escape_len,
) {
*parse_consumed = *parse_pos;
*vte_state = State::Normal;
true
} else {
false
*vte_state != state_before
}
}
State::Dcs | State::Apc | State::Pm | State::Sos => {
let state_before = *vte_state;
if Self::consume_string_impl(
handler,
buf,
parse_pos,
parse_sz,
parse_consumed,
vte_state,
string_buffer,
escape_len,
@@ -682,7 +695,7 @@ impl SharedParser {
*vte_state = State::Normal;
true
} else {
false
*vte_state != state_before
}
}
};
@@ -837,6 +850,11 @@ impl SharedParser {
b'\\' => {
*vte_state = State::Normal;
}
0x1B => {
// ESC followed by ESC. Start new escape sequence.
*vte_state = State::Escape;
return true;
}
_ => {
log::debug!("Unknown escape sequence: ESC {:02x}", ch);
*vte_state = State::Normal;
@@ -913,7 +931,8 @@ impl SharedParser {
buf: &[u8; BUF_SIZE],
parse_pos: &mut usize,
parse_sz: usize,
parse_consumed: usize,
parse_consumed: &mut usize,
vte_state: &mut State,
csi: &mut CsiParams,
escape_len: &mut usize,
) -> bool {
@@ -922,8 +941,15 @@ impl SharedParser {
*parse_pos += 1;
*escape_len += 1;
if ch == 0x1B {
*vte_state = State::Escape;
*parse_consumed = *parse_pos;
*escape_len = 0;
return false; // Aborted by new ESC
}
// Handle embedded control characters
if ch <= 0x1F && ch != 0x1B {
if ch <= 0x1F {
handler.control(ch);
continue;
}
@@ -1023,7 +1049,7 @@ impl SharedParser {
}
// Check max length
if *parse_pos - parse_consumed > MAX_ESCAPE_LEN {
if *parse_pos - *parse_consumed > MAX_ESCAPE_LEN {
log::debug!("CSI escape too long, ignoring");
return true;
}
@@ -1037,6 +1063,7 @@ impl SharedParser {
buf: &[u8; BUF_SIZE],
parse_pos: &mut usize,
parse_sz: usize,
parse_consumed: &mut usize,
vte_state: &mut State,
osc_buffer: &mut Vec<u8>,
escape_len: &mut usize,
@@ -1069,6 +1096,7 @@ impl SharedParser {
*parse_pos += 1;
handler.osc(osc_buffer);
*vte_state = State::Escape;
*parse_consumed = *parse_pos;
*escape_len = 0;
return false;
} else {
@@ -1098,6 +1126,7 @@ impl SharedParser {
buf: &[u8; BUF_SIZE],
parse_pos: &mut usize,
parse_sz: usize,
parse_consumed: &mut usize,
vte_state: &mut State,
string_buffer: &mut Vec<u8>,
escape_len: &mut usize,
@@ -1128,10 +1157,17 @@ impl SharedParser {
);
return true;
} else if *parse_pos + 1 < parse_sz {
// ESC not followed by \ - include in buffer
string_buffer.push(ch);
// ESC not followed by \ - abort string, start new escape
*parse_pos += 1;
*escape_len += 1;
Self::dispatch_string_command(
handler,
vte_state,
string_buffer,
);
*vte_state = State::Escape;
*parse_consumed = *parse_pos;
*escape_len = 0;
return false;
} else {
// ESC at end of buffer - need more data
return false;
@@ -1368,6 +1404,11 @@ impl Parser {
self.state = State::Normal;
1
}
0x1B => {
// ESC followed by ESC. Start new escape sequence.
self.state = State::Escape;
1
}
_ => {
// Unknown escape sequence, ignore and return to normal
log::debug!("Unknown escape sequence: ESC {:02x}", ch);
@@ -1442,8 +1483,17 @@ impl Parser {
return consumed;
}
if ch == 0x1B {
self.state = State::Escape;
self.escape_len = 0;
// Wait! If it's ESC, we consumed it. But the next loop in parse() will call consume_escape,
// which EXPECTS the char AFTER ESC.
// So consuming it is PERFECT!
return consumed;
}
// Handle control characters embedded in CSI (common to all states)
if ch <= 0x1F && ch != 0x1B {
if ch <= 0x1F {
handler.control(ch);
continue;
}