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

nfs: fix false positives in nfs40_walk_client_list()

It's possible that two different servers can return the same (clientid,
verifier) pair purely by coincidence. Both are 64-bit values, but
depending on the server implementation, they can be highly predictable
and collisions may be quite likely, especially when there are lots of
servers.

So, check for this case. If the clientid and verifier both match, then
we actually know they *can't* be the same server, since a new
SETCLIENTID to an already-known server should have changed the verifier.

This helps fix a bug that could cause the client to mount a filesystem
from the wrong server.

Reviewed-by: Jeff Layton <jlayton@redhat.com>
Tested-by: Yongcheng Yang <yoyang@redhat.com>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>

authored by

J. Bruce Fields and committed by
Trond Myklebust
ced85a75 2c2ee6d2

+21 -1
+21 -1
fs/nfs/nfs4client.c
··· 464 464 return strcmp(clp1->cl_owner_id, clp2->cl_owner_id) == 0; 465 465 } 466 466 467 + static bool nfs4_same_verifier(nfs4_verifier *v1, nfs4_verifier *v2) 468 + { 469 + return memcmp(v1->data, v2->data, sizeof(v1->data)) == 0; 470 + } 471 + 467 472 /** 468 473 * nfs40_walk_client_list - Find server that recognizes a client ID 469 474 * ··· 526 521 527 522 if (!nfs4_match_client_owner_id(pos, new)) 528 523 continue; 529 - 524 + /* 525 + * We just sent a new SETCLIENTID, which should have 526 + * caused the server to return a new cl_confirm. So if 527 + * cl_confirm is the same, then this is a different 528 + * server that just returned the same cl_confirm by 529 + * coincidence: 530 + */ 531 + if ((new != pos) && nfs4_same_verifier(&pos->cl_confirm, 532 + &new->cl_confirm)) 533 + continue; 534 + /* 535 + * But if the cl_confirm's are different, then the only 536 + * way that a SETCLIENTID_CONFIRM to pos can succeed is 537 + * if new and pos point to the same server: 538 + */ 530 539 atomic_inc(&pos->cl_count); 531 540 spin_unlock(&nn->nfs_client_lock); 532 541 ··· 553 534 break; 554 535 case 0: 555 536 nfs4_swap_callback_idents(pos, new); 537 + pos->cl_confirm = new->cl_confirm; 556 538 557 539 prev = NULL; 558 540 *result = pos;