Skip to content

[Bug] [Smart][tty] tty_wait_background 未守卫 td->lwp==NULL,finsh 首次 read(stdin) page fault #11717

Description

@loongson-gz

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_backgroundpg = 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 ✓ 是 已触发,已修

其他架构没触发,主要是因为:

  1. 大多数 Smart BSP 还没把 finsh 切到 tty——它们要么没开 Smart,要么 finsh 还绑在 raw /dev/uart0 上,根本没走到 ttydisc_readtty_wait_background 这条路。

  2. 只有 finsh 真的去读 /dev/ttyS0、并且 tshell 是内核线程时,才会触发 pg = p->pgrp 的 NULL 解引用。LoongArch/LS2K300 是首批把这条路径走通的 BSP,所以第一个撞到。

根因:BSD tty 代码移植到 RT-Thread 时的遗漏

从代码角度看,tty_wait_backgroundtd->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_backgroundfp = 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 独有。其他架构没爆出来,主要是因为:

  1. 大多数 Smart BSP 还没把 finsh 切到 tty——它们要么没开 Smart,要么 finsh 还绑在 raw /dev/uart0 上,根本没走到 ttydisc_readtty_wait_background 这条路。

  2. 只有 finsh 真的去读 /dev/ttyS0、并且 tshell 是内核线程时,才会触发 pg = p->pgrp 的 NULL 解引用。LoongArch/LS2K300 是首批把这条路径走通的 BSP,所以第一个撞到。

  3. 从代码角度看,tty_wait_backgroundtd->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: 546bbac22c serial/tty: tty_wait_background 加 p==NULL 守卫,内核线程直接放行
  • 关键代码位置:
    • components/lwp/terminal/freebsd/tty.c:414tty_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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    BSPBSP: LoongsonBSP related with LoongsonRT-SmartRT-Thread Smart related PR or issuesbugThis PR/issue is a bug in the current code.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions