- Validate the offsets and lengths a server puts in its responses before

using them, so that a malicious or broken server can no longer make the
    client read past the received data: POSIX lock data (which leaked
    kernel bytes through F_GETLK), NT transaction responses and SMB1 EA
    lists (both wrapping their pointer checks on 32-bit), short
    SESSION_SETUP responses (which sent stale buffer content back to the
    server in the NTLMv2 AUTHENTICATE message), SMB2 READ data areas and
    empty truncated fiemap responses
 
  - Fix a heap overflow in SMB2_tcon() with UNC paths longer than
    MAX_SHARENAME_LENGTH
 
  - Fix a slab-out-of-bounds write when chmod() rewrites a security
    descriptor that needs more room than was reserved
 
  - Fix a use-after-free of the server info between the cifsd thread and
    the release of the last session reference (reported by syzbot)
 
  - Fix a use-after-free in the demultiplex thread when a receive error
    reconnects and frees the read request still being processed
 
  - Fix a double teardown of a tcon that the cached directory laundromat
    took a reference on while it was being freed
 
  - Fix a NULL pointer dereference when /proc/fs/cifs/DebugData is read
    while the module is still initializing
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQTcqRusfSdYROJQwGkpVtNKoQNdYwUCauUmHQAKCRApVtNKoQNd
 YwbQAPsHdFjf5JOXEs1QNEf9GWYT47vHAnXt4ADbFwSokfLB3QEAqI1W7P6KJ3HO
 6ckrTF9VyZ75ZaStkESSlqsuYRwpiQs=
 =GLt9
 -----END PGP SIGNATURE-----

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

Pull smb client fixes from Paulo Alcantara:

 - Validate the offsets and lengths a server puts in its responses
   before using them, so that a malicious or broken server can no longer
   make the client read past the received data:
     - POSIX lock data (which leaked kernel bytes through F_GETLK)
     - NT transaction responses and SMB1 EA lists (both wrapping their
       pointer checks on 32-bit)
     - short SESSION_SETUP responses (which sent stale buffer content
       back to the server in the NTLMv2 AUTHENTICATE message)
     - SMB2 READ data areas
     - empty truncated fiemap responses

 - Fix a heap overflow in SMB2_tcon() with UNC paths longer than
   MAX_SHARENAME_LENGTH

 - Fix a slab-out-of-bounds write when chmod() rewrites a security
   descriptor that needs more room than was reserved

 - Fix a use-after-free of the server info between the cifsd thread and
   the release of the last session reference (reported by syzbot)

 - Fix a use-after-free in the demultiplex thread when a receive error
   reconnects and frees the read request still being processed

 - Fix a double teardown of a tcon that the cached directory laundromat
   took a reference on while it was being freed

 - Fix a NULL pointer dereference when /proc/fs/cifs/DebugData is read
   while the module is still initializing

* tag 'cifs-fixes-7.4-rc1' of https://git.manguebit.org/linux:
  smb: client: validate SMB1 EA list bounds before parsing
  smb: client: stop processing a read response after a receive error
  smb: client: don't resurrect an exiting tcon from cached dir workers
  smb: client: fix early procfs exposure race
  smb: client: fix slab-out-of-bounds write in id_mode_to_cifs_acl()
  smb: client: hold server info until cifsd exits
  smb: client: fix fiemap wild read on empty truncated response
  smb: client: validate the data area in SMB2_read()
  smb: client: reject short SESSION_SETUP responses
  smb: client: fix buffer overflow in SMB2_tcon()
  smb: client: validate NT transaction response layout and offsets
  smb: client: bound POSIX lock data area by response length
This commit is contained in:
Linus Torvalds 2026-10-30 13:40:03 -10:00
commit 51b4bf5fe7
11 changed files with 191 additions and 64 deletions

View File

