aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJ. Bruce Fields <bfields@redhat.com>2013-03-07 17:26:18 -0500
committerJ. Bruce Fields <bfields@redhat.com>2013-04-03 11:48:31 -0400
commitb0a9d3ab577464529f6649ec54f8a0de160866e3 (patch)
treead63af0413f042b41cb472f905383078e9260f12
parent9d313b17db965ae42137c5d4dd3063037544c4cd (diff)
nfsd4: fix race on client shutdown
Dropping the session's reference count after the client's means we leave a window where the session's se_client pointer is NULL. An xpt_user callback that encounters such a session may then crash: [ 303.956011] BUG: unable to handle kernel NULL pointer dereference at 0000000000000318 [ 303.959061] IP: [<ffffffff81481a8e>] _raw_spin_lock+0x1e/0x40 [ 303.959061] PGD 37811067 PUD 3d498067 PMD 0 [ 303.959061] Oops: 0002 [#8] PREEMPT SMP [ 303.959061] Modules linked in: md5 nfsd auth_rpcgss nfs_acl snd_hda_intel snd_hda_codec snd_hwdep snd_pcm snd_page_alloc microcode psmouse snd_timer serio_raw pcspkr evdev snd soundcore i2c_piix4 i2c_core intel_agp intel_gtt processor button nfs lockd sunrpc fscache ata_generic pata_acpi ata_piix uhci_hcd libata btrfs usbcore usb_common crc32c scsi_mod libcrc32c zlib_deflate floppy virtio_balloon virtio_net virtio_pci virtio_blk virtio_ring virtio [ 303.959061] CPU 0 [ 303.959061] Pid: 264, comm: nfsd Tainted: G D 3.8.0-ARCH+ #156 Bochs Bochs [ 303.959061] RIP: 0010:[<ffffffff81481a8e>] [<ffffffff81481a8e>] _raw_spin_lock+0x1e/0x40 [ 303.959061] RSP: 0018:ffff880037877dd8 EFLAGS: 00010202 [ 303.959061] RAX: 0000000000000100 RBX: ffff880037a2b698 RCX: ffff88003d879278 [ 303.959061] RDX: ffff88003d879278 RSI: dead000000100100 RDI: 0000000000000318 [ 303.959061] RBP: ffff880037877dd8 R08: ffff88003c5a0f00 R09: 0000000000000002 [ 303.959061] R10: 0000000000000001 R11: 0000000000000000 R12: 0000000000000000 [ 303.959061] R13: 0000000000000318 R14: ffff880037a2b680 R15: ffff88003c1cbe00 [ 303.959061] FS: 0000000000000000(0000) GS:ffff88003fc00000(0000) knlGS:0000000000000000 [ 303.959061] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b [ 303.959061] CR2: 0000000000000318 CR3: 000000003d49c000 CR4: 00000000000006f0 [ 303.959061] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 303.959061] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 [ 303.959061] Process nfsd (pid: 264, threadinfo ffff880037876000, task ffff88003c1fd0a0) [ 303.959061] Stack: [ 303.959061] ffff880037877e08 ffffffffa03772ec ffff88003d879000 ffff88003d879278 [ 303.959061] ffff88003d879080 0000000000000000 ffff880037877e38 ffffffffa0222a1f [ 303.959061] 0000000000107ac0 ffff88003c22e000 ffff88003d879000 ffff88003c1cbe00 [ 303.959061] Call Trace: [ 303.959061] [<ffffffffa03772ec>] nfsd4_conn_lost+0x3c/0xa0 [nfsd] [ 303.959061] [<ffffffffa0222a1f>] svc_delete_xprt+0x10f/0x180 [sunrpc] [ 303.959061] [<ffffffffa0223d96>] svc_recv+0xe6/0x580 [sunrpc] [ 303.959061] [<ffffffffa03587c5>] nfsd+0xb5/0x140 [nfsd] [ 303.959061] [<ffffffffa0358710>] ? nfsd_destroy+0x90/0x90 [nfsd] [ 303.959061] [<ffffffff8107ae00>] kthread+0xc0/0xd0 [ 303.959061] [<ffffffff81010000>] ? perf_trace_xen_mmu_set_pte_at+0x50/0x100 [ 303.959061] [<ffffffff8107ad40>] ? kthread_freezable_should_stop+0x70/0x70 [ 303.959061] [<ffffffff814898ec>] ret_from_fork+0x7c/0xb0 [ 303.959061] [<ffffffff8107ad40>] ? kthread_freezable_should_stop+0x70/0x70 [ 303.959061] Code: ff ff 5d c3 90 90 90 90 90 90 90 90 90 0f 1f 44 00 00 55 65 48 8b 04 25 f0 c6 00 00 48 89 e5 83 80 44 e0 ff ff 01 b8 00 01 00 00 <3e> 66 0f c1 07 0f b6 d4 38 c2 74 0f 66 0f 1f 44 00 00 f3 90 0f [ 303.959061] RIP [<ffffffff81481a8e>] _raw_spin_lock+0x1e/0x40 [ 303.959061] RSP <ffff880037877dd8> [ 303.959061] CR2: 0000000000000318 [ 304.001218] ---[ end trace 2d809cd4a7931f5a ]--- [ 304.001903] note: nfsd[264] exited with preempt_count 2 Reported-by: Bryan Schumaker <bjschuma@netapp.com> Signed-off-by: J. Bruce Fields <bfields@redhat.com>
-rw-r--r--fs/nfsd/nfs4state.c12
-rw-r--r--fs/nfsd/nfs4xdr.c1
-rw-r--r--fs/nfsd/state.h2
3 files changed, 8 insertions, 7 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 2e27430b9070..3e5cbfe8a967 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -864,7 +864,7 @@ static void free_session(struct kref *kref)
864 __free_session(ses); 864 __free_session(ses);
865} 865}
866 866
867void nfsd4_put_session(struct nfsd4_session *ses) 867static void nfsd4_put_session(struct nfsd4_session *ses)
868{ 868{
869 struct nfsd_net *nn = net_generic(ses->se_client->net, nfsd_net_id); 869 struct nfsd_net *nn = net_generic(ses->se_client->net, nfsd_net_id);
870 870
@@ -1057,12 +1057,16 @@ release_session_client(struct nfsd4_session *session)
1057 struct nfs4_client *clp = session->se_client; 1057 struct nfs4_client *clp = session->se_client;
1058 struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); 1058 struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
1059 1059
1060 nfsd4_put_session(session);
1060 if (!atomic_dec_and_lock(&clp->cl_refcount, &nn->client_lock)) 1061 if (!atomic_dec_and_lock(&clp->cl_refcount, &nn->client_lock))
1061 return; 1062 return;
1062 if (is_client_expired(clp)) { 1063 /*
1064 * At this point we also know all sessions have refcnt 1,
1065 * so free_client will delete them all if necessary:
1066 */
1067 if (is_client_expired(clp))
1063 free_client(clp); 1068 free_client(clp);
1064 session->se_client = NULL; 1069 else
1065 } else
1066 renew_client_locked(clp); 1070 renew_client_locked(clp);
1067 spin_unlock(&nn->client_lock); 1071 spin_unlock(&nn->client_lock);
1068} 1072}
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 229b3ac246e1..9b02b6652f2b 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -3685,7 +3685,6 @@ nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compo
3685 } 3685 }
3686 /* Renew the clientid on success and on replay */ 3686 /* Renew the clientid on success and on replay */
3687 release_session_client(cs->session); 3687 release_session_client(cs->session);
3688 nfsd4_put_session(cs->session);
3689 } 3688 }
3690 return 1; 3689 return 1;
3691} 3690}
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 1a8c7391f7ae..327552bb6dba 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -209,8 +209,6 @@ struct nfsd4_session {
209 struct nfsd4_slot *se_slots[]; /* forward channel slots */ 209 struct nfsd4_slot *se_slots[]; /* forward channel slots */
210}; 210};
211 211
212extern void nfsd4_put_session(struct nfsd4_session *ses);
213
214/* formatted contents of nfs4_sessionid */ 212/* formatted contents of nfs4_sessionid */
215struct nfsd4_sessionid { 213struct nfsd4_sessionid {
216 clientid_t clientid; 214 clientid_t clientid;