mirror of
https://github.com/torvalds/linux.git
synced 2026-10-08 11:36:02 +02:00
ksmbd: fix SMB2 byte-range lock end offset
SMB2 describes a byte-range lock using an offset and a length, while Linux file_lock uses an inclusive end offset. smb2_lock() currently sets fl_end to start + length and consequently locks one extra byte for every nonzero-length request. Translate nonzero lengths to start + length - 1 and reject ranges that cannot be represented by loff_t instead of silently truncating them at OFFSET_MAX. Track zero-length locks from the request length so one-byte ranges are not mistaken for zero-length locks after endpoint conversion. Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
This commit is contained in:
parent
b0148dc562
commit
54d90311f9
|
|
@ -9131,7 +9131,7 @@ static int smb2_set_flock_flags(struct file_lock *flock, int flags)
|
|||
}
|
||||
|
||||
static struct ksmbd_lock *smb2_lock_init(struct file_lock *flock,
|
||||
unsigned int cmd, int flags,
|
||||
unsigned int cmd, int flags, bool zero_len,
|
||||
struct list_head *lock_list)
|
||||
{
|
||||
struct ksmbd_lock *lock;
|
||||
|
|
@ -9145,8 +9145,7 @@ static struct ksmbd_lock *smb2_lock_init(struct file_lock *flock,
|
|||
lock->start = flock->fl_start;
|
||||
lock->end = flock->fl_end;
|
||||
lock->flags = flags;
|
||||
if (lock->start == lock->end)
|
||||
lock->zero_len = 1;
|
||||
lock->zero_len = zero_len;
|
||||
INIT_LIST_HEAD(&lock->clist);
|
||||
INIT_LIST_HEAD(&lock->flist);
|
||||
INIT_LIST_HEAD(&lock->llist);
|
||||
|
|
@ -9255,32 +9254,18 @@ int smb2_lock(struct ksmbd_work *work)
|
|||
|
||||
lock_start = le64_to_cpu(lock_ele[i].Offset);
|
||||
lock_length = le64_to_cpu(lock_ele[i].Length);
|
||||
if (lock_start > U64_MAX - lock_length) {
|
||||
if (lock_start > OFFSET_MAX ||
|
||||
(lock_length &&
|
||||
lock_length - 1 > OFFSET_MAX - lock_start)) {
|
||||
pr_err("Invalid lock range requested\n");
|
||||
rsp->hdr.Status = STATUS_INVALID_LOCK_RANGE;
|
||||
locks_free_lock(flock);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (lock_start > OFFSET_MAX)
|
||||
flock->fl_start = OFFSET_MAX;
|
||||
else
|
||||
flock->fl_start = lock_start;
|
||||
|
||||
lock_length = le64_to_cpu(lock_ele[i].Length);
|
||||
if (lock_length > OFFSET_MAX - flock->fl_start)
|
||||
lock_length = OFFSET_MAX - flock->fl_start;
|
||||
|
||||
flock->fl_end = flock->fl_start + lock_length;
|
||||
|
||||
if (flock->fl_end < flock->fl_start) {
|
||||
ksmbd_debug(SMB,
|
||||
"the end offset(%llx) is smaller than the start offset(%llx)\n",
|
||||
flock->fl_end, flock->fl_start);
|
||||
rsp->hdr.Status = STATUS_INVALID_LOCK_RANGE;
|
||||
locks_free_lock(flock);
|
||||
goto out;
|
||||
}
|
||||
flock->fl_start = lock_start;
|
||||
flock->fl_end = lock_length ?
|
||||
flock->fl_start + lock_length - 1 : flock->fl_start;
|
||||
|
||||
/* Check conflict locks in one request */
|
||||
list_for_each_entry(cmp_lock, &lock_list, llist) {
|
||||
|
|
@ -9296,7 +9281,8 @@ int smb2_lock(struct ksmbd_work *work)
|
|||
}
|
||||
}
|
||||
|
||||
smb_lock = smb2_lock_init(flock, cmd, flags, &lock_list);
|
||||
smb_lock = smb2_lock_init(flock, cmd, flags, !lock_length,
|
||||
&lock_list);
|
||||
if (!smb_lock) {
|
||||
err = -EINVAL;
|
||||
locks_free_lock(flock);
|
||||
|
|
@ -9370,7 +9356,7 @@ int smb2_lock(struct ksmbd_work *work)
|
|||
/* check zero byte lock range */
|
||||
if (cmp_lock->zero_len && !smb_lock->zero_len &&
|
||||
cmp_lock->start > smb_lock->start &&
|
||||
cmp_lock->start < smb_lock->end) {
|
||||
cmp_lock->start <= smb_lock->end) {
|
||||
spin_unlock(&conn->llist_lock);
|
||||
up_read(&conn_list_lock);
|
||||
pr_err("previous lock conflict with zero byte lock range\n");
|
||||
|
|
@ -9379,17 +9365,15 @@ int smb2_lock(struct ksmbd_work *work)
|
|||
|
||||
if (smb_lock->zero_len && !cmp_lock->zero_len &&
|
||||
smb_lock->start > cmp_lock->start &&
|
||||
smb_lock->start < cmp_lock->end) {
|
||||
smb_lock->start <= cmp_lock->end) {
|
||||
spin_unlock(&conn->llist_lock);
|
||||
up_read(&conn_list_lock);
|
||||
pr_err("current lock conflict with zero byte lock range\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (((cmp_lock->start <= smb_lock->start &&
|
||||
cmp_lock->end > smb_lock->start) ||
|
||||
(cmp_lock->start < smb_lock->end &&
|
||||
cmp_lock->end >= smb_lock->end)) &&
|
||||
if (cmp_lock->start <= smb_lock->end &&
|
||||
smb_lock->start <= cmp_lock->end &&
|
||||
!cmp_lock->zero_len && !smb_lock->zero_len) {
|
||||
spin_unlock(&conn->llist_lock);
|
||||
up_read(&conn_list_lock);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user