smb: client: fix unaligned access in WSL reparse point parser
Subsystems (3)
Paulo Alcantara
maintainerPATCH 1/2 smb: client: fix unaligned access in WSL reparse point parser
When wsl_to_fattr() parses WSL extended attributes, it computes a payload pointer from ea->ea_data + ea_name_length + 1. Since the smb2_file_full_ea_info struct is __packed and all WSL xattr names are 6 bytes long, the value pointer always lands at an odd byte offset, never satisfying __le32 or __le64 alignment requirements.
The code then casts this pointer to __le32 * or __le64 * and dereferences it directly, which may cause alignment faults on some architectures.
Replace all such casts with get_unaligned_le32() and get_unaligned_le64() in reparse_mkdev(), wsl_make_kuid(), wsl_make_kgid() and wsl_to_fattr().
--- fs/smb/client/reparse.c | 4 ++-- fs/smb/client/reparse.h | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-)
fs/smb/client/reparse.c +2 -2
@@ -1201,9 +1201,9 @@ static bool wsl_to_fattr(struct cifs_open_info_data *data, fattr->cf_gid =wsl_make_kgid (cifs_sb, v); }else if (!strncmp (name,SMB2_WSL_XATTR_MODE , nlen)) {/* File type in reparse point tag and in xattr mode must match. */ -if (S_DT (fattr->cf_mode) !=S_DT (le32_to_cpu (*(__le32 *)v))) +if (S_DT (fattr->cf_mode) !=S_DT (get_unaligned_le32 (v)))return false; - fattr->cf_mode = (umode_t )le32_to_cpu (*(__le32 *)v); + fattr->cf_mode = (umode_t )get_unaligned_le32 (v); }else if (!strncmp (name,SMB2_WSL_XATTR_DEV , nlen)) { fattr->cf_rdev =reparse_mkdev (v); have_xattr_dev = true;
fs/smb/client/reparse.h +4 -3
@@ -9,6 +9,7 @@#include <linux/fs.h> #include <linux/stat.h> #include <linux/uidgid.h> +#include <linux/unaligned.h> #include "fs_context.h" #include "cifsglob.h" #include "../common/smbfsctl.h"
@@ -23,7 +24,7 @@static inline dev_t reparse_mkdev (void *ptr) { -u64 v =le64_to_cpu (*(__le64 *)ptr); +u64 v =get_unaligned_le64 (ptr);return MKDEV (v &0xffffffff , v >>32 ); }
@@ -31,7 +32,7 @@ static inline dev_t reparse_mkdev(void *ptr)static inline kuid_t wsl_make_kuid (struct cifs_sb_info *cifs_sb,void *ptr) { -u32 uid =le32_to_cpu (*(__le32 *)ptr); +u32 uid =get_unaligned_le32 (ptr);if (cifs_sb_flags (cifs_sb) &CIFS_MOUNT_OVERR_UID )return cifs_sb->ctx->linux_uid;
@@ -41,7 +42,7 @@ static inline kuid_t wsl_make_kuid(struct cifs_sb_info *cifs_sb,static inline kgid_t wsl_make_kgid (struct cifs_sb_info *cifs_sb,void *ptr) { -u32 gid =le32_to_cpu (*(__le32 *)ptr); +u32 gid =get_unaligned_le32 (ptr);if (cifs_sb_flags (cifs_sb) &CIFS_MOUNT_OVERR_GID )return cifs_sb->ctx->linux_gid;
Signature · 1 line
2.55.0
Paulo Alcantara
maintainerPATCH 2/2 smb: client: fix fattr leaking on wsl_to_fattr() failure
wsl_to_fattr() mutates fattr fields as it parses each WSL EA. If validation later fails, the function returns false with partially mutated fattr fields that callers do not reset.
Fix this by parsing into local variables and only committing them to fattr on success.
--- fs/smb/client/reparse.c | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-)
fs/smb/client/reparse.c +19 -15
@@ -1149,29 +1149,30 @@ static bool wsl_to_fattr(struct cifs_open_info_data *data, u32 tag, struct cifs_fattr *fattr) {unsigned int sbflags =cifs_sb_flags (cifs_sb); +kuid_t uid = cifs_sb->ctx->linux_uid; +kgid_t gid = cifs_sb->ctx->linux_gid;struct smb2_file_full_ea_info *ea;bool have_xattr_dev = false; +dev_t rdev =0 ; +umode_t mode;u32 next =0 ; - fattr->cf_uid = cifs_sb->ctx->linux_uid; - fattr->cf_gid = cifs_sb->ctx->linux_gid; - - fattr->cf_mode &= ~S_IFMT ; + mode = fattr->cf_mode & ~S_IFMT ;switch (tag) {case IO_REPARSE_TAG_LX_SYMLINK : - fattr->cf_mode |=S_IFLNK ; + mode |=S_IFLNK ;break ;case IO_REPARSE_TAG_LX_FIFO : - fattr->cf_mode |=S_IFIFO ; + mode |=S_IFIFO ;break ;case IO_REPARSE_TAG_AF_UNIX : - fattr->cf_mode |=S_IFSOCK ; + mode |=S_IFSOCK ;break ;case IO_REPARSE_TAG_LX_CHR : - fattr->cf_mode |=S_IFCHR ; + mode |=S_IFCHR ;break ;case IO_REPARSE_TAG_LX_BLK : - fattr->cf_mode |=S_IFBLK ; + mode |=S_IFBLK ;break ; }
@@ -1195,26 +1196,29 @@ static bool wsl_to_fattr(struct cifs_open_info_data *data,if (!strncmp (name,SMB2_WSL_XATTR_UID , nlen)) {if (!(sbflags &CIFS_MOUNT_OVERR_UID )) - fattr->cf_uid =wsl_make_kuid (cifs_sb, v); + uid =wsl_make_kuid (cifs_sb, v); }else if (!strncmp (name,SMB2_WSL_XATTR_GID , nlen)) {if (!(sbflags &CIFS_MOUNT_OVERR_GID )) - fattr->cf_gid =wsl_make_kgid (cifs_sb, v); + gid =wsl_make_kgid (cifs_sb, v); }else if (!strncmp (name,SMB2_WSL_XATTR_MODE , nlen)) {/* File type in reparse point tag and in xattr mode must match. */ -if (S_DT (fattr->cf_mode) !=S_DT (get_unaligned_le32 (v))) +if (S_DT (mode) !=S_DT (get_unaligned_le32 (v)))return false; - fattr->cf_mode = (umode_t )get_unaligned_le32 (v); + mode =get_unaligned_le32 (v); }else if (!strncmp (name,SMB2_WSL_XATTR_DEV , nlen)) { - fattr->cf_rdev =reparse_mkdev (v); + rdev =reparse_mkdev (v); have_xattr_dev = true; } }while (next); out: -/* Major and minor numbers for char and block devices are mandatory. */ if (!have_xattr_dev && (tag ==IO_REPARSE_TAG_LX_CHR || tag ==IO_REPARSE_TAG_LX_BLK ))return false; + fattr->cf_uid = uid; + fattr->cf_gid = gid; + fattr->cf_mode = mode; + fattr->cf_rdev = rdev;return true; }
Signature · 1 line
2.55.0
Re: [PATCH 2/2] smb: client: fix fattr leaking on wsl_to_fattr() failure
Quoted 8 lines · Paulo Alcantara
wsl_to_fattr()mutates fattr fields as it parses each WSL EA. If validation later fails, the function returns false with partially mutated fattr fields that callers do not reset.Fix this by parsing into local variables and only committing them to fattr on success.
Quoted 10 lines
Fixes 78e26bec4d6d ("smb: client: parse uid, gid, mode and dev from WSL reparse points")Signed-off-by Paulo Alcantara <[email protected]>Cc David Howells <[email protected]>Cc Tom Talpey <[email protected]>Cc Shyam Prasad N <[email protected]>Cc Ronnie Sahlberg <[email protected]>Cc Bharath SM <[email protected]>Cc Namjae Jeon <[email protected]>
Thanks!
Re: [PATCH 2/2] smb: client: fix fattr leaking on wsl_to_fattr() failure
In reply to Paulo AlcantaraQuoted 7 lines · Paulo Alcantara
wsl_to_fattr()mutates fattr fields as it parses each WSL EA. If validation later fails, the function returns false with partially mutated fattr fields that callers do not reset.Fix this by parsing into local variables and only committing them to fattr on success.
...
Applied.
Quoted 15 lines · Paulo Alcantara
When
wsl_to_fattr()parses WSL extended attributes, it computes a payload pointer fromea->ea_data+ea_name_length+ 1. Since thesmb2_file_full_ea_infostruct is__packedand all WSL xattr names are 6 bytes long, the value pointer always lands at an odd byte offset, never satisfying__le32or__le64alignment requirements.The code then casts this pointer to
__le32* or__le64* and dereferences it directly, which may cause alignment faults on some architectures.Replace all such casts with
get_unaligned_le32()andget_unaligned_le64()inreparse_mkdev(),wsl_make_kuid(),wsl_make_kgid()andwsl_to_fattr().
Quoted 10 lines
Fixes 78e26bec4d6d ("smb: client: parse uid, gid, mode and dev from WSL reparse points")Signed-off-by Paulo Alcantara <[email protected]>Cc David Howells <[email protected]>Cc Tom Talpey <[email protected]>Cc Shyam Prasad N <[email protected]>Cc Ronnie Sahlberg <[email protected]>Cc Bharath SM <[email protected]>Cc Namjae Jeon <[email protected]>
Thanks!
Quoted 6 lines · Paulo Alcantara
When
wsl_to_fattr()parses WSL extended attributes, it computes a payload pointer fromea->ea_data+ea_name_length+ 1. Since thesmb2_file_full_ea_infostruct is__packedand all WSL xattr names are 6 bytes long, the value pointer always lands at an odd byte offset, never satisfying__le32or__le64alignment requirements.
Quoted 8 lines
The code then casts this pointer to
__le32* or__le64* and dereferences it directly, which may cause alignment faults on some architectures.Replace all such casts with
get_unaligned_le32()andget_unaligned_le64()inreparse_mkdev(),wsl_make_kuid(),wsl_make_kgid()andwsl_to_fattr().
...
Applied.