17 smb3 client fixes

-----BEGIN PGP SIGNATURE-----
 
 iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAmo9mPQACgkQiiy9cAdy
 T1HWIQv+IV73XayJX9VoeEqulKAksrH4oW2/IjPw93JqlwGSXkzTwdkl8lE+wkKf
 vxmjRNpJmqMLoyPxLq07/MvuiDowqnlz/Vb5qOrsDLjjpSCegECKyejnmAOU+yVU
 W1+6RKOpMgWqcADzJcvKpWQJsMwpVXOquLragsWQL3bOG6RSqYAnFYhufmsGFJly
 +Xsbm7CazFd+T3LSG3A74mN+ip18uTnmOijLwMZ1wdIkwOV/eFkCtyAbwQzxM424
 dF7dScw+jV6jtKFiHe4QPSfQRBwEUNdSMUlvOGlzLDm7JmzXLeqM4rY6p3zK7syD
 lXJ0ZCBRo9Jy2ibJleFuJCoTyeb+whlx2A+T78o8t/Grw0gMPsFyk5m7DOCeFEIM
 PdtG4VYgf6byB5vRYMXwJt+FVp4cj/pLF9+Hoi3E5lUHe2ZFnFf7BdRVW9Xk+Nzj
 z8xknYMl8GlNpIQFEPTRQ355zTNSceco+tWydjOCiav4gkKwNrj4L8/SVF61IsQ6
 GxrnaiI6
 =vIzG
 -----END PGP SIGNATURE-----

Merge tag 'v7.2-rc-part2-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6

Pull smb client fixes from Steve French:
 - fix potential double frees
 - fix potential memory leak in receiving compound response
 - querydir improvement
 - fix chown with smb311 posix extensions
 - ACL setting fixes
 - minor debug improvement and cleanup
 - add some missing protocol defines
 - sparse file fixes

* tag 'v7.2-rc-part2-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6:
  cifs: define variable sized buffer for querydir responses
  smb/client: do not account EOF extension as allocation
  smb/client: preserve errors from smb2_set_sparse()
  smb: client: Fix next buffer leak in receive_encrypted_standard()
  smb/client: use %pe to print error pointer
  smb/client: name the default fallocate mode
  smb common: add missing AAPL defines
  smb/client: fix chown/chgrp with SMB3 POSIX Extensions
  smb/client: fix security flag calculation when setting security descriptors
  smb: client: refactor ACL setting control flow in id_mode_to_cifs_acl()
  smb: client: fix query directory replay double-free
  smb: client: fix change notify replay double-free
  smb: client: fix query_info() replay double-free
  smb: client: fix double-free in SMB2_close() replay
  smb: client: fix double-free in SMB2_ioctl() replay
  smb: client: fix double-free in SMB2_open() replay
  smb: client: fix double-free in SMB2_flush() replay
This commit is contained in:
Linus Torvalds 2026-06-25 17:16:26 -07:00
commit ad054be811
9 changed files with 117 additions and 41 deletions

View File

@ -1185,7 +1185,8 @@ static void populate_new_aces(char *nacl_base,
static __u16 replace_sids_and_copy_aces(struct smb_acl *pdacl, struct smb_acl *pndacl,
struct smb_sid *pownersid, struct smb_sid *pgrpsid,
struct smb_sid *pnownersid, struct smb_sid *pngrpsid)
struct smb_sid *pnownersid, struct smb_sid *pngrpsid,
int *aclflag)
{
int i;
u16 size = 0;
@ -1209,12 +1210,15 @@ static __u16 replace_sids_and_copy_aces(struct smb_acl *pdacl, struct smb_acl *p
pntace = (struct smb_ace *) (acl_base + size);
pnntace = (struct smb_ace *) (nacl_base + nsize);
if (pnownersid && compare_sids(&pntace->sid, pownersid) == 0)
if (pnownersid && compare_sids(&pntace->sid, pownersid) == 0) {
ace_size = cifs_copy_ace(pnntace, pntace, pnownersid);
else if (pngrpsid && compare_sids(&pntace->sid, pgrpsid) == 0)
*aclflag |= CIFS_ACL_DACL;
} else if (pngrpsid && compare_sids(&pntace->sid, pgrpsid) == 0) {
ace_size = cifs_copy_ace(pnntace, pntace, pngrpsid);
else
*aclflag |= CIFS_ACL_DACL;
} else {
ace_size = cifs_copy_ace(pnntace, pntace, NULL);
}
size += le16_to_cpu(pntace->size);
nsize += ace_size;
@ -1521,7 +1525,8 @@ static int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *pnntsd,
/* Replace ACEs for old owner with new one */
size = replace_sids_and_copy_aces(dacl_ptr, ndacl_ptr,
owner_sid_ptr, group_sid_ptr,
nowner_sid_ptr, ngroup_sid_ptr);
nowner_sid_ptr, ngroup_sid_ptr,
aclflag);
ndacl_ptr->size = cpu_to_le16(size);
}
@ -1738,7 +1743,7 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
kuid_t uid, kgid_t gid)
{
int rc = 0;
int aclflag = CIFS_ACL_DACL; /* default flag to set */
int aclflag = 0;
__u32 secdesclen = 0;
__u32 nsecdesclen = 0;
__u32 dacloffset = 0;
@ -1834,14 +1839,23 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
cifs_dbg(NOISY, "build_sec_desc rc: %d\n", rc);
if (ops->set_acl == NULL)
rc = -EOPNOTSUPP;
if (rc != 0)
goto id_mode_to_cifs_acl_exit;
if (!rc) {
/* Set the security descriptor */
rc = ops->set_acl(pnntsd, nsecdesclen, inode, path, aclflag);
cifs_dbg(NOISY, "set_cifs_acl rc: %d\n", rc);
if (aclflag == 0) {
cifs_dbg(FYI, "set_cifs_acl aclflag=0, no change mapped\n");
goto id_mode_to_cifs_acl_exit;
}
if (ops->set_acl == NULL) {
rc = -EOPNOTSUPP;
goto id_mode_to_cifs_acl_exit;
}
/* Set the security descriptor */
rc = ops->set_acl(pnntsd, nsecdesclen, inode, path, aclflag);
cifs_dbg(NOISY, "set_cifs_acl rc: %d\n", rc);
id_mode_to_cifs_acl_exit:
cifs_put_tlink(tlink);

View File

@ -1393,6 +1393,7 @@ struct cifs_search_info {
bool emptyDir:1;
bool unicode:1;
bool smallBuf:1; /* so we know which buf_release function to call */
bool is_dynamic_buf:1; /* dynamically allocated buffer - can be variable size */
};
#define ACL_NO_MODE ((umode_t)(-1))
@ -1906,6 +1907,7 @@ enum cifs_find_flags {
#define CIFS_NO_BUFFER 0 /* Response buffer not returned */
#define CIFS_SMALL_BUFFER 1
#define CIFS_LARGE_BUFFER 2
#define CIFS_DYNAMIC_BUFFER 3 /* Dynamically allocated buffer */
#define CIFS_IOVEC 4 /* array of response buffers */
/* Type of Request to SendReceive2 */

View File

@ -457,8 +457,8 @@ static int __reconnect_target_locked(struct TCP_Server_Info *server,
server->hostname = hostname;
spin_unlock(&server->srv_lock);
} else {
cifs_dbg(FYI, "%s: couldn't extract hostname or address from dfs target: %ld\n",
__func__, PTR_ERR(hostname));
cifs_dbg(FYI, "%s: couldn't extract hostname or address from dfs target: %pe\n",
__func__, hostname);
cifs_dbg(FYI, "%s: default to last target server: %s\n", __func__,
server->hostname);
}

View File

@ -1563,6 +1563,8 @@ int cifs_closedir(struct inode *inode, struct file *file)
cfile->srch_inf.ntwrk_buf_start = NULL;
if (cfile->srch_inf.smallBuf)
cifs_small_buf_release(buf);
else if (cfile->srch_inf.is_dynamic_buf)
kfree(buf);
else
cifs_buf_release(buf);
}

View File

@ -3038,13 +3038,20 @@ int cifs_fiemap(struct inode *inode, struct fiemap_extent_info *fei, u64 start,
void cifs_setsize(struct inode *inode, loff_t offset)
{
loff_t old_size;
u64 blocks = CIFS_INO_BLOCKS(offset);
spin_lock(&inode->i_lock);
old_size = i_size_read(inode);
i_size_write(inode, offset);
/*
* Until we can query the server for actual allocation size,
* this is best estimate we have for blocks allocated for a file.
* Extending EOF does not allocate the intervening range. Only clamp
* i_blocks on shrink; allocation growth comes from writes or from the
* server-reported AllocationSize.
*/
inode->i_blocks = CIFS_INO_BLOCKS(offset);
if (offset < old_size && (u64)inode->i_blocks > blocks)
inode->i_blocks = blocks;
spin_unlock(&inode->i_lock);
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
truncate_pagecache(inode, offset);
@ -3376,7 +3383,8 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
if (attrs->ia_valid & ATTR_GID)
gid = attrs->ia_gid;
if (sbflags & (CIFS_MOUNT_CIFS_ACL | CIFS_MOUNT_MODE_FROM_SID)) {
if ((sbflags & (CIFS_MOUNT_CIFS_ACL | CIFS_MOUNT_MODE_FROM_SID)) ||
cifs_sb_master_tcon(cifs_sb)->posix_extensions) {
if (uid_valid(uid) || gid_valid(gid)) {
mode = NO_CHANGE_64;
rc = id_mode_to_cifs_acl(inode, full_path, &mode,

View File

@ -732,6 +732,8 @@ find_cifs_entry(const unsigned int xid, struct cifs_tcon *tcon, loff_t pos,
if (cfile->srch_inf.smallBuf)
cifs_small_buf_release(cfile->srch_inf.
ntwrk_buf_start);
else if (cfile->srch_inf.is_dynamic_buf)
kfree(cfile->srch_inf.ntwrk_buf_start);
else
cifs_buf_release(cfile->srch_inf.
ntwrk_buf_start);

View File

@ -2117,8 +2117,9 @@ smb2_sync_write(const unsigned int xid, struct cifs_fid *pfid,
}
/* Set or clear the SPARSE_FILE attribute based on value passed in setsparse */
static bool smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon,
struct cifsFileInfo *cfile, struct inode *inode, __u8 setsparse)
static int smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon,
struct cifsFileInfo *cfile, struct inode *inode,
__u8 setsparse)
{
struct cifsInodeInfo *cifsi;
int rc;
@ -2127,31 +2128,31 @@ static bool smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon,
/* if file already sparse don't bother setting sparse again */
if ((cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) && setsparse)
return true; /* already sparse */
return 0; /* already sparse */
if (!(cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) && !setsparse)
return true; /* already not sparse */
return 0; /* already not sparse */
/*
* Can't check for sparse support on share the usual way via the
* FS attribute info (FILE_SUPPORTS_SPARSE_FILES) on the share
* since Samba server doesn't set the flag on the share, yet
* supports the set sparse FSCTL and returns sparse correctly
* in the file attributes. If we fail setting sparse though we
* mark that server does not support sparse files for this share
* to avoid repeatedly sending the unsupported fsctl to server
* if the file is repeatedly extended.
* in the file attributes. If the server returns EOPNOTSUPP, mark
* that sparse files are not supported on this share to avoid
* repeatedly sending the unsupported FSCTL.
*/
if (tcon->broken_sparse_sup)
return false;
return -EOPNOTSUPP;
rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
cfile->fid.volatile_fid, FSCTL_SET_SPARSE,
&setsparse, 1, CIFSMaxBufSize, NULL, NULL);
if (rc) {
tcon->broken_sparse_sup = true;
if (rc == -EOPNOTSUPP)
tcon->broken_sparse_sup = true;
cifs_dbg(FYI, "set sparse rc = %d\n", rc);
return false;
return rc;
}
if (setsparse)
@ -2159,7 +2160,7 @@ static bool smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon,
else
cifsi->cifsAttrs &= (~FILE_ATTRIBUTE_SPARSE_FILE);
return true;
return 0;
}
static int
@ -3483,10 +3484,9 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon,
/* Need to make file sparse, if not already, before freeing range. */
/* Consider adding equivalent for compressed since it could also work */
if (!smb2_set_sparse(xid, tcon, cfile, inode, set_sparse)) {
rc = -EOPNOTSUPP;
rc = smb2_set_sparse(xid, tcon, cfile, inode, set_sparse);
if (rc)
goto out;
}
filemap_invalidate_lock(inode->i_mapping);
/*
@ -4071,7 +4071,7 @@ static long smb3_fallocate(struct file *file, struct cifs_tcon *tcon, int mode,
return smb3_collapse_range(file, tcon, off, len);
else if (mode == FALLOC_FL_INSERT_RANGE)
return smb3_insert_range(file, tcon, off, len);
else if (mode == 0)
else if (mode == FALLOC_FL_ALLOCATE_RANGE)
return smb3_simple_falloc(file, tcon, off, len, false);
return -EOPNOTSUPP;
@ -5111,6 +5111,12 @@ receive_encrypted_standard(struct TCP_Server_Info *server,
one_more:
shdr = (struct smb2_hdr *)buf;
next_cmd = le32_to_cpu(shdr->NextCommand);
if (*num_mids >= MAX_COMPOUND) {
cifs_server_dbg(VFS, "too many PDUs in compound\n");
return -1;
}
if (next_cmd) {
if (WARN_ON_ONCE(next_cmd > pdu_length))
return -1;
@ -5134,10 +5140,6 @@ receive_encrypted_standard(struct TCP_Server_Info *server,
mid_entry->resp_buf_size = server->pdu_size;
}
if (*num_mids >= MAX_COMPOUND) {
cifs_server_dbg(VFS, "too many PDUs in compound\n");
return -1;
}
bufs[*num_mids] = buf;
mids[(*num_mids)++] = mid_entry;

View File

@ -3305,6 +3305,8 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
replay_again:
/* reinitialize for possible replay */
resp_buftype = CIFS_NO_BUFFER;
memset(&rsp_iov, 0, sizeof(rsp_iov));
flags = 0;
server = cifs_pick_channel(ses);
oparms->replay = !!(retries);
@ -3530,6 +3532,8 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
replay_again:
/* reinitialize for possible replay */
resp_buftype = CIFS_NO_BUFFER;
memset(&rsp_iov, 0, sizeof(rsp_iov));
flags = 0;
server = cifs_pick_channel(ses);
@ -3724,6 +3728,8 @@ __SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
replay_again:
/* reinitialize for possible replay */
resp_buftype = CIFS_NO_BUFFER;
memset(&rsp_iov, 0, sizeof(rsp_iov));
flags = 0;
query_attrs = false;
server = cifs_pick_channel(ses);
@ -3936,6 +3942,8 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon,
replay_again:
/* reinitialize for possible replay */
resp_buftype = CIFS_NO_BUFFER;
memset(&rsp_iov, 0, sizeof(rsp_iov));
flags = 0;
allocated = false;
server = cifs_pick_channel(ses);
@ -4108,6 +4116,8 @@ SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon,
replay_again:
/* reinitialize for possible replay */
resp_buftype = CIFS_NO_BUFFER;
memset(&rsp_iov, 0, sizeof(rsp_iov));
flags = 0;
server = cifs_pick_channel(ses);
@ -4450,6 +4460,8 @@ SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
replay_again:
/* reinitialize for possible replay */
resp_buftype = CIFS_NO_BUFFER;
memset(&rsp_iov, 0, sizeof(rsp_iov));
flags = 0;
server = cifs_pick_channel(ses);
@ -5661,6 +5673,8 @@ smb2_parse_query_directory(struct cifs_tcon *tcon,
if (srch_inf->ntwrk_buf_start) {
if (srch_inf->smallBuf)
cifs_small_buf_release(srch_inf->ntwrk_buf_start);
else if (srch_inf->is_dynamic_buf)
kfree(srch_inf->ntwrk_buf_start);
else
cifs_buf_release(srch_inf->ntwrk_buf_start);
}
@ -5680,12 +5694,18 @@ smb2_parse_query_directory(struct cifs_tcon *tcon,
cifs_dbg(FYI, "num entries %d last_index %lld srch start %p srch end %p\n",
srch_inf->entries_in_buffer, srch_inf->index_of_last_entry,
srch_inf->srch_entries_start, srch_inf->last_entry);
if (resp_buftype == CIFS_LARGE_BUFFER)
if (resp_buftype == CIFS_LARGE_BUFFER) {
srch_inf->smallBuf = false;
else if (resp_buftype == CIFS_SMALL_BUFFER)
srch_inf->is_dynamic_buf = false;
} else if (resp_buftype == CIFS_SMALL_BUFFER) {
srch_inf->smallBuf = true;
else
srch_inf->is_dynamic_buf = false;
} else if (resp_buftype == CIFS_DYNAMIC_BUFFER) {
srch_inf->smallBuf = false;
srch_inf->is_dynamic_buf = true;
} else {
cifs_tcon_dbg(VFS, "Invalid search buffer type\n");
}
return 0;
}
@ -5708,6 +5728,8 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon,
replay_again:
/* reinitialize for possible replay */
resp_buftype = CIFS_NO_BUFFER;
memset(&rsp_iov, 0, sizeof(rsp_iov));
flags = 0;
server = cifs_pick_channel(ses);

View File

@ -1243,6 +1243,30 @@ struct create_mxac_req {
__le64 Timestamp;
} __packed;
/*
* AAPL flags. See Samba libcli/smb/smb2_create_ctx.h
*/
/* "AAPL" Context Command Codes */
#define SMB2_CRTCTX_AAPL_SERVER_QUERY 1
#define SMB2_CRTCTX_AAPL_RESOLVE_ID 2
/* "AAPL" Server Query request/response bitmap */
#define SMB2_CRTCTX_AAPL_SERVER_CAPS 1
#define SMB2_CRTCTX_AAPL_VOLUME_CAPS 2
#define SMB2_CRTCTX_AAPL_MODEL_INFO 4
/* "AAPL" Client/Server Capabilities bitmap */
#define SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR 1
#define SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE 2
#define SMB2_CRTCTX_AAPL_UNIX_BASED 4
#define SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE 8
/* "AAPL" Volume Capabilities bitmap */
#define SMB2_CRTCTX_AAPL_SUPPORT_RESOLVE_ID 1
#define SMB2_CRTCTX_AAPL_CASE_SENSITIVE 2
#define SMB2_CRTCTX_AAPL_FULL_SYNC 4
/*
* Flags
* See MS-SMB2 2.2.13.2.11