mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-15 22:31:47 -04:00
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:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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) &&
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user