Skip to content

Segfault in selection_extract when dragging selected text to another window #2

Description

@blshkv

Summary

Selecting text in glass and dragging it to another window (which triggers a SelectionRequest) causes a segfault.

Steps to reproduce

  1. Open glass with any shell.
  2. Select any text with the mouse.
  3. Drag / paste it into another window.

Crash details

Segmentation fault    glass
glass[341666]: segfault at 21b7004 ip 00000000004090ae sp 00007ffe7eb5a310 error 4 in glass[90ae,401000+12000]
Code: 00 00 48 6b c0 10 48 8d 98 20 cd a4 01 4d 39 f5 0f 8d 54 01 00 00 49 81 ff fc 3f 00
      00 0f 8d 8e 01 00 00 4c 89 ea 48 6b d2 10 <f6> 44 13 04 08 74 18 0f b7 4c 13 06 81
      f9 00 04 00 00 73 0b 8b 04

error 4 = page-not-present user-mode read. The faulting address 0x21b7004 is past the end of the grid buffer.

Decoded instruction sequence at crash site

imul rax, rax, 16              ; rax = row_index * 16  (actually row * MAX_COLS*CELL_SIZE computed earlier)
lea  rbx, [rax + 0x1a4cd20]   ; rbx = &grid[row]
cmp  r13, r14                  ; bounds check: col vs grid_cols  ✓
jge  .skip
cmp  r15, 0x3ffc               ; bounds check: sel_buf output index vs 16380  ✓
jge  .done
mov  rdx, r13
imul rdx, rdx, 16              ; rdx = col * CELL_SIZE
test byte [rbx + rdx + 0x4], 0x08   ; ← SEGFAULT: rbx is past grid end

Root cause

selection_extract (line ~11533) explicitly allows sel_start_row/sel_end_row to exceed MAX_ROWS - 1 (127) during scrollback selection (see comment at line ~11440):

"Let sel_start_row grow past grid_rows-1; selection_extract's .se_row_grid_shifted path maps r12 → live[r12 - scroll_offset] which stays in bounds because we increment scroll_offset and sel_start_row in lockstep."

The invariant holds while scroll_offset is still set. But when the user drags the selection to another window, the SelectionRequest arrives after the button release. By that point scroll_offset may have been reset to 0 (e.g. by snapping back to the bottom of the viewport). The .se_row_live path is then taken:

.se_row_live:
    mov rax, r12
    imul rax, MAX_COLS
    imul rax, CELL_SIZE
    lea rbx, [grid + rax]      ; r12 >= MAX_ROWS → out of bounds

The two row-bounds checks in the extracted loop (cmp r12, [sel_end_row] and cmp r15, 0x3ffc) guard against overrunning sel_buf, but neither prevents computing an rbx that already points outside grid.

Suggested fix

In selection_extract, after resolving rbx via .se_row_live, add a hard clamp:

.se_row_live:
    mov rax, r12
    cmp rax, MAX_ROWS          ; clamp row to valid grid range
    jge .se_done
    imul rax, MAX_COLS
    imul rax, CELL_SIZE
    lea rbx, [grid + rax]

Similarly for .se_row_grid_shifted, verify r12 - scroll_offset is in [0, MAX_ROWS) before indexing.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions