Merge tag 'cifs-fixes-7.3-rc1' of https://git.manguebit.org/linux

Pull smb client updates from Paulo Alcantara:

 - clear sensitive data after use (stack and heap cryptographic
   keys/hashes)

 - file size and cache synchronization fixes (fscache cookie
   serialization and truncation handling)

 - protocol validation and buffer safety fixes (prevent OOB access and
   loff_t underflow)

 - metadata and POSIX attribute fixes (proper hard-link counts and
   setuid/setgid stripping)

 - DFS cache and unmount fixes (prevent target-hint UAF and unmount
   hangs)

 - general client improvements (fix read request leaks, stats loops,
   handle servers that don't support O_TMPFILE)

* tag 'cifs-fixes-7.3-rc1' of https://git.manguebit.org/linux: (33 commits)
  cifs: fix loff_t underflow in cifs_remap_file_range() when len == 0
  smb: client: reject a tree connect response whose byte count is too small
  cifs: call pagecache_isize_extended() in cifs_setsize() when extending
  smb: client: fix copy-paste error in WSL EA length accounting for $LXDEV
  smb: client: remove redundant NULL check before kfree()
  smb: client: restore the data_offset bound in is_valid_oplock_break()
  cifs: clear tcon after cifsFileInfo_put() in cifs_file_set_size()
  smb: client: Avoid leaking sensitive data to the heap in connect.c
  smb: client: Clear sensitive stack data in smb1encrypt.c
  smb: client: Clear sensitive stack data in cifsencrypt.c
  smb: client: Clear sensitive stack and heap data in smb2ops.c
  smb: client: Clear sensitive stack data in smb2transport.c
  Revert "cifs: remove all cifs files before kill super"
  smb: client: fix use-before-check of ReparseDataLength in reparse_buf_ptr()
  smb: client: fix ALIGN() overflow in symlink_data() error context loop
  smb: client: simplify __build_path_from_dentry_optional_prefix()
  smb: client: fix UAF and buffer leak in cifs_check_trans2() for malformed secondary T2
  smb: client: fix OOB read/write from unvalidated DataOffset in coalesce_t2()
  smb/client: decode reparse metadata using its payload type
  smb/client: preserve open info type across compound queries
  ...
This commit is contained in:
Linus Torvalds
2026-08-24 18:11:49 -07:00
22 changed files with 223 additions and 108 deletions

View File

@@ -754,7 +754,7 @@ static ssize_t cifs_stats_proc_write(struct file *file,
atomic_set(&server->smb2slowcmd[i], 0);
server->time_per_cmd[i] = 0;
server->slowest_cmd[i] = 0;
server->fastest_cmd[0] = 0;
server->fastest_cmd[i] = 0;
}
#endif /* CONFIG_CIFS_STATS2 */
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {

View File

@@ -249,12 +249,13 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash,
E_md4hash(ses->password, nt_hash, nls_cp);
hmac_md5_init_usingrawkey(&hmac_ctx, nt_hash, CIFS_NTHASH_SIZE);
memzero_explicit(nt_hash, sizeof(nt_hash));
/* convert ses->user_name to unicode */
len = ses->user_name ? strlen(ses->user_name) : 0;
user = kmalloc(2 + (len * 2), GFP_KERNEL);
if (user == NULL)
return -ENOMEM;
goto out_nomem;
if (len) {
len = cifs_strtoUTF16(user, ses->user_name, len, nls_cp);
@@ -272,7 +273,7 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash,
domain = kmalloc(2 + (len * 2), GFP_KERNEL);
if (domain == NULL)
return -ENOMEM;
goto out_nomem;
len = cifs_strtoUTF16((__le16 *)domain, ses->domainName, len,
nls_cp);
@@ -284,7 +285,7 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash,
server = kmalloc(2 + (len * 2), GFP_KERNEL);
if (server == NULL)
return -ENOMEM;
goto out_nomem;
len = cifs_strtoUTF16((__le16 *)server, ses->ip_addr, len, nls_cp);
hmac_md5_update(&hmac_ctx, (const u8 *)server, 2 * len);
@@ -293,6 +294,10 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash,
hmac_md5_final(&hmac_ctx, ntlmv2_hash);
return 0;
out_nomem:
memzero_explicit(&hmac_ctx, sizeof(hmac_ctx));
return -ENOMEM;
}
static void CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash)
@@ -463,6 +468,7 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
rc = 0;
unlock:
cifs_server_unlock(ses->server);
memzero_explicit(ntlmv2_hash, sizeof(ntlmv2_hash));
setup_ntlmv2_rsp_ret:
kfree_sensitive(tiblob);

View File

@@ -1413,8 +1413,19 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off,
*/
lock_two_nondirectories(target_inode, src_inode);
if (len == 0)
len = src_inode->i_size - off;
if (len == 0) {
loff_t src_size = i_size_read(src_inode);
if (off > src_size) {
rc = -EINVAL;
goto unlock;
}
len = src_size - off;
if (!len) {
rc = 0;
goto unlock;
}
}
cifs_dbg(FYI, "clone range\n");
@@ -1466,11 +1477,7 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off,
if (target_tcon->ses->server->ops->duplicate_extents) {
rc = target_tcon->ses->server->ops->duplicate_extents(xid,
smb_file_src, smb_file_target, off, len, destoff);
if (rc == 0 && new_size > i_size) {
truncate_setsize(target_inode, new_size);
fscache_resize_cookie(cifs_inode_cookie(target_inode),
new_size);
} else if (rc == -EOPNOTSUPP) {
if (rc == -EOPNOTSUPP) {
/*
* copy_file_range syscall man page indicates EINVAL
* is returned e.g when "fd_in and fd_out refer to the

View File

@@ -147,6 +147,7 @@ ssize_t cifs_file_copychunk_range(unsigned int xid, struct file *src_file,
long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg);
void cifs_setsize(struct inode *inode, loff_t offset);
void cifs_resize_file_locked(struct inode *inode, loff_t offset);
struct fs_context;
struct smb3_fs_context;

View File

@@ -615,6 +615,11 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses,
tcon->tid = smb_buffer_response->Tid;
bcc_ptr = pByteArea(smb_buffer_response);
bytes_left = get_bcc(smb_buffer_response);
if (bytes_left < 2) {
rc = smb_EIO2(smb_eio_trace_tcon_bcc_too_small,
bytes_left, 2);
goto out;
}
length = strnlen(bcc_ptr, bytes_left - 2);
if (smb_buffer->Flags2 & SMBFLG2_UNICODE)
is_unicode = true;
@@ -670,6 +675,7 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses,
reset_cifs_unix_caps(xid, tcon, NULL, NULL);
}
}
out:
cifs_buf_release(smb_buffer);
return rc;
}

View File

@@ -1143,7 +1143,7 @@ clean_demultiplex_info(struct TCP_Server_Info *server)
put_net(cifs_net_ns(server));
kfree(server->leaf_fullpath);
kfree(server->hostname);
kfree(server);
kfree_sensitive(server);
length = atomic_dec_return(&tcpSesAllocCount);
if (length > 0)
@@ -4003,9 +4003,6 @@ cifs_umount(struct cifs_sb_info *cifs_sb)
}
spin_unlock(&cifs_sb->tlink_tree_lock);
flush_workqueue(serverclose_wq);
flush_workqueue(fileinfo_put_wq);
kfree(cifs_sb->prepath);
call_rcu(&cifs_sb->rcu, delayed_free);
}

View File

@@ -122,6 +122,8 @@ static inline void free_tgts(struct cache_entry *ce)
kfree(t->name);
kfree(t);
}
WRITE_ONCE(ce->tgthint, NULL);
}
static inline void flush_cache_ent(struct cache_entry *ce)
@@ -869,13 +871,22 @@ int dfs_cache_find(const unsigned int xid, struct cifs_ses *ses, const struct nl
goto out_free_path;
}
if (ref)
rc = setup_referral(path, ce, ref, get_tgt_name(ce));
else
if (ref) {
char *target = get_tgt_name(ce);
if (IS_ERR(target)) {
rc = PTR_ERR(target);
goto out_unlock;
}
rc = setup_referral(path, ce, ref, target);
} else {
rc = 0;
}
if (!rc && tgt_list)
rc = get_targets(ce, tgt_list);
out_unlock:
up_read(&htable_rw_lock);
out_free_path:
@@ -915,10 +926,17 @@ int dfs_cache_noreq_find(const char *path, struct dfs_info3_param *ref,
goto out_unlock;
}
if (ref)
rc = setup_referral(path, ce, ref, get_tgt_name(ce));
else
if (ref) {
char *target = get_tgt_name(ce);
if (IS_ERR(target)) {
rc = PTR_ERR(target);
goto out_unlock;
}
rc = setup_referral(path, ce, ref, target);
} else {
rc = 0;
}
if (!rc && tgt_list)
rc = get_targets(ce, tgt_list);
@@ -959,7 +977,8 @@ void dfs_cache_noreq_update_tgthint(const char *path, const struct dfs_cache_tgt
t = READ_ONCE(ce->tgthint);
if (unlikely(!strcasecmp(it->it_name, t->name)))
/* Check 't' in case ce->tgthint was cleared by free_tgts() */
if (t && unlikely(!strcasecmp(it->it_name, t->name)))
goto out_unlock;
list_for_each_entry(t, &ce->tlist, list) {

View File

@@ -115,11 +115,7 @@ char *__build_path_from_dentry_optional_prefix(struct dentry *direntry, void *pa
}
if (dirsep != '/') {
/* BB test paths to Windows with '/' in the midst of prepath */
char *p;
for (p = s; *p; p++)
if (*p == '/')
*p = dirsep;
strreplace(s, '/', dirsep);
}
if (dfsplen) {
s -= dfsplen;
@@ -1138,6 +1134,8 @@ int cifs_tmpfile(struct mnt_idmap *idmap, struct inode *dir,
} while (unlikely(rc == -EEXIST) && ++retries < max_retries);
if (rc) {
if (rc == -ENOENT)
rc = -EOPNOTSUPP;
cifs_del_pending_open(&open);
goto out;
}

View File

@@ -1016,6 +1016,7 @@ static int cifs_do_truncate(const unsigned int xid, struct dentry *dentry)
if (!rc) {
netfs_resize_file(&cinode->netfs, 0, true);
cifs_setsize(inode, 0);
cifs_invalidate_cache(inode, 0);
}
}
if (cfile)

View File

@@ -1215,7 +1215,7 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
break;
case IO_REPARSE_TAG_INTERNAL:
rc = 0;
if (le32_to_cpu(data->fi.Attributes) & ATTR_DIRECTORY) {
if (cifs_open_data_attrs(data) & ATTR_DIRECTORY) {
cifs_create_junction_fattr(fattr, sb);
goto out;
}
@@ -1239,7 +1239,7 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
*/
if (rc == -EOPNOTSUPP &&
IS_REPARSE_TAG_NAME_SURROGATE(data->reparse.tag) &&
(le32_to_cpu(data->fi.Attributes) & ATTR_DIRECTORY)) {
(cifs_open_data_attrs(data) & ATTR_DIRECTORY)) {
rc = 0;
cifs_create_junction_fattr(fattr, sb);
goto out;
@@ -1257,13 +1257,14 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
}
if (data->reparse.tag == IO_REPARSE_TAG_SYMLINK && !rc) {
bool directory = le32_to_cpu(data->fi.Attributes) & ATTR_DIRECTORY;
bool directory = cifs_open_data_attrs(data) & ATTR_DIRECTORY;
rc = smb2_fix_symlink_target_type(&data->symlink_target, directory, cifs_sb);
}
break;
}
if (tcon->posix_extensions)
if (data->contains_posix_file_info)
smb311_posix_info_to_fattr(fattr, data, sb);
else
cifs_open_info_to_fattr(fattr, data, sb);
@@ -2655,11 +2656,8 @@ cifs_rename2(struct mnt_idmap *idmap, struct inode *source_dir,
if (d_really_is_positive(target_dentry)) {
if (!rc) {
struct inode *inode = d_inode(target_dentry);
/*
* Samba and ksmbd servers allow renaming a target
* directory that is open, so make sure to update
* ->i_nlink and then mark it as delete pending.
*/
/* Update the target link count after rename. */
if (S_ISDIR(inode->i_mode)) {
drop_cached_dir_by_name(xid, tcon, to_name, cifs_sb);
spin_lock(&inode->i_lock);
@@ -2670,6 +2668,10 @@ cifs_rename2(struct mnt_idmap *idmap, struct inode *source_dir,
CIFS_I(inode)->time = 0; /* force reval */
inode_set_ctime_current(inode);
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
} else {
cifs_mark_open_handles_for_deleted_file(inode, to_name);
cifs_drop_nlink(inode);
inode_set_ctime_current(inode);
}
} else if (rc == -EACCES || rc == -EEXIST) {
/*
@@ -3064,9 +3066,27 @@ void cifs_setsize(struct inode *inode, loff_t offset)
inode->i_blocks = blocks;
spin_unlock(&inode->i_lock);
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
if (offset > old_size)
pagecache_isize_extended(inode, old_size, offset);
truncate_pagecache(inode, offset);
netfs_wait_for_outstanding_io(inode);
fscache_resize_cookie(cifs_inode_cookie(inode), offset);
}
void cifs_resize_file_locked(struct inode *inode, loff_t offset)
{
struct fscache_cookie *cookie = cifs_inode_cookie(inode);
lockdep_assert_held_write(&inode->i_rwsem);
netfs_resize_file(netfs_inode(inode), offset, true);
cifs_setsize(inode, offset);
if (!cookie)
return;
fscache_use_cookie(cookie, true);
fscache_resize_cookie(cookie, offset);
cifs_fscache_unuse_inode_cookie(inode, true);
}
int cifs_file_set_size(const unsigned int xid, struct dentry *dentry,
@@ -3107,6 +3127,7 @@ int cifs_file_set_size(const unsigned int xid, struct dentry *dentry,
size, false);
cifs_dbg(FYI, "%s: set_file_size: rc = %d\n", __func__, rc);
cifsFileInfo_put(open_file);
tcon = NULL;
}
}
@@ -3132,10 +3153,8 @@ int cifs_file_set_size(const unsigned int xid, struct dentry *dentry,
cifs_put_tlink(tlink);
set_size_out:
if (rc == 0) {
netfs_resize_file(&cifsInode->netfs, size, true);
cifs_setsize(inode, size);
}
if (rc == 0)
cifs_resize_file_locked(inode, size);
return rc;
}
@@ -3221,9 +3240,13 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
attrs->ia_valid &= ~(ATTR_CTIME | ATTR_MTIME);
}
/* skip mode change if it's just for clearing setuid/setgid */
if (attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID))
attrs->ia_valid &= ~ATTR_MODE;
/*
* This function is only called when Unix extensions are in effect,
* so the mode is always sent to and stored on the server. Do not
* skip the mode change when clearing setuid/setgid bits: dropping
* ATTR_MODE here would leave those bits set on the server after a
* write, which is a security issue.
*/
args = kmalloc_obj(*args);
if (args == NULL) {
@@ -3296,13 +3319,6 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
if (rc)
goto out;
if ((attrs->ia_valid & ATTR_SIZE) &&
attrs->ia_size != i_size_read(inode)) {
truncate_setsize(inode, attrs->ia_size);
netfs_resize_file(&cifsInode->netfs, attrs->ia_size, true);
fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size);
}
setattr_copy(&nop_mnt_idmap, inode, attrs);
mark_inode_dirty(inode);
@@ -3432,8 +3448,23 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
attrs->ia_valid &= ~(ATTR_UID | ATTR_GID);
}
/* skip mode change if it's just for clearing setuid/setgid */
if (attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID))
/*
* Skip the mode change if it is only being done to clear the
* setuid/setgid bits *and* the mode is emulated via the DOS
* read-only attribute (the default, non-ACL case), which cannot
* represent the setuid/setgid bits anyway.
*
* When the mode is instead stored on the server - i.e. with the
* cifsacl or modefromsid mount options (via an ACL) or with the
* SMB3.1.1 POSIX extensions - the cleared mode must be pushed to
* the server. Dropping ATTR_MODE here would leave the setuid/
* setgid bit set on the server after a write, which is a security
* issue (the bits are not stripped as they are on local
* filesystems).
*/
if ((attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID)) &&
!((sbflags & (CIFS_MOUNT_CIFS_ACL | CIFS_MOUNT_MODE_FROM_SID)) ||
cifs_sb_master_tcon(cifs_sb)->posix_extensions))
attrs->ia_valid &= ~ATTR_MODE;
if (attrs->ia_valid & ATTR_MODE) {
@@ -3508,13 +3539,6 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
if (rc)
goto cifs_setattr_exit;
if ((attrs->ia_valid & ATTR_SIZE) &&
attrs->ia_size != i_size_read(inode)) {
truncate_setsize(inode, attrs->ia_size);
netfs_resize_file(&cifsInode->netfs, attrs->ia_size, true);
fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size);
}
setattr_copy(&nop_mnt_idmap, inode, attrs);
mark_inode_dirty(inode);

View File

@@ -133,8 +133,7 @@ static int cifs_set_compression_by_path(unsigned int xid, struct file *filep,
close:
server->ops->close(xid, tcon, &fid);
if (tmp_cfile)
kfree(tmp_cfile);
kfree(tmp_cfile);
cifs_free_open_info(&data);
out:
free_dentry_path(page);

View File

@@ -98,15 +98,21 @@ static inline bool reparse_inode_match(struct inode *inode,
timespec64_equal(&ctime, &fattr->cf_ctime);
}
static inline u32 cifs_open_data_attrs(const struct cifs_open_info_data *data)
{
if (data->contains_posix_file_info)
return le32_to_cpu(data->posix_fi.DosAttributes);
return le32_to_cpu(data->fi.Attributes);
}
static inline bool cifs_open_data_reparse(struct cifs_open_info_data *data)
{
u32 attrs;
bool ret;
u32 attrs = cifs_open_data_attrs(data);
if (data->contains_posix_file_info) {
struct smb311_posix_qinfo *fi = &data->posix_fi;
attrs = le32_to_cpu(fi->DosAttributes);
if (data->reparse_point) {
attrs |= ATTR_REPARSE_POINT;
fi->DosAttributes = cpu_to_le32(attrs);
@@ -115,16 +121,13 @@ static inline bool cifs_open_data_reparse(struct cifs_open_info_data *data)
} else {
struct smb2_file_all_info *fi = &data->fi;
attrs = le32_to_cpu(fi->Attributes);
if (data->reparse_point) {
attrs |= ATTR_REPARSE_POINT;
fi->Attributes = cpu_to_le32(attrs);
}
}
ret = attrs & ATTR_REPARSE_POINT;
return ret;
return attrs & ATTR_REPARSE_POINT;
}
bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,

View File

@@ -81,6 +81,7 @@ int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server,
else
memcpy(cifs_pdu->Signature.SecuritySignature, smb_signature, 8);
memzero_explicit(smb_signature, sizeof(smb_signature));
return rc;
}
@@ -126,15 +127,13 @@ int cifs_verify_signature(struct smb_rqst *rqst,
rc = cifs_calc_signature(rqst, server, what_we_think_sig_should_be);
cifs_server_unlock(server);
if (rc)
return rc;
/* cifs_dump_mem("what we think it should be: ",
what_we_think_sig_should_be, 16); */
if (crypto_memneq(server_response_sig, what_we_think_sig_should_be, 8))
return -EACCES;
else
return 0;
if (!rc) {
if (crypto_memneq(server_response_sig,
what_we_think_sig_should_be, 8))
rc = -EACCES;
}
memzero_explicit(what_we_think_sig_should_be,
sizeof(what_we_think_sig_should_be));
return rc;
}

View File

@@ -80,7 +80,8 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
(struct smb_com_transaction_change_notify_rsp *)buf;
struct file_notify_information *pnotify;
__u32 data_offset = 0;
size_t len = srv->total_read - srv->pdu_size;
/* total_read excludes the RFC1002 preamble */
size_t len = srv->total_read;
if (get_bcc(buf) > sizeof(struct file_notify_information)) {
data_offset = le32_to_cpu(pSMBr->DataOffset);

View File

@@ -542,6 +542,7 @@ static int cifs_query_path_info(const unsigned int xid,
data->reparse_point = false;
data->adjust_tz = false;
data->unknown_nlink = false;
/*
* First try CIFSSMBQPathInfo() function which returns more info
@@ -608,6 +609,7 @@ static int cifs_query_path_info(const unsigned int xid,
fi.EASize = di->EaSize;
}
fi.NumberOfLinks = cpu_to_le32(1);
data->unknown_nlink = true;
fi.DeletePending = 0;
fi.Directory = !!(le32_to_cpu(fi.Attributes) & ATTR_DIRECTORY);
cifs_buf_release(search_info.ntwrk_buf_start);
@@ -630,6 +632,8 @@ static int cifs_query_path_info(const unsigned int xid,
rc = SMBQueryInformation(xid, tcon, full_path, &fi, cifs_sb->local_nls,
cifs_remap(cifs_sb));
data->adjust_tz = true;
if (!rc)
data->unknown_nlink = true;
} else if ((rc == -EOPNOTSUPP || rc == -EINVAL) && non_unicode_wildcard) {
/* Path with non-UNICODE wildcard character cannot exist. */
rc = -ENOENT;
@@ -717,7 +721,7 @@ static int cifs_query_path_info(const unsigned int xid,
ea->ea_value_length = cpu_to_le16(SMB2_WSL_XATTR_DEV_SIZE);
memcpy(&ea->ea_data[0], SMB2_WSL_XATTR_DEV, SMB2_WSL_XATTR_NAME_LEN + 1);
data->wsl.eas_len += ALIGN(sizeof(*ea) + SMB2_WSL_XATTR_NAME_LEN + 1 +
SMB2_WSL_XATTR_MODE_SIZE, 4);
SMB2_WSL_XATTR_DEV_SIZE, 4);
rc = 0;
} else if (rc >= 0) {
/* It is an error if EA $LXDEV has wrong size. */
@@ -893,8 +897,10 @@ static int cifs_open_file(const unsigned int xid, struct cifs_open_parms *oparms
else
rc = CIFS_open(xid, oparms, oplock, &fi);
if (!rc && data)
if (!rc && data) {
move_cifs_info_to_smb2(&data->fi, &fi);
data->unknown_nlink = true;
}
return rc;
}

View File

@@ -375,12 +375,31 @@ coalesce_t2(char *second_buf, struct smb_hdr *target_hdr, unsigned int *pdu_len)
data_area_of_tgt = (char *)&pSMBt->hdr.Protocol +
get_unaligned_le16(&pSMBt->t2_rsp.DataOffset);
/* validate target area */
data_area_of_src = (char *)&pSMBs->hdr.Protocol +
get_unaligned_le16(&pSMBs->t2_rsp.DataOffset);
data_area_of_tgt += total_in_tgt;
/*
* DataOffset fields are server-supplied and not validated against
* buffer bounds; check both data pointers before mutating the
* target header.
*/
if (data_area_of_tgt < (char *)target_hdr +
sizeof(struct smb_t2_rsp) + sizeof(__le16) ||
data_area_of_tgt + total_in_src >
(char *)target_hdr + CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
cifs_dbg(VFS, "%s: target data area out of bounds\n", __func__);
return -EPROTO;
}
if (data_area_of_src < second_buf +
sizeof(struct smb_t2_rsp) + sizeof(__le16) ||
data_area_of_src + total_in_src >
second_buf + smbCalcSize((struct smb_hdr *)second_buf)) {
cifs_dbg(VFS, "%s: secondary data area out of bounds\n", __func__);
return -EPROTO;
}
total_in_tgt += total_in_src;
/* is the result too big for the field? */
if (total_in_tgt > USHRT_MAX) {
@@ -430,10 +449,18 @@ bool
cifs_check_trans2(struct mid_q_entry *mid, struct TCP_Server_Info *server,
char *buf, int malformed)
{
if (malformed)
return false;
if (check2ndT2(buf) <= 0)
if (malformed || check2ndT2(buf) <= 0) {
/* mid->multiRsp blocks the server buf detach in handle_mid();
* returning false here would leak resp_buf and leave a dangling
* server->smallbuf/bigbuf after the user thread frees resp_buf.
*/
if (mid->multiRsp) {
mid->multiEnd = true;
dequeue_mid(server, mid, true);
return true;
}
return false;
}
mid->multiRsp = true;
if (mid->resp_buf) {
/* merge response - fix up 1st*/

View File

@@ -61,7 +61,10 @@ static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov)
cifs_dbg(FYI, "%s: skipping unhandled error context: 0x%x\n",
__func__, le32_to_cpu(p->ErrorId));
len = ALIGN(le32_to_cpu(p->ErrorDataLength), 8);
len = le32_to_cpu(p->ErrorDataLength);
if (len > end - ((u8 *)p + sizeof(*p)))
return ERR_PTR(-EINVAL);
len = ALIGN(len, 8);
if (len > end - ((u8 *)p + sizeof(*p)))
return ERR_PTR(-EINVAL);

View File

@@ -22,6 +22,7 @@
#include "smb2glob.h"
#include "smb2proto.h"
#include "cached_dir.h"
#include "reparse.h"
#include "../common/smb2status.h"
#include "../common/smbfsctl.h"
@@ -40,9 +41,11 @@ static struct reparse_data_buffer *reparse_buf_ptr(struct kvec *iov)
buf = (struct reparse_data_buffer *)((u8 *)io + off);
len = sizeof(*buf);
rdlen = le16_to_cpu(buf->ReparseDataLength);
if (count < len)
return ERR_PTR(smb_EIO2(smb_eio_trace_reparse_rdlen, count, 0));
if (count < len || count < rdlen + len)
rdlen = le16_to_cpu(buf->ReparseDataLength);
if (count < rdlen + len)
return ERR_PTR(smb_EIO2(smb_eio_trace_reparse_rdlen, count, rdlen));
return buf;
}
@@ -574,8 +577,10 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
idata->fi.Attributes = create_rsp->FileAttributes;
idata->fi.AllocationSize = create_rsp->AllocationSize;
idata->fi.EndOfFile = create_rsp->EndofFile;
idata->contains_posix_file_info = false;
if (le32_to_cpu(idata->fi.NumberOfLinks) == 0)
idata->fi.NumberOfLinks = cpu_to_le32(1); /* dummy value */
idata->unknown_nlink = true;
idata->fi.DeletePending = 0; /* successful open = not delete pending */
idata->fi.Directory = !!(le32_to_cpu(create_rsp->FileAttributes) & ATTR_DIRECTORY);
@@ -596,7 +601,6 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
switch (cmds[i]) {
case SMB2_OP_QUERY_INFO:
idata = in_iov[i].iov_base;
idata->contains_posix_file_info = false;
if (rc == 0 && cfile && cfile->symlink_target) {
idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
if (!idata->symlink_target)
@@ -609,6 +613,8 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
le16_to_cpu(qi_rsp->OutputBufferOffset),
le32_to_cpu(qi_rsp->OutputBufferLength),
&rsp_iov[i + 1], sizeof(idata->fi), (char *)&idata->fi);
if (!rc)
idata->contains_posix_file_info = false;
}
SMB2_query_info_free(&rqst[num_rqst++]);
if (rc)
@@ -620,7 +626,6 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
break;
case SMB2_OP_POSIX_QUERY_INFO:
idata = in_iov[i].iov_base;
idata->contains_posix_file_info = true;
if (rc == 0 && cfile && cfile->symlink_target) {
idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
if (!idata->symlink_target)
@@ -634,6 +639,8 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
le32_to_cpu(qi_rsp->OutputBufferLength),
&rsp_iov[i + 1], sizeof(idata->posix_fi) /* add SIDs */,
(char *)&idata->posix_fi);
if (!rc)
idata->contains_posix_file_info = true;
}
if (rc == 0)
rc = parse_posix_sids(idata, &rsp_iov[i + 1]);
@@ -705,7 +712,6 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
idata = in_iov[i].iov_base;
idata->reparse.io.iov = *iov;
idata->reparse.io.buftype = resp_buftype[i + 1];
idata->contains_posix_file_info = false; /* BB VERIFY */
rbuf = reparse_buf_ptr(iov);
if (IS_ERR(rbuf)) {
rc = PTR_ERR(rbuf);
@@ -727,7 +733,6 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
case SMB2_OP_QUERY_WSL_EA:
if (!rc) {
idata = in_iov[i].iov_base;
idata->contains_posix_file_info = false;
qi_rsp = rsp_iov[i + 1].iov_base;
data[0] = (u8 *)qi_rsp + le16_to_cpu(qi_rsp->OutputBufferOffset);
size[0] = le32_to_cpu(qi_rsp->OutputBufferLength);
@@ -1000,12 +1005,13 @@ int smb2_query_path_info(const unsigned int xid,
/*
* If the symlink was already parsed in create response then it is needed to fix
* its type now (after the second call with OPEN_REPARSE_POINT which filled the
* data->fi.Attributes). If the symlink was not parsed in create response then
* metadata attributes). If the symlink was not parsed in create response then
* the data->symlink_target was not filled yet and then the type will be fixed
* later after data->symlink_target is filled.
*/
if (data->reparse.tag == IO_REPARSE_TAG_SYMLINK && !rc && data->symlink_target) {
bool directory = le32_to_cpu(data->fi.Attributes) & ATTR_DIRECTORY;
bool directory = cifs_open_data_attrs(data) & ATTR_DIRECTORY;
rc = smb2_fix_symlink_target_type(&data->symlink_target, directory, cifs_sb);
}
break;

View File

@@ -1569,7 +1569,7 @@ SMB2_request_res_key(const unsigned int xid, struct cifs_tcon *tcon,
memcpy(pcchunk->SourceKey, res_key->ResumeKey, COPY_CHUNK_RES_KEY_SIZE);
req_res_key_exit:
kfree(res_key);
kfree_sensitive(res_key);
return rc;
}
@@ -2222,8 +2222,7 @@ smb2_duplicate_extents(const unsigned int xid,
rc = smb2_set_file_size(xid, tcon, trgtfile, dest_off + len, false);
if (rc)
goto duplicate_extents_out;
netfs_resize_file(netfs_inode(inode), dest_off + len, true);
cifs_setsize(inode, dest_off + len);
cifs_resize_file_locked(inode, dest_off + len);
}
rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid,
trgtfile->fid.volatile_fid,
@@ -3776,8 +3775,7 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
}
new_eof = off + len;
netfs_resize_file(&cifsi->netfs, new_eof, true);
cifs_setsize(inode, new_eof);
cifs_resize_file_locked(inode, new_eof);
qrc = SMB2_query_info(xid, tcon,
cfile->fid.persistent_fid,
@@ -3825,8 +3823,7 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
if (rc)
goto out;
netfs_resize_file(&cifsi->netfs, new_eof, true);
cifs_setsize(inode, new_eof);
cifs_resize_file_locked(inode, new_eof);
qrc = SMB2_query_info(xid, tcon,
cfile->fid.persistent_fid,
@@ -4636,7 +4633,7 @@ crypt_message(struct TCP_Server_Info *server, int num_rqst,
rc = crypto_aead_setkey(tfm, key, SMB3_GCM256_CRYPTKEY_SIZE);
else
rc = crypto_aead_setkey(tfm, key, SMB3_GCM128_CRYPTKEY_SIZE);
memzero_explicit(key, sizeof(key));
if (rc) {
cifs_server_dbg(VFS, "%s: Failed to set aead key %d\n", __func__, rc);
return rc;

View File

@@ -3372,6 +3372,7 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
#endif /* CIFS_DEBUG2 */
if (file_info) {
buf->contains_posix_file_info = false;
file_info->CreationTime = rsp->CreationTime;
file_info->LastAccessTime = rsp->LastAccessTime;
file_info->LastWriteTime = rsp->LastWriteTime;
@@ -4564,8 +4565,10 @@ smb2_new_read_req(void **buf, unsigned int *total_len,
if (rc)
return rc;
if (server == NULL)
return -ECONNABORTED;
if (!server) {
rc = -ECONNABORTED;
goto free_req;
}
shdr = &req->hdr;
shdr->Id.SyncId.ProcessId = cpu_to_le32(io_parms->pid);
@@ -4596,8 +4599,10 @@ smb2_new_read_req(void **buf, unsigned int *total_len,
rdata->mr = smbd_register_mr(server->smbd_conn, &rdata->subreq.io_iter,
true, need_invalidate);
if (!rdata->mr)
return -EAGAIN;
if (!rdata->mr) {
rc = -EAGAIN;
goto free_req;
}
req->Channel = SMB2_CHANNEL_RDMA_V1_INVALIDATE;
if (need_invalidate)
@@ -4638,6 +4643,10 @@ smb2_new_read_req(void **buf, unsigned int *total_len,
*buf = req;
return rc;
free_req:
cifs_small_buf_release(req);
return rc;
}
static void
@@ -4885,6 +4894,7 @@ smb2_async_readv(struct cifs_io_subrequest *rdata)
smb2_should_replay(tcon,
&rdata->retries,
&rdata->cur_sleep)) {
rdata->replay = true;
trace_netfs_sreq(&rdata->subreq, netfs_sreq_trace_io_retry_needed);
__set_bit(NETFS_SREQ_NEED_RETRY, &rdata->subreq.flags);
}

View File

@@ -249,6 +249,8 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
if (!rc)
memcpy(shdr->Signature, smb2_signature, SMB2_SIGNATURE_SIZE);
memzero_explicit(key, sizeof(key));
memzero_explicit(&hmac_ctx, sizeof(hmac_ctx));
return rc;
}
@@ -283,6 +285,7 @@ static void generate_key(struct cifs_ses *ses, struct kvec label,
hmac_sha256_final(&hmac_ctx, prfhash);
memcpy(key, prfhash, key_size);
memzero_explicit(prfhash, sizeof(prfhash));
}
struct derivation {
@@ -482,6 +485,7 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE);
rc = aes_cmac_preparekey(&cmac_key, key, SMB2_CMACAES_SIZE);
memzero_explicit(key, sizeof(key));
if (rc) {
cifs_server_dbg(VFS, "%s: Could not set key for cmac aes\n", __func__);
return rc;

View File

@@ -133,6 +133,7 @@
EM(smb_eio_trace_sym_slash, "sym_slash") \
EM(smb_eio_trace_sym_target_len, "sym_target_len") \
EM(smb_eio_trace_symlink_file_size, "symlink_file_size") \
EM(smb_eio_trace_tcon_bcc_too_small, "tcon_bcc_too_small") \
EM(smb_eio_trace_tdis_in_reconnect, "tdis_in_reconnect") \
EM(smb_eio_trace_tx_chained_async, "tx_chained_async") \
EM(smb_eio_trace_tx_compress_failed, "tx_compress_failed") \
@@ -213,7 +214,7 @@
#define EM(a, b) a,
#define E_(a, b) a
enum smb_eio_trace { smb_eio_traces } __mode(byte);
enum smb_eio_trace { smb_eio_traces };
enum smb3_rw_credits_trace { smb3_rw_credits_traces } __mode(byte);
enum smb3_tcon_ref_trace { smb3_tcon_ref_traces } __mode(byte);