mirror of
https://github.com/fish-shell/fish-shell.git
synced 2025-02-19 16:02:45 +08:00
Sanitize some inputs in CSI parser
This was copied from C++ code but we have overflow checks, which forces us to actually handle errors. While at it, add some basic error logging. Fixes #11092
This commit is contained in:
parent
2d234bb676
commit
4c28a7771e
|
@ -959,9 +959,13 @@ pub trait InputEventQueuer {
|
||||||
while count < 16 && c >= 0x30 && c <= 0x3f {
|
while count < 16 && c >= 0x30 && c <= 0x3f {
|
||||||
if c.is_ascii_digit() {
|
if c.is_ascii_digit() {
|
||||||
// Return None on invalid ascii numeric CSI parameter exceeding u32 bounds
|
// Return None on invalid ascii numeric CSI parameter exceeding u32 bounds
|
||||||
params[count][subcount] = params[count][subcount]
|
match params[count][subcount]
|
||||||
.checked_mul(10)
|
.checked_mul(10)
|
||||||
.and_then(|result| result.checked_add(u32::from(c - b'0')))?;
|
.and_then(|result| result.checked_add(u32::from(c - b'0')))
|
||||||
|
{
|
||||||
|
Some(c) => params[count][subcount] = c,
|
||||||
|
None => return invalid_sequence(buffer),
|
||||||
|
};
|
||||||
} else if c == b':' && subcount < 3 {
|
} else if c == b':' && subcount < 3 {
|
||||||
subcount += 1;
|
subcount += 1;
|
||||||
} else if c == b';' {
|
} else if c == b';' {
|
||||||
|
@ -1028,27 +1032,28 @@ pub trait InputEventQueuer {
|
||||||
if !sgr && c == b'm' {
|
if !sgr && c == b'm' {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let button = if sgr {
|
let Some(button) = (if sgr {
|
||||||
params[0][0]
|
Some(params[0][0])
|
||||||
} else {
|
} else {
|
||||||
u32::from(next_char(self)) - 32
|
u32::from(next_char(self)).checked_sub(32)
|
||||||
|
}) else {
|
||||||
|
return invalid_sequence(buffer);
|
||||||
};
|
};
|
||||||
let x = usize::try_from(
|
let mut convert = |param| {
|
||||||
if sgr {
|
(if sgr {
|
||||||
params[1][0]
|
Some(param)
|
||||||
} else {
|
} else {
|
||||||
u32::from(next_char(self)) - 32
|
u32::from(next_char(self)).checked_sub(32)
|
||||||
} - 1,
|
})
|
||||||
)
|
.and_then(|coord| coord.checked_sub(1))
|
||||||
.unwrap();
|
.and_then(|coord| usize::try_from(coord).ok())
|
||||||
let y = usize::try_from(
|
};
|
||||||
if sgr {
|
let Some(x) = convert(params[1][0]) else {
|
||||||
params[2][0]
|
return invalid_sequence(buffer);
|
||||||
} else {
|
};
|
||||||
u32::from(next_char(self)) - 32
|
let Some(y) = convert(params[2][0]) else {
|
||||||
} - 1,
|
return invalid_sequence(buffer);
|
||||||
)
|
};
|
||||||
.unwrap();
|
|
||||||
let position = ViewportPosition { x, y };
|
let position = ViewportPosition { x, y };
|
||||||
let modifiers = parse_mask((button >> 2) & 0x07);
|
let modifiers = parse_mask((button >> 2) & 0x07);
|
||||||
let code = button & 0x43;
|
let code = button & 0x43;
|
||||||
|
@ -1089,8 +1094,18 @@ pub trait InputEventQueuer {
|
||||||
b'P' => masked_key(function_key(1), None),
|
b'P' => masked_key(function_key(1), None),
|
||||||
b'Q' => masked_key(function_key(2), None),
|
b'Q' => masked_key(function_key(2), None),
|
||||||
b'R' => {
|
b'R' => {
|
||||||
let y = usize::try_from(params[0][0] - 1).unwrap();
|
let Some(y) = params[0][0]
|
||||||
let x = usize::try_from(params[1][0] - 1).unwrap();
|
.checked_sub(1)
|
||||||
|
.and_then(|y| usize::try_from(y).ok())
|
||||||
|
else {
|
||||||
|
return invalid_sequence(buffer);
|
||||||
|
};
|
||||||
|
let Some(x) = params[1][0]
|
||||||
|
.checked_sub(1)
|
||||||
|
.and_then(|x| usize::try_from(x).ok())
|
||||||
|
else {
|
||||||
|
return invalid_sequence(buffer);
|
||||||
|
};
|
||||||
FLOG!(reader, "Received cursor position report y:", y, "x:", x);
|
FLOG!(reader, "Received cursor position report y:", y, "x:", x);
|
||||||
let Some(BlockingWait::CursorPosition(wait)) = self.blocking_wait() else {
|
let Some(BlockingWait::CursorPosition(wait)) = self.blocking_wait() else {
|
||||||
CURSOR_POSITION_REPORTING_SUPPORTED.store(true);
|
CURSOR_POSITION_REPORTING_SUPPORTED.store(true);
|
||||||
|
@ -1573,6 +1588,29 @@ pub trait InputEventQueuer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn invalid_sequence(buffer: &[u8]) -> Option<Key> {
|
||||||
|
FLOG!(
|
||||||
|
reader,
|
||||||
|
"Error: invalid escape sequence: ",
|
||||||
|
DisplayBytes(buffer)
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DisplayBytes<'a>(&'a [u8]);
|
||||||
|
|
||||||
|
impl<'a> std::fmt::Display for DisplayBytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
for (i, &c) in self.0.iter().enumerate() {
|
||||||
|
if i != 0 {
|
||||||
|
write!(f, " ")?;
|
||||||
|
}
|
||||||
|
write!(f, "{}", char_to_symbol(char::from(c)))?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A simple, concrete implementation of InputEventQueuer.
|
/// A simple, concrete implementation of InputEventQueuer.
|
||||||
pub struct InputEventQueue {
|
pub struct InputEventQueue {
|
||||||
data: InputData,
|
data: InputData,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user