[Bug] 9PFS client trusts server-controlled stat/name lengths in getdents path, leading to out-of-bounds read
I maintainer di solito rispondono entro 1 giorno
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 42/100
- Tipo di issue
- Bug
- Chiarezza
- Abbastanza chiara
- Stato di attività
- Tranquilla
- Stack tecnologico
- c
- Ambito
- operating-systems, security
Direzione di ricerca
Iniziate in components/dfs/dfs_v1/filesystems/9pfs/dfs_9pfs.c, in p9_transaction(), dfs_9pfs_read(), dfs_9pfs_getdents() e negli helper get_rx_valueXX_of(); esaminate anche le dichiarazioni correlate in dfs_9pfs.h e dirent.h. Seguite come viene gestita la dimensione della risposta riportata dal trasporto, quindi verificate che le risposte Rread malformate vengano rifiutate prima dell’accesso ai campi, dell’avanzamento del record o della copia dei nomi.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
RT-Thread Version
master, verified on current worktree at commit 25295501c0cc7181d6a541a867fdf7214879ddf8
Hardware Type/Architectures
Any BSP using DFS v1 9PFS with an untrusted 9P transport/peer
Develop Toolchain
GCC
Describe the bug
Summary
A 9PFS client-side parsing vulnerability exists in RT-Thread DFS v1 9PFS.
RT-Thread's 9PFS client receives the actual reply size from the transport layer in p9_transaction(), but later parsing code such as dfs_9pfs_getdents() ignores that received size and instead trusts server-controlled fields embedded in the reply body, including count, stat_size, and name_len.
As a result, a malicious 9P server can return a short or malformed reply whose internal size fields cause the client to read beyond the received message buffer while parsing directory entries.
The most realistic impact is denial of service (crash / fault) in the 9PFS client. Depending on memory layout, the client may also copy out-of-bounds data into returned dirent entries and expose unintended memory contents to local callers.
Affected Components
components/dfs/dfs_v1/filesystems/9pfs/dfs_9pfs.ccomponents/dfs/dfs_v1/filesystems/9pfs/dfs_9pfs.hcomponents/libc/compilers/common/include/dirent.h
Key Evidence
1. Actual reply length is discarded by callers
p9_transaction() receives the transport-reported reply length:
components/dfs/dfs_v1/filesystems/9pfs/dfs_9pfs.c:179components/dfs/dfs_v1/filesystems/9pfs/dfs_9pfs.h:132
But callers such as dfs_9pfs_read() and dfs_9pfs_getdents() pass RT_NULL for out_rx_size, so the actual received length is discarded.
2. Field access helpers have no bounds checking
components/dfs/dfs_v1/filesystems/9pfs/dfs_9pfs.c:48
get_rx_value16_of() / get_rx_value32_of() directly read from conn->rx_buffer[idx] by offset without any bounds validation.
3. dfs_9pfs_getdents() trusts server-controlled lengths
Relevant locations:
components/dfs/dfs_v1/filesystems/9pfs/dfs_9pfs.c:815components/dfs/dfs_v1/filesystems/9pfs/dfs_9pfs.c:846components/dfs/dfs_v1/filesystems/9pfs/dfs_9pfs.c:854components/dfs/dfs_v1/filesystems/9pfs/dfs_9pfs.c:880components/dfs/dfs_v1/filesystems/9pfs/dfs_9pfs.c:882
The function uses:
ret = get_rx_value32_of(conn, P9_MSG_READ_COUNT);
stat_size = get_rx_value16_of(conn, off + P9_MSG_STAT_SIZE) + sizeof(rt_uint16_t);
dirp->d_namlen = get_rx_value16_of(conn, off + P9_MSG_STAT_NAME_LEN);
rt_strncpy(dirp->d_name, conn->rx_buffer + off + P9_MSG_STAT_NAME, dirp->d_namlen);
But it does not verify that:
ret <= actual_rx_size - P9_MSG_READ_DATAoff + P9_MSG_STAT_SIZE + 2 <= actual_rx_sizestat_size <= remaining returned dataoff + P9_MSG_STAT_NAME + d_namlen <= actual_rx_size
4. getdents() is worse than read()
dfs_9pfs_read() is still problematic because it ignores the actual reply size, but got is at least constrained by got <= set, and set <= conn->msg_size - P9_MSG_READ_DATA.
dfs_9pfs_getdents() is worse because ret, stat_size, and name_len are all trusted and off keeps increasing based on unvalidated values.
5. Primary issue is source-buffer over-read, not a local dirent destination overflow
components/libc/compilers/common/include/dirent.h:57
d_namlen is uint8_t and d_name is 256 bytes, so the more credible issue is reading past the received 9P reply buffer and then copying that data into the returned dirent.
Impact
If a system mounts an untrusted 9P peer, the peer can send malformed directory reply data that drives the client into out-of-bounds reads while parsing directory entries.
Practical impact:
- Client crash / fault / denial of service
- Undefined behavior during directory enumeration
- Possible copying of out-of-bounds data into returned
direntstructures, which may leak unintended memory contents to local callers
Steps to Reproduce
A practical PoC requires a malicious or instrumented 9P server / transport peer.
- Build RT-Thread with DFS v1 9PFS enabled.
- Connect or mount a 9P peer that the attacker controls.
- Trigger a directory listing on the mounted 9P filesystem so that
dfs_9pfs_getdents()is used. - Return a malformed
Rreadreply where:- The actual received packet is short, but
P9_MSG_READ_COUNTis set larger than the real reply payload, and/or- The embedded stat record
sizefield is inflated, and/or name_lenpoints beyond the actual received message body.
- Observe that the RT-Thread client continues parsing using those server-controlled lengths and reads beyond the valid received reply data.
Expected Behavior
The 9PFS client should track the actual reply size returned by the transport and reject any response whose internal fields exceed that boundary.
In particular:
dfs_9pfs_getdents()should reject replies ifretexceeds the actual received payload size- Each parsed
stat_sizeshould be validated before advancingoff - Each
name_lenshould be validated against the remaining received message length before reading or copying the name
Actual Behavior
The 9PFS client ignores the transport-reported reply length in this parsing path and trusts internal reply fields from the server to drive further buffer reads.
This allows a malicious 9P server to induce out-of-bounds reads during directory entry parsing, likely causing a crash or other denial-of-service condition.
Suggested Fix
The fix should make reply parsing length-aware and reject malformed replies early.
At minimum:
- Propagate and use the actual
rx_sizereturned byp9_transaction()in all reply parsing paths - Reject any
RreadwhereP9_MSG_READ_COUNTexceedsrx_size - P9_MSG_READ_DATA - In
dfs_9pfs_getdents(), before each field access, validate that the corresponding offset is still within the actual received reply length - Reject any record where:
stat_sizeis smaller than the fixed stat headerstat_sizeexceeds the remaining returned dataname_lenextends past the received message boundary
- Stop using raw
get_rx_valueXX_of()on unvalidated offsets for attacker-controlled message bodies
Kindly let me know if you intend to request a CVE ID upon confirmation of the vulnerability.
Other additional context
No response
- Lingua principale
- C
- Stelle
- 12.3k
- Fork
- 5.5k
- Merge medio
- 4g 12h
- PR unite (30g)
- 32
Preparare l'ambiente
Avvia il container di sviluppo del progetto nel browser, con il tuo account GitHub.
- Nessun Dockerfile né file Docker Compose
- Ha un modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di RT-Thread/rt-thread
-
bug Component component: net
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 90/100
RT-Thread/rt-thread#11852 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
[bsp][stm32][bluepill] README「快速上手」缺少重新生成 MDK 工程这一步,按文档操作无法编译通过Forse già presa @moment-NEW l’ha presa 2 giorni fa. Apertain progress
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
RT-Thread/rt-thread#11818 · 4 commenti · 1 assegnatario ·
I maintainer di solito rispondono entro 1 giorno
-
BSP BSP: Loongson bug RT-Smart
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
RT-Thread/rt-thread#11717 · 2 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
Arch: RISC-V BSP BSP: HPMicro bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
RT-Thread/rt-thread#11687 · 3 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
RT-Thread/rt-thread#11472 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di RT-Thread/rt-thread
Issue simili
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
libsdl-org/SDL#16444 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
MiSTer-devel/ao486_MiSTer#243 ·
-
Dropped last row with parallel scan of attached SQLite tables if the rowid range is a multiple of 122,880Forse già presa @staticlibs l’ha presa oggi. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
duckdb/duckdb-sqlite#240 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 66/100
siderolabs/pkgs#1710 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
I maintainer di solito rispondono entro 1 giorno