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 * DECCRA not clamping or verifying the destination rectangle
([#2352][2352]). ([#2352][2352]).
* Empty selection clearing the clipboard ([#2327][2327]). * 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 [2353]: https://codeberg.org/dnkl/foot/issues/2353
[2352]: https://codeberg.org/dnkl/foot/issues/2352 [2352]: https://codeberg.org/dnkl/foot/issues/2352
[2327]: https://codeberg.org/dnkl/foot/issues/2327 [2327]: https://codeberg.org/dnkl/foot/issues/2327
[2360]: https://codeberg.org/dnkl/foot/issues/2360
### Security ### Security

39
csi.c
View file

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