@ -649,6 +649,17 @@ void invalidate_all_cached_dirs(struct cifs_tcon *tcon, bool sync)
flush_delayed_work(&cfids->laundromat_work);
}
static bool cached_dir_get_tcon(struct cifs_tcon *tcon,
enum smb3_tcon_ref_trace trace)
{
guard(spinlock)(&tcon->tc_lock);
if (tcon->status == TID_EXITING || tcon->tc_count <= 0)
return false;
++tcon->tc_count;
trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, trace);
return true;
}
static void
cached_dir_offload_close(struct work_struct *work)
{
@ -693,6 +704,9 @@ bool cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16])
!memcmp(lease_key,
cfid->fid.lease_key,
SMB2_LEASE_KEY_SIZE)) {
if (!cached_dir_get_tcon(tcon,
netfs_trace_tcon_ref_get_cached_lease_break))
break;
cfid->has_lease = false;
cfid->time = 0;
/*
@ -703,9 +717,6 @@ bool cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16])
cfid->on_list = false;
cfids->num_entries--;
++tcon->tc_count;
trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
netfs_trace_tcon_ref_get_cached_lease_break);
queue_work(cfid_put_wq, &cfid->put_work);
spin_unlock(&cfids->cfid_list_lock);
return true;
@ -813,12 +824,13 @@ static void cfids_laundromat_worker(struct work_struct *work)
cfid->dentry = NULL;
if (cfid->is_open) {
spin_lock(&cfid->tcon->tc_lock);
++cfid->tcon->tc_count;
trace_smb3_tcon_ref(cfid->tcon->debug_id, cfid->tcon->tc_count,
netfs_trace_tcon_ref_get_cached_laundromat);
spin_unlock(&cfid->tcon->tc_lock);
queue_work(serverclose_wq, &cfid->close_work);
if (cached_dir_get_tcon(cfid->tcon,
netfs_trace_tcon_ref_get_cached_laundromat)) {
queue_work(serverclose_wq, &cfid->close_work);
continue;
}
scoped_guard(spinlock, &cfids->cfid_list_lock)
list_add_tail(&cfid->entry, &cfids->dying);
} else
/*
* Drop the ref-count from above, either the lease-ref (if there

View File

@ -1826,12 +1826,15 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
/* Potentially, five new ACEs can be added to the ACL for U,G,O mapping */
if (pnmode && *pnmode != NO_CHANGE_64) { /* chmod */
nsecdesclen = sizeof(struct smb_acl) +
2 * sizeof(struct smb_sid);
if (posix)
nsecdesclen = 1 * sizeof(struct smb_ace);
nsecdesclen += sizeof(struct smb_ntsd) +
sizeof(struct smb_ace);
else if (mode_from_sid)
nsecdesclen = secdesclen + (2 * sizeof(struct smb_ace));
nsecdesclen += secdesclen + 2 * sizeof(struct smb_ace);
else /* cifsacl */
nsecdesclen = secdesclen + (5 * sizeof(struct smb_ace));
nsecdesclen += secdesclen + 5 * sizeof(struct smb_ace);
} else { /* chown */
/* When ownership changes, changes new owner sid length could be different */
nsecdesclen = sizeof(struct smb_ntsd) + (sizeof(struct smb_sid) * 2);

View File

@ -2019,7 +2019,6 @@ init_cifs(void)
if (rc)
return rc;
cifs_proc_init();
INIT_LIST_HEAD(&cifs_tcp_ses_list);
/*
* Initialize Global counters
@ -2068,10 +2067,8 @@ init_cifs(void)
cifsiod_wq = alloc_workqueue("cifsiod",
WQ_FREEZABLE | WQ_MEM_RECLAIM | WQ_PERCPU,
0);
if (!cifsiod_wq) {
rc = -ENOMEM;
goto out_clean_proc;
}
if (!cifsiod_wq)
return -ENOMEM;
/*
* Consider in future setting limit!=0 maybe to min(num_of_cores - 1, 3)
@ -2162,19 +2159,22 @@ init_cifs(void)
if (rc)
goto out_cifs_swn_init;
cifs_proc_init();
rc = register_filesystem(&cifs_fs_type);
if (rc)
goto out_init_cifs_idmap;
goto out_clean_proc;
rc = register_filesystem(&smb3_fs_type);
if (rc) {
unregister_filesystem(&cifs_fs_type);
goto out_init_cifs_idmap;
goto out_clean_proc;
}
return 0;
out_init_cifs_idmap:
out_clean_proc:
cifs_proc_clean();
exit_cifs_idmap();
out_cifs_swn_init:
#ifdef CONFIG_CIFS_SWN_UPCALL
@ -2210,8 +2210,6 @@ init_cifs(void)
destroy_workqueue(decrypt_wq);
out_destroy_cifsiod_wq:
destroy_workqueue(cifsiod_wq);
out_clean_proc:
cifs_proc_clean();
return rc;
}
@ -2221,6 +2219,7 @@ exit_cifs(void)
cifs_dbg(NOISY, "exit_smb3\n");
unregister_filesystem(&cifs_fs_type);
unregister_filesystem(&smb3_fs_type);
cifs_proc_clean();
cifs_release_automount_timer();
exit_cifs_idmap();
#ifdef CONFIG_CIFS_SWN_UPCALL
@ -2243,7 +2242,6 @@ exit_cifs(void)
destroy_workqueue(serverclose_wq);
destroy_workqueue(cfid_put_wq);
destroy_workqueue(cifsiod_wq);
cifs_proc_clean();
}
MODULE_AUTHOR("Steve French");

View File

@ -16,6 +16,7 @@
#include <linux/scatterlist.h>
#include <linux/mm.h>
#include <linux/mempool.h>
#include <linux/refcount.h>
#include <linux/workqueue.h>
#include <linux/utsname.h>
#include <linux/sched/mm.h>
@ -681,6 +682,7 @@ struct TCP_Server_Info {
spinlock_t srv_lock; /* protect anything here that is not protected */
__u64 conn_id; /* connection identifier (useful for debugging) */
int srv_count; /* reference counter */
refcount_t obj_refs; /* struct lifetime: srv_count users + cifsd */
int rfc1001_sessinit; /* whether to estasblish netbios session */
bool with_rfc1001; /* if netbios session is used */
/* 15 character server name + 0x20 16th byte indicating type = srv */

View File

@ -2369,6 +2369,7 @@ CIFSSMBPosixLock(const unsigned int xid, struct cifs_tcon *tcon,
cifs_dbg(FYI, "Send error in Posix Lock = %d\n", rc);
} else if (pLockData) {
/* lock structure can be returned on get */
unsigned int rsp_len;
__u16 data_offset;
__u16 data_count;
rc = validate_t2((struct smb_t2_rsp *)pSMBr);
@ -2380,11 +2381,18 @@ CIFSSMBPosixLock(const unsigned int xid, struct cifs_tcon *tcon,
}
data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
data_count = le16_to_cpu(pSMBr->t2.DataCount);
rsp_len = smbCalcSize(&pSMBr->hdr);
if (data_count < sizeof(struct cifs_posix_lock)) {
rc = smb_EIO2(smb_eio_trace_lock_data_too_small,
data_count, sizeof(struct cifs_posix_lock));
goto plk_err_exit;
}
if (data_offset > rsp_len ||
data_count > rsp_len - data_offset) {
rc = smb_EIO2(smb_eio_trace_lock_data_area,
data_offset, data_count);
goto plk_err_exit;
}
parm_data = (struct cifs_posix_lock *)
((char *)&pSMBr->hdr.Protocol + data_offset);
if (parm_data->lock_type == cpu_to_le16(CIFS_UNLCK))
@ -3779,9 +3787,10 @@ static int
validate_ntransact(char *buf, char **ppparm, char **ppdata,
__u32 *pparmlen, __u32 *pdatalen)
{
char *end_of_smb;
__u32 data_count, data_offset, parm_count, parm_offset;
struct smb_com_ntransact_rsp *pSMBr;
size_t buflen;
char *end_buf;
u16 bcc;
*pdatalen = 0;
@ -3792,37 +3801,42 @@ validate_ntransact(char *buf, char **ppparm, char **ppdata,
pSMBr = (struct smb_com_ntransact_rsp *)buf;
if (pSMBr->hdr.WordCount != 18) {
cifs_dbg(FYI, "invalid NT transaction WordCount %u\n",
pSMBr->hdr.WordCount);
return -EINVAL;
}
bcc = get_bcc(&pSMBr->hdr);
end_of_smb = 2 /* sizeof byte count */ + bcc +
(char *)&pSMBr->ByteCount;
end_buf = 2 /* sizeof byte count */ + bcc + (char *)&pSMBr->ByteCount;
buflen = end_buf - buf;
data_offset = le32_to_cpu(pSMBr->DataOffset);
data_count = le32_to_cpu(pSMBr->DataCount);
parm_offset = le32_to_cpu(pSMBr->ParameterOffset);
parm_count = le32_to_cpu(pSMBr->ParameterCount);
*ppparm = (char *)&pSMBr->hdr.Protocol + parm_offset;
*ppdata = (char *)&pSMBr->hdr.Protocol + data_offset;
/* should we also check that parm and data areas do not overlap? */
if (*ppparm > end_of_smb) {
if (parm_offset > buflen) {
cifs_dbg(FYI, "parms start after end of smb\n");
return -EINVAL;
} else if (parm_count + *ppparm > end_of_smb) {
} else if (parm_count > buflen - parm_offset) {
cifs_dbg(FYI, "parm end after end of smb\n");
return -EINVAL;
} else if (*ppdata > end_of_smb) {
} else if (data_offset > buflen) {
cifs_dbg(FYI, "data starts after end of smb\n");
return -EINVAL;
} else if (data_count + *ppdata > end_of_smb) {
cifs_dbg(FYI, "data %p + count %d (%p) past smb end %p start %p\n",
*ppdata, data_count, (data_count + *ppdata),
end_of_smb, pSMBr);
} else if (data_count > buflen - data_offset) {
cifs_dbg(FYI, "data offset %u + count %u past smb length %zu\n",
data_offset, data_count, buflen);
return -EINVAL;
} else if (parm_count + data_count > bcc) {
cifs_dbg(FYI, "parm count and data count larger than SMB\n");
return -EINVAL;
}
*ppparm = buf + parm_offset;
*ppdata = buf + data_offset;
*pdatalen = data_count;
*pparmlen = parm_count;
return 0;
@ -6201,9 +6215,11 @@ CIFSSMBQAllEAs(const unsigned int xid, struct cifs_tcon *tcon,
int rc = 0;
int bytes_returned;
int list_len;
u32 ea_list_len;
struct fealist *ea_response_data;
struct fea *temp_fea;
char *temp_ptr;
char *byte_area;
char *end_of_smb;
__u16 params, byte_count, data_offset;
unsigned int ea_name_len = ea_name ? strlen(ea_name) : 0;
@ -6282,9 +6298,19 @@ CIFSSMBQAllEAs(const unsigned int xid, struct cifs_tcon *tcon,
ea_response_data = (struct fealist *)
(((char *) &pSMBr->hdr.Protocol) + data_offset);
list_len = le32_to_cpu(ea_response_data->list_len);
cifs_dbg(FYI, "ea length %d\n", list_len);
if (list_len <= 8) {
byte_area = pByteArea(&pSMBr->hdr);
end_of_smb = byte_area + get_bcc(&pSMBr->hdr);
if ((char *)ea_response_data < byte_area ||
(char *)ea_response_data >
end_of_smb - sizeof(ea_response_data->list_len)) {
rc = smb_EIO2(smb_eio_trace_qalleas_overlong,
data_offset, get_bcc(&pSMBr->hdr));
goto QAllEAsOut;
}
ea_list_len = le32_to_cpu(ea_response_data->list_len);
cifs_dbg(FYI, "%s: ea length: %u\n", __func__, ea_list_len);
if (ea_list_len <= 8) {
cifs_dbg(FYI, "empty EA list returned from server\n");
/* didn't find the named attribute */
if (ea_name)
@ -6293,14 +6319,14 @@ CIFSSMBQAllEAs(const unsigned int xid, struct cifs_tcon *tcon,
}
/* make sure list_len doesn't go past end of SMB */
end_of_smb = (char *)pByteArea(&pSMBr->hdr) + get_bcc(&pSMBr->hdr);
if ((char *)ea_response_data + list_len > end_of_smb) {
if (ea_list_len > end_of_smb - (char *)ea_response_data) {
cifs_dbg(FYI, "EA list appears to go beyond SMB\n");
rc = smb_EIO2(smb_eio_trace_qalleas_overlong,
(unsigned long)ea_response_data + list_len - (unsigned long)pSMBr,
(unsigned long)end_of_smb - (unsigned long)pSMBr);
ea_list_len,
end_of_smb - (char *)ea_response_data);
goto QAllEAsOut;
}
list_len = ea_list_len;
/* account for ea list len */
list_len -= 4;

View File

@ -1083,6 +1083,19 @@ cifs_enable_signing(struct TCP_Server_Info *server, bool mnt_sign_required)
return 0;
}
static void cifs_tcp_server_put(struct TCP_Server_Info *server)
{
if (!refcount_dec_and_test(&server->obj_refs))
return;
cifs_crypto_secmech_release(server);
kfree_sensitive(server->session_key.response);
put_net(cifs_net_ns(server));
kfree(server->leaf_fullpath);
kfree(server->hostname);
kfree_sensitive(server);
}
static noinline_for_stack void
clean_demultiplex_info(struct TCP_Server_Info *server)
{
@ -1168,14 +1181,11 @@ clean_demultiplex_info(struct TCP_Server_Info *server)
*/
}
put_net(cifs_net_ns(server));
kfree(server->leaf_fullpath);
kfree(server->hostname);
kfree_sensitive(server);
length = atomic_dec_return(&tcpSesAllocCount);
if (length > 0)
mempool_resize(cifs_req_poolp, length + cifs_min_rcv);
cifs_tcp_server_put(server);
}
static int
@ -1751,7 +1761,7 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect)
else
cancel_delayed_work_sync(&server->reconnect);
/* For secondary channels, we pick up ref-count on the primary server */
/* For secondary channels, drop the reference on the primary server */
if (SERVER_IS_CHAN(server))
cifs_put_tcp_session(server->primary_server, from_reconnect);
@ -1759,15 +1769,11 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect)
server->tcpStatus = CifsExiting;
spin_unlock(&server->srv_lock);
cifs_crypto_secmech_release(server);
kfree_sensitive(server->session_key.response);
server->session_key.response = NULL;
server->session_key.len = 0;
task = xchg(&server->tsk, NULL);
if (task)
send_sig(SIGKILL, task, 1);
cifs_tcp_server_put(server);
}
struct TCP_Server_Info *
@ -1789,6 +1795,7 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
rc = -ENOMEM;
goto out_err;
}
refcount_set(&tcp_ses->obj_refs, 1);
tcp_ses->hostname = kstrdup(ctx->server_hostname, GFP_KERNEL);
if (!tcp_ses->hostname) {
@ -1927,6 +1934,8 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
module_put(THIS_MODULE);
goto out_err_crypto_release;
}
/* Keep the server alive until the demultiplex thread has exited */
refcount_inc(&tcp_ses->obj_refs);
/* thread created, put it on the list */
spin_lock(&cifs_tcp_ses_lock);
list_add(&tcp_ses->tcp_ses_list, &cifs_tcp_ses_list);

View File

@ -225,6 +225,22 @@ smb2_check_message(char *buf, unsigned int pdu_len, unsigned int len,
return 1;
}
/*
* A SESSION_SETUP response that asks for more processing carries a
* security blob and is parsed as a normal reply. Do not let the
* short-error path below accept a frame that cannot hold the fixed
* response fields; the minimum-PDU check later is unreachable once
* the early return has accepted the frame.
*/
if (command == SMB2_SESSION_SETUP_HE &&
shdr->Status == STATUS_MORE_PROCESSING_REQUIRED &&
len < smb2_min_pdu_len[command]) {
cifs_server_dbg(VFS,
"%s: session setup response too short: %u < %zu\n",
__func__, len, smb2_min_pdu_len[command]);
return 1;
}
if (len < pdu_size) {
if ((len >= hdr_size)
&& (shdr->Status != 0)) {

View File

@ -4360,6 +4360,15 @@ static int smb3_fiemap(struct cifs_tcon *tcon,
goto out;
}
/*
* A truncated range blob must contain at least one range, otherwise
* there is no offset to resume from and out_data is NULL.
*/
if (!last_blob && out_data_len == 0) {
rc = -EINVAL;
goto out;
}
num = out_data_len / sizeof(struct file_allocated_range_buffer);
for (i = 0; i < num; i++) {
flags = 0;
@ -4380,10 +4389,25 @@ static int smb3_fiemap(struct cifs_tcon *tcon,
}
if (!last_blob) {
next = le64_to_cpu(out_data[num - 1].file_offset) +
le64_to_cpu(out_data[num - 1].length);
len = len - (next - start);
u64 last_start, last_len;
last_start = le64_to_cpu(out_data[num - 1].file_offset);
last_len = le64_to_cpu(out_data[num - 1].length);
if (check_add_overflow(last_start, last_len, &next) ||
next <= start) {
rc = -EINVAL;
goto out;
}
if (next - start >= len) {
rc = 0;
goto out;
}
len -= next - start;
start = next;
kfree(out_data);
out_data = NULL;
goto again;
}

View File

@ -1857,14 +1857,20 @@ SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data)
goto out;
u16 boff = le16_to_cpu(rsp->SecurityBufferOffset);
u16 blen = le16_to_cpu(rsp->SecurityBufferLength);
if (offsetof(struct smb2_sess_setup_rsp, Buffer) != boff) {
cifs_dbg(VFS, "Invalid security buffer offset %d\n", boff);
rc = smb_EIO1(smb_eio_trace_sess_buf_off, boff);
goto out;
}
rc = decode_ntlmssp_challenge(rsp->Buffer,
le16_to_cpu(rsp->SecurityBufferLength), ses);
if ((u32)boff + blen > sess_data->iov[0].iov_len) {
cifs_dbg(VFS, "%s: invalid security buffer length: %u\n",
__func__, blen);
rc = -EINVAL;
goto out;
}
rc = decode_ntlmssp_challenge(rsp->Buffer, blen, ses);
if (rc)
goto out;
@ -2104,8 +2110,6 @@ SMB2_logoff(const unsigned int xid, struct cifs_ses *ses)
return rc;
}
#define MAX_SHARENAME_LENGTH (255 /* server */ + 80 /* share */ + 1 /* NULL */)
/* These are similar values to what Windows uses */
static inline void init_copy_chunk_defaults(struct cifs_tcon *tcon)
{
@ -2135,7 +2139,15 @@ int SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
if (!server || !tree)
return smb_EIO(smb_eio_trace_null_pointers);
unc_path = kmalloc(MAX_SHARENAME_LENGTH * 2, GFP_KERNEL);
total_len = strlen(tree);
if (total_len > MAX_TREE_SIZE) {
cifs_server_dbg(VFS | ONCE,
"%s: tree name exceeded %u characters\n",
__func__, MAX_TREE_SIZE);
return -EINVAL;
}
unc_path = kmalloc((total_len + 1) * sizeof(*unc_path), GFP_KERNEL);
if (unc_path == NULL)
return -ENOMEM;
@ -4936,6 +4948,7 @@ SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms,
struct kvec iov[1];
struct kvec rsp_iov;
unsigned int total_len;
u16 data_offset;
int flags = CIFS_LOG_ERROR;
struct cifs_ses *ses = io_parms->tcon->ses;
@ -4995,8 +5008,29 @@ SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms,
*nbytes = 0;
}
data_offset = rsp->DataOffset;
if (data_offset < io_parms->server->vals->read_rsp_size) {
/*
* win2k8 sometimes sends an offset of 0 when the read is beyond
* the EOF. Treat it as if the data starts just after the header,
* as handle_read_data() does.
*/
cifs_dbg(FYI, "data offset %u inside read response header\n",
data_offset);
data_offset = io_parms->server->vals->read_rsp_size;
}
/* The data area must lie within the response actually received */
if (*nbytes && (size_t)data_offset + *nbytes > rsp_iov.iov_len) {
cifs_dbg(FYI, "bad data offset %u length %u for response of %zu\n",
data_offset, *nbytes, rsp_iov.iov_len);
rc = smb_EIO2(smb_eio_trace_read_bad_offset,
data_offset, *nbytes);
*nbytes = 0;
}
if (*buf) {
memcpy(*buf, (char *)rsp + rsp->DataOffset, *nbytes);
memcpy(*buf, (char *)rsp + data_offset, *nbytes);
free_rsp_buf(resp_buftype, rsp_iov.iov_base);
} else if (resp_buftype != CIFS_NO_BUFFER) {
*buf = rsp_iov.iov_base;

View File

@ -44,6 +44,7 @@
EM(smb_eio_trace_ioctl_no_rsp, "ioctl_no_rsp") \
EM(smb_eio_trace_ioctl_out_off, "ioctl_out_off") \
EM(smb_eio_trace_lock_bcc_too_small, "lock_bcc_too_small") \
EM(smb_eio_trace_lock_data_area, "lock_data_area") \
EM(smb_eio_trace_lock_data_too_small, "lock_data_too_small") \
EM(smb_eio_trace_malformed_ksid_key, "malformed_ksid_key") \
EM(smb_eio_trace_malformed_sid_key, "malformed_sid_key") \

View File

@ -1345,6 +1345,8 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
#endif
length = cifs_read_iter_from_socket(server, &rdata->subreq.io_iter,
data_len);
if (length < 0)
return length;
if (length > 0)
rdata->got_bytes += length;
server->total_read += length;