A text cursor in the tty; LDLS sets net_device.priv_len

The virtual terminals never drew a cursor. Each now paints a steady
block (the cell with its colours swapped), erased before and redrawn
after every output byte, redrawn by a repaint, and hidden by
ESC [ ? 25 l as curses programs ask.

r8169 under LDLS on the TUF A15 got through probe and died in ndo_open
on __fortify_panic. netdev_priv() is `dev->priv` now, a flexible array
counted by net_device.priv_len, and rtl_open's memset of its ring
bookkeeping is bounds-checked against it. The allocator left priv_len
zero, so the driver saw an area of no bytes. It is set from BTF when the
kernel has the member. __fortify_panic also says which check, how many
bytes it had and wanted, and where from.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015f73FesGZGYPkmDvUcj6QX
This commit is contained in:
Nexoniarz 2026-10-25 18:25:30 +01:00
commit 96389ada8f
7 changed files with 86 additions and 5 deletions

View file

@ -235,11 +235,46 @@ vt_current(void)
// Output
// ---------------------------------------------------------------------------
// The text cursor. See struct vt. Drawn as the cell with its colours swapped
// -- relative to what the cell would show, so a cursor on reverse-video text
// is the plain text and every cursor on screen is visible against its cell.
static void
cursor_erase(struct vt *v)
{
if (!v->curs_drawn)
return;
v->curs_drawn = 0;
if (v->curs_x < v->cols && v->curs_y < v->rows)
show(v, v->curs_x, v->curs_y);
}
static void
cursor_draw(struct vt *v)
{
if (v->curs_hidden || !is_active(v))
return;
// A glyph in the last column leaves the cursor *on* it with the wrap
// pending, and that is where it is drawn.
int col = v->cx >= v->cols ? v->cols - 1 : v->cx;
int row = v->cy >= v->rows ? v->rows - 1 : v->cy;
if (col < 0 || row < 0)
return;
struct vtcell *k = cell_at(v, col, row);
if (k->attr & VT_ATTR_REVERSE)
gfx_draw_cell(col, row, k->ch, vt_rgb(k->fg), vt_rgb(k->bg));
else
gfx_draw_cell(col, row, k->ch, vt_rgb(k->bg), vt_rgb(k->fg));
v->curs_x = col;
v->curs_y = row;
v->curs_drawn = 1;
}
void
vt_repaint(struct vt *v)
{
if (!is_active(v))
return;
v->curs_drawn = 0; // the repaint below paints over it
// The margins first: the screen is rarely a whole number of cells, and the
// strip right of the last column and below the last row belongs to nobody.
@ -263,6 +298,7 @@ vt_repaint(struct vt *v)
else
gfx_draw_cell(c, r, k->ch, vt_rgb(k->fg), vt_rgb(k->bg));
}
cursor_draw(v);
}
void
@ -906,7 +942,7 @@ vt_feed_1(struct vt *v, int c)
switch (v->esc_state) {
case VE_ESC:
if (c == '[') { v->esc_state = VE_CSI;
v->esc_nparm = 0; v->esc_private = 0;
v->esc_nparm = 0; v->esc_private = 0; v->esc_qmark = 0;
for (int i = 0; i < VT_MAXPARM; i++) v->esc_parm[i] = 0;
return; }
if (c == ']') { v->esc_state = VE_OSC; return; }
@ -962,7 +998,11 @@ vt_feed_1(struct vt *v, int c)
// this terminal does not speak, but must still consume to the final byte.
// Ending at the marker instead printed the rest -- tmux asking for
// extended keys as ESC [ > 4 ; 1 m left "4;1m" on the screen.
if (c >= '<' && c <= '?') { v->esc_private = 1; return; }
if (c >= '<' && c <= '?') {
v->esc_private = 1;
if (c == '?') v->esc_qmark = 1;
return;
}
if (c >= 0x20 && c <= 0x2F) { v->esc_private = 1; return; }
if (c >= '0' && c <= '9') {
if (v->esc_nparm == 0) v->esc_nparm = 1;
@ -975,8 +1015,15 @@ vt_feed_1(struct vt *v, int c)
return;
}
v->esc_state = VE_NORMAL;
if (!v->esc_private)
if (!v->esc_private) {
vt_csi(v, c);
} else if (v->esc_qmark && (c == 'h' || c == 'l')) {
// DECSET/DECRST. Only the cursor's visibility is ours; the rest of
// ?1049 and its kin are still ignored, as before.
for (int i = 0; i < v->esc_nparm && i < VT_MAXPARM; i++)
if (v->esc_parm[i] == 25)
v->curs_hidden = (c == 'l');
}
return;
}
@ -1194,5 +1241,7 @@ vt_cycle(int dir)
void
vt_feed(struct vt *v, int c)
{
cursor_erase(v);
vt_feed_1(v, c);
cursor_draw(v);
}