[PATCH] smb: client: reject query directory replies with no usable entries
From: Yuanfu Xie
Date: Sun Sep 27 2026 - 06:42:55 EST
A server can answer every SMB2 QUERY_DIRECTORY with SUCCESS and a
first entry whose FileNameLength extends past the end of the PDU.
num_entries() counts zero entries, but smb2_parse_query_directory()
still reports success, so the index of the last entry and endOfSearch
stay unchanged. find_cifs_entry() issues another QUERY_DIRECTORY.
Responses keep arriving, so the echo timeout and reconnect do not run,
and getdents() on that directory never returns.
The loop stops if a later reply is a well-formed page or
STATUS_NO_MORE_FILES. An empty directory ends on that status before
this parse. The hang requires the server to keep sending the
zero-entry SUCCESS.
smb2_validate_iov() only checks that the output buffer can hold the
fixed header of the requested info level. A count of zero means the
first entry could not be parsed: its name does not fit in the PDU
after that header.
Reject that reply before the response buffer is adopted. The
unpatched client logs the overflow warning thousands of times per
second and getdents() never returns. With this change the same
replies fail the search once, with the new message, and getdents()
returns an error. Ordinary directory listings on the patched kernel
still complete.
Fixes: d324f08d6a871 ("CIFS: Add readdir support for SMB2")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Yuanfu Xie <yuanfuxie@xxxxxxxxxxxxxx>
---
fs/smb/client/smb2pdu.c | 38 ++++++++++++++++++++++++++------------
1 file changed, 26 insertions(+), 12 deletions(-)
diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
index 3d7ead36d1a0e..84d6d5ccacf06 100644
--- a/fs/smb/client/smb2pdu.c
+++ b/fs/smb/client/smb2pdu.c
@@ -5679,6 +5679,9 @@ smb2_parse_query_directory(struct cifs_tcon *tcon,
struct smb2_query_directory_rsp *rsp;
size_t info_buf_size;
char *end_of_smb;
+ char *entries_start;
+ char *last_entry;
+ unsigned int entries;
int rc;
rsp = (struct smb2_query_directory_rsp *)rsp_iov->iov_base;
@@ -5713,6 +5716,23 @@ smb2_parse_query_directory(struct cifs_tcon *tcon,
srch_inf->unicode = true;
+ entries_start = (char *)rsp + le16_to_cpu(rsp->OutputBufferOffset);
+ end_of_smb = rsp_iov->iov_len + (char *)rsp;
+ last_entry = entries_start;
+
+ entries = num_entries(srch_inf->info_level, entries_start, end_of_smb,
+ &last_entry, info_buf_size);
+ if (!entries) {
+ /*
+ * The fixed header fit, but no directory entry could
+ * be parsed. For these info levels the first name
+ * does not fit in the PDU after that header. Fail
+ * the search so find_cifs_entry() does not issue
+ * another QUERY_DIRECTORY. The server has to keep
+ * sending this reply for the hang to continue.
+ */
+ cifs_tcon_dbg(VFS, "no usable entries in query directory reply\n");
+ return -EIO;
+ }
+
if (srch_inf->ntwrk_buf_start) {
if (srch_inf->smallBuf)
cifs_small_buf_release(srch_inf->ntwrk_buf_start);
@@ -5722,18 +5744,10 @@ smb2_parse_query_directory(struct cifs_tcon *tcon,
cifs_buf_release(srch_inf->ntwrk_buf_start);
}
srch_inf->ntwrk_buf_start = (char *)rsp;
- srch_inf->srch_entries_start = srch_inf->last_entry =
- (char *)rsp + le16_to_cpu(rsp->OutputBufferOffset);
- end_of_smb = rsp_iov->iov_len + (char *)rsp;
-
- srch_inf->entries_in_buffer = num_entries(
- srch_inf->info_level,
- srch_inf->srch_entries_start,
- end_of_smb,
- &srch_inf->last_entry,
- info_buf_size);
-
- srch_inf->index_of_last_entry += srch_inf->entries_in_buffer;
+ srch_inf->srch_entries_start = entries_start;
+ srch_inf->last_entry = last_entry;
+ srch_inf->entries_in_buffer = entries;
+ srch_inf->index_of_last_entry += entries;
cifs_dbg(FYI, "num entries %d last_index %lld srch start %p srch end %p\n",
srch_inf->entries_in_buffer, srch_inf->index_of_last_entry,
srch_inf->srch_entries_start, srch_inf->last_entry);
--
2.43.0