Skip to content

fix(driver/tty): 修复 TTY 读取时的缓冲区偏移错误和规范模式语义问题 #1707

Merged
fslongjin merged 2 commits intoDragonOS-Community:masterfrom
xboHodx:fix/tty
Jan 27, 2026
Merged

fix(driver/tty): 修复 TTY 读取时的缓冲区偏移错误和规范模式语义问题 #1707
fslongjin merged 2 commits intoDragonOS-Community:masterfrom
xboHodx:fix/tty

Conversation

@xboHodx
Copy link
Copy Markdown
Contributor

@xboHodx xboHodx commented Jan 26, 2026

Fix(driver/tty): 修复 TTY 读取时的缓冲区偏移错误和规范模式语义问题

概述

修复 TtyDevice::read_at 中的两个关键 bug,这些 bug 导致在输入密码时触发 kernel panic("range end index out of range")。


问题描述

Bug 1: TtyDevice::read_at 传递错误的缓冲区切片

位置: kernel/src/driver/tty/tty_device.rs

错误代码:

size = ld.read(tty.clone(), buf, size, &mut cookie, offset, flags)?;

问题: 传递整个 buf 给 NTTY,但 offset 参数被累加。这导致:

次数 offset NTTY 写入位置 返回值 访问位置 结果
1 0 buf[0] 1 buf[0] ✅ 正常
2 1 buf[0] 2 buf[2] ❌ 越界
3 2 buf[0] 3 buf[4] ❌ 越界

正确代码:

size = ld.read(tty.clone(), &mut buf[offset..], size, &mut cookie, 0, flags)?;

Bug 2: canon_copy_from_read_buf 返回值语义错误

位置: kernel/src/driver/tty/tty_ldisc/ntty.rs

问题: canon_copy_from_read_buf 在请求满足时(nr=0)仍返回 true,导致 NTTY 继续读取并累加额外的 offset。

正确代码:

if *nr == 0 {
    Ok(false)
} else {
    Ok(self.read_tail != canon_head)
}

修复内容

文件 1: kernel/src/driver/tty/tty_device.rs

第 316 行:

- size = ld.read(tty.clone(), buf, size, &mut cookie, offset, flags)?;
+ size = ld.read(tty.clone(), &mut buf[offset..], size, &mut cookie, 0, flags)?;

说明:

  • 传递 &mut buf[offset..] 而非整个 buf
  • NTTY 的 offset 参数改为 0(因为切片已经偏移)

文件 2: kernel/src/driver/tty/tty_ldisc/ntty.rs

canon_copy_from_read_buf 方法:

- Ok(self.read_tail != canon_head)
+ if *nr == 0 {
+     Ok(false)
+ } else {
+     Ok(self.read_tail != canon_head)
+ }

说明:

  • nr=0(请求已满足)时返回 false,即使缓冲区还有更多数据
  • 符合 POSIX read() 系统调用语义

测试验证

修复前

$ busybox passwd
Changing password for root
New password: [KERNEL PANIC] range end index 2 out of range for slice of length 1

修复后

$ busybox passwd
Changing password for root
New password: [正常输入]
Retype password: [正常输入]
Password changed successfully

影响范围

场景 影响
密码输入(逐字节读取) ✅ 修复 panic
正常命令输入(大缓冲区) ✅ 无影响
TTY 规范模式 ✅ 修复
TTY 非规范模式 ✅ 无影响

技术分析

为什么密码输入触发 bug 而正常命令不触发?

特性 密码输入 正常命令
读取方式 多次小 read() 一次大 read()
缓冲区 1 字节 8KB+
offset 累加 明显 不触发

BorrowedCursor capacity 为 1 的原因

Rust 标准库 Buffer::read_more():

pub fn read_more(&mut self, mut reader: impl Read) -> io::Result<usize> {
    let mut buf = BorrowedBuf::from(&mut self.buf[self.filled..]);  // ← 关键
    // ...
}

filled = capacity - 1 时,切片长度为 1,导致 read(fd, ..., 1)


Review 检查清单

  • offset 重置为 0 是正确的(切片已处理偏移)
  • nr == 0 返回 false 符合 POSIX 语义
  • 不影响 TtyDevice::write_at
  • 不影响非 TTY 设备
  • 通过密码输入测试

@fslongjin fslongjin merged commit d3b796b into DragonOS-Community:master Jan 27, 2026
12 of 13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug fix A bug is fixed in this pull request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants