Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

fs: dlm: fix mismatch of plock results from userspace

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>

authored by

Alexander Aring and committed by
David Teigland
57e2c2f2 0f2b1cb8

+45 -13
+45 -13
fs/dlm/plock.c
··· 395 395 if (op->info.flags & DLM_PLOCK_FL_CLOSE) 396 396 list_del(&op->list); 397 397 else 398 - list_move(&op->list, &recv_list); 398 + list_move_tail(&op->list, &recv_list); 399 399 memcpy(&info, &op->info, sizeof(info)); 400 400 } 401 401 spin_unlock(&ops_lock); ··· 433 433 if (check_version(&info)) 434 434 return -EINVAL; 435 435 436 + /* 437 + * The results for waiting ops (SETLKW) can be returned in any 438 + * order, so match all fields to find the op. The results for 439 + * non-waiting ops are returned in the order that they were sent 440 + * to userspace, so match the result with the first non-waiting op. 441 + */ 436 442 spin_lock(&ops_lock); 437 - list_for_each_entry(iter, &recv_list, list) { 438 - if (iter->info.fsid == info.fsid && 439 - iter->info.number == info.number && 440 - iter->info.owner == info.owner) { 441 - list_del_init(&iter->list); 442 - memcpy(&iter->info, &info, sizeof(info)); 443 - if (iter->data) 444 - do_callback = 1; 445 - else 446 - iter->done = 1; 447 - op = iter; 448 - break; 443 + if (info.wait) { 444 + list_for_each_entry(iter, &recv_list, list) { 445 + if (iter->info.fsid == info.fsid && 446 + iter->info.number == info.number && 447 + iter->info.owner == info.owner && 448 + iter->info.pid == info.pid && 449 + iter->info.start == info.start && 450 + iter->info.end == info.end && 451 + iter->info.ex == info.ex && 452 + iter->info.wait) { 453 + op = iter; 454 + break; 455 + } 449 456 } 457 + } else { 458 + list_for_each_entry(iter, &recv_list, list) { 459 + if (!iter->info.wait) { 460 + op = iter; 461 + break; 462 + } 463 + } 464 + } 465 + 466 + if (op) { 467 + /* Sanity check that op and info match. */ 468 + if (info.wait) 469 + WARN_ON(op->info.optype != DLM_PLOCK_OP_LOCK); 470 + else 471 + WARN_ON(op->info.fsid != info.fsid || 472 + op->info.number != info.number || 473 + op->info.owner != info.owner || 474 + op->info.optype != info.optype); 475 + 476 + list_del_init(&op->list); 477 + memcpy(&op->info, &info, sizeof(info)); 478 + if (op->data) 479 + do_callback = 1; 480 + else 481 + op->done = 1; 450 482 } 451 483 spin_unlock(&ops_lock); 452 484