Skip to content

Buffer overflow in handle_input() — src/tty_interface.c line 334 #206

Description

@ActionPace

Buffer overflow in handle_input() — src/tty_interface.c line 334

Summary: strcat(state->input, s) in handle_input() appends terminal input to a fixed 32-byte buffer without bounds checking. Accumulated input that exceeds 32 bytes overflows into the adjacent exit field in tty_interface_t, causing forced silent termination. The struct is stack-allocated, so further overflow reaches the stack frame.


Location

src/tty_interface.c, function handle_input(), line 334:

strcat(state->input, s);

Root cause

state->input is declared as char input[32] in tty_interface_t (tty_interface.h line 24). handle_input() accumulates terminal input across calls — appending each chunk until a complete keybinding is matched, then resetting with strcpy(input, ""). There is no check that the accumulated content fits within the 32-byte buffer before strcat is called.

Struct layout

typedef struct {
    tty_t     *tty;
    choices_t *choices;
    options_t *options;
    char       search[SEARCH_SIZE_MAX + 1];
    char       last_search[SEARCH_SIZE_MAX + 1];
    size_t     cursor;
    int        ambiguous_key_pending;
    char       input[32];      /* ← overflow origin */
    int        exit;           /* ← first field overwritten */
} tty_interface_t;

The struct is stack-allocated in fzy.c line 63: tty_interface_t tty_interface;

Impact

Confirmed: Overflow into the adjacent exit int field causes tty_interface_run() to terminate its loop — forced silent exit of fzy. A shell pipeline using fzy breaks silently when this is triggered.

Plausible: The struct is stack-allocated. Overflow past exit reaches the stack frame. Exploitability depends on build mitigations.

Exploitation requirement: Attacker must supply terminal input to fzy — requires local access or control of a terminal session running fzy.

Suggested fix

Replace strcat with a bounds-checked equivalent:

/* Before */
strcat(state->input, s);

/* After */
strncat(state->input, s, sizeof(state->input) - strlen(state->input) - 1);

A belt-and-suspenders version that makes the constraint explicit:

size_t remaining = sizeof(state->input) - strlen(state->input) - 1;
if (remaining > 0) {
    strncat(state->input, s, remaining);
}

How this was found

This finding was produced by an ensemble divergence code scanner applying the Intent Scanner (does/says gap analysis) to fzy's source. The finding was Haiku-exclusive on the cold run — not confirmed by Sonnet — and has been manually verified against the source. The struct layout, buffer size, and stack allocation were confirmed by direct inspection.

Happy to provide additional detail or test a patch. Thank you for maintaining fzy.

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