Repository navigation
[dfs][9pfs] fix unaligned access in P9_OF_OPS_GET/PUT - #11876
cms19859230182-lang wants to merge 1 commit into
Conversation
|
👋 感谢您对 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-17 14:09 CST)
📝 Review Instructions
|
The 9P accessors cast a byte pointer inside rx_buffer/tx_buffer to a rt_uintN_t * and dereference it. The 9P messages are packed at arbitrary offsets, so a 64 bit field is usually not 8 byte aligned: the length field of an Rstat reply always starts at P9_MSG_STAT_LEN == 42, and with sizeof(struct p9_connection) == 292 in an rt_malloc()ed block rx_buffer is always 4 (mod 8), so &rx_buffer[42] is always 6 (mod 8). On ARMv7-A GCC emits LDRD/STRD for those accesses, which requires 8 byte alignment, so every Rstat reply (and every Tread/Twrite request that carries a 64 bit offset) takes a data abort in dfs_9pfs_stat()/dfs_9pfs_open() on 32 bit ARM targets. Copy the field through an aligned local variable with rt_memcpy() and only then apply the byte order conversion, for both the get and the put side. The rt_le8_to_cpu()/rt_cpu_to_le8() fallbacks also became function like macros, they are used as such by the width == 8 instantiations. Fixes RT-Thread#11868 Co-authored-by: Grok <grok@x.ai> Signed-off-by: seteiro <cms19859230182@gmail.com>
8d5d940 to
8aec923
Compare
|
9pfs 会用于什么场合啊 😬 |
问题
components/dfs/dfs_v1/filesystems/9pfs/dfs_9pfs.c里 9P 报文的访问器把rx_buffer/tx_buffer里的字节指针直接强转成rt_uintN_t *再解引用:9P 报文里各字段是紧凑排列的,64 位字段几乎总是 8 字节不对齐:
sizeof(struct p9_connection) == 292,conn来自rt_malloc()(8 字节对齐),所以
rx_buffer恒为≡ 4 (mod 8);P9_MSG_STAT_LEN == 42,&rx_buffer[42]恒为≡ 6 (mod 8)。ARMv7-A 上 GCC 把这种解引用编译成
LDRD,LDRD要求 8 字节对齐,于是任何应答
Rstat的 9P 服务端都会在dfs_9pfs_stat()/dfs_9pfs_open()里触发 data abort。put_value16/32/64()以及 16/32 位访问器是同一个写法,属于同类问题。修复
三个宏都改成「先通过
rt_memcpy把字段拷到对齐的局部变量,再做字节序转换」:写方向对称处理(先
rt_cpu_to_leN,再rt_memcpy写回)。另外把兜底宏#define rt_le8_to_cpu/#define rt_cpu_to_le8(空展开)改成函数式#define rt_le8_to_cpu(x) (x),否则width == 8的实例化会展开成return ;/value = ;。函数签名、调用点、代码风格都不变,diff 只有 30+/17-,一个文件。
验证
bsp/qemu-vexpress-a9(ARMv7-A,QEMU 10.0.13)+ lwIP over lan9118 + 一个最小 9P2000.uTCP 服务端(应答完全合法),guest 里注册 9p transport、
dfs_mount("/mnt", "9p", 0, tag),然后
stat/open一个文件。修复前(
stat("/mnt")就崩,gdb 不用就能看出来):修复后(同一个用例):
mode是S_IFDIR/S_IFREG,size与服务端报告的 1260 字节一致(说明字节序也正确),msh 仍可正常交互,不再有 data abort。
编译产物对比:修复前
ldrd r2, [r3](r3 = 报文缓冲地址),修复后 8 字节先memcpy到8 字节对齐的栈槽,
ldrd只作用在栈上。兼容性
rt_memcpy()在所有平台都有;另外发现(本次 PR 不改,建议另开 issue)
p9_walk_path_raw()忘记put_tx_value32_of(conn, P9_MSG_SIZE, size),Twalk的长度字段带的是上一条消息的残值(实测第一次发出00000000),标准服务端会直接丢包,导致「能 mount 但 stat/open 全部失败」。
Rstat解析的字段偏移整体偏 +2 字节(P9_MSG_STAT_SIZE == 9,而 9P2000.u 是 7),导致
mode/atime/len/uid/gid读到错位数据。put_header()/put_bytes()/put_string()里还有对tx_buffer + 1、+ 3的16 位非对齐写,实测在 ARMv7-A 上没崩,但同样不合规。
致谢
本次问题的复现、定位与修复由 Grok(AI 助手)参与完成,已在 commit 中标注
Co-authored-by: Grok <grok@x.ai>。Fixes #11868