From a5e581093b1d9321cbb627dd8c209d0d4e0a988a Mon Sep 17 00:00:00 2001 From: ZhangGuoDong Date: Tue, 3 Mar 2026 15:13:14 +0000 Subject: [PATCH 01/16] smb: move some definitions from common/smb2pdu.h into common/fscc.h These definitions are specified in MS-FSCC, so move them into fscc.h. Only add some documentation references, no other changes. Signed-off-by: ZhangGuoDong Reviewed-by: ChenXiaoSong Reviewed-by: Steve French Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/common/fscc.h | 315 ++++++++++++++++++++++++++++++++++++++++ fs/smb/common/smb2pdu.h | 307 --------------------------------------- 2 files changed, 315 insertions(+), 307 deletions(-) diff --git a/fs/smb/common/fscc.h b/fs/smb/common/fscc.h index 0123f34db1e8..415cba02d1c9 100644 --- a/fs/smb/common/fscc.h +++ b/fs/smb/common/fscc.h @@ -12,6 +12,210 @@ #ifndef _COMMON_SMB_FSCC_H #define _COMMON_SMB_FSCC_H +/* Reparse structures - see MS-FSCC 2.1.2 */ + +/* struct fsctl_reparse_info_req is empty, only response structs (see below) */ +struct reparse_data_buffer { + __le32 ReparseTag; + __le16 ReparseDataLength; + __u16 Reserved; + __u8 DataBuffer[]; /* Variable Length */ +} __packed; + +struct reparse_guid_data_buffer { + __le32 ReparseTag; + __le16 ReparseDataLength; + __u16 Reserved; + __u8 ReparseGuid[16]; + __u8 DataBuffer[]; /* Variable Length */ +} __packed; + +struct reparse_mount_point_data_buffer { + __le32 ReparseTag; + __le16 ReparseDataLength; + __u16 Reserved; + __le16 SubstituteNameOffset; + __le16 SubstituteNameLength; + __le16 PrintNameOffset; + __le16 PrintNameLength; + __u8 PathBuffer[]; /* Variable Length */ +} __packed; + +#define SYMLINK_FLAG_RELATIVE 0x00000001 + +struct reparse_symlink_data_buffer { + __le32 ReparseTag; + __le16 ReparseDataLength; + __u16 Reserved; + __le16 SubstituteNameOffset; + __le16 SubstituteNameLength; + __le16 PrintNameOffset; + __le16 PrintNameLength; + __le32 Flags; + __u8 PathBuffer[]; /* Variable Length */ +} __packed; + +/* For IO_REPARSE_TAG_NFS - see MS-FSCC 2.1.2.6 */ +#define NFS_SPECFILE_LNK 0x00000000014B4E4C +#define NFS_SPECFILE_CHR 0x0000000000524843 +#define NFS_SPECFILE_BLK 0x00000000004B4C42 +#define NFS_SPECFILE_FIFO 0x000000004F464946 +#define NFS_SPECFILE_SOCK 0x000000004B434F53 +struct reparse_nfs_data_buffer { + __le32 ReparseTag; + __le16 ReparseDataLength; + __u16 Reserved; + __le64 InodeType; /* NFS_SPECFILE_* */ + __u8 DataBuffer[]; +} __packed; + +/* For IO_REPARSE_TAG_LX_SYMLINK - see MS-FSCC 2.1.2.7 */ +struct reparse_wsl_symlink_data_buffer { + __le32 ReparseTag; + __le16 ReparseDataLength; + __u16 Reserved; + __le32 Version; /* Always 2 */ + __u8 Target[]; /* Variable Length UTF-8 string without nul-term */ +} __packed; + +/* See MS-FSCC 2.3.7 */ +struct duplicate_extents_to_file { + __u64 PersistentFileHandle; /* source file handle, opaque endianness */ + __u64 VolatileFileHandle; + __le64 SourceFileOffset; + __le64 TargetFileOffset; + __le64 ByteCount; /* Bytes to be copied */ +} __packed; + +/* See MS-FSCC 2.3.9 */ +#define DUPLICATE_EXTENTS_DATA_EX_SOURCE_ATOMIC 0x00000001 +struct duplicate_extents_to_file_ex { + __le64 StructureSize; /* MUST be set to 0x30 */ + __u64 PersistentFileHandle; /* source file handle, opaque endianness */ + __u64 VolatileFileHandle; + __le64 SourceFileOffset; + __le64 TargetFileOffset; + __le64 ByteCount; /* Bytes to be copied */ + __le32 Flags; + __le32 Reserved; +} __packed; + +/* See MS-FSCC 2.3.20 */ +struct fsctl_get_integrity_information_rsp { + __le16 ChecksumAlgorithm; + __le16 Reserved; + __le32 Flags; + __le32 ChecksumChunkSizeInBytes; + __le32 ClusterSizeInBytes; +} __packed; + +/* See MS-FSCC 2.3.52 */ +struct file_allocated_range_buffer { + __le64 file_offset; + __le64 length; +} __packed; + +/* See MS-FSCC 2.3.55 */ +struct fsctl_query_file_regions_req { + __le64 FileOffset; + __le64 Length; + __le32 DesiredUsage; + __le32 Reserved; +} __packed; + +/* DesiredUsage flags see MS-FSCC 2.3.56.1 */ +#define FILE_USAGE_INVALID_RANGE 0x00000000 +#define FILE_USAGE_VALID_CACHED_DATA 0x00000001 +#define FILE_USAGE_NONCACHED_DATA 0x00000002 +struct file_region_info { + __le64 FileOffset; + __le64 Length; + __le32 DesiredUsage; + __le32 Reserved; +} __packed; + +/* See MS-FSCC 2.3.56 */ +struct fsctl_query_file_region_rsp { + __le32 Flags; + __le32 TotalRegionEntryCount; + __le32 RegionEntryCount; + __u32 Reserved; + struct file_region_info Regions[]; +} __packed; + +/* See MS-FSCC 2.3.58 */ +struct fsctl_query_on_disk_vol_info_rsp { + __le64 DirectoryCount; + __le64 FileCount; + __le16 FsFormatMajVersion; + __le16 FsFormatMinVersion; + __u8 FsFormatName[24]; + __le64 FormatTime; + __le64 LastUpdateTime; + __u8 CopyrightInfo[68]; + __u8 AbstractInfo[68]; + __u8 FormatImplInfo[68]; + __u8 LastModifyImplInfo[68]; +} __packed; + +/* See MS-FSCC 2.3.73 */ +struct fsctl_set_integrity_information_req { + __le16 ChecksumAlgorithm; + __le16 Reserved; + __le32 Flags; +} __packed; + +/* See MS-FSCC 2.3.75 */ +struct fsctl_set_integrity_info_ex_req { + __u8 EnableIntegrity; + __u8 KeepState; + __u16 Reserved; + __le32 Flags; + __u8 Version; + __u8 Reserved2[7]; +} __packed; + +/* + * this goes in the ioctl buffer when doing FSCTL_SET_ZERO_DATA + * See MS-FSCC 2.3.85 + */ +struct file_zero_data_information { + __le64 FileOffset; + __le64 BeyondFinalZero; +} __packed; + +/* + * This level 18, although with struct with same name is different from cifs + * level 0x107. Level 0x107 has an extra u64 between AccessFlags and + * CurrentByteOffset. + * See MS-FSCC 2.4.2 + */ +struct smb2_file_all_info { /* data block encoding of response to level 18 */ + __le64 CreationTime; /* Beginning of FILE_BASIC_INFO equivalent */ + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le32 Attributes; + __u32 Pad1; /* End of FILE_BASIC_INFO_INFO equivalent */ + __le64 AllocationSize; /* Beginning of FILE_STANDARD_INFO equivalent */ + __le64 EndOfFile; /* size ie offset to first free byte in file */ + __le32 NumberOfLinks; /* hard links */ + __u8 DeletePending; + __u8 Directory; + __u16 Pad2; /* End of FILE_STANDARD_INFO equivalent */ + __le64 IndexNumber; + __le32 EASize; + __le32 AccessFlags; + __le64 CurrentByteOffset; + __le32 Mode; + __le32 AlignmentRequirement; + __le32 FileNameLength; + union { + char __pad; /* Legacy structure padding */ + DECLARE_FLEX_ARRAY(char, FileName); + }; +} __packed; /* level 18 Query */ + /* See MS-FSCC 2.4.8 */ typedef struct { __le32 NextEntryOffset; @@ -46,6 +250,11 @@ typedef struct { char FileName[]; } __packed FILE_DIRECTORY_INFO; /* level 0x101 FF resp data */ +/* See MS-FSCC 2.4.13 */ +struct smb2_file_eof_info { /* encoding of request for level 10 */ + __le64 EndOfFile; /* new end of file value */ +} __packed; /* level 20 Set */ + /* See MS-FSCC 2.4.14 */ typedef struct { __le32 NextEntryOffset; @@ -80,6 +289,26 @@ typedef struct { char FileName[]; } __packed FILE_ID_FULL_DIR_INFO; /* level 0x105 FF rsp data */ +/* See MS-FSCC 2.4.27 */ +struct smb2_file_internal_info { + __le64 IndexNumber; +} __packed; /* level 6 Query */ + +/* See MS-FSCC 2.4.28.2 */ +struct smb2_file_link_info { /* encoding of request for level 11 */ + /* New members MUST be added within the struct_group() macro below. */ + __struct_group(smb2_file_link_info_hdr, __hdr, __packed, + __u8 ReplaceIfExists; /* 1 = replace existing link with new */ + /* 0 = fail if link already exists */ + __u8 Reserved[7]; + __u64 RootDirectory; /* MBZ for network operations (why says spec?) */ + __le32 FileNameLength; + ); + char FileName[]; /* Name to be assigned to new link */ +} __packed; /* level 11 Set */ +static_assert(offsetof(struct smb2_file_link_info, FileName) == sizeof(struct smb2_file_link_info_hdr), + "struct member likely outside of __struct_group()"); + /* See MS-FSCC 2.4.34 */ struct smb2_file_network_open_info { struct_group_attr(network_open_info, __packed, @@ -94,6 +323,37 @@ struct smb2_file_network_open_info { __le32 Reserved; } __packed; /* level 34 Query also similar returned in close rsp and open rsp */ +/* See MS-FSCC 2.4.42.2 */ +struct smb2_file_rename_info { /* encoding of request for level 10 */ + /* New members MUST be added within the struct_group() macro below. */ + __struct_group(smb2_file_rename_info_hdr, __hdr, __packed, + __u8 ReplaceIfExists; /* 1 = replace existing target with new */ + /* 0 = fail if target already exists */ + __u8 Reserved[7]; + __u64 RootDirectory; /* MBZ for network operations (why says spec?) */ + __le32 FileNameLength; + ); + char FileName[]; /* New name to be assigned */ + /* padding - overall struct size must be >= 24 so filename + pad >= 6 */ +} __packed; /* level 10 Set */ +static_assert(offsetof(struct smb2_file_rename_info, FileName) == sizeof(struct smb2_file_rename_info_hdr), + "struct member likely outside of __struct_group()"); + +/* File System Information Classes */ +/* See MS-FSCC 2.5 */ +#define FS_VOLUME_INFORMATION 1 /* Query */ +#define FS_LABEL_INFORMATION 2 /* Set */ +#define FS_SIZE_INFORMATION 3 /* Query */ +#define FS_DEVICE_INFORMATION 4 /* Query */ +#define FS_ATTRIBUTE_INFORMATION 5 /* Query */ +#define FS_CONTROL_INFORMATION 6 /* Query, Set */ +#define FS_FULL_SIZE_INFORMATION 7 /* Query */ +#define FS_OBJECT_ID_INFORMATION 8 /* Query, Set */ +#define FS_DRIVER_PATH_INFORMATION 9 /* Query */ +#define FS_SECTOR_SIZE_INFORMATION 11 /* SMB3 or later. Query */ +/* See POSIX Extensions to MS-FSCC 2.3.1.1 */ +#define FS_POSIX_INFORMATION 100 /* SMB3.1.1 POSIX. Query */ + /* See MS-FSCC 2.5.1 */ #define MAX_FS_NAME_LEN 52 typedef struct { @@ -130,6 +390,45 @@ typedef struct { #define FILE_CASE_PRESERVED_NAMES 0x00000002 #define FILE_CASE_SENSITIVE_SEARCH 0x00000001 +/* + * File System Control Information + * See MS-FSCC 2.5.2 + */ +struct smb2_fs_control_info { + __le64 FreeSpaceStartFiltering; + __le64 FreeSpaceThreshold; + __le64 FreeSpaceStopFiltering; + __le64 DefaultQuotaThreshold; + __le64 DefaultQuotaLimit; + __le32 FileSystemControlFlags; + __le32 Padding; +} __packed; + +/* See MS-FSCC 2.5.4 */ +struct smb2_fs_full_size_info { + __le64 TotalAllocationUnits; + __le64 CallerAvailableAllocationUnits; + __le64 ActualAvailableAllocationUnits; + __le32 SectorsPerAllocationUnit; + __le32 BytesPerSector; +} __packed; + +/* See MS-FSCC 2.5.7 */ +#define SSINFO_FLAGS_ALIGNED_DEVICE 0x00000001 +#define SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE 0x00000002 +#define SSINFO_FLAGS_NO_SEEK_PENALTY 0x00000004 +#define SSINFO_FLAGS_TRIM_ENABLED 0x00000008 +/* sector size info struct */ +struct smb3_fs_ss_info { + __le32 LogicalBytesPerSector; + __le32 PhysicalBytesPerSectorForAtomicity; + __le32 PhysicalBytesPerSectorForPerf; + __le32 FSEffPhysicalBytesPerSectorForAtomicity; + __le32 Flags; + __le32 ByteOffsetForSectorAlignment; + __le32 ByteOffsetForPartitionAlignment; +} __packed; + /* See MS-FSCC 2.5.8 */ typedef struct { __le64 TotalAllocationUnits; @@ -189,6 +488,22 @@ typedef struct { #define FILE_ATTRIBUTE_NO_SCRUB_DATA_LE cpu_to_le32(FILE_ATTRIBUTE_NO_SCRUB_DATA) #define FILE_ATTRIBUTE_MASK_LE cpu_to_le32(FILE_ATTRIBUTE_MASK) +/* + * SMB2 Notify Action Flags + * See MS-FSCC 2.7.1 + */ +#define FILE_ACTION_ADDED 0x00000001 +#define FILE_ACTION_REMOVED 0x00000002 +#define FILE_ACTION_MODIFIED 0x00000003 +#define FILE_ACTION_RENAMED_OLD_NAME 0x00000004 +#define FILE_ACTION_RENAMED_NEW_NAME 0x00000005 +#define FILE_ACTION_ADDED_STREAM 0x00000006 +#define FILE_ACTION_REMOVED_STREAM 0x00000007 +#define FILE_ACTION_MODIFIED_STREAM 0x00000008 +#define FILE_ACTION_REMOVED_BY_DELETE 0x00000009 +#define FILE_ACTION_ID_NOT_TUNNELLED 0x0000000A +#define FILE_ACTION_TUNNELLED_ID_COLLISION 0x0000000B + /* * Response contains array of the following structures * See MS-FSCC 2.7.1 diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h index e482c86ceb00..538e7a12ea93 100644 --- a/fs/smb/common/smb2pdu.h +++ b/fs/smb/common/smb2pdu.h @@ -1006,22 +1006,6 @@ struct smb2_set_info_rsp { #define FILE_NOTIFY_CHANGE_STREAM_SIZE 0x00000400 #define FILE_NOTIFY_CHANGE_STREAM_WRITE 0x00000800 -/* - * SMB2 Notify Action Flags - * See MS-FSCC 2.7.1 - */ -#define FILE_ACTION_ADDED 0x00000001 -#define FILE_ACTION_REMOVED 0x00000002 -#define FILE_ACTION_MODIFIED 0x00000003 -#define FILE_ACTION_RENAMED_OLD_NAME 0x00000004 -#define FILE_ACTION_RENAMED_NEW_NAME 0x00000005 -#define FILE_ACTION_ADDED_STREAM 0x00000006 -#define FILE_ACTION_REMOVED_STREAM 0x00000007 -#define FILE_ACTION_MODIFIED_STREAM 0x00000008 -#define FILE_ACTION_REMOVED_BY_DELETE 0x00000009 -#define FILE_ACTION_ID_NOT_TUNNELLED 0x0000000A -#define FILE_ACTION_TUNNELLED_ID_COLLISION 0x0000000B - /* See MS-SMB2 2.2.35 */ struct smb2_change_notify_req { struct smb2_hdr hdr; @@ -1499,105 +1483,6 @@ struct network_interface_info_ioctl_rsp { }; } __packed; -/* this goes in the ioctl buffer when doing FSCTL_SET_ZERO_DATA */ -struct file_zero_data_information { - __le64 FileOffset; - __le64 BeyondFinalZero; -} __packed; - -/* See MS-FSCC 2.3.7 */ -struct duplicate_extents_to_file { - __u64 PersistentFileHandle; /* source file handle, opaque endianness */ - __u64 VolatileFileHandle; - __le64 SourceFileOffset; - __le64 TargetFileOffset; - __le64 ByteCount; /* Bytes to be copied */ -} __packed; - -/* See MS-FSCC 2.3.9 */ -#define DUPLICATE_EXTENTS_DATA_EX_SOURCE_ATOMIC 0x00000001 -struct duplicate_extents_to_file_ex { - __le64 StructureSize; /* MUST be set to 0x30 */ - __u64 PersistentFileHandle; /* source file handle, opaque endianness */ - __u64 VolatileFileHandle; - __le64 SourceFileOffset; - __le64 TargetFileOffset; - __le64 ByteCount; /* Bytes to be copied */ - __le32 Flags; - __le32 Reserved; -} __packed; - - -/* See MS-FSCC 2.3.20 */ -struct fsctl_get_integrity_information_rsp { - __le16 ChecksumAlgorithm; - __le16 Reserved; - __le32 Flags; - __le32 ChecksumChunkSizeInBytes; - __le32 ClusterSizeInBytes; -} __packed; - -/* See MS-FSCC 2.3.55 */ -struct fsctl_query_file_regions_req { - __le64 FileOffset; - __le64 Length; - __le32 DesiredUsage; - __le32 Reserved; -} __packed; - -/* DesiredUsage flags see MS-FSCC 2.3.56.1 */ -#define FILE_USAGE_INVALID_RANGE 0x00000000 -#define FILE_USAGE_VALID_CACHED_DATA 0x00000001 -#define FILE_USAGE_NONCACHED_DATA 0x00000002 - -struct file_region_info { - __le64 FileOffset; - __le64 Length; - __le32 DesiredUsage; - __le32 Reserved; -} __packed; - -/* See MS-FSCC 2.3.56 */ -struct fsctl_query_file_region_rsp { - __le32 Flags; - __le32 TotalRegionEntryCount; - __le32 RegionEntryCount; - __u32 Reserved; - struct file_region_info Regions[]; -} __packed; - -/* See MS-FSCC 2.3.58 */ -struct fsctl_query_on_disk_vol_info_rsp { - __le64 DirectoryCount; - __le64 FileCount; - __le16 FsFormatMajVersion; - __le16 FsFormatMinVersion; - __u8 FsFormatName[24]; - __le64 FormatTime; - __le64 LastUpdateTime; - __u8 CopyrightInfo[68]; - __u8 AbstractInfo[68]; - __u8 FormatImplInfo[68]; - __u8 LastModifyImplInfo[68]; -} __packed; - -/* See MS-FSCC 2.3.73 */ -struct fsctl_set_integrity_information_req { - __le16 ChecksumAlgorithm; - __le16 Reserved; - __le32 Flags; -} __packed; - -/* See MS-FSCC 2.3.75 */ -struct fsctl_set_integrity_info_ex_req { - __u8 EnableIntegrity; - __u8 KeepState; - __u16 Reserved; - __le32 Flags; - __u8 Version; - __u8 Reserved2[7]; -} __packed; - /* Integrity ChecksumAlgorithm choices for above */ #define CHECKSUM_TYPE_NONE 0x0000 #define CHECKSUM_TYPE_CRC64 0x0002 @@ -1606,72 +1491,6 @@ struct fsctl_set_integrity_info_ex_req { /* Integrity flags for above */ #define FSCTL_INTEGRITY_FLAG_CHECKSUM_ENFORCEMENT_OFF 0x00000001 -/* Reparse structures - see MS-FSCC 2.1.2 */ - -/* struct fsctl_reparse_info_req is empty, only response structs (see below) */ -struct reparse_data_buffer { - __le32 ReparseTag; - __le16 ReparseDataLength; - __u16 Reserved; - __u8 DataBuffer[]; /* Variable Length */ -} __packed; - -struct reparse_guid_data_buffer { - __le32 ReparseTag; - __le16 ReparseDataLength; - __u16 Reserved; - __u8 ReparseGuid[16]; - __u8 DataBuffer[]; /* Variable Length */ -} __packed; - -struct reparse_mount_point_data_buffer { - __le32 ReparseTag; - __le16 ReparseDataLength; - __u16 Reserved; - __le16 SubstituteNameOffset; - __le16 SubstituteNameLength; - __le16 PrintNameOffset; - __le16 PrintNameLength; - __u8 PathBuffer[]; /* Variable Length */ -} __packed; - -#define SYMLINK_FLAG_RELATIVE 0x00000001 - -struct reparse_symlink_data_buffer { - __le32 ReparseTag; - __le16 ReparseDataLength; - __u16 Reserved; - __le16 SubstituteNameOffset; - __le16 SubstituteNameLength; - __le16 PrintNameOffset; - __le16 PrintNameLength; - __le32 Flags; - __u8 PathBuffer[]; /* Variable Length */ -} __packed; - -/* For IO_REPARSE_TAG_NFS - see MS-FSCC 2.1.2.6 */ -#define NFS_SPECFILE_LNK 0x00000000014B4E4C -#define NFS_SPECFILE_CHR 0x0000000000524843 -#define NFS_SPECFILE_BLK 0x00000000004B4C42 -#define NFS_SPECFILE_FIFO 0x000000004F464946 -#define NFS_SPECFILE_SOCK 0x000000004B434F53 -struct reparse_nfs_data_buffer { - __le32 ReparseTag; - __le16 ReparseDataLength; - __u16 Reserved; - __le64 InodeType; /* NFS_SPECFILE_* */ - __u8 DataBuffer[]; -} __packed; - -/* For IO_REPARSE_TAG_LX_SYMLINK - see MS-FSCC 2.1.2.7 */ -struct reparse_wsl_symlink_data_buffer { - __le32 ReparseTag; - __le16 ReparseDataLength; - __u16 Reserved; - __le32 Version; /* Always 2 */ - __u8 Target[]; /* Variable Length UTF-8 string without nul-term */ -} __packed; - struct validate_negotiate_info_req { __le32 Capabilities; __u8 Guid[SMB2_CLIENT_GUID_SIZE]; @@ -1791,84 +1610,6 @@ struct smb2_query_info_rsp { __u8 Buffer[]; } __packed; -/* - * PDU query infolevel structure definitions - */ - -/* See MS-FSCC 2.3.52 */ -struct file_allocated_range_buffer { - __le64 file_offset; - __le64 length; -} __packed; - -struct smb2_file_internal_info { - __le64 IndexNumber; -} __packed; /* level 6 Query */ - -struct smb2_file_rename_info { /* encoding of request for level 10 */ - /* New members MUST be added within the struct_group() macro below. */ - __struct_group(smb2_file_rename_info_hdr, __hdr, __packed, - __u8 ReplaceIfExists; /* 1 = replace existing target with new */ - /* 0 = fail if target already exists */ - __u8 Reserved[7]; - __u64 RootDirectory; /* MBZ for network operations (why says spec?) */ - __le32 FileNameLength; - ); - char FileName[]; /* New name to be assigned */ - /* padding - overall struct size must be >= 24 so filename + pad >= 6 */ -} __packed; /* level 10 Set */ -static_assert(offsetof(struct smb2_file_rename_info, FileName) == sizeof(struct smb2_file_rename_info_hdr), - "struct member likely outside of __struct_group()"); - -struct smb2_file_link_info { /* encoding of request for level 11 */ - /* New members MUST be added within the struct_group() macro below. */ - __struct_group(smb2_file_link_info_hdr, __hdr, __packed, - __u8 ReplaceIfExists; /* 1 = replace existing link with new */ - /* 0 = fail if link already exists */ - __u8 Reserved[7]; - __u64 RootDirectory; /* MBZ for network operations (why says spec?) */ - __le32 FileNameLength; - ); - char FileName[]; /* Name to be assigned to new link */ -} __packed; /* level 11 Set */ -static_assert(offsetof(struct smb2_file_link_info, FileName) == sizeof(struct smb2_file_link_info_hdr), - "struct member likely outside of __struct_group()"); - -/* - * This level 18, although with struct with same name is different from cifs - * level 0x107. Level 0x107 has an extra u64 between AccessFlags and - * CurrentByteOffset. - */ -struct smb2_file_all_info { /* data block encoding of response to level 18 */ - __le64 CreationTime; /* Beginning of FILE_BASIC_INFO equivalent */ - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le32 Attributes; - __u32 Pad1; /* End of FILE_BASIC_INFO_INFO equivalent */ - __le64 AllocationSize; /* Beginning of FILE_STANDARD_INFO equivalent */ - __le64 EndOfFile; /* size ie offset to first free byte in file */ - __le32 NumberOfLinks; /* hard links */ - __u8 DeletePending; - __u8 Directory; - __u16 Pad2; /* End of FILE_STANDARD_INFO equivalent */ - __le64 IndexNumber; - __le32 EASize; - __le32 AccessFlags; - __le64 CurrentByteOffset; - __le32 Mode; - __le32 AlignmentRequirement; - __le32 FileNameLength; - union { - char __pad; /* Legacy structure padding */ - DECLARE_FLEX_ARRAY(char, FileName); - }; -} __packed; /* level 18 Query */ - -struct smb2_file_eof_info { /* encoding of request for level 10 */ - __le64 EndOfFile; /* new end of file value */ -} __packed; /* level 20 Set */ - /* Level 100 query info */ struct smb311_posix_qinfo { __le64 CreationTime; @@ -1894,54 +1635,6 @@ struct smb311_posix_qinfo { */ } __packed; -/* File System Information Classes */ -#define FS_VOLUME_INFORMATION 1 /* Query */ -#define FS_LABEL_INFORMATION 2 /* Set */ -#define FS_SIZE_INFORMATION 3 /* Query */ -#define FS_DEVICE_INFORMATION 4 /* Query */ -#define FS_ATTRIBUTE_INFORMATION 5 /* Query */ -#define FS_CONTROL_INFORMATION 6 /* Query, Set */ -#define FS_FULL_SIZE_INFORMATION 7 /* Query */ -#define FS_OBJECT_ID_INFORMATION 8 /* Query, Set */ -#define FS_DRIVER_PATH_INFORMATION 9 /* Query */ -#define FS_SECTOR_SIZE_INFORMATION 11 /* SMB3 or later. Query */ -#define FS_POSIX_INFORMATION 100 /* SMB3.1.1 POSIX. Query */ - -struct smb2_fs_full_size_info { - __le64 TotalAllocationUnits; - __le64 CallerAvailableAllocationUnits; - __le64 ActualAvailableAllocationUnits; - __le32 SectorsPerAllocationUnit; - __le32 BytesPerSector; -} __packed; - -#define SSINFO_FLAGS_ALIGNED_DEVICE 0x00000001 -#define SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE 0x00000002 -#define SSINFO_FLAGS_NO_SEEK_PENALTY 0x00000004 -#define SSINFO_FLAGS_TRIM_ENABLED 0x00000008 - -/* sector size info struct */ -struct smb3_fs_ss_info { - __le32 LogicalBytesPerSector; - __le32 PhysicalBytesPerSectorForAtomicity; - __le32 PhysicalBytesPerSectorForPerf; - __le32 FSEffPhysicalBytesPerSectorForAtomicity; - __le32 Flags; - __le32 ByteOffsetForSectorAlignment; - __le32 ByteOffsetForPartitionAlignment; -} __packed; - -/* File System Control Information */ -struct smb2_fs_control_info { - __le64 FreeSpaceStartFiltering; - __le64 FreeSpaceThreshold; - __le64 FreeSpaceStopFiltering; - __le64 DefaultQuotaThreshold; - __le64 DefaultQuotaLimit; - __le32 FileSystemControlFlags; - __le32 Padding; -} __packed; - /* volume info struct - see MS-FSCC 2.5.9 */ #define MAX_VOL_LABEL_LEN 32 struct smb3_fs_vol_info { From 31884d4bc981bb7663d33ce594f761796d0f4f55 Mon Sep 17 00:00:00 2001 From: ZhangGuoDong Date: Tue, 3 Mar 2026 15:13:15 +0000 Subject: [PATCH 02/16] smb: move file_basic_info into common/fscc.h This struct definition is specified in MS-FSCC, so move them into fscc.h. Modify the following places: - smb2_file_basic_info -> file_basic_info - Pad1 -> Pad Signed-off-by: ZhangGuoDong Reviewed-by: ChenXiaoSong Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/client/smb1pdu.h | 9 --------- fs/smb/common/fscc.h | 10 ++++++++++ fs/smb/server/smb2pdu.c | 14 +++++++------- fs/smb/server/smb2pdu.h | 9 --------- 4 files changed, 17 insertions(+), 25 deletions(-) diff --git a/fs/smb/client/smb1pdu.h b/fs/smb/client/smb1pdu.h index 97f7e1244a8b..7584e94d9b2b 100644 --- a/fs/smb/client/smb1pdu.h +++ b/fs/smb/client/smb1pdu.h @@ -2061,15 +2061,6 @@ typedef struct { __le32 EASize; } __packed FILE_INFO_STANDARD; /* level 1 SetPath/FileInfo */ -typedef struct { - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le32 Attributes; - __u32 Pad; -} __packed FILE_BASIC_INFO; /* size info, level 0x101 */ - struct file_allocation_info { __le64 AllocationSize; /* Note old Samba srvr rounds this up too much */ } __packed; /* size used on disk, for level 0x103 for set, 0x105 for query */ diff --git a/fs/smb/common/fscc.h b/fs/smb/common/fscc.h index 415cba02d1c9..076cbcffa26a 100644 --- a/fs/smb/common/fscc.h +++ b/fs/smb/common/fscc.h @@ -216,6 +216,16 @@ struct smb2_file_all_info { /* data block encoding of response to level 18 */ }; } __packed; /* level 18 Query */ +/* See MS-FSCC 2.4.7 */ +typedef struct file_basic_info { /* data block encoding of response to level 18 */ + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le32 Attributes; + __u32 Pad; +} __packed FILE_BASIC_INFO; /* size info, level 0x101 */ + /* See MS-FSCC 2.4.8 */ typedef struct { __le32 NextEntryOffset; diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 8e4cfdc0ba02..18b9b14d1753 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -4858,7 +4858,7 @@ static void get_file_access_info(struct smb2_query_info_rsp *rsp, static int get_file_basic_info(struct smb2_query_info_rsp *rsp, struct ksmbd_file *fp, void *rsp_org) { - struct smb2_file_basic_info *basic_info; + struct file_basic_info *basic_info; struct kstat stat; u64 time; int ret; @@ -4874,7 +4874,7 @@ static int get_file_basic_info(struct smb2_query_info_rsp *rsp, if (ret) return ret; - basic_info = (struct smb2_file_basic_info *)rsp->Buffer; + basic_info = (struct file_basic_info *)rsp->Buffer; basic_info->CreationTime = cpu_to_le64(fp->create_time); time = ksmbd_UnixTimeToNT(stat.atime); basic_info->LastAccessTime = cpu_to_le64(time); @@ -4883,9 +4883,9 @@ static int get_file_basic_info(struct smb2_query_info_rsp *rsp, time = ksmbd_UnixTimeToNT(stat.ctime); basic_info->ChangeTime = cpu_to_le64(time); basic_info->Attributes = fp->f_ci->m_fattr; - basic_info->Pad1 = 0; + basic_info->Pad = 0; rsp->OutputBufferLength = - cpu_to_le32(sizeof(struct smb2_file_basic_info)); + cpu_to_le32(sizeof(struct file_basic_info)); return 0; } @@ -6222,7 +6222,7 @@ out: } static int set_file_basic_info(struct ksmbd_file *fp, - struct smb2_file_basic_info *file_info, + struct file_basic_info *file_info, struct ksmbd_share_config *share) { struct iattr attrs; @@ -6504,10 +6504,10 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, switch (req->FileInfoClass) { case FILE_BASIC_INFORMATION: { - if (buf_len < sizeof(struct smb2_file_basic_info)) + if (buf_len < sizeof(struct file_basic_info)) return -EMSGSIZE; - return set_file_basic_info(fp, (struct smb2_file_basic_info *)buffer, share); + return set_file_basic_info(fp, (struct file_basic_info *)buffer, share); } case FILE_ALLOCATION_INFORMATION: { diff --git a/fs/smb/server/smb2pdu.h b/fs/smb/server/smb2pdu.h index 8b6eafb70dca..e7cf573e59f0 100644 --- a/fs/smb/server/smb2pdu.h +++ b/fs/smb/server/smb2pdu.h @@ -186,15 +186,6 @@ struct smb2_file_alignment_info { __le32 AlignmentRequirement; } __packed; -struct smb2_file_basic_info { /* data block encoding of response to level 18 */ - __le64 CreationTime; /* Beginning of FILE_BASIC_INFO equivalent */ - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le32 Attributes; - __u32 Pad1; /* End of FILE_BASIC_INFO_INFO equivalent */ -} __packed; - struct smb2_file_alt_name_info { __le32 FileNameLength; char FileName[]; From 95e1d378ce30af134e6abb4db4e2891864467e39 Mon Sep 17 00:00:00 2001 From: ZhangGuoDong Date: Tue, 3 Mar 2026 15:13:16 +0000 Subject: [PATCH 03/16] smb: move filesystem_vol_info into common/fscc.h The structure definition on the server side is specified in MS-CIFS 2.2.8.2.3, but we should instead refer to MS-FSCC 2.5.9, just as the client side does. Modify the following places: - smb3_fs_vol_info -> filesystem_vol_info - SerialNumber -> VolumeSerialNumber - VolumeLabelSize -> VolumeLabelLength Then move it into common header file. Signed-off-by: ZhangGuoDong Reviewed-by: ChenXiaoSong Reviewed-by: Steve French Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/client/smb2pdu.c | 8 ++++---- fs/smb/common/fscc.h | 11 +++++++++++ fs/smb/common/smb2pdu.h | 11 ----------- fs/smb/server/smb2pdu.c | 5 +++-- fs/smb/server/smb_common.h | 8 -------- 5 files changed, 18 insertions(+), 25 deletions(-) diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index 5188218c25be..59d7418cc480 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -6147,8 +6147,8 @@ replay_again: max_len = sizeof(struct smb3_fs_ss_info); min_len = sizeof(struct smb3_fs_ss_info); } else if (level == FS_VOLUME_INFORMATION) { - max_len = sizeof(struct smb3_fs_vol_info) + MAX_VOL_LABEL_LEN; - min_len = sizeof(struct smb3_fs_vol_info); + max_len = sizeof(struct filesystem_vol_info) + MAX_VOL_LABEL_LEN; + min_len = sizeof(struct filesystem_vol_info); } else { cifs_dbg(FYI, "Invalid qfsinfo level %d\n", level); return -EINVAL; @@ -6203,9 +6203,9 @@ replay_again: tcon->perf_sector_size = le32_to_cpu(ss_info->PhysicalBytesPerSectorForPerf); } else if (level == FS_VOLUME_INFORMATION) { - struct smb3_fs_vol_info *vol_info = (struct smb3_fs_vol_info *) + struct filesystem_vol_info *vol_info = (struct filesystem_vol_info *) (offset + (char *)rsp); - tcon->vol_serial_number = vol_info->VolumeSerialNumber; + tcon->vol_serial_number = le32_to_cpu(vol_info->VolumeSerialNumber); tcon->vol_create_time = vol_info->VolumeCreationTime; } diff --git a/fs/smb/common/fscc.h b/fs/smb/common/fscc.h index 076cbcffa26a..b4ccddca9256 100644 --- a/fs/smb/common/fscc.h +++ b/fs/smb/common/fscc.h @@ -447,6 +447,17 @@ typedef struct { __le32 BytesPerSector; } __packed FILE_SYSTEM_SIZE_INFO; /* size info, level 0x103 */ +/* volume info struct - see MS-FSCC 2.5.9 */ +#define MAX_VOL_LABEL_LEN 32 +struct filesystem_vol_info { + __le64 VolumeCreationTime; + __le32 VolumeSerialNumber; + __le32 VolumeLabelLength; /* includes trailing null */ + __u8 SupportsObjects; /* True if eg like NTFS, supports objects */ + __u8 Reserved; + __u8 VolumeLabel[]; /* variable len */ +} __packed; + /* See MS-FSCC 2.5.10 */ typedef struct { __le32 DeviceType; diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h index 538e7a12ea93..85a4248d4f29 100644 --- a/fs/smb/common/smb2pdu.h +++ b/fs/smb/common/smb2pdu.h @@ -1635,17 +1635,6 @@ struct smb311_posix_qinfo { */ } __packed; -/* volume info struct - see MS-FSCC 2.5.9 */ -#define MAX_VOL_LABEL_LEN 32 -struct smb3_fs_vol_info { - __le64 VolumeCreationTime; - __u32 VolumeSerialNumber; - __le32 VolumeLabelLength; /* includes trailing null */ - __u8 SupportsObjects; /* True if eg like NTFS, supports objects */ - __u8 Reserved; - __u8 VolumeLabel[]; /* variable len */ -} __packed; - /* See MS-SMB2 2.2.23 through 2.2.25 */ struct smb2_oplock_break { struct smb2_hdr hdr; diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 18b9b14d1753..764a78dec46a 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -5583,13 +5583,14 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, serial_crc = crc32_le(serial_crc, ksmbd_netbios_name(), strlen(ksmbd_netbios_name())); /* Taking dummy value of serial number*/ - info->SerialNumber = cpu_to_le32(serial_crc); + info->VolumeSerialNumber = cpu_to_le32(serial_crc); len = smbConvertToUTF16((__le16 *)info->VolumeLabel, share->name, PATH_MAX, conn->local_nls, 0); len = len * 2; - info->VolumeLabelSize = cpu_to_le32(len); + info->VolumeLabelLength = cpu_to_le32(len); info->Reserved = 0; + info->SupportsObjects = 0; sz = sizeof(struct filesystem_vol_info) + len; rsp->OutputBufferLength = cpu_to_le32(sz); break; diff --git a/fs/smb/server/smb_common.h b/fs/smb/server/smb_common.h index ca7e3610d074..b090b56743c4 100644 --- a/fs/smb/server/smb_common.h +++ b/fs/smb/server/smb_common.h @@ -90,14 +90,6 @@ struct smb_negotiate_rsp { __le16 ByteCount; } __packed; -struct filesystem_vol_info { - __le64 VolumeCreationTime; - __le32 SerialNumber; - __le32 VolumeLabelSize; - __le16 Reserved; - __le16 VolumeLabel[]; -} __packed; - #define EXTENDED_INFO_MAGIC 0x43667364 /* Cfsd */ #define STRING_LENGTH 28 From 3df614ebc976bb23d2f99734695c1b7ff126d7fc Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Thu, 5 Mar 2026 22:35:22 -0800 Subject: [PATCH 04/16] ksmbd: ipc: use kzalloc_flex and __counted_by The former is just a nice macro and the latter allows runtime analysis of the allocation and its size. Signed-off-by: Rosen Penev Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/transport_ipc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fs/smb/server/transport_ipc.c b/fs/smb/server/transport_ipc.c index 2dbabe2d8005..f7aa427a06fe 100644 --- a/fs/smb/server/transport_ipc.c +++ b/fs/smb/server/transport_ipc.c @@ -55,7 +55,7 @@ static bool ksmbd_ipc_validate_version(struct genl_info *m) struct ksmbd_ipc_msg { unsigned int type; unsigned int sz; - unsigned char payload[]; + unsigned char payload[] __counted_by(sz); }; struct ipc_msg_table_entry { @@ -242,9 +242,8 @@ static void ipc_update_last_active(void) static struct ksmbd_ipc_msg *ipc_msg_alloc(size_t sz) { struct ksmbd_ipc_msg *msg; - size_t msg_sz = sz + sizeof(struct ksmbd_ipc_msg); - msg = kvzalloc(msg_sz, KSMBD_DEFAULT_GFP); + msg = kvzalloc_flex(*msg, payload, sz, KSMBD_DEFAULT_GFP); if (msg) msg->sz = sz; return msg; From 235e32320a470fcd3998fb3774f2290a0eb302a1 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sat, 4 Apr 2026 21:09:02 +0900 Subject: [PATCH 05/16] ksmbd: fix use-after-free in __ksmbd_close_fd() via durable scavenger When a durable file handle survives session disconnect (TCP close without SMB2_LOGOFF), session_fd_check() sets fp->conn = NULL to preserve the handle for later reconnection. However, it did not clean up the byte-range locks on fp->lock_list. Later, when the durable scavenger thread times out and calls __ksmbd_close_fd(NULL, fp), the lock cleanup loop did: spin_lock(&fp->conn->llist_lock); This caused a slab use-after-free because fp->conn was NULL and the original connection object had already been freed by ksmbd_tcp_disconnect(). The root cause is asymmetric cleanup: lock entries (smb_lock->clist) were left dangling on the freed conn->lock_list while fp->conn was nulled out. To fix this issue properly, we need to handle the lifetime of smb_lock->clist across three paths: - Safely skip clist deletion when list is empty and fp->conn is NULL. - Remove the lock from the old connection's lock_list in session_fd_check() - Re-add the lock to the new connection's lock_list in ksmbd_reopen_durable_fd(). Fixes: c8efcc786146 ("ksmbd: add support for durable handles v1/v2") Co-developed-by: munan Huang Signed-off-by: munan Huang Reviewed-by: ChenXiaoSong Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/vfs_cache.c | 41 ++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index 168f2dd7e200..87f63525062b 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -463,9 +463,11 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) * there are not accesses to fp->lock_list. */ list_for_each_entry_safe(smb_lock, tmp_lock, &fp->lock_list, flist) { - spin_lock(&fp->conn->llist_lock); - list_del(&smb_lock->clist); - spin_unlock(&fp->conn->llist_lock); + if (!list_empty(&smb_lock->clist) && fp->conn) { + spin_lock(&fp->conn->llist_lock); + list_del(&smb_lock->clist); + spin_unlock(&fp->conn->llist_lock); + } list_del(&smb_lock->flist); locks_free_lock(smb_lock->fl); @@ -995,6 +997,7 @@ static bool session_fd_check(struct ksmbd_tree_connect *tcon, struct ksmbd_inode *ci; struct oplock_info *op; struct ksmbd_conn *conn; + struct ksmbd_lock *smb_lock, *tmp_lock; if (!is_reconnectable(fp)) return false; @@ -1011,6 +1014,12 @@ static bool session_fd_check(struct ksmbd_tree_connect *tcon, } up_write(&ci->m_lock); + list_for_each_entry_safe(smb_lock, tmp_lock, &fp->lock_list, flist) { + spin_lock(&fp->conn->llist_lock); + list_del_init(&smb_lock->clist); + spin_unlock(&fp->conn->llist_lock); + } + fp->conn = NULL; fp->tcon = NULL; fp->volatile_id = KSMBD_NO_FID; @@ -1090,6 +1099,9 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp) { struct ksmbd_inode *ci; struct oplock_info *op; + struct ksmbd_conn *conn = work->conn; + struct ksmbd_lock *smb_lock; + unsigned int old_f_state; if (!fp->is_durable || fp->conn || fp->tcon) { pr_err("Invalid durable fd [%p:%p]\n", fp->conn, fp->tcon); @@ -1101,9 +1113,23 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp) return -EBADF; } - fp->conn = work->conn; + old_f_state = fp->f_state; + fp->f_state = FP_NEW; + __open_id(&work->sess->file_table, fp, OPEN_ID_TYPE_VOLATILE_ID); + if (!has_file_id(fp->volatile_id)) { + fp->f_state = old_f_state; + return -EBADF; + } + + fp->conn = conn; fp->tcon = work->tcon; + list_for_each_entry(smb_lock, &fp->lock_list, flist) { + spin_lock(&conn->llist_lock); + list_add_tail(&smb_lock->clist, &conn->lock_list); + spin_unlock(&conn->llist_lock); + } + ci = fp->f_ci; down_write(&ci->m_lock); list_for_each_entry_rcu(op, &ci->m_op_list, op_entry) { @@ -1114,13 +1140,6 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp) } up_write(&ci->m_lock); - fp->f_state = FP_NEW; - __open_id(&work->sess->file_table, fp, OPEN_ID_TYPE_VOLATILE_ID); - if (!has_file_id(fp->volatile_id)) { - fp->conn = NULL; - fp->tcon = NULL; - return -EBADF; - } return 0; } From 49110a8ce654bbe56bef7c5e44cce31f4b102b8a Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sun, 12 Apr 2026 22:16:16 +0900 Subject: [PATCH 06/16] ksmbd: validate owner of durable handle on reconnect Currently, ksmbd does not verify if the user attempting to reconnect to a durable handle is the same user who originally opened the file. This allows any authenticated user to hijack an orphaned durable handle by predicting or brute-forcing the persistent ID. According to MS-SMB2, the server MUST verify that the SecurityContext of the reconnect request matches the SecurityContext associated with the existing open. Add a durable_owner structure to ksmbd_file to store the original opener's UID, GID, and account name. and catpure the owner information when a file handle becomes orphaned. and implementing ksmbd_vfs_compare_durable_owner() to validate the identity of the requester during SMB2_CREATE (DHnC). Fixes: c8efcc786146 ("ksmbd: add support for durable handles v1/v2") Reported-by: Davide Ornaghi Reported-by: Navaneeth K Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/mgmt/user_session.c | 8 ++- fs/smb/server/oplock.c | 7 +++ fs/smb/server/oplock.h | 1 + fs/smb/server/smb2pdu.c | 3 +- fs/smb/server/vfs_cache.c | 87 +++++++++++++++++++++++++++---- fs/smb/server/vfs_cache.h | 12 ++++- 6 files changed, 102 insertions(+), 16 deletions(-) diff --git a/fs/smb/server/mgmt/user_session.c b/fs/smb/server/mgmt/user_session.c index 39be2d2be86c..a86589408835 100644 --- a/fs/smb/server/mgmt/user_session.c +++ b/fs/smb/server/mgmt/user_session.c @@ -382,12 +382,10 @@ void ksmbd_session_destroy(struct ksmbd_session *sess) return; delete_proc_session(sess); - + ksmbd_tree_conn_session_logoff(sess); + ksmbd_destroy_file_table(sess); if (sess->user) ksmbd_free_user(sess->user); - - ksmbd_tree_conn_session_logoff(sess); - ksmbd_destroy_file_table(&sess->file_table); ksmbd_launch_ksmbd_durable_scavenger(); ksmbd_session_rpc_clear_list(sess); free_channel_list(sess); @@ -618,7 +616,7 @@ void destroy_previous_session(struct ksmbd_conn *conn, goto out; } - ksmbd_destroy_file_table(&prev_sess->file_table); + ksmbd_destroy_file_table(prev_sess); prev_sess->state = SMB2_SESSION_EXPIRED; ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_SETUP); ksmbd_launch_ksmbd_durable_scavenger(); diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c index 9b2bb8764a80..cd3f28b0e7cb 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -1841,6 +1841,7 @@ int smb2_check_durable_oplock(struct ksmbd_conn *conn, struct ksmbd_share_config *share, struct ksmbd_file *fp, struct lease_ctx_info *lctx, + struct ksmbd_user *user, char *name) { struct oplock_info *opinfo = opinfo_get(fp); @@ -1849,6 +1850,12 @@ int smb2_check_durable_oplock(struct ksmbd_conn *conn, if (!opinfo) return 0; + if (ksmbd_vfs_compare_durable_owner(fp, user) == false) { + ksmbd_debug(SMB, "Durable handle reconnect failed: owner mismatch\n"); + ret = -EBADF; + goto out; + } + if (opinfo->is_lease == false) { if (lctx) { pr_err("create context include lease\n"); diff --git a/fs/smb/server/oplock.h b/fs/smb/server/oplock.h index 921e3199e4df..d91a8266e065 100644 --- a/fs/smb/server/oplock.h +++ b/fs/smb/server/oplock.h @@ -126,5 +126,6 @@ int smb2_check_durable_oplock(struct ksmbd_conn *conn, struct ksmbd_share_config *share, struct ksmbd_file *fp, struct lease_ctx_info *lctx, + struct ksmbd_user *user, char *name); #endif /* __KSMBD_OPLOCK_H */ diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 764a78dec46a..d0b05878757e 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -3013,7 +3013,8 @@ int smb2_open(struct ksmbd_work *work) } if (dh_info.reconnected == true) { - rc = smb2_check_durable_oplock(conn, share, dh_info.fp, lc, name); + rc = smb2_check_durable_oplock(conn, share, dh_info.fp, + lc, sess->user, name); if (rc) { ksmbd_put_durable_fd(dh_info.fp); goto err_out2; diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index 87f63525062b..3551f01a3fa0 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -19,6 +19,7 @@ #include "misc.h" #include "mgmt/tree_connect.h" #include "mgmt/user_session.h" +#include "mgmt/user_config.h" #include "smb_common.h" #include "server.h" #include "smb2pdu.h" @@ -476,6 +477,8 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) if (ksmbd_stream_fd(fp)) kfree(fp->stream.name); + kfree(fp->owner.name); + kmem_cache_free(filp_cache, fp); } @@ -787,11 +790,13 @@ void ksmbd_update_fstate(struct ksmbd_file_table *ft, struct ksmbd_file *fp, } static int -__close_file_table_ids(struct ksmbd_file_table *ft, +__close_file_table_ids(struct ksmbd_session *sess, struct ksmbd_tree_connect *tcon, bool (*skip)(struct ksmbd_tree_connect *tcon, - struct ksmbd_file *fp)) + struct ksmbd_file *fp, + struct ksmbd_user *user)) { + struct ksmbd_file_table *ft = &sess->file_table; struct ksmbd_file *fp; unsigned int id = 0; int num = 0; @@ -804,7 +809,7 @@ __close_file_table_ids(struct ksmbd_file_table *ft, break; } - if (skip(tcon, fp) || + if (skip(tcon, fp, sess->user) || !atomic_dec_and_test(&fp->refcount)) { id++; write_unlock(&ft->lock); @@ -856,7 +861,8 @@ static inline bool is_reconnectable(struct ksmbd_file *fp) } static bool tree_conn_fd_check(struct ksmbd_tree_connect *tcon, - struct ksmbd_file *fp) + struct ksmbd_file *fp, + struct ksmbd_user *user) { return fp->tcon != tcon; } @@ -991,8 +997,62 @@ void ksmbd_stop_durable_scavenger(void) kthread_stop(server_conf.dh_task); } +/* + * ksmbd_vfs_copy_durable_owner - Copy owner info for durable reconnect + * @fp: ksmbd file pointer to store owner info + * @user: user pointer to copy from + * + * This function binds the current user's identity to the file handle + * to satisfy MS-SMB2 Step 8 (SecurityContext matching) during reconnect. + * + * Return: 0 on success, or negative error code on failure + */ +static int ksmbd_vfs_copy_durable_owner(struct ksmbd_file *fp, + struct ksmbd_user *user) +{ + if (!user) + return -EINVAL; + + /* Duplicate the user name to ensure identity persistence */ + fp->owner.name = kstrdup(user->name, GFP_KERNEL); + if (!fp->owner.name) + return -ENOMEM; + + fp->owner.uid = user->uid; + fp->owner.gid = user->gid; + + return 0; +} + +/** + * ksmbd_vfs_compare_durable_owner - Verify if the requester is original owner + * @fp: existing ksmbd file pointer + * @user: user pointer of the reconnect requester + * + * Compares the UID, GID, and name of the current requester against the + * original owner stored in the file handle. + * + * Return: true if the user matches, false otherwise + */ +bool ksmbd_vfs_compare_durable_owner(struct ksmbd_file *fp, + struct ksmbd_user *user) +{ + if (!user || !fp->owner.name) + return false; + + /* Check if the UID and GID match first (fast path) */ + if (fp->owner.uid != user->uid || fp->owner.gid != user->gid) + return false; + + /* Validate the account name to ensure the same SecurityContext */ + if (strcmp(fp->owner.name, user->name)) + return false; + + return true; +} + static bool session_fd_check(struct ksmbd_tree_connect *tcon, - struct ksmbd_file *fp) + struct ksmbd_file *fp, struct ksmbd_user *user) { struct ksmbd_inode *ci; struct oplock_info *op; @@ -1002,6 +1062,9 @@ static bool session_fd_check(struct ksmbd_tree_connect *tcon, if (!is_reconnectable(fp)) return false; + if (ksmbd_vfs_copy_durable_owner(fp, user)) + return false; + conn = fp->conn; ci = fp->f_ci; down_write(&ci->m_lock); @@ -1033,7 +1096,7 @@ static bool session_fd_check(struct ksmbd_tree_connect *tcon, void ksmbd_close_tree_conn_fds(struct ksmbd_work *work) { - int num = __close_file_table_ids(&work->sess->file_table, + int num = __close_file_table_ids(work->sess, work->tcon, tree_conn_fd_check); @@ -1042,7 +1105,7 @@ void ksmbd_close_tree_conn_fds(struct ksmbd_work *work) void ksmbd_close_session_fds(struct ksmbd_work *work) { - int num = __close_file_table_ids(&work->sess->file_table, + int num = __close_file_table_ids(work->sess, work->tcon, session_fd_check); @@ -1140,6 +1203,10 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp) } up_write(&ci->m_lock); + fp->owner.uid = fp->owner.gid = 0; + kfree(fp->owner.name); + fp->owner.name = NULL; + return 0; } @@ -1154,12 +1221,14 @@ int ksmbd_init_file_table(struct ksmbd_file_table *ft) return 0; } -void ksmbd_destroy_file_table(struct ksmbd_file_table *ft) +void ksmbd_destroy_file_table(struct ksmbd_session *sess) { + struct ksmbd_file_table *ft = &sess->file_table; + if (!ft->idr) return; - __close_file_table_ids(ft, NULL, session_fd_check); + __close_file_table_ids(sess, NULL, session_fd_check); idr_destroy(ft->idr); kfree(ft->idr); ft->idr = NULL; diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h index 78b506c5ef03..866f32c10d4d 100644 --- a/fs/smb/server/vfs_cache.h +++ b/fs/smb/server/vfs_cache.h @@ -68,6 +68,13 @@ enum { FP_CLOSED }; +/* Owner information for durable handle reconnect */ +struct durable_owner { + unsigned int uid; + unsigned int gid; + char *name; +}; + struct ksmbd_file { struct file *filp; u64 persistent_id; @@ -114,6 +121,7 @@ struct ksmbd_file { bool is_resilient; bool is_posix_ctxt; + struct durable_owner owner; }; static inline void set_ctx_actor(struct dir_context *ctx, @@ -140,7 +148,7 @@ static inline bool ksmbd_stream_fd(struct ksmbd_file *fp) } int ksmbd_init_file_table(struct ksmbd_file_table *ft); -void ksmbd_destroy_file_table(struct ksmbd_file_table *ft); +void ksmbd_destroy_file_table(struct ksmbd_session *sess); int ksmbd_close_fd(struct ksmbd_work *work, u64 id); struct ksmbd_file *ksmbd_lookup_fd_fast(struct ksmbd_work *work, u64 id); struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, u64 id); @@ -166,6 +174,8 @@ void ksmbd_free_global_file_table(void); void ksmbd_set_fd_limit(unsigned long limit); void ksmbd_update_fstate(struct ksmbd_file_table *ft, struct ksmbd_file *fp, unsigned int state); +bool ksmbd_vfs_compare_durable_owner(struct ksmbd_file *fp, + struct ksmbd_user *user); /* * INODE hash From 1c137636c9fd1f65fb1a0246abb1af3f906dd5ec Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 4 Apr 2026 12:43:38 -0700 Subject: [PATCH 07/16] ksmbd: Remove unnecessary selection of CRYPTO_ECB Since the SMB server never uses any ecb(...) algorithm from the crypto_skcipher API, selecting CRYPTO_ECB is unnecessary. Remove it along with the unused CRYPTO_BLK_* constants. Signed-off-by: Eric Biggers Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/Kconfig | 1 - fs/smb/server/crypto_ctx.h | 5 ----- fs/smb/server/server.c | 1 - 3 files changed, 7 deletions(-) diff --git a/fs/smb/server/Kconfig b/fs/smb/server/Kconfig index 12594879cb64..96aa8e2a8770 100644 --- a/fs/smb/server/Kconfig +++ b/fs/smb/server/Kconfig @@ -7,7 +7,6 @@ config SMB_SERVER select NLS_UTF8 select NLS_UCS2_UTILS select CRYPTO - select CRYPTO_ECB select CRYPTO_LIB_ARC4 select CRYPTO_LIB_DES select CRYPTO_LIB_MD5 diff --git a/fs/smb/server/crypto_ctx.h b/fs/smb/server/crypto_ctx.h index b9476ed520ae..27fd553d10aa 100644 --- a/fs/smb/server/crypto_ctx.h +++ b/fs/smb/server/crypto_ctx.h @@ -20,11 +20,6 @@ enum { CRYPTO_AEAD_MAX, }; -enum { - CRYPTO_BLK_ECBDES = 32, - CRYPTO_BLK_MAX, -}; - struct ksmbd_crypto_ctx { struct list_head list; diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c index 422d9d978285..d8893079abdb 100644 --- a/fs/smb/server/server.c +++ b/fs/smb/server/server.c @@ -629,7 +629,6 @@ static void __exit ksmbd_server_exit(void) MODULE_AUTHOR("Namjae Jeon "); MODULE_DESCRIPTION("Linux kernel CIFS/SMB SERVER"); MODULE_LICENSE("GPL"); -MODULE_SOFTDEP("pre: ecb"); MODULE_SOFTDEP("pre: nls"); MODULE_SOFTDEP("pre: aes"); MODULE_SOFTDEP("pre: cmac"); From 66751841212c2cc196577453c37f7774ff363f02 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 6 Apr 2026 15:46:46 +0200 Subject: [PATCH 08/16] ksmbd: validate EaNameLength in smb2_get_ea() smb2_get_ea() reads ea_req->EaNameLength from the client request and passes it directly to strncmp() as the comparison length without verifying that the length of the name really is the size of the input buffer received. Fix this up by properly checking the size of the name based on the value received and the overall size of the request, to prevent a later strncmp() call to use the length as a "trusted" size of the buffer. Without this check, uninitialized heap values might be slowly leaked to the client. Cc: Namjae Jeon Cc: Steve French Cc: Sergey Senozhatsky Cc: Tom Talpey Cc: linux-cifs@vger.kernel.org Cc: Assisted-by: gregkh_clanker_t1000 Signed-off-by: Greg Kroah-Hartman Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index d0b05878757e..c61fcc88d6f2 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -4717,6 +4717,11 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp, ea_req = (struct smb2_ea_info_req *)((char *)req + le16_to_cpu(req->InputBufferOffset)); + + if (le32_to_cpu(req->InputBufferLength) < + offsetof(struct smb2_ea_info_req, name) + + ea_req->EaNameLength) + return -EINVAL; } else { /* need to send all EAs, if no specific EA is requested*/ if (le32_to_cpu(req->Flags) & SL_RETURN_SINGLE_ENTRY) From 53370cf9090777774e07fd9a8ebce67c6cc333ab Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 6 Apr 2026 15:46:47 +0200 Subject: [PATCH 09/16] ksmbd: require 3 sub-authorities before reading sub_auth[2] parse_dacl() compares each ACE SID against sid_unix_NFS_mode and on match reads sid.sub_auth[2] as the file mode. If sid_unix_NFS_mode is the prefix S-1-5-88-3 with num_subauth = 2 then compare_sids() compares only min(num_subauth, 2) sub-authorities so a client SID with num_subauth = 2 and sub_auth = {88, 3} will match. If num_subauth = 2 and the ACE is placed at the very end of the security descriptor, sub_auth[2] will be 4 bytes past end_of_acl. The out-of-band bytes will then be masked to the low 9 bits and applied as the file's POSIX mode, probably not something that is good to have happen. Fix this up by forcing the SID to actually carry a third sub-authority before reading it at all. Cc: Namjae Jeon Cc: Steve French Cc: Sergey Senozhatsky Cc: Tom Talpey Cc: linux-cifs@vger.kernel.org Cc: Assisted-by: gregkh_clanker_t1000 Signed-off-by: Greg Kroah-Hartman Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smbacl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c index c30d01877c41..061a305bf9c8 100644 --- a/fs/smb/server/smbacl.c +++ b/fs/smb/server/smbacl.c @@ -451,7 +451,8 @@ static void parse_dacl(struct mnt_idmap *idmap, ppace[i]->access_req = smb_map_generic_desired_access(ppace[i]->access_req); - if (!(compare_sids(&ppace[i]->sid, &sid_unix_NFS_mode))) { + if (ppace[i]->sid.num_subauth >= 3 && + !(compare_sids(&ppace[i]->sid, &sid_unix_NFS_mode))) { fattr->cf_mode = le32_to_cpu(ppace[i]->sid.sub_auth[2]); break; From ad0057fb91218914d6c98268718ceb9d59b388e1 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 6 Apr 2026 15:46:48 +0200 Subject: [PATCH 10/16] ksmbd: fix mechToken leak when SPNEGO decode fails after token alloc The kernel ASN.1 BER decoder calls action callbacks incrementally as it walks the input. When ksmbd_decode_negTokenInit() reaches the mechToken [2] OCTET STRING element, ksmbd_neg_token_alloc() allocates conn->mechToken immediately via kmemdup_nul(). If a later element in the same blob is malformed, then the decoder will return nonzero after the allocation is already live. This could happen if mechListMIC [3] overrunse the enclosing SEQUENCE. decode_negotiation_token() then sets conn->use_spnego = false because both the negTokenInit and negTokenTarg grammars failed. The cleanup at the bottom of smb2_sess_setup() is gated on use_spnego: if (conn->use_spnego && conn->mechToken) { kfree(conn->mechToken); conn->mechToken = NULL; } so the kfree is skipped, causing the mechToken to never be freed. This codepath is reachable pre-authentication, so untrusted clients can cause slow memory leaks on a server without even being properly authenticated. Fix this up by not checking check for use_spnego, as it's not required, so the memory will always be properly freed. At the same time, always free the memory in ksmbd_conn_free() incase some other failure path forgot to free it. Cc: Namjae Jeon Cc: Steve French Cc: Sergey Senozhatsky Cc: Tom Talpey Cc: linux-cifs@vger.kernel.org Cc: Assisted-by: gregkh_clanker_t1000 Signed-off-by: Greg Kroah-Hartman Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/connection.c | 1 + fs/smb/server/smb2pdu.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c index 1bb2081c492c..26cfce344861 100644 --- a/fs/smb/server/connection.c +++ b/fs/smb/server/connection.c @@ -96,6 +96,7 @@ void ksmbd_conn_free(struct ksmbd_conn *conn) xa_destroy(&conn->sessions); kvfree(conn->request_buf); kfree(conn->preauth_info); + kfree(conn->mechToken); if (atomic_dec_and_test(&conn->refcnt)) { conn->transport->ops->free_transport(conn->transport); kfree(conn); diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index c61fcc88d6f2..10ae77dae5a1 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -1915,7 +1915,7 @@ out_err: else if (rc) rsp->hdr.Status = STATUS_LOGON_FAILURE; - if (conn->use_spnego && conn->mechToken) { + if (conn->mechToken) { kfree(conn->mechToken); conn->mechToken = NULL; } From 3e298897f41c61450c2e7a4f457e8b2485eb35b3 Mon Sep 17 00:00:00 2001 From: Joshua Klinesmith Date: Mon, 6 Apr 2026 22:31:12 -0400 Subject: [PATCH 11/16] ksmbd: fix use-after-free from async crypto on Qualcomm crypto engine ksmbd_crypt_message() sets a NULL completion callback on AEAD requests and does not handle the -EINPROGRESS return code from async hardware crypto engines like the Qualcomm Crypto Engine (QCE). When QCE returns -EINPROGRESS, ksmbd treats it as an error and immediately frees the request while the hardware DMA operation is still in flight. The DMA completion callback then dereferences freed memory, causing a NULL pointer crash: pc : qce_skcipher_done+0x24/0x174 lr : vchan_complete+0x230/0x27c ... el1h_64_irq+0x68/0x6c ksmbd_free_work_struct+0x20/0x118 [ksmbd] ksmbd_exit_file_cache+0x694/0xa4c [ksmbd] Use the standard crypto_wait_req() pattern with crypto_req_done() as the completion callback, matching the approach used by the SMB client in fs/smb/client/smb2ops.c. This properly handles both synchronous engines (immediate return) and async engines (-EINPROGRESS followed by callback notification). Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3") Link: https://github.com/openwrt/openwrt/issues/21822 Signed-off-by: Joshua Klinesmith Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/auth.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/fs/smb/server/auth.c b/fs/smb/server/auth.c index af5f40304331..7d0691f7263f 100644 --- a/fs/smb/server/auth.c +++ b/fs/smb/server/auth.c @@ -827,6 +827,7 @@ int ksmbd_crypt_message(struct ksmbd_work *work, struct kvec *iov, struct smb2_transform_hdr *tr_hdr = smb_get_msg(iov[0].iov_base); unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20; int rc; + DECLARE_CRYPTO_WAIT(wait); struct scatterlist *sg; u8 sign[SMB2_SIGNATURE_SIZE] = {}; u8 key[SMB3_ENC_DEC_KEY_SIZE]; @@ -913,12 +914,12 @@ int ksmbd_crypt_message(struct ksmbd_work *work, struct kvec *iov, aead_request_set_crypt(req, sg, sg, crypt_len, iv); aead_request_set_ad(req, assoc_data_len); - aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP, NULL, NULL); + aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | + CRYPTO_TFM_REQ_MAY_SLEEP, + crypto_req_done, &wait); - if (enc) - rc = crypto_aead_encrypt(req); - else - rc = crypto_aead_decrypt(req); + rc = crypto_wait_req(enc ? crypto_aead_encrypt(req) : + crypto_aead_decrypt(req), &wait); if (rc) goto free_iv; From 27b7c3e916218b5eb2ee350211140e961bfc49be Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 10 Apr 2026 12:48:54 +0200 Subject: [PATCH 12/16] smb: client: avoid double-free in smbd_free_send_io() after smbd_send_batch_flush() smbd_send_batch_flush() already calls smbd_free_send_io(), so we should not call it again after smbd_post_send() moved it to the batch list. Reported-by: Ruikai Peng Closes: https://lore.kernel.org/linux-cifs/CAFD3drNOSJ05y3A+jNXSDxW-2w09KHQ0DivhxQ_pcc7immVVOQ@mail.gmail.com/ Fixes: 21538121efe6 ("smb: client: make use of smbdirect_socket.send_io.bcredits") Cc: stable@kernel.org Cc: Steve French Cc: Tom Talpey Cc: Long Li Cc: Ruikai Peng Cc: Sergey Senozhatsky Cc: linux-cifs@vger.kernel.org Cc: samba-technical@lists.samba.org Cc: security@kernel.org Acked-by: Paulo Alcantara (Red Hat) Acked-by: Namjae Jeon Signed-off-by: Stefan Metzmacher Tested-by: Ruikai Peng Signed-off-by: Steve French --- fs/smb/client/smbdirect.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fs/smb/client/smbdirect.c b/fs/smb/client/smbdirect.c index c79304012b08..461658105013 100644 --- a/fs/smb/client/smbdirect.c +++ b/fs/smb/client/smbdirect.c @@ -1551,17 +1551,25 @@ static int smbd_post_send_iter(struct smbdirect_socket *sc, rc = smbd_post_send(sc, batch, request); if (!rc) { + /* + * From here request is moved to batch + * and we should not free it explicitly. + */ + if (batch != &_batch) return 0; rc = smbd_send_batch_flush(sc, batch, true); if (!rc) return 0; + + goto err_flush; } err_dma: smbd_free_send_io(request); +err_flush: err_alloc: atomic_inc(&sc->send_io.credits.count); wake_up(&sc->send_io.credits.wait_queue); From 84ff995ae826aa6bbcc6c7b9ea569ff67c021d72 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 10 Apr 2026 12:48:54 +0200 Subject: [PATCH 13/16] smb: server: avoid double-free in smb_direct_free_sendmsg after smb_direct_flush_send_list() smb_direct_flush_send_list() already calls smb_direct_free_sendmsg(), so we should not call it again after post_sendmsg() moved it to the batch list. Reported-by: Ruikai Peng Closes: https://lore.kernel.org/linux-cifs/CAFD3drNOSJ05y3A+jNXSDxW-2w09KHQ0DivhxQ_pcc7immVVOQ@mail.gmail.com/ Fixes: 34abd408c8ba ("smb: server: make use of smbdirect_socket.send_io.bcredits") Cc: stable@kernel.org Cc: Steve French Cc: Tom Talpey Cc: Ruikai Peng Cc: Sergey Senozhatsky Cc: linux-cifs@vger.kernel.org Cc: samba-technical@lists.samba.org Cc: security@kernel.org Signed-off-by: Stefan Metzmacher Acked-by: Namjae Jeon Acked-by: Paulo Alcantara (Red Hat) Tested-by: Ruikai Peng Signed-off-by: Steve French --- fs/smb/server/transport_rdma.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/fs/smb/server/transport_rdma.c b/fs/smb/server/transport_rdma.c index 188572491d53..dbc8dedb85dc 100644 --- a/fs/smb/server/transport_rdma.c +++ b/fs/smb/server/transport_rdma.c @@ -1588,15 +1588,21 @@ static int smb_direct_post_send_data(struct smbdirect_socket *sc, if (ret) goto err; + /* + * From here msg is moved to send_ctx + * and we should not free it explicitly. + */ + if (send_ctx == &_send_ctx) { ret = smb_direct_flush_send_list(sc, send_ctx, true); if (ret) - goto err; + goto flush_failed; } return 0; err: smb_direct_free_sendmsg(sc, msg); +flush_failed: header_failed: atomic_inc(&sc->send_io.credits.count); credit_failed: From 731a5302bedc5e2ce41b2c87e7aaaab00a6dcfa7 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 27 Oct 2025 16:51:12 +0100 Subject: [PATCH 14/16] smb: smbdirect: let smbdirect.h include #include This will make it easier to use. Cc: Steve French Cc: Tom Talpey Cc: Long Li Cc: Namjae Jeon Cc: linux-cifs@vger.kernel.org Cc: samba-technical@lists.samba.org Signed-off-by: Stefan Metzmacher Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/common/smbdirect/smbdirect.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/smb/common/smbdirect/smbdirect.h b/fs/smb/common/smbdirect/smbdirect.h index 05cc6a9d0ccd..821a34c4cc47 100644 --- a/fs/smb/common/smbdirect/smbdirect.h +++ b/fs/smb/common/smbdirect/smbdirect.h @@ -7,6 +7,8 @@ #ifndef __FS_SMB_COMMON_SMBDIRECT_SMBDIRECT_H__ #define __FS_SMB_COMMON_SMBDIRECT_SMBDIRECT_H__ +#include + /* SMB-DIRECT buffer descriptor V1 structure [MS-SMBD] 2.2.3.1 */ struct smbdirect_buffer_descriptor_v1 { __le64 offset; From c31823988260a55e050a33d8f27be8ee9ee8d0c7 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 23 Oct 2025 02:31:28 +0200 Subject: [PATCH 15/16] smb: smbdirect: introduce smbdirect_socket.logging infrastructure This will be used by client and server in order to keep controlling the logging when we move to shared functions. Cc: Steve French Cc: Tom Talpey Cc: Long Li Cc: Namjae Jeon Cc: linux-cifs@vger.kernel.org Cc: samba-technical@lists.samba.org Signed-off-by: Stefan Metzmacher Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/common/smbdirect/smbdirect_socket.h | 127 +++++++++++++++++++++ 1 file changed, 127 insertions(+) diff --git a/fs/smb/common/smbdirect/smbdirect_socket.h b/fs/smb/common/smbdirect/smbdirect_socket.h index 95265192bb01..568965172b93 100644 --- a/fs/smb/common/smbdirect/smbdirect_socket.h +++ b/fs/smb/common/smbdirect/smbdirect_socket.h @@ -350,6 +350,35 @@ struct smbdirect_socket { u64 dequeue_reassembly_queue; u64 send_empty; } statistics; + + struct { +#define SMBDIRECT_LOG_ERR 0x0 +#define SMBDIRECT_LOG_INFO 0x1 + +#define SMBDIRECT_LOG_OUTGOING 0x1 +#define SMBDIRECT_LOG_INCOMING 0x2 +#define SMBDIRECT_LOG_READ 0x4 +#define SMBDIRECT_LOG_WRITE 0x8 +#define SMBDIRECT_LOG_RDMA_SEND 0x10 +#define SMBDIRECT_LOG_RDMA_RECV 0x20 +#define SMBDIRECT_LOG_KEEP_ALIVE 0x40 +#define SMBDIRECT_LOG_RDMA_EVENT 0x80 +#define SMBDIRECT_LOG_RDMA_MR 0x100 +#define SMBDIRECT_LOG_RDMA_RW 0x200 +#define SMBDIRECT_LOG_NEGOTIATE 0x400 + void *private_ptr; + bool (*needed)(struct smbdirect_socket *sc, + void *private_ptr, + unsigned int lvl, + unsigned int cls); + void (*vaprintf)(struct smbdirect_socket *sc, + const char *func, + unsigned int line, + void *private_ptr, + unsigned int lvl, + unsigned int cls, + struct va_format *vaf); + } logging; }; static void __smbdirect_socket_disabled_work(struct work_struct *work) @@ -360,6 +389,100 @@ static void __smbdirect_socket_disabled_work(struct work_struct *work) WARN_ON_ONCE(1); } +static bool __smbdirect_log_needed(struct smbdirect_socket *sc, + void *private_ptr, + unsigned int lvl, + unsigned int cls) +{ + /* + * Should never be called, the caller should + * set it's own functions. + */ + WARN_ON_ONCE(1); + return false; +} + +static void __smbdirect_log_vaprintf(struct smbdirect_socket *sc, + const char *func, + unsigned int line, + void *private_ptr, + unsigned int lvl, + unsigned int cls, + struct va_format *vaf) +{ + /* + * Should never be called, the caller should + * set it's own functions. + */ + WARN_ON_ONCE(1); +} + +__printf(6, 7) +static void __smbdirect_log_printf(struct smbdirect_socket *sc, + const char *func, + unsigned int line, + unsigned int lvl, + unsigned int cls, + const char *fmt, + ...); +__maybe_unused +static void __smbdirect_log_printf(struct smbdirect_socket *sc, + const char *func, + unsigned int line, + unsigned int lvl, + unsigned int cls, + const char *fmt, + ...) +{ + struct va_format vaf; + va_list args; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + sc->logging.vaprintf(sc, + func, + line, + sc->logging.private_ptr, + lvl, + cls, + &vaf); + va_end(args); +} + +#define ___smbdirect_log_generic(sc, func, line, lvl, cls, fmt, args...) do { \ + if (sc->logging.needed(sc, sc->logging.private_ptr, lvl, cls)) { \ + __smbdirect_log_printf(sc, func, line, lvl, cls, fmt, ##args); \ + } \ +} while (0) +#define __smbdirect_log_generic(sc, lvl, cls, fmt, args...) \ + ___smbdirect_log_generic(sc, __func__, __LINE__, lvl, cls, fmt, ##args) + +#define smbdirect_log_outgoing(sc, lvl, fmt, args...) \ + __smbdirect_log_generic(sc, lvl, SMBDIRECT_LOG_OUTGOING, fmt, ##args) +#define smbdirect_log_incoming(sc, lvl, fmt, args...) \ + __smbdirect_log_generic(sc, lvl, SMBDIRECT_LOG_INCOMING, fmt, ##args) +#define smbdirect_log_read(sc, lvl, fmt, args...) \ + __smbdirect_log_generic(sc, lvl, SMBDIRECT_LOG_READ, fmt, ##args) +#define smbdirect_log_write(sc, lvl, fmt, args...) \ + __smbdirect_log_generic(sc, lvl, SMBDIRECT_LOG_WRITE, fmt, ##args) +#define smbdirect_log_rdma_send(sc, lvl, fmt, args...) \ + __smbdirect_log_generic(sc, lvl, SMBDIRECT_LOG_RDMA_SEND, fmt, ##args) +#define smbdirect_log_rdma_recv(sc, lvl, fmt, args...) \ + __smbdirect_log_generic(sc, lvl, SMBDIRECT_LOG_RDMA_RECV, fmt, ##args) +#define smbdirect_log_keep_alive(sc, lvl, fmt, args...) \ + __smbdirect_log_generic(sc, lvl, SMBDIRECT_LOG_KEEP_ALIVE, fmt, ##args) +#define smbdirect_log_rdma_event(sc, lvl, fmt, args...) \ + __smbdirect_log_generic(sc, lvl, SMBDIRECT_LOG_RDMA_EVENT, fmt, ##args) +#define smbdirect_log_rdma_mr(sc, lvl, fmt, args...) \ + __smbdirect_log_generic(sc, lvl, SMBDIRECT_LOG_RDMA_MR, fmt, ##args) +#define smbdirect_log_rdma_rw(sc, lvl, fmt, args...) \ + __smbdirect_log_generic(sc, lvl, SMBDIRECT_LOG_RDMA_RW, fmt, ##args) +#define smbdirect_log_negotiate(sc, lvl, fmt, args...) \ + __smbdirect_log_generic(sc, lvl, SMBDIRECT_LOG_NEGOTIATE, fmt, ##args) + static __always_inline void smbdirect_socket_init(struct smbdirect_socket *sc) { /* @@ -420,6 +543,10 @@ static __always_inline void smbdirect_socket_init(struct smbdirect_socket *sc) INIT_WORK(&sc->mr_io.recovery_work, __smbdirect_socket_disabled_work); disable_work_sync(&sc->mr_io.recovery_work); init_waitqueue_head(&sc->mr_io.cleanup.wait_queue); + + sc->logging.private_ptr = NULL; + sc->logging.needed = __smbdirect_log_needed; + sc->logging.vaprintf = __smbdirect_log_vaprintf; } #define __SMBDIRECT_CHECK_STATUS_FAILED(__sc, __expected_status, __error_cmd, __unexpected_cmd) ({ \ From 2de35d992ba1e4017ec21cde06a85ca7fca94810 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 21 Nov 2025 10:56:34 +0100 Subject: [PATCH 16/16] smb: smbdirect: add some logging to SMBDIRECT_CHECK_STATUS_{WARN,DISCONNECT}() This should make it easier to analyze any possible problems. Cc: Steve French Cc: Tom Talpey Cc: Long Li Cc: Namjae Jeon Cc: linux-cifs@vger.kernel.org Cc: samba-technical@lists.samba.org Signed-off-by: Stefan Metzmacher Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/common/smbdirect/smbdirect_socket.h | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/fs/smb/common/smbdirect/smbdirect_socket.h b/fs/smb/common/smbdirect/smbdirect_socket.h index 568965172b93..22184e53d445 100644 --- a/fs/smb/common/smbdirect/smbdirect_socket.h +++ b/fs/smb/common/smbdirect/smbdirect_socket.h @@ -563,7 +563,6 @@ static __always_inline void smbdirect_socket_init(struct smbdirect_socket *sc) #define __SMBDIRECT_CHECK_STATUS_WARN(__sc, __expected_status, __unexpected_cmd) \ __SMBDIRECT_CHECK_STATUS_FAILED(__sc, __expected_status, \ - , \ { \ const struct sockaddr_storage *__src = NULL; \ const struct sockaddr_storage *__dst = NULL; \ @@ -571,6 +570,26 @@ static __always_inline void smbdirect_socket_init(struct smbdirect_socket *sc) __src = &(__sc)->rdma.cm_id->route.addr.src_addr; \ __dst = &(__sc)->rdma.cm_id->route.addr.dst_addr; \ } \ + smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_INFO, \ + "expected[%s] != %s first_error=%1pe local=%pISpsfc remote=%pISpsfc\n", \ + smbdirect_socket_status_string(__expected_status), \ + smbdirect_socket_status_string((__sc)->status), \ + SMBDIRECT_DEBUG_ERR_PTR((__sc)->first_error), \ + __src, __dst); \ + }, \ + { \ + const struct sockaddr_storage *__src = NULL; \ + const struct sockaddr_storage *__dst = NULL; \ + if ((__sc)->rdma.cm_id) { \ + __src = &(__sc)->rdma.cm_id->route.addr.src_addr; \ + __dst = &(__sc)->rdma.cm_id->route.addr.dst_addr; \ + } \ + smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_ERR, \ + "expected[%s] != %s first_error=%1pe local=%pISpsfc remote=%pISpsfc\n", \ + smbdirect_socket_status_string(__expected_status), \ + smbdirect_socket_status_string((__sc)->status), \ + SMBDIRECT_DEBUG_ERR_PTR((__sc)->first_error), \ + __src, __dst); \ WARN_ONCE(1, \ "expected[%s] != %s first_error=%1pe local=%pISpsfc remote=%pISpsfc\n", \ smbdirect_socket_status_string(__expected_status), \