aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2012-07-25 11:53:36 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2012-07-30 18:57:39 -0400
commita427b9ec4eda8cd6e641ea24541d30b641fc3140 (patch)
treed836f21f22ac9034f2ec9b7d0e39e283f460ced6
parent5cf02d09b50b1ee1c2d536c9cf64af5a7d433f56 (diff)
NFS: Fix a number of bugs in the idmapper
Fix a number of bugs in the NFS idmapper code: (1) Only registered key types can be passed to the core keys code, so register the legacy idmapper key type. This is a requirement because the unregister function cleans up keys belonging to that key type so that there aren't dangling pointers to the module left behind - including the key->type pointer. (2) Rename the legacy key type. You can't have two key types with the same name, and (1) would otherwise require that. (3) complete_request_key() must be called in the error path of nfs_idmap_legacy_upcall(). (4) There is one idmap struct for each nfs_client struct. This means that idmap->idmap_key_cons is shared without the use of a lock. This is a problem because key_instantiate_and_link() - as called indirectly by idmap_pipe_downcall() - releases anyone waiting for the key to be instantiated. What happens is that idmap_pipe_downcall() running in the rpc.idmapd thread, releases the NFS filesystem in whatever thread that is running in to continue. This may then make another idmapper call, overwriting idmap_key_cons before idmap_pipe_downcall() gets the chance to call complete_request_key(). I *think* that reading idmap_key_cons only once, before key_instantiate_and_link() is called, and then caching the result in a variable is sufficient. Bug (4) is the cause of: BUG: unable to handle kernel NULL pointer dereference at (null) IP: [< (null)>] (null) PGD 0 Oops: 0010 [#1] SMP CPU 1 Modules linked in: ppdev parport_pc lp parport ip6table_filter ip6_tables ebtable_nat ebtables ipt_MASQUERADE iptable_nat nf_nat nf_conntrack_ipv4 nf_defrag_ipv4 xt_state nf_conntrack nfs fscache xt_CHECKSUM auth_rpcgss iptable_mangle nfs_acl bridge stp llc lockd be2iscsi iscsi_boot_sysfs bnx2i cnic uio cxgb4i cxgb4 cxgb3i libcxgbi cxgb3 mdio ib_iser rdma_cm ib_cm iw_cm ib_sa ib_mad ib_core ib_addr iscsi_tcp libiscsi_tcp libiscsi scsi_transport_iscsi snd_hda_codec_realtek snd_usb_audio snd_hda_intel snd_hda_codec snd_seq snd_pcm snd_hwdep snd_usbmidi_lib snd_rawmidi snd_timer uvcvideo videobuf2_core videodev media videobuf2_vmalloc snd_seq_device videobuf2_memops e1000e vhost_net iTCO_wdt joydev coretemp snd soundcore macvtap macvlan i2c_i801 snd_page_alloc tun iTCO_vendor_support microcode kvm_intel kvm sunrpc hid_logitech_dj usb_storage i915 drm_kms_helper drm i2c_algo_bit i2c_core video [last unloaded: scsi_wait_scan] Pid: 1229, comm: rpc.idmapd Not tainted 3.4.2-1.fc16.x86_64 #1 Gateway DX4710-UB801A/G33M05G1 RIP: 0010:[<0000000000000000>] [< (null)>] (null) RSP: 0018:ffff8801a3645d40 EFLAGS: 00010246 RAX: ffff880077707e30 RBX: ffff880077707f50 RCX: ffff8801a18ccd80 RDX: 0000000000000006 RSI: ffff8801a3645e75 RDI: ffff880077707f50 RBP: ffff8801a3645d88 R08: ffff8801a430f9c0 R09: ffff8801a3645db0 R10: 000000000000000a R11: 0000000000000246 R12: ffff8801a18ccd80 R13: ffff8801a3645e75 R14: ffff8801a430f9c0 R15: 0000000000000006 FS: 00007fb6fb51a700(0000) GS:ffff8801afc80000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000000 CR3: 00000001a49b0000 CR4: 00000000000027e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 Process rpc.idmapd (pid: 1229, threadinfo ffff8801a3644000, task ffff8801a3bf9710) Stack: ffffffff81260878 ffff8801a3645db0 ffff8801a3645db0 ffff880077707a90 ffff880077707f50 ffff8801a18ccd80 0000000000000006 ffff8801a3645e75 ffff8801a430f9c0 ffff8801a3645dd8 ffffffff81260983 ffff8801a3645de8 Call Trace: [<ffffffff81260878>] ? __key_instantiate_and_link+0x58/0x100 [<ffffffff81260983>] key_instantiate_and_link+0x63/0xa0 [<ffffffffa057062b>] idmap_pipe_downcall+0x1cb/0x1e0 [nfs] [<ffffffffa0107f57>] rpc_pipe_write+0x67/0x90 [sunrpc] [<ffffffff8117f833>] vfs_write+0xb3/0x180 [<ffffffff8117fb5a>] sys_write+0x4a/0x90 [<ffffffff81600329>] system_call_fastpath+0x16/0x1b Code: Bad RIP value. RIP [< (null)>] (null) RSP <ffff8801a3645d40> CR2: 0000000000000000 Signed-off-by: David Howells <dhowells@redhat.com> Reviewed-by: Steve Dickson <steved@redhat.com> Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com> Cc: stable@vger.kernel.org [>= 3.4]
-rw-r--r--fs/nfs/idmap.c26
1 files changed, 20 insertions, 6 deletions
diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c
index 864c51e4b400..1b5058b4043b 100644
--- a/fs/nfs/idmap.c
+++ b/fs/nfs/idmap.c
@@ -205,12 +205,18 @@ static int nfs_idmap_init_keyring(void)
205 if (ret < 0) 205 if (ret < 0)
206 goto failed_put_key; 206 goto failed_put_key;
207 207
208 ret = register_key_type(&key_type_id_resolver_legacy);
209 if (ret < 0)
210 goto failed_reg_legacy;
211
208 set_bit(KEY_FLAG_ROOT_CAN_CLEAR, &keyring->flags); 212 set_bit(KEY_FLAG_ROOT_CAN_CLEAR, &keyring->flags);
209 cred->thread_keyring = keyring; 213 cred->thread_keyring = keyring;
210 cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING; 214 cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
211 id_resolver_cache = cred; 215 id_resolver_cache = cred;
212 return 0; 216 return 0;
213 217
218failed_reg_legacy:
219 unregister_key_type(&key_type_id_resolver);
214failed_put_key: 220failed_put_key:
215 key_put(keyring); 221 key_put(keyring);
216failed_put_cred: 222failed_put_cred:
@@ -222,6 +228,7 @@ static void nfs_idmap_quit_keyring(void)
222{ 228{
223 key_revoke(id_resolver_cache->thread_keyring); 229 key_revoke(id_resolver_cache->thread_keyring);
224 unregister_key_type(&key_type_id_resolver); 230 unregister_key_type(&key_type_id_resolver);
231 unregister_key_type(&key_type_id_resolver_legacy);
225 put_cred(id_resolver_cache); 232 put_cred(id_resolver_cache);
226} 233}
227 234
@@ -385,7 +392,7 @@ static const struct rpc_pipe_ops idmap_upcall_ops = {
385}; 392};
386 393
387static struct key_type key_type_id_resolver_legacy = { 394static struct key_type key_type_id_resolver_legacy = {
388 .name = "id_resolver", 395 .name = "id_legacy",
389 .instantiate = user_instantiate, 396 .instantiate = user_instantiate,
390 .match = user_match, 397 .match = user_match,
391 .revoke = user_revoke, 398 .revoke = user_revoke,
@@ -674,6 +681,7 @@ static int nfs_idmap_legacy_upcall(struct key_construction *cons,
674 if (ret < 0) 681 if (ret < 0)
675 goto out2; 682 goto out2;
676 683
684 BUG_ON(idmap->idmap_key_cons != NULL);
677 idmap->idmap_key_cons = cons; 685 idmap->idmap_key_cons = cons;
678 686
679 ret = rpc_queue_upcall(idmap->idmap_pipe, msg); 687 ret = rpc_queue_upcall(idmap->idmap_pipe, msg);
@@ -687,8 +695,7 @@ out2:
687out1: 695out1:
688 kfree(msg); 696 kfree(msg);
689out0: 697out0:
690 key_revoke(cons->key); 698 complete_request_key(cons, ret);
691 key_revoke(cons->authkey);
692 return ret; 699 return ret;
693} 700}
694 701
@@ -722,11 +729,18 @@ idmap_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
722{ 729{
723 struct rpc_inode *rpci = RPC_I(filp->f_path.dentry->d_inode); 730 struct rpc_inode *rpci = RPC_I(filp->f_path.dentry->d_inode);
724 struct idmap *idmap = (struct idmap *)rpci->private; 731 struct idmap *idmap = (struct idmap *)rpci->private;
725 struct key_construction *cons = idmap->idmap_key_cons; 732 struct key_construction *cons;
726 struct idmap_msg im; 733 struct idmap_msg im;
727 size_t namelen_in; 734 size_t namelen_in;
728 int ret; 735 int ret;
729 736
737 /* If instantiation is successful, anyone waiting for key construction
738 * will have been woken up and someone else may now have used
739 * idmap_key_cons - so after this point we may no longer touch it.
740 */
741 cons = ACCESS_ONCE(idmap->idmap_key_cons);
742 idmap->idmap_key_cons = NULL;
743
730 if (mlen != sizeof(im)) { 744 if (mlen != sizeof(im)) {
731 ret = -ENOSPC; 745 ret = -ENOSPC;
732 goto out; 746 goto out;
@@ -739,7 +753,7 @@ idmap_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
739 753
740 if (!(im.im_status & IDMAP_STATUS_SUCCESS)) { 754 if (!(im.im_status & IDMAP_STATUS_SUCCESS)) {
741 ret = mlen; 755 ret = mlen;
742 complete_request_key(idmap->idmap_key_cons, -ENOKEY); 756 complete_request_key(cons, -ENOKEY);
743 goto out_incomplete; 757 goto out_incomplete;
744 } 758 }
745 759
@@ -756,7 +770,7 @@ idmap_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
756 } 770 }
757 771
758out: 772out:
759 complete_request_key(idmap->idmap_key_cons, ret); 773 complete_request_key(cons, ret);
760out_incomplete: 774out_incomplete:
761 return ret; 775 return ret;
762} 776}