ksmbd_vfs_truncate() uses check_lock_range() with arguments that are incorrect for shrink, and can underflow when size==0:
- For shrink, the code passed [inode->i_size, size-1], which is reversed. - When size==0, "size-1" underflows to -1, so the range becomes [old_size, -1], effectively skipping the intended [0, old_size-1].
Fix by: - Rejecting negative size with -EINVAL. - For shrink (size < old): check [size, old-1]. - For grow (size > old): check [old, size-1]. - Skip lock check when size == old. - Keep the return value on conflict as -EAGAIN (no noisy pr_err()).
This avoids the size==0 underflow and uses the correct range order, preserving byte-range lock semantics.
Reported-by: Qianchang Zhao pioooooooooip@gmail.com Reported-by: Zhitong Liu liuzhitong1993@gmail.com Cc: stable@vger.kernel.org Signed-off-by: Qianchang Zhao pioooooooooip@gmail.com --- fs/smb/server/vfs.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-)
diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c index 891ed2dc2..e7843ec9b 100644 --- a/fs/smb/server/vfs.c +++ b/fs/smb/server/vfs.c @@ -825,17 +825,27 @@ int ksmbd_vfs_truncate(struct ksmbd_work *work, if (!work->tcon->posix_extensions) { struct inode *inode = file_inode(filp);
- if (size < inode->i_size) { - err = check_lock_range(filp, size, - inode->i_size - 1, WRITE); - } else { - err = check_lock_range(filp, inode->i_size, - size - 1, WRITE); + loff_t old = i_size_read(inode); + loff_t start = 0, end = -1; + bool need_check = false; + + if (size < 0) + return -EINVAL; + + if (size < old) { + start = size; + end = old - 1; + need_check = true; + } else if (size > old) { + start = old; + end = size - 1; + need_check = true; }
- if (err) { - pr_err("failed due to lock\n"); - return -EAGAIN; + if (need_check) { + err = check_lock_range(filp, start, end, WRITE); + if (err) + return -EAGAIN; } }
linux-stable-mirror@lists.linaro.org