ksmbd: fix partial file information responses

Variable-length file information handlers use the client output length
while constructing the response. FILE_ALL_INFORMATION can consequently
return -EINVAL before the common buffer check, while stream information
can stop building the complete result too early.

Build the complete response within the available server response buffer
and apply the client output length only when selecting the final status
and transmitted length. Use the protocol-defined fixed sizes for all,
alternate-name, and stream information to distinguish
STATUS_INFO_LENGTH_MISMATCH from STATUS_BUFFER_OVERFLOW.

This fixes smb2.getinfo.qfile_buffercheck.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
This commit is contained in:
Namjae Jeon 2026-07-07 00:07:09 +09:00
parent d40c24634f
commit 6b8b79226b

View File

@ -5486,7 +5486,6 @@ static int get_file_all_info(struct ksmbd_work *work,
char *filename;
u64 time;
int ret, buf_free_len, filename_len;
struct smb2_query_info_req *req = ksmbd_req_buf_next(work);
if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) {
ksmbd_debug(SMB, "no right to read the attributes : 0x%x\n",
@ -5499,10 +5498,9 @@ static int get_file_all_info(struct ksmbd_work *work,
return PTR_ERR(filename);
filename_len = strlen(filename);
buf_free_len = smb2_calc_max_out_buf_len(work,
buf_free_len = smb2_resp_buf_len(work,
offsetof(struct smb2_query_info_rsp, Buffer) +
offsetof(struct smb2_file_all_info, FileName),
le32_to_cpu(req->OutputBufferLength));
offsetof(struct smb2_file_all_info, FileName));
if (buf_free_len < (filename_len + 1) * 2) {
kfree(filename);
return -EINVAL;
@ -5593,7 +5591,6 @@ static int get_file_stream_info(struct ksmbd_work *work,
ssize_t xattr_list_len;
int nbytes = 0, streamlen, stream_name_len, next, idx = 0;
int buf_free_len;
struct smb2_query_info_req *req = ksmbd_req_buf_next(work);
int ret;
ret = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS,
@ -5603,10 +5600,8 @@ static int get_file_stream_info(struct ksmbd_work *work,
file_info = (struct smb2_file_stream_info *)rsp->Buffer;
buf_free_len =
smb2_calc_max_out_buf_len(work,
offsetof(struct smb2_query_info_rsp, Buffer),
le32_to_cpu(req->OutputBufferLength));
buf_free_len = smb2_resp_buf_len(work,
offsetof(struct smb2_query_info_rsp, Buffer));
if (buf_free_len < 0)
goto out;
@ -5919,6 +5914,7 @@ static int smb2_get_info_file(struct ksmbd_work *work,
struct ksmbd_file *fp;
int fileinfoclass = 0;
int rc = 0;
unsigned int fixed_len;
unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID;
if (test_share_config_flag(work->tcon->share_conf,
@ -6022,10 +6018,23 @@ static int smb2_get_info_file(struct ksmbd_work *work,
fileinfoclass);
rc = -EOPNOTSUPP;
}
if (!rc)
if (!rc) {
fixed_len = le32_to_cpu(rsp->OutputBufferLength);
switch (fileinfoclass) {
case FILE_ALL_INFORMATION:
fixed_len = FILE_ALL_INFORMATION_SIZE;
break;
case FILE_ALTERNATE_NAME_INFORMATION:
fixed_len = FILE_ALTERNATE_NAME_INFORMATION_SIZE;
break;
case FILE_STREAM_INFORMATION:
fixed_len = FILE_STREAM_INFORMATION_SIZE;
break;
}
rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength),
le32_to_cpu(rsp->OutputBufferLength),
fixed_len,
rsp, work->response_buf);
}
ksmbd_fd_put(work, fp);
iov_pin_out: