csi: refactor CHT/CBT

Eliminate the outer loop, so that when moving the cursor multiple
tab stops, we no longer iterate the tab stop list all over again.

This also fixes a DoS flaw when passing a very large value as
CHT/CBT argument, which may hang the terminal.

Closes #2360
This commit is contained in:
CismonX 2026-05-25 06:31:04 +08:00
parent 4bf60d0fbc
commit 814b7e1a48
No known key found for this signature in database
GPG key ID: 250FCE3001350BCA
2 changed files with 24 additions and 18 deletions

View file

@ -82,10 +82,13 @@
* DECCRA not clamping or verifying the destination rectangle
([#2352][2352]).
* Empty selection clearing the clipboard ([#2327][2327]).
* Passing a very large value as CHT/CBT argument hangs the terminal
([#2360][2360]).
[2353]: https://codeberg.org/dnkl/foot/issues/2353
[2352]: https://codeberg.org/dnkl/foot/issues/2352
[2327]: https://codeberg.org/dnkl/foot/issues/2327
[2360]: https://codeberg.org/dnkl/foot/issues/2360
### Security

39
csi.c
View file

@ -1171,37 +1171,40 @@ csi_dispatch(struct terminal *term, uint8_t final)
case 'I': {
/* CHT - Tab Forward (param is number of tab stops to move through) */
for (int i = 0; i < vt_param_get(term, 0, 1); i++) {
int new_col = term->cols - 1;
tll_foreach(term->tab_stops, it) {
if (it->item > term->grid->cursor.point.col) {
new_col = it->item;
int count = vt_param_get(term, 0, 1);
int new_col = term->grid->cursor.point.col;
tll_foreach(term->tab_stops, it) {
if (it->item > new_col) {
if (--count < 0) {
break;
}
new_col = it->item;
}
xassert(new_col >= term->grid->cursor.point.col);
bool lcf = term->grid->cursor.lcf;
term_cursor_right(term, new_col - term->grid->cursor.point.col);
term->grid->cursor.lcf = lcf;
}
xassert(new_col >= term->grid->cursor.point.col);
bool lcf = term->grid->cursor.lcf;
term_cursor_right(term, new_col - term->grid->cursor.point.col);
term->grid->cursor.lcf = lcf;
break;
}
case 'Z':
case 'Z': {
/* CBT - Back tab (param is number of tab stops to move back through) */
for (int i = 0; i < vt_param_get(term, 0, 1); i++) {
int new_col = 0;
tll_rforeach(term->tab_stops, it) {
if (it->item < term->grid->cursor.point.col) {
new_col = it->item;
int count = vt_param_get(term, 0, 1);
int new_col = term->grid->cursor.point.col;
tll_rforeach(term->tab_stops, it) {
if (it->item < new_col) {
if (--count < 0) {
break;
}
new_col = it->item;
}
xassert(term->grid->cursor.point.col >= new_col);
term_cursor_left(term, term->grid->cursor.point.col - new_col);
}
xassert(term->grid->cursor.point.col >= new_col);
term_cursor_left(term, term->grid->cursor.point.col - new_col);
break;
}
case 'h':
case 'l': {