[Bug] [Smart][tty] tty_wait_background 未守卫 td->lwp==NULL,finsh 首次 read(stdin) page fault
まだ誰も着手していません。
評価
- 難易度
- 2/5
- 見積もり時間
- 1〜3時間
- 初心者へのやさしさ
- 72/100
- issue の種類
- バグ
- 明瞭さ
- 明確に書かれている
- 活発さ
- 静か
- 技術スタック
- c
調査の方向性
components/lwp/terminal/freebsd/tty.c:414 から始め、components/lwp/terminal/freebsd/tty_ttydisc.c:143 からの呼び出しを追跡してください。コミット 546bbac22c を確認し、LS2K300 Smart finsh の初回読み取りパスを検証してください。完了条件は、td->lwp が NULL の場合でも、/dev/ttyS0 経由の kernel thread からの読み取りで page fault が発生しなくなることです。
索引モデルが issue の本文から書いたものです。
説明
RT-Thread Version
master
Affected area
RT-Smart
Hardware/BSP vendor
Loongson
Architecture
Not applicable / Other
Board and hardware details
LS2K0300
Develop Toolchain
GCC
Describe the bug
问题现象
LoongArch/LS2K300 启用 RT-Thread Smart 模式后,finsh 启动到 msh 提示符,第一次按键就 page fault:
era = tty_wait_background
fp = NULL
shell 线程首次 read(stdin) 时,进入 tty_wait_background,执行到 pg = p->pgrp 这行,p 是 NULL,直接解引用 → page fault。
调用链
tty_wait_background 是从 ttydisc_read_*(tty_ttydisc.c)一路调上来的,完整调用链:
finsh tshell 线程
→ read(fd) / finsh 调 rt_posix_stdio_get_console() 拿 fd
→ ttydev_read (tty.c)
→ ttydisc_read (tty_ttydisc.c:340)
→ ttydisc_read_canonical / ttydisc_read_raw_*
→ tty_wait_background(tp, curthread, SIGTTIN)
→ p = td->lwp; // ← p == NULL
→ pg = p->pgrp; // ← 解引用 NULL,page fault
触发条件:两个条件同时成立
条件 1:finsh 的 tshell 线程走到 ttydev_read,即读到的是 tty 设备(/dev/ttyS0),而不是 raw 串口(/dev/uart0)。
条件 2:curthread->lwp 为 NULL,即调用者是内核线程而非 lwp 用户态进程。
LoongArch/LS2K300 撞上是因为它同时满足两个条件:Smart 模式下 finsh 切到 /dev/ttyS0(条件 1),finsh 的 tshell 是内核线程(条件 2),首次 read(stdin) 就进 tty_wait_background → pg = p->pgrp 解引用 NULL → page fault。
为什么其他架构没触发
| 架构组合 | 条件 1:finsh 读 tty? | 条件 2:tshell 是内核线程? | 触发? |
|---|---|---|---|
| 非 Smart + newlib(raspi/qemu-vexpress) | ✗ finsh 绑 raw /dev/uart0,不走 tty 层 |
✓ 是 | 不触发(条件 1 不成立) |
| Smart + musl(d1/k230/cv18xx) | ✓ finsh 切到 /dev/ttyS0 |
✓ 是 | 理论触发,但没爆出来 |
| Smart + bare-metal(imx6ull/raspi-dm2.0/rockchip) | 看是否切 tty | ✓ 是 | 同上 |
| LoongArch/LS2K300 Smart(已修) | ✓ 切到 /dev/ttyS0 |
✓ 是 | 已触发,已修 |
其他架构没触发,主要是因为:
-
大多数 Smart BSP 还没把 finsh 切到 tty——它们要么没开 Smart,要么 finsh 还绑在 raw
/dev/uart0上,根本没走到ttydisc_read→tty_wait_background这条路。 -
只有 finsh 真的去读
/dev/ttyS0、并且 tshell 是内核线程时,才会触发pg = p->pgrp的 NULL 解引用。LoongArch/LS2K300 是首批把这条路径走通的 BSP,所以第一个撞到。
根因:BSD tty 代码移植到 RT-Thread 时的遗漏
从代码角度看,tty_wait_background 对 td->lwp == NULL 的处理本来就应该有守卫——这是 BSD tty 代码移植到 RT-Thread 时的一个遗漏。
BSD 原版里 td->td_proc 不会为 NULL(FreeBSD 内核线程也有 proc),但 RT-Thread 的内核线程 lwp 字段是 NULL,所以这个移植性差异在"内核线程读 tty"这个场景下暴露了。
复现环境
- BSP:
bsp/loongarch/ls2k300_dev - 配置:
CONFIG_RT_USING_SMART=y+ musl 工具链 (loongarch64-unknown-linux-musl-) - 现象: finsh 启动到 msh 提示符后首次按键 page fault,
era = tty_wait_background,fp = NULL
修复方案
补丁在 tty_wait_background() 入口加了一个 NULL 守卫:
// components/lwp/terminal/freebsd/tty.c
int tty_wait_background(struct lwp_tty *tp, struct rt_thread *td, int sig)
{
struct rt_lwp *p;
...
p = td->lwp;
if (p == NULL) // ← 新增守卫
return 0; // ← 内核线程无 lwp,直接放行
for (;;)
{
pg = p->pgrp; // ← 原来这里解引用 NULL 就 page fault
...
}
}
内核线程无 lwp/进程组,本不参与终端前后台作业控制,所以直接返回 0 放行 read/write 是正确的语义。
关键结论
这个 NULL 守卫补丁修的是一个所有 Smart 架构(finsh 走 tty 路径 + tshell 是内核线程)共有的潜在 bug,不是 LoongArch 独有。其他架构没爆出来,主要是因为:
-
大多数 Smart BSP 还没把 finsh 切到 tty——它们要么没开 Smart,要么 finsh 还绑在 raw
/dev/uart0上,根本没走到ttydisc_read→tty_wait_background这条路。 -
只有 finsh 真的去读
/dev/ttyS0、并且 tshell 是内核线程时,才会触发pg = p->pgrp的 NULL 解引用。LoongArch/LS2K300 是首批把这条路径走通的 BSP,所以第一个撞到。 -
从代码角度看,
tty_wait_background对td->lwp == NULL的处理本来就应该有守卫——这是 BSD tty 代码移植到 RT-Thread 时的一个遗漏。BSD 原版里td->td_proc不会为 NULL(FreeBSD 内核线程也有 proc),但 RT-Thread 的内核线程lwp字段是 NULL,所以这个移植性差异在"内核线程读 tty"这个场景下暴露了。
一句话:这个补丁修的是"内核线程读 tty 时 NULL 解引用"的普遍性 bug,对其他 Smart 架构是预防性修复——只要它们哪天把 finsh 切到 /dev/ttyS0,就会触发同样的 page fault。补丁本身架构无关,加在公共 tty.c 里是正确的位置。
参考
- 修复 commit:
546bbac22cserial/tty: tty_wait_background 加 p==NULL 守卫,内核线程直接放行 - 关键代码位置:
components/lwp/terminal/freebsd/tty.c:414(tty_wait_background函数)components/lwp/terminal/freebsd/tty_ttydisc.c:143(调用tty_wait_background的位置)
- 详细分析文档:
bsp/loongarch/ls2k300_dev/docs-bak/tty_wait_background_NULL守卫分析.md
Other additional context
No response
- 主要言語
- C
- スター
- 12.2k
- フォーク
- 5.4k
- 平均マージ
- 5日 14時間
- マージ済み PR(30日)
- 48
コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
RT-Thread/rt-thread のほかの issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
-
Arch: RISC-V BSP BSP: HPMicro bug
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
-
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
-
Arch: RISC-V enhancement Kernel
難易度 5/5 1週間以上 初心者へのやさしさ 35/100
-
bug Component component: drivers
難易度 3/5 1〜2日 初心者へのやさしさ 72/100
RT-Thread/rt-thread の issue をすべて見る
似ている issue
-
task
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
vsanthanam/JBird#429 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
-
bug documentation
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
es-ude/OnDeviceTraining#459 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 65/100
bilelmoussaoui/gobject-linter#199 · コメント 1 件 ·
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
bradcypert/plum#53 ·