From 8aec92388b3da461eb8c86cbd0999ec63841fb9b Mon Sep 17 00:00:00 2001 From: seteiro Date: Sat, 17 Oct 2026 10:35:50 +0800 Subject: [PATCH] [dfs][9pfs] fix unaligned access in P9_OF_OPS_GET/PUT 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 #11868 Co-authored-by: Grok Signed-off-by: seteiro --- .../dfs/dfs_v1/filesystems/9pfs/dfs_9pfs.c | 58 ++++++++++++------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/components/dfs/dfs_v1/filesystems/9pfs/dfs_9pfs.c b/components/dfs/dfs_v1/filesystems/9pfs/dfs_9pfs.c index 6e842f51c858..910bfa3d672d 100644 --- a/components/dfs/dfs_v1/filesystems/9pfs/dfs_9pfs.c +++ b/components/dfs/dfs_v1/filesystems/9pfs/dfs_9pfs.c @@ -39,35 +39,48 @@ static rt_list_t _protocol_nodes = RT_LIST_OBJECT_INIT(_protocol_nodes); static struct rt_spinlock _protocol_lock = { 0 }; #ifndef rt_le8_to_cpu -#define rt_le8_to_cpu +#define rt_le8_to_cpu(x) (x) #endif #ifndef rt_cpu_to_le8 -#define rt_cpu_to_le8 +#define rt_cpu_to_le8(x) (x) #endif -#define P9_OF_OPS_GET(width, dir) \ - rt_inline rt_uint##width##_t get_##dir##_##value##width##_of( \ - struct p9_connection *conn, unsigned idx) \ - { \ - rt_uint##width##_t *vp = (void *)&conn->dir##_buffer[idx]; \ - return rt_le##width##_to_cpu(*vp); \ +/* + * The 9P messages are not aligned inside `rx_buffer`/`tx_buffer`, a 64 bit + * field can start at any offset (e.g. the `length` field of an `Rstat` reply + * always starts at P9_MSG_STAT_LEN == 42). Dereferencing a casted pointer + * makes GCC emit `LDRD`/`STRD` on ARMv7-A, which faults on any address that + * is not 8 byte aligned, so always go through an aligned local variable. + */ +#define P9_OF_OPS_GET(width, dir) \ + rt_inline rt_uint##width##_t get_##dir##_##value##width##_of( \ + struct p9_connection *conn, unsigned idx) \ + { \ + rt_uint##width##_t value; \ + \ + rt_memcpy(&value, &conn->dir##_buffer[idx], sizeof(value)); \ + \ + return rt_le##width##_to_cpu(value); \ } -#define P9_OF_OPS_PUT(width, dir) \ - rt_inline void put_##dir##_##value##width##_of( \ - struct p9_connection *conn, unsigned idx, \ - rt_uint##width##_t value) \ - { \ - rt_uint##width##_t *vp = (void *)&conn->dir##_buffer[idx]; \ - *vp = rt_cpu_to_le##width(value); \ +#define P9_OF_OPS_PUT(width, dir) \ + rt_inline void put_##dir##_##value##width##_of( \ + struct p9_connection *conn, unsigned idx, \ + rt_uint##width##_t value) \ + { \ + value = rt_cpu_to_le##width(value); \ + \ + rt_memcpy(&conn->dir##_buffer[idx], &value, sizeof(value)); \ } #define P9_OPS_PUT(width) \ rt_inline rt_off_t put_value##width(struct p9_connection *conn, \ rt_off_t off, rt_uint##width##_t value) \ { \ - ((rt_uint##width##_t *)(conn->tx_buffer + off))[0] = \ - rt_cpu_to_le##width(value); \ + value = rt_cpu_to_le##width(value); \ + \ + rt_memcpy(conn->tx_buffer + off, &value, sizeof(value)); \ + \ return off + sizeof(value); \ } @@ -367,7 +380,7 @@ rt_err_t dfs_9pfs_del_tag(struct p9_protocol *p9p) static int p9_to_fs_err(int rc) { - const int p9_err[] = { + const int p9_err[-P9_MSG_SIZE_TOO_BIG + 1] = { [0] = RT_EOK, [-P9_ERROR] = -EIO, [-P9_UNKNOWN_VERSION] = -ENOSYS, @@ -384,7 +397,12 @@ static int p9_to_fs_err(int rc) [-P9_MSG_SIZE_TOO_BIG] = -ENOSPC, }; - return p9_err[-rc]; + if (rc <= 0 && -rc < (int)(sizeof(p9_err) / sizeof(p9_err[0]))) + { + return p9_err[-rc]; + } + + return -EIO; } static int p9_alloc_fid(struct p9_connection *conn) @@ -547,7 +565,7 @@ rt_inline int p9_walk_subpath(struct p9_connection *conn, const char *path) static int dfs_9pfs_open(struct dfs_file *fd) { - int rc; + int rc = 0; rt_uint32_t size; rt_bool_t is_root; struct p9_connection *conn;