Repository navigation
Conversation
dfs_cromfs_getdents() used the directory entry's own name_size field from the image as the length of a memcpy into d_name. It never compared that value against DIRENT_NAME_MAX and never checked that the name lies inside the directory blob it had just loaded. d_name is DIRENT_NAME_MAX (256) bytes, and the line right after the copy stores the terminating NUL at index name_size, so even the original code already wrote one byte past the array whenever name_size reached 256. name_size is a uint16 taken straight from the image, so it can be 65535. Callers do not clip the result. msh's ls() keeps a single struct dirent on the shell thread stack and asks getdents for sizeof(struct dirent), so a crafted entry overflows that stack object with image-controlled bytes. The reported crash ends with the saved frame pointer holding 0x42424242 and the return from ls() transferring control to an attacker-chosen address. The v1 and v2 copies are affected in the same way and both are fixed. An entry whose name_size does not fit in d_name, or whose name does not fit in the loaded blob, now makes getdents return -EINVAL instead of copying. The entry header itself is bounds-checked against the blob as well, so the sub_dirent index cannot run past the buffer either. Refs RT-Thread#11905
|
👋 感谢您对 RT-Thread 的贡献!Thank you for your contribution to RT-Thread! 为确保代码符合 RT-Thread 的编码规范,请在你的仓库中执行以下步骤运行代码格式化工作流(如果格式化CI运行失败)。 🛠 操作步骤 | Steps
完成后,提交将自动更新至 如有问题欢迎联系我们,再次感谢您的贡献!💐 |
📌 Code Review Assignment🏷️ Tag: componentsReviewers: @Maihuanyi Changed Files (Click to expand)
📊 Current Review Status (Last Updated: 2026-10-21 14:10 CST)
📝 Review Instructions
|
Cover the check added by the previous commit without needing a block device or
zlib: cromfs can be mounted from a memory image through the data pointer of
dfs_mount(), which is how bsp/k230 and bsp/cvitek mount their root filesystem.
Listing a directory never decompresses file data, so the test stays free of the
optional zlib dependency.
The test builds a cromfs image in RAM (256 byte header plus one dirent) and
lists its root directory twice:
1. Control entry, name_size = 2 -> getdents returns a positive byte count and
d_namlen is 2, so valid entries are still accepted.
2. name_size = 512, well above DIRENT_NAME_MAX -> getdents must return -EINVAL
and must leave the caller's dirent untouched.
Verified on bsp/qemu-vexpress-a9 (RT_USING_DFS_V2 + RT_USING_DFS_CROMFS):
without the fix : case 2 returns 260 and the 512 byte copy smashes the caller
stack, the test then dies with a prefetch abort
with the fix : case 1 returns 260 with d_namlen 2, case 2 returns -EINVAL,
no fault
components/dfs/dfs_v2/filesystems/cromfs/SConscript gains the usual recursion
into subdirectories so the new utest/ directory is picked up, and
components/utilities/utest/Kconfig sources its Kconfig.
Refs RT-Thread#11905
|
回归测试补上了(commit 用例怎么做的cromfs 支持通过 在 RAM 里构造 256 字节头 + 一个目录项:
分别挂到两个独立挂载点, 实验结果两件事值得注意:
附带的构建改动
用例已过仓库 |
拉取/合并请求描述:(PR description)
[
为什么提交这份PR (why to submit this PR)
关联 issue #11905(我已认领)。
dfs_cromfs_getdents()直接用镜像里的uint16 name_size作为memcpy长度往d_name里拷,既不与DIRENT_NAME_MAX比较,也不校验名字是否落在刚加载的目录 blob 内:d_name只有DIRENT_NAME_MAX(256) 字节,紧接着那行又把 NUL 写在name_size位置,所以name_size到 256 时原代码本身就已经越界一个字节;而name_size是镜像给的 uint16,最大可达 65535。调用方也不做裁剪:msh 的
ls()把struct dirent放在 shell 线程栈上,向 getdents 要sizeof(struct dirent)一个条目,于是超长目录名覆盖的是栈上对象,被覆盖的字节完全由镜像决定。issue 里的崩溃现场是fp=0x42424242(即'B'),ls返回时跳到0x42424246。v1 与 v2 两份实现的这四行完全一样,都受影响。
你的解决方案是什么 (what is your solution)
在两份实现的循环里,先校验、再使用:
file->pos + sizeof(cromfs_dirent) <= fi->size),否则sub_dirent的索引本身就越界;name_size < DIRENT_NAME_MAX(要留出结尾 NUL 的位置);file->pos + sizeof(cromfs_dirent) + name_size <= fi->size,保证名字不跑出 blob。任一条不满足即
result = -EINVAL跳出循环,函数返回-EINVAL,不再执行拷贝。校验通过后改用一个已在范围内的局部量entry_name_size参与memcpy、d_namlen与file->pos推进,避免继续读镜像字段。v1 用
file->pos,v2 用file->fpos,其余逻辑一致。请提供验证的bsp和config (provide the config and bsp)
CONFIG_RT_USING_DFS_CROMFS=y验证情况:
dfs_v2路径换成dfs_v1)跑-fsyntax-only,与未修改的原始文件报出的错误完全相同(都只有_crom_fops.lseek一处类型不匹配,那是用 V2 的 rtconfig.h 编 V1 源码造成的环境错配,与本次改动无关)运行时验证用内存镜像绕开了 zlib 依赖:cromfs 支持通过
dfs_mount(..., "crom", ..., data)的data指针直接读内存镜像(k230 / cvitek 就这么挂根文件系统),而目录遍历不涉及解压。本 PR 新增 utestcomponents.dfs.v2.cromfs.dirent_name,在 RAM 里构造 256 字节头 + 一个目录项,挂载后调用getdents。同一个用例在本 PR 与 master 上的结果完全相反:
这个用例不需要块设备、不需要 zlib,可以直接进 CI。若维护者希望把它挂到
.github/utest/configs/的某个 cfg 里,请指出放在哪一组合适。]
当前拉取/合并请求的状态 Intent for your PR
必须选择一项 Choose one (Mandatory):
代码质量 Code Quality:
我在这个拉取/合并请求中已经考虑了 As part of this pull request, I've considered the following:
#if 0代码,不包含已经被注释了的代码 All redundant code is removed and cleaned up