Skip to content

[Bug]: 终端设备查询响应泄漏进 stdin,破坏输入行(乱码注入) #434

Description

@jexShain

Summary

终端设备查询响应(CPR / DA / DSR)泄漏进 stdin,被 rustyline 当作按键事件读入,导致输入行被注入乱码(如 0;115;0cRRR),破坏用户输入。

Use case

当 aish 转发 PTY 子进程输出(如远程 ssh/telnet 会话的 prompt tool、tmux/screen 初始化、man/less 等)到真实终端时,子进程发出的设备查询 请求(CPR ESC[6n、DA2 ESC[>c、DA1 ESC[c、DA3 ESC[=c、DSR ESC[5n)被原样转发到真实终端。真实终端响应这些查询,响应字节(ESC[2;2RESC[>0;115;0cESC[?64;1c)进入 stdin 队列。

此时 shell 处于 cooked(canonical + ECHO)模式(两次 readline 之间),rustyline 把这些响应字节当作按键事件读取,表现:

  • 输入行被插入乱码片段(如 0;115;0cRRR 拼接到正在输入的命令里)
  • 下一个 prompt 的输入被破坏,用户不得不手动清除
  • aish 自身调用 cursor::position()ESC[6n)的 CPR 响应也会泄漏,造成同样的污染

Proposed solution

双层防御,互为兜底:

  1. 输出端aish-ptywrite_stdout_all):剥离转发到真实终端的设备查询 请求 序列(CPR / DA1 / DA2 / DA3 / DSR 请求),从源头阻止真实终端被诱导响应。真正的 TUI / 远程程序(vim / less / ssh / tmux …,经 is_interactive_command / is_session_command 白名单判定)走 passthrough 透传,保留其查询能力。passthrough 标志用引用计数(AtomicUsize)而非 bool,使嵌套 / 并发命令正确(外层 guard 直到所有内层 guard 退出才关闭)。

  2. 输入端aish-shelldrain_terminal_responses):每次 readline 前,以非阻塞 poll 探测 stdin;若有残留,进 raw 模式读取,丢弃前导完整 report 序列(ESC[…R / ESC[?…c / ESC[>…c / ESC[=…c / DSR ESC[…n / text-area ESC[…t),剩余字节(真实用户 type-ahead)用 TIOCSTI 重新注入以保留。当 TIOCSTI 被内核禁用(dev.tty.legacy_tiocsti=0)导致非 report 字节无法放回时,记 warn 日志(含 tail_len / reinjected 字段)便于诊断。

Alternatives considered

  • 仅输出端剥离:覆盖大部分场景,但 aish 自身 cursor::position() 的 CPR 响应仍会泄漏,且无法处理查询请求跨 8 KB read 边界拆分时的漏剥离,需输入端兜底。
  • 仅输入端 drain:可行,但每次 readline 都进 raw 模式探测开销较大,且无法阻止真实终端被诱导响应(终端可能闪烁 / 刷新)。
  • 当前双层方案:源头治理 + 兜底清理,开销低(无 ESC 时 fast-path 零分配;无 pending 时单次 poll(fd,0) 立即返回,无 prompt 延迟)。

Additional information

  • strip_device_queries 精确区分 请求响应:请求侧 private marker 只认 > / =(不认 ?),避开误删 DA1 响应 ESC[?64;1c;响应因参数带 ; 自然落出 digit 扫描而被保留。
  • 测试:strip_device_queries 7 个单测、report_prefix_len 8 个单测;cargo clippy --all-targets -- -D warnings 零警告。
  • TIOCSTI 在 Linux 6.2+ 部分发行版默认禁用;该场景下纯 report 清理不受影响(无需 reinject),仅「report + 用户输入混合」时丢失用户输入并记日志——触发条件苛刻(prompt 显示前用户基本不会在打字)。

Metadata

Metadata

Assignees

No one assigned

    Labels

    observabilityLogging or observability issueptyPTY or interactive process issuetoolsTool integration issue

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions