Merge tag 'v7.1-rc4-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6

Pull smb client fixes from Steve French:

 - Fix integer overflow in read

 - Fix smbdirect error cleanup

 - Multichannel reconnect fix

 - Add some missing defines and correct some references to protocol spec

 - Fix oob symlink read

* tag 'v7.1-rc4-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6:
  smbdirect: Fix error cleanup in smbdirect_map_sges_from_iter()
  smb: client: avoid integer overflow in SMB2 READ length check
  cifs: client: stage smb3_reconfigure() updates and restore ctx on failure
  smb/client: fix possible infinite loop and oob read in symlink_data()
  SMB3.1.1: add missing QUERY_DIR info levels
This commit is contained in:
Linus Torvalds
2026-05-15 14:52:17 -07:00
7 changed files with 131 additions and 64 deletions

View File

@@ -736,7 +736,7 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
static int smb3_fs_context_parse_monolithic(struct fs_context *fc,
void *data);
static int smb3_get_tree(struct fs_context *fc);
static void smb3_sync_ses_chan_max(struct cifs_ses *ses, unsigned int max_channels);
static void smb3_sync_ses_chan_max(struct cifs_ses *ses, size_t max_channels);
static int smb3_reconfigure(struct fs_context *fc);
static const struct fs_context_operations smb3_fs_context_ops = {
@@ -1010,25 +1010,34 @@ do { \
int smb3_sync_session_ctx_passwords(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses)
{
char *password = NULL, *password2 = NULL;
if (ses->password &&
cifs_sb->ctx->password &&
strcmp(ses->password, cifs_sb->ctx->password)) {
kfree_sensitive(cifs_sb->ctx->password);
cifs_sb->ctx->password = kstrdup(ses->password, GFP_KERNEL);
if (!cifs_sb->ctx->password)
password = kstrdup(ses->password, GFP_KERNEL);
if (!password)
return -ENOMEM;
}
if (ses->password2 &&
cifs_sb->ctx->password2 &&
strcmp(ses->password2, cifs_sb->ctx->password2)) {
kfree_sensitive(cifs_sb->ctx->password2);
cifs_sb->ctx->password2 = kstrdup(ses->password2, GFP_KERNEL);
if (!cifs_sb->ctx->password2) {
kfree_sensitive(cifs_sb->ctx->password);
cifs_sb->ctx->password = NULL;
password2 = kstrdup(ses->password2, GFP_KERNEL);
if (!password2) {
kfree_sensitive(password);
return -ENOMEM;
}
}
if (password) {
kfree_sensitive(cifs_sb->ctx->password);
cifs_sb->ctx->password = password;
}
if (password2) {
kfree_sensitive(cifs_sb->ctx->password2);
cifs_sb->ctx->password2 = password2;
}
return 0;
}
@@ -1041,7 +1050,7 @@ int smb3_sync_session_ctx_passwords(struct cifs_sb_info *cifs_sb, struct cifs_se
* with the session's channel lock. This should be called whenever the maximum
* allowed channels for a session changes (e.g., after a remount or reconfigure).
*/
static void smb3_sync_ses_chan_max(struct cifs_ses *ses, unsigned int max_channels)
static void smb3_sync_ses_chan_max(struct cifs_ses *ses, size_t max_channels)
{
spin_lock(&ses->chan_lock);
ses->chan_max = max_channels;
@@ -1051,12 +1060,15 @@ static void smb3_sync_ses_chan_max(struct cifs_ses *ses, unsigned int max_channe
static int smb3_reconfigure(struct fs_context *fc)
{
struct smb3_fs_context *ctx = smb3_fc2context(fc);
struct smb3_fs_context *new_ctx = NULL;
struct smb3_fs_context *old_ctx = NULL;
struct dentry *root = fc->root;
struct cifs_sb_info *cifs_sb = CIFS_SB(root->d_sb);
struct cifs_ses *ses = cifs_sb_master_tcon(cifs_sb)->ses;
unsigned int rsize = ctx->rsize, wsize = ctx->wsize;
char *new_password = NULL, *new_password2 = NULL;
bool need_recon = false;
bool need_mchan_update;
int rc;
if (ses->expired_pwd)
@@ -1066,6 +1078,16 @@ static int smb3_reconfigure(struct fs_context *fc)
if (rc)
return rc;
old_ctx = kzalloc_obj(*old_ctx);
if (!old_ctx)
return -ENOMEM;
rc = smb3_fs_context_dup(old_ctx, cifs_sb->ctx);
if (rc) {
kfree(old_ctx);
return rc;
}
/*
* We can not change UNC/username/password/domainname/
* workstation_name/nodename/iocharset
@@ -1075,16 +1097,22 @@ static int smb3_reconfigure(struct fs_context *fc)
STEAL_STRING(cifs_sb, ctx, UNC);
STEAL_STRING(cifs_sb, ctx, source);
STEAL_STRING(cifs_sb, ctx, username);
STEAL_STRING(cifs_sb, ctx, domainname);
STEAL_STRING(cifs_sb, ctx, nodename);
STEAL_STRING(cifs_sb, ctx, iocharset);
if (need_recon == false)
if (!need_recon) {
STEAL_STRING_SENSITIVE(cifs_sb, ctx, password);
else {
} else {
if (ctx->password) {
new_password = kstrdup(ctx->password, GFP_KERNEL);
if (!new_password)
return -ENOMEM;
} else
if (!new_password) {
rc = -ENOMEM;
goto restore_ctx;
}
} else {
STEAL_STRING_SENSITIVE(cifs_sb, ctx, password);
}
}
/*
@@ -1094,11 +1122,29 @@ static int smb3_reconfigure(struct fs_context *fc)
if (ctx->password2) {
new_password2 = kstrdup(ctx->password2, GFP_KERNEL);
if (!new_password2) {
kfree_sensitive(new_password);
return -ENOMEM;
rc = -ENOMEM;
goto restore_ctx;
}
} else
} else {
STEAL_STRING_SENSITIVE(cifs_sb, ctx, password2);
}
/* if rsize or wsize not passed in on remount, use previous values */
ctx->rsize = rsize ? CIFS_ALIGN_RSIZE(fc, rsize) : cifs_sb->ctx->rsize;
ctx->wsize = wsize ? CIFS_ALIGN_WSIZE(fc, wsize) : cifs_sb->ctx->wsize;
new_ctx = kzalloc_obj(*new_ctx);
if (!new_ctx) {
rc = -ENOMEM;
goto restore_ctx;
}
rc = smb3_fs_context_dup(new_ctx, ctx);
if (rc)
goto restore_ctx;
need_mchan_update = ctx->multichannel != cifs_sb->ctx->multichannel ||
ctx->max_channels != cifs_sb->ctx->max_channels;
/*
* we may update the passwords in the ses struct below. Make sure we do
@@ -1109,54 +1155,55 @@ static int smb3_reconfigure(struct fs_context *fc)
/*
* smb2_reconnect may swap password and password2 in case session setup
* failed. First get ctx passwords in sync with ses passwords. It should
* be okay to do this even if this function were to return an error at a
* later stage
* be done before committing new passwords.
*/
rc = smb3_sync_session_ctx_passwords(cifs_sb, ses);
if (rc) {
mutex_unlock(&ses->session_mutex);
kfree_sensitive(new_password);
kfree_sensitive(new_password2);
return rc;
}
/*
* now that allocations for passwords are done, commit them
*/
if (new_password) {
kfree_sensitive(ses->password);
ses->password = new_password;
}
if (new_password2) {
kfree_sensitive(ses->password2);
ses->password2 = new_password2;
goto cleanup_new_ctx;
}
/*
* If multichannel or max_channels has changed, update the session's channels accordingly.
* This may add or remove channels to match the new configuration.
*/
if ((ctx->multichannel != cifs_sb->ctx->multichannel) ||
(ctx->max_channels != cifs_sb->ctx->max_channels)) {
/* Synchronize ses->chan_max with the new mount context */
smb3_sync_ses_chan_max(ses, ctx->max_channels);
/* Now update the session's channels to match the new configuration */
if (need_mchan_update) {
/* Prevent concurrent scaling operations */
spin_lock(&ses->ses_lock);
if (ses->flags & CIFS_SES_FLAG_SCALE_CHANNELS) {
spin_unlock(&ses->ses_lock);
mutex_unlock(&ses->session_mutex);
return -EINVAL;
rc = -EINVAL;
goto cleanup_new_ctx;
}
ses->flags |= CIFS_SES_FLAG_SCALE_CHANNELS;
spin_unlock(&ses->ses_lock);
}
/*
* Commit session passwords before any channel work so newly added
* channels authenticate with the new credentials.
*/
if (new_password) {
kfree_sensitive(ses->password);
ses->password = new_password;
new_password = NULL;
}
if (new_password2) {
kfree_sensitive(ses->password2);
ses->password2 = new_password2;
new_password2 = NULL;
}
if (need_mchan_update) {
/* Synchronize ses->chan_max with the new mount context */
smb3_sync_ses_chan_max(ses, ctx->max_channels);
mutex_unlock(&ses->session_mutex);
rc = smb3_update_ses_channels(ses, ses->server,
false /* from_reconnect */,
false /* disable_mchan */);
smb3_update_ses_channels(ses, ses->server,
false /* from_reconnect */,
false /* disable_mchan */);
/* Clear scaling flag after operation */
spin_lock(&ses->ses_lock);
@@ -1166,22 +1213,30 @@ static int smb3_reconfigure(struct fs_context *fc)
mutex_unlock(&ses->session_mutex);
}
STEAL_STRING(cifs_sb, ctx, domainname);
STEAL_STRING(cifs_sb, ctx, nodename);
STEAL_STRING(cifs_sb, ctx, iocharset);
/* if rsize or wsize not passed in on remount, use previous values */
ctx->rsize = rsize ? CIFS_ALIGN_RSIZE(fc, rsize) : cifs_sb->ctx->rsize;
ctx->wsize = wsize ? CIFS_ALIGN_WSIZE(fc, wsize) : cifs_sb->ctx->wsize;
smb3_cleanup_fs_context_contents(cifs_sb->ctx);
rc = smb3_fs_context_dup(cifs_sb->ctx, ctx);
memcpy(cifs_sb->ctx, new_ctx, sizeof(*new_ctx));
kfree(new_ctx);
new_ctx = NULL;
smb3_cleanup_fs_context(old_ctx);
old_ctx = NULL;
smb3_update_mnt_flags(cifs_sb);
#ifdef CONFIG_CIFS_DFS_UPCALL
if (!rc)
rc = dfs_cache_remount_fs(cifs_sb);
#endif
return rc;
cleanup_new_ctx:
smb3_cleanup_fs_context_contents(new_ctx);
restore_ctx:
kfree(new_ctx);
kfree_sensitive(new_password);
kfree_sensitive(new_password2);
smb3_cleanup_fs_context_contents(cifs_sb->ctx);
memcpy(cifs_sb->ctx, old_ctx, sizeof(*old_ctx));
kfree(old_ctx);
return rc;
}

View File

@@ -49,6 +49,9 @@ static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov)
__func__, le32_to_cpu(p->ErrorId));
len = ALIGN(le32_to_cpu(p->ErrorDataLength), 8);
if (len > end - ((u8 *)p + sizeof(*p)))
return ERR_PTR(-EINVAL);
p = (struct smb2_error_context_rsp *)(p->ErrorContextData + len);
}
} else if (le32_to_cpu(err->ByteCount) >= sizeof(*sym) &&

View File

@@ -4721,6 +4721,7 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
{
unsigned int data_offset;
unsigned int data_len;
unsigned int end_off;
unsigned int cur_off;
unsigned int cur_page_idx;
unsigned int pad_len;
@@ -4836,7 +4837,8 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
}
rdata->got_bytes = buffer_len;
} else if (buf_len >= data_offset + data_len) {
} else if (!check_add_overflow(data_offset, data_len, &end_off) &&
buf_len >= end_off) {
/* read response payload is in buf */
WARN_ONCE(buffer, "read data can be either in buf or in buffer");
copied = copy_to_iter(buf + data_offset, data_len, &rdata->subreq.io_iter);

View File

@@ -1158,7 +1158,7 @@ int
cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
{
int length, len;
unsigned int data_offset, data_len;
unsigned int data_offset, data_len, end_off;
struct cifs_io_subrequest *rdata = mid->callback_data;
char *buf = server->smallbuf;
unsigned int buflen = server->pdu_size;
@@ -1256,11 +1256,14 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
use_rdma_mr = rdata->mr;
#endif
data_len = server->ops->read_data_length(buf, use_rdma_mr);
if (!use_rdma_mr && (data_offset + data_len > buflen)) {
/* data_len is corrupt -- discard frame */
rdata->result = smb_EIO2(smb_eio_trace_read_rsp_malformed,
data_offset + data_len, buflen);
return cifs_readv_discard(server, mid);
if (!use_rdma_mr) {
if (check_add_overflow(data_offset, data_len, &end_off) ||
end_off > buflen) {
/* data_len is corrupt -- discard frame */
rdata->result = smb_EIO2(smb_eio_trace_read_rsp_malformed,
end_off, buflen);
return cifs_readv_discard(server, mid);
}
}
#ifdef CONFIG_CIFS_SMB_DIRECT

View File

@@ -260,12 +260,12 @@ typedef struct {
char FileName[];
} __packed FILE_DIRECTORY_INFO; /* level 0x101 FF resp data */
/* See MS-FSCC 2.4.13 */
/* See MS-FSCC 2.4.14 */
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 */
/* See MS-FSCC 2.4.15 */
typedef struct {
__le32 NextEntryOffset;
__u32 FileIndex;

View File

@@ -1566,6 +1566,10 @@ struct validate_negotiate_info_rsp {
#define FILE_STANDARD_LINK_INFORMATION 54
#define FILE_ID_INFORMATION 59
#define FILE_ID_EXTD_DIRECTORY_INFORMATION 60 /* also for QUERY_DIR */
#define FileId64ExtdDirectoryInformation 78 /* also for QUERY_DIR */
#define FileId64ExtdBothDirectoryInformation 79 /* also for QUERY_DIR */
#define FileIdAllExtdDirectoryInformation 80 /* also for QUERY_DIR */
#define FileIdAllExtdBothDirectoryInformation 81 /* also for QUERY_DIR */
/* Used for Query Info and Find File POSIX Info for SMB3.1.1 and SMB1 */
#define SMB_FIND_FILE_POSIX_INFO 0x064

View File

@@ -2168,7 +2168,7 @@ static ssize_t smbdirect_map_sges_from_iter(struct iov_iter *iter, size_t len,
if (ret < 0) {
while (state->num_sge > before) {
struct ib_sge *sge = &state->sge[state->num_sge--];
struct ib_sge *sge = &state->sge[--state->num_sge];
ib_dma_unmap_page(state->device,
sge->addr,