eight import client fixes addressing, most also for stable
-----BEGIN PGP SIGNATURE----- iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAmWDm5MACgkQiiy9cAdy T1G34gv+N6wKK2jsQp6zPTGkMNDCcTb2JluSFyTw9IXJDXRICSaVAmG1aBY6X/GT pe4NRDd9fD2KZu9wf9Sw4oVpmEoXU4uiQwSGYYkUBMRBj1jqpWYe+Vs7m3ShQyJM CAReHCV/TAbLNgjC8ZzrkuyHOh9jSAr3lWYPX9caTWC1n1KkFc1gGBi9A8PhVUJn MMKwbugc7bCzXhiAmLy1X7EhLtDvjLsby7r0lveK8OR+iSr9Nf59inX+cwmcePe1 8pCNrQUX72O5jQ8y7eXIloZaFUwEvXx4TlYR6Ty3TL3h+f2tvQzbKhzKQQmCnB17 gbInx4rgn5irI8RYgRca3pyXyB0Xv0H3lG0hy7qjvjTdlTYLYhLL18fTy1r9L7f2 VJ/5aa6fT/WFNezQmyvQ5VSg9n5lQ+Pg5aX6QtjmDWxlOu/VrBddpC1ScjUYBdPa 0Ep7xUqbDxgUBONa4gQMVHpiiZDfUCibl6pTH+kl6N5EMzLtj0uQhdM37P5B0znS s3dQNeWD =+3SX -----END PGP SIGNATURE----- Merge tag '6.7-rc6-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6 Pull smb client fixes from Steve French: - two multichannel reconnect fixes, one fixing an important refcounting problem that can lead to umount problems - atime fix - five fixes for various potential OOB accesses, including a CVE fix, and two additional fixes for problems pointed out by Robert Morris's fuzzing investigation * tag '6.7-rc6-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6: cifs: do not let cifs_chan_update_iface deallocate channels cifs: fix a pending undercount of srv_count fs: cifs: Fix atime update check smb: client: fix potential OOB in smb2_dump_detail() smb: client: fix potential OOB in cifs_dump_detail() smb: client: fix OOB in smbCalcSize() smb: client: fix OOB in SMB2_query_info_init() smb: client: fix OOB in cifsd when receiving compounded resps
This commit is contained in:
commit
eee7f5b48e
@ -40,11 +40,13 @@ void cifs_dump_detail(void *buf, struct TCP_Server_Info *server)
|
||||
#ifdef CONFIG_CIFS_DEBUG2
|
||||
struct smb_hdr *smb = buf;
|
||||
|
||||
cifs_dbg(VFS, "Cmd: %d Err: 0x%x Flags: 0x%x Flgs2: 0x%x Mid: %d Pid: %d\n",
|
||||
smb->Command, smb->Status.CifsError,
|
||||
smb->Flags, smb->Flags2, smb->Mid, smb->Pid);
|
||||
cifs_dbg(VFS, "smb buf %p len %u\n", smb,
|
||||
server->ops->calc_smb_size(smb));
|
||||
cifs_dbg(VFS, "Cmd: %d Err: 0x%x Flags: 0x%x Flgs2: 0x%x Mid: %d Pid: %d Wct: %d\n",
|
||||
smb->Command, smb->Status.CifsError, smb->Flags,
|
||||
smb->Flags2, smb->Mid, smb->Pid, smb->WordCount);
|
||||
if (!server->ops->check_message(buf, server->total_read, server)) {
|
||||
cifs_dbg(VFS, "smb buf %p len %u\n", smb,
|
||||
server->ops->calc_smb_size(smb));
|
||||
}
|
||||
#endif /* CONFIG_CIFS_DEBUG2 */
|
||||
}
|
||||
|
||||
|
@ -532,7 +532,8 @@ struct smb_version_operations {
|
||||
struct mid_q_entry **, char **, int *);
|
||||
enum securityEnum (*select_sectype)(struct TCP_Server_Info *,
|
||||
enum securityEnum);
|
||||
int (*next_header)(char *);
|
||||
int (*next_header)(struct TCP_Server_Info *server, char *buf,
|
||||
unsigned int *noff);
|
||||
/* ioctl passthrough for query_info */
|
||||
int (*ioctl_query_info)(const unsigned int xid,
|
||||
struct cifs_tcon *tcon,
|
||||
|
@ -1201,7 +1201,12 @@ next_pdu:
|
||||
server->total_read += length;
|
||||
|
||||
if (server->ops->next_header) {
|
||||
next_offset = server->ops->next_header(buf);
|
||||
if (server->ops->next_header(server, buf, &next_offset)) {
|
||||
cifs_dbg(VFS, "%s: malformed response (next_offset=%u)\n",
|
||||
__func__, next_offset);
|
||||
cifs_reconnect(server, true);
|
||||
continue;
|
||||
}
|
||||
if (next_offset)
|
||||
server->pdu_size = next_offset;
|
||||
}
|
||||
|
@ -4671,7 +4671,7 @@ static int cifs_readpage_worker(struct file *file, struct page *page,
|
||||
/* we do not want atime to be less than mtime, it broke some apps */
|
||||
atime = inode_set_atime_to_ts(inode, current_time(inode));
|
||||
mtime = inode_get_mtime(inode);
|
||||
if (timespec64_compare(&atime, &mtime))
|
||||
if (timespec64_compare(&atime, &mtime) < 0)
|
||||
inode_set_atime_to_ts(inode, inode_get_mtime(inode));
|
||||
|
||||
if (PAGE_SIZE > rc)
|
||||
|
@ -363,6 +363,10 @@ checkSMB(char *buf, unsigned int total_read, struct TCP_Server_Info *server)
|
||||
cifs_dbg(VFS, "Length less than smb header size\n");
|
||||
}
|
||||
return -EIO;
|
||||
} else if (total_read < sizeof(*smb) + 2 * smb->WordCount) {
|
||||
cifs_dbg(VFS, "%s: can't read BCC due to invalid WordCount(%u)\n",
|
||||
__func__, smb->WordCount);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* otherwise, there is enough to get to the BCC */
|
||||
|
@ -439,7 +439,7 @@ cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
|
||||
cifs_dbg(FYI, "unable to find a suitable iface\n");
|
||||
}
|
||||
|
||||
if (!chan_index && !iface) {
|
||||
if (!iface) {
|
||||
cifs_dbg(FYI, "unable to get the interface matching: %pIS\n",
|
||||
&ss);
|
||||
spin_unlock(&ses->iface_lock);
|
||||
@ -447,7 +447,7 @@ cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
|
||||
}
|
||||
|
||||
/* now drop the ref to the current iface */
|
||||
if (old_iface && iface) {
|
||||
if (old_iface) {
|
||||
cifs_dbg(FYI, "replacing iface: %pIS with %pIS\n",
|
||||
&old_iface->sockaddr,
|
||||
&iface->sockaddr);
|
||||
@ -460,44 +460,32 @@ cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
|
||||
|
||||
kref_put(&old_iface->refcount, release_iface);
|
||||
} else if (old_iface) {
|
||||
cifs_dbg(FYI, "releasing ref to iface: %pIS\n",
|
||||
/* if a new candidate is not found, keep things as is */
|
||||
cifs_dbg(FYI, "could not replace iface: %pIS\n",
|
||||
&old_iface->sockaddr);
|
||||
|
||||
old_iface->num_channels--;
|
||||
if (old_iface->weight_fulfilled)
|
||||
old_iface->weight_fulfilled--;
|
||||
|
||||
kref_put(&old_iface->refcount, release_iface);
|
||||
} else if (!chan_index) {
|
||||
/* special case: update interface for primary channel */
|
||||
cifs_dbg(FYI, "referencing primary channel iface: %pIS\n",
|
||||
&iface->sockaddr);
|
||||
iface->num_channels++;
|
||||
iface->weight_fulfilled++;
|
||||
} else {
|
||||
WARN_ON(!iface);
|
||||
cifs_dbg(FYI, "adding new iface: %pIS\n", &iface->sockaddr);
|
||||
if (iface) {
|
||||
cifs_dbg(FYI, "referencing primary channel iface: %pIS\n",
|
||||
&iface->sockaddr);
|
||||
iface->num_channels++;
|
||||
iface->weight_fulfilled++;
|
||||
}
|
||||
}
|
||||
spin_unlock(&ses->iface_lock);
|
||||
|
||||
spin_lock(&ses->chan_lock);
|
||||
chan_index = cifs_ses_get_chan_index(ses, server);
|
||||
if (chan_index == CIFS_INVAL_CHAN_INDEX) {
|
||||
if (iface) {
|
||||
spin_lock(&ses->chan_lock);
|
||||
chan_index = cifs_ses_get_chan_index(ses, server);
|
||||
if (chan_index == CIFS_INVAL_CHAN_INDEX) {
|
||||
spin_unlock(&ses->chan_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ses->chans[chan_index].iface = iface;
|
||||
spin_unlock(&ses->chan_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ses->chans[chan_index].iface = iface;
|
||||
|
||||
/* No iface is found. if secondary chan, drop connection */
|
||||
if (!iface && SERVER_IS_CHAN(server))
|
||||
ses->chans[chan_index].server = NULL;
|
||||
|
||||
spin_unlock(&ses->chan_lock);
|
||||
|
||||
if (!iface && SERVER_IS_CHAN(server))
|
||||
cifs_put_tcp_session(server, false);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -173,6 +173,21 @@ smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *server)
|
||||
}
|
||||
|
||||
mid = le64_to_cpu(shdr->MessageId);
|
||||
if (check_smb2_hdr(shdr, mid))
|
||||
return 1;
|
||||
|
||||
if (shdr->StructureSize != SMB2_HEADER_STRUCTURE_SIZE) {
|
||||
cifs_dbg(VFS, "Invalid structure size %u\n",
|
||||
le16_to_cpu(shdr->StructureSize));
|
||||
return 1;
|
||||
}
|
||||
|
||||
command = le16_to_cpu(shdr->Command);
|
||||
if (command >= NUMBER_OF_SMB2_COMMANDS) {
|
||||
cifs_dbg(VFS, "Invalid SMB2 command %d\n", command);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (len < pdu_size) {
|
||||
if ((len >= hdr_size)
|
||||
&& (shdr->Status != 0)) {
|
||||
@ -193,21 +208,6 @@ smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *server)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (check_smb2_hdr(shdr, mid))
|
||||
return 1;
|
||||
|
||||
if (shdr->StructureSize != SMB2_HEADER_STRUCTURE_SIZE) {
|
||||
cifs_dbg(VFS, "Invalid structure size %u\n",
|
||||
le16_to_cpu(shdr->StructureSize));
|
||||
return 1;
|
||||
}
|
||||
|
||||
command = le16_to_cpu(shdr->Command);
|
||||
if (command >= NUMBER_OF_SMB2_COMMANDS) {
|
||||
cifs_dbg(VFS, "Invalid SMB2 command %d\n", command);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (smb2_rsp_struct_sizes[command] != pdu->StructureSize2) {
|
||||
if (command != SMB2_OPLOCK_BREAK_HE && (shdr->Status == 0 ||
|
||||
pdu->StructureSize2 != SMB2_ERROR_STRUCTURE_SIZE2_LE)) {
|
||||
|
@ -403,8 +403,10 @@ smb2_dump_detail(void *buf, struct TCP_Server_Info *server)
|
||||
cifs_server_dbg(VFS, "Cmd: %d Err: 0x%x Flags: 0x%x Mid: %llu Pid: %d\n",
|
||||
shdr->Command, shdr->Status, shdr->Flags, shdr->MessageId,
|
||||
shdr->Id.SyncId.ProcessId);
|
||||
cifs_server_dbg(VFS, "smb buf %p len %u\n", buf,
|
||||
server->ops->calc_smb_size(buf));
|
||||
if (!server->ops->check_message(buf, server->total_read, server)) {
|
||||
cifs_server_dbg(VFS, "smb buf %p len %u\n", buf,
|
||||
server->ops->calc_smb_size(buf));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -5074,17 +5076,22 @@ smb3_handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid)
|
||||
NULL, 0, false);
|
||||
}
|
||||
|
||||
static int
|
||||
smb2_next_header(char *buf)
|
||||
static int smb2_next_header(struct TCP_Server_Info *server, char *buf,
|
||||
unsigned int *noff)
|
||||
{
|
||||
struct smb2_hdr *hdr = (struct smb2_hdr *)buf;
|
||||
struct smb2_transform_hdr *t_hdr = (struct smb2_transform_hdr *)buf;
|
||||
|
||||
if (hdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM)
|
||||
return sizeof(struct smb2_transform_hdr) +
|
||||
le32_to_cpu(t_hdr->OriginalMessageSize);
|
||||
|
||||
return le32_to_cpu(hdr->NextCommand);
|
||||
if (hdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) {
|
||||
*noff = le32_to_cpu(t_hdr->OriginalMessageSize);
|
||||
if (unlikely(check_add_overflow(*noff, sizeof(*t_hdr), noff)))
|
||||
return -EINVAL;
|
||||
} else {
|
||||
*noff = le32_to_cpu(hdr->NextCommand);
|
||||
}
|
||||
if (unlikely(*noff && *noff < MID_HEADER_SIZE(server)))
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cifs_sfu_make_node(unsigned int xid, struct inode *inode,
|
||||
|
@ -411,8 +411,7 @@ skip_sess_setup:
|
||||
}
|
||||
|
||||
if (smb2_command != SMB2_INTERNAL_CMD)
|
||||
if (mod_delayed_work(cifsiod_wq, &server->reconnect, 0))
|
||||
cifs_put_tcp_session(server, false);
|
||||
mod_delayed_work(cifsiod_wq, &server->reconnect, 0);
|
||||
|
||||
atomic_inc(&tconInfoReconnectCount);
|
||||
out:
|
||||
@ -471,10 +470,15 @@ static int __smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon,
|
||||
void **request_buf, unsigned int *total_len)
|
||||
{
|
||||
/* BB eventually switch this to SMB2 specific small buf size */
|
||||
if (smb2_command == SMB2_SET_INFO)
|
||||
switch (smb2_command) {
|
||||
case SMB2_SET_INFO:
|
||||
case SMB2_QUERY_INFO:
|
||||
*request_buf = cifs_buf_get();
|
||||
else
|
||||
break;
|
||||
default:
|
||||
*request_buf = cifs_small_buf_get();
|
||||
break;
|
||||
}
|
||||
if (*request_buf == NULL) {
|
||||
/* BB should we add a retry in here if not a writepage? */
|
||||
return -ENOMEM;
|
||||
@ -3587,8 +3591,13 @@ SMB2_query_info_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server,
|
||||
struct smb2_query_info_req *req;
|
||||
struct kvec *iov = rqst->rq_iov;
|
||||
unsigned int total_len;
|
||||
size_t len;
|
||||
int rc;
|
||||
|
||||
if (unlikely(check_add_overflow(input_len, sizeof(*req), &len) ||
|
||||
len > CIFSMaxBufSize))
|
||||
return -EINVAL;
|
||||
|
||||
rc = smb2_plain_req_init(SMB2_QUERY_INFO, tcon, server,
|
||||
(void **) &req, &total_len);
|
||||
if (rc)
|
||||
@ -3610,7 +3619,7 @@ SMB2_query_info_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server,
|
||||
|
||||
iov[0].iov_base = (char *)req;
|
||||
/* 1 for Buffer */
|
||||
iov[0].iov_len = total_len - 1 + input_len;
|
||||
iov[0].iov_len = len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -3618,7 +3627,7 @@ void
|
||||
SMB2_query_info_free(struct smb_rqst *rqst)
|
||||
{
|
||||
if (rqst && rqst->rq_iov)
|
||||
cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */
|
||||
cifs_buf_release(rqst->rq_iov[0].iov_base); /* request */
|
||||
}
|
||||
|
||||
static int
|
||||
@ -5493,6 +5502,11 @@ build_qfs_info_req(struct kvec *iov, struct cifs_tcon *tcon,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void free_qfs_info_req(struct kvec *iov)
|
||||
{
|
||||
cifs_buf_release(iov->iov_base);
|
||||
}
|
||||
|
||||
int
|
||||
SMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata)
|
||||
@ -5524,7 +5538,7 @@ SMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
|
||||
rc = cifs_send_recv(xid, ses, server,
|
||||
&rqst, &resp_buftype, flags, &rsp_iov);
|
||||
cifs_small_buf_release(iov.iov_base);
|
||||
free_qfs_info_req(&iov);
|
||||
if (rc) {
|
||||
cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE);
|
||||
goto posix_qfsinf_exit;
|
||||
@ -5575,7 +5589,7 @@ SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
|
||||
rc = cifs_send_recv(xid, ses, server,
|
||||
&rqst, &resp_buftype, flags, &rsp_iov);
|
||||
cifs_small_buf_release(iov.iov_base);
|
||||
free_qfs_info_req(&iov);
|
||||
if (rc) {
|
||||
cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE);
|
||||
goto qfsinf_exit;
|
||||
@ -5642,7 +5656,7 @@ SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
|
||||
rc = cifs_send_recv(xid, ses, server,
|
||||
&rqst, &resp_buftype, flags, &rsp_iov);
|
||||
cifs_small_buf_release(iov.iov_base);
|
||||
free_qfs_info_req(&iov);
|
||||
if (rc) {
|
||||
cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE);
|
||||
goto qfsattr_exit;
|
||||
|
Loading…
x
Reference in New Issue
Block a user