summaryrefslogtreecommitdiffstats
path: root/fs/nfs/nfs4client.c
diff options
context:
space:
mode:
authorJ. Bruce Fields <bfields@redhat.com>2016-11-28 09:02:52 -0500
committerTrond Myklebust <trond.myklebust@primarydata.com>2016-12-01 17:43:31 -0500
commitced85a7568b57a671fe186562dfc601a5baab54d (patch)
treebf8045efb9b8580aed2095d9c838e21702a57e33 /fs/nfs/nfs4client.c
parent2c2ee6d20b10594892c1d31c7b959f4780adde63 (diff)
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>
Diffstat (limited to 'fs/nfs/nfs4client.c')
-rw-r--r--fs/nfs/nfs4client.c22
1 files changed, 21 insertions, 1 deletions
diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c
index 074ac7131459..9301040b553d 100644
--- a/fs/nfs/nfs4client.c
+++ b/fs/nfs/nfs4client.c
@@ -464,6 +464,11 @@ static bool nfs4_match_client_owner_id(const struct nfs_client *clp1,
464 return strcmp(clp1->cl_owner_id, clp2->cl_owner_id) == 0; 464 return strcmp(clp1->cl_owner_id, clp2->cl_owner_id) == 0;
465} 465}
466 466
467static 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 * nfs40_walk_client_list - Find server that recognizes a client ID 473 * nfs40_walk_client_list - Find server that recognizes a client ID
469 * 474 *
@@ -521,7 +526,21 @@ int nfs40_walk_client_list(struct nfs_client *new,
521 526
522 if (!nfs4_match_client_owner_id(pos, new)) 527 if (!nfs4_match_client_owner_id(pos, new))
523 continue; 528 continue;
524 529 /*
530 * We just sent a new SETCLIENTID, which should have
531 * caused the server to return a new cl_confirm. So if
532 * cl_confirm is the same, then this is a different
533 * server that just returned the same cl_confirm by
534 * coincidence:
535 */
536 if ((new != pos) && nfs4_same_verifier(&pos->cl_confirm,
537 &new->cl_confirm))
538 continue;
539 /*
540 * But if the cl_confirm's are different, then the only
541 * way that a SETCLIENTID_CONFIRM to pos can succeed is
542 * if new and pos point to the same server:
543 */
525 atomic_inc(&pos->cl_count); 544 atomic_inc(&pos->cl_count);
526 spin_unlock(&nn->nfs_client_lock); 545 spin_unlock(&nn->nfs_client_lock);
527 546
@@ -534,6 +553,7 @@ int nfs40_walk_client_list(struct nfs_client *new,
534 break; 553 break;
535 case 0: 554 case 0:
536 nfs4_swap_callback_idents(pos, new); 555 nfs4_swap_callback_idents(pos, new);
556 pos->cl_confirm = new->cl_confirm;
537 557
538 prev = NULL; 558 prev = NULL;
539 *result = pos; 559 *result = pos;