fs: dlm: fix mismatch of plock results from userspace

[ Upstream commit 57e2c2f2d94cfd551af91cedfa1af6d972487197 ]

When a waiting plock request (F_SETLKW) is sent to userspace
for processing (dlm_controld), the result is returned at a
later time. That result could be incorrectly matched to a
different waiting request in cases where the owner field is
the same (e.g. different threads in a process.) This is fixed
by comparing all the properties in the request and reply.

The results for non-waiting plock requests are now matched
based on list order because the results are returned in the
same order they were sent.

Cc: stable@vger.kernel.org
Signed-off-by: Alexander Aring <aahringo@redhat.com>
Signed-off-by: David Teigland <teigland@redhat.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Alexander Aring 2023-05-24 12:02:04 -04:00 committed by Greg Kroah-Hartman
parent 721d5b514d
commit 7b57fc3f4c

View File

@ -405,7 +405,7 @@ static ssize_t dev_read(struct file *file, char __user *u, size_t count,
if (op->info.flags & DLM_PLOCK_FL_CLOSE) if (op->info.flags & DLM_PLOCK_FL_CLOSE)
list_del(&op->list); list_del(&op->list);
else else
list_move(&op->list, &recv_list); list_move_tail(&op->list, &recv_list);
memcpy(&info, &op->info, sizeof(info)); memcpy(&info, &op->info, sizeof(info));
} }
spin_unlock(&ops_lock); spin_unlock(&ops_lock);
@ -443,20 +443,52 @@ static ssize_t dev_write(struct file *file, const char __user *u, size_t count,
if (check_version(&info)) if (check_version(&info))
return -EINVAL; return -EINVAL;
/*
* The results for waiting ops (SETLKW) can be returned in any
* order, so match all fields to find the op. The results for
* non-waiting ops are returned in the order that they were sent
* to userspace, so match the result with the first non-waiting op.
*/
spin_lock(&ops_lock); spin_lock(&ops_lock);
list_for_each_entry(iter, &recv_list, list) { if (info.wait) {
if (iter->info.fsid == info.fsid && list_for_each_entry(iter, &recv_list, list) {
iter->info.number == info.number && if (iter->info.fsid == info.fsid &&
iter->info.owner == info.owner) { iter->info.number == info.number &&
list_del_init(&iter->list); iter->info.owner == info.owner &&
memcpy(&iter->info, &info, sizeof(info)); iter->info.pid == info.pid &&
if (iter->data) iter->info.start == info.start &&
do_callback = 1; iter->info.end == info.end &&
else iter->info.ex == info.ex &&
iter->done = 1; iter->info.wait) {
op = iter; op = iter;
break; break;
}
} }
} else {
list_for_each_entry(iter, &recv_list, list) {
if (!iter->info.wait) {
op = iter;
break;
}
}
}
if (op) {
/* Sanity check that op and info match. */
if (info.wait)
WARN_ON(op->info.optype != DLM_PLOCK_OP_LOCK);
else
WARN_ON(op->info.fsid != info.fsid ||
op->info.number != info.number ||
op->info.owner != info.owner ||
op->info.optype != info.optype);
list_del_init(&op->list);
memcpy(&op->info, &info, sizeof(info));
if (op->data)
do_callback = 1;
else
op->done = 1;
} }
spin_unlock(&ops_lock); spin_unlock(&ops_lock);