Date: Fri, 13 Oct 2006 09:20:09 -0400 From: Steve Dickson <SteveD@redhat.com> Subject: [RHEL5/FC6][NFS4] - BUG in __list_add at lib/list_debug.c:31! The attached upstream patch fixes NFSv4 problem of using memory after its been freed which was causing a BUG() to pop in the lists code. This patch was found and tested by IBM. I also was able to reproduce this problem verified this patch indeed fixed the problem. The bz is: https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=206996 steved. Committer: Trond Myklebust <Trond.Myklebust@netapp.com> 2006-09-22 23:24:54 Parent: 275a082fe9308e710324e26ccb5363c53d8fd45f Child: 158998b6fe36f6acef087f574c96d44713499cc9 NFSv4: Fix a use-after-free issue with the nfs server. Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com> --- linux-2.6.18.i686/fs/nfs/client.c.orig 2006-10-12 07:30:35.875265000 -0400 +++ linux-2.6.18.i686/fs/nfs/client.c 2006-10-12 09:19:15.135509000 -0400 @@ -165,6 +165,26 @@ error_0: return NULL; } +static void nfs4_shutdown_client(struct nfs_client *clp) +{ +#ifdef CONFIG_NFS_V4 + if (__test_and_clear_bit(NFS_CS_RENEWD, &clp->cl_res_state)) + nfs4_kill_renewd(clp); + while (!list_empty(&clp->cl_unused)) { + struct nfs4_state_owner *sp; + + sp = list_entry(clp->cl_unused.next, + struct nfs4_state_owner, + so_list); + list_del(&sp->so_list); + kfree(sp); + } + BUG_ON(!list_empty(&clp->cl_state_owners)); + if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state)) + nfs_idmap_delete(clp); +#endif +} + /* * Destroy a shared client record */ @@ -172,21 +192,7 @@ static void nfs_free_client(struct nfs_c { dprintk("--> nfs_free_client(%d)\n", clp->cl_nfsversion); -#ifdef CONFIG_NFS_V4 - if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state)) { - while (!list_empty(&clp->cl_unused)) { - struct nfs4_state_owner *sp; - - sp = list_entry(clp->cl_unused.next, - struct nfs4_state_owner, - so_list); - list_del(&sp->so_list); - kfree(sp); - } - BUG_ON(!list_empty(&clp->cl_state_owners)); - nfs_idmap_delete(clp); - } -#endif + nfs4_shutdown_client(clp); nfs_fscache_release_client_cookie(clp); @@ -320,25 +326,11 @@ found_client: if (new) nfs_free_client(new); - if (clp->cl_cons_state == NFS_CS_INITING) { - DECLARE_WAITQUEUE(myself, current); - - add_wait_queue(&nfs_client_active_wq, &myself); - - for (;;) { - set_current_state(TASK_INTERRUPTIBLE); - if (signal_pending(current) || - clp->cl_cons_state > NFS_CS_READY) - break; - schedule(); - } - - remove_wait_queue(&nfs_client_active_wq, &myself); - - if (signal_pending(current)) { - nfs_put_client(clp); - return ERR_PTR(-ERESTARTSYS); - } + error = wait_event_interruptible(nfs_client_active_wq, + clp->cl_cons_state != NFS_CS_INITING); + if (error < 0) { + nfs_put_client(clp); + return ERR_PTR(-ERESTARTSYS); } if (clp->cl_cons_state < NFS_CS_READY) { --- linux-2.6.18.i686/fs/nfs/nfs4renewd.c.orig 2006-10-12 07:30:33.897855000 -0400 +++ linux-2.6.18.i686/fs/nfs/nfs4renewd.c 2006-10-12 09:02:34.847340000 -0400 @@ -121,6 +121,7 @@ nfs4_schedule_state_renewal(struct nfs_c __FUNCTION__, (timeout + HZ - 1) / HZ); cancel_delayed_work(&clp->cl_renewd); schedule_delayed_work(&clp->cl_renewd, timeout); + set_bit(NFS_CS_RENEWD, &clp->cl_res_state); spin_unlock(&clp->cl_lock); } --- linux-2.6.18.i686/fs/nfs/super.c.orig 2006-10-12 07:30:35.956265000 -0400 +++ linux-2.6.18.i686/fs/nfs/super.c 2006-10-12 09:02:35.474171000 -0400 @@ -881,13 +881,15 @@ static int nfs4_get_sb(struct file_syste goto out_free; } + if (s->s_fs_info != server) { + nfs_free_server(server); + server = NULL; + } + if (!s->s_root) { /* initial superblock/root creation */ s->s_flags = flags; - nfs4_fill_super(s); - } else { - nfs_free_server(server); } mntroot = nfs4_get_root(s, &mntfh); --- linux-2.6.18.i686/include/linux/nfs_fs_sb.h.orig 2006-10-12 07:30:35.319560000 -0400 +++ linux-2.6.18.i686/include/linux/nfs_fs_sb.h 2006-10-12 09:02:35.494151000 -0400 @@ -20,6 +20,7 @@ struct nfs_client { #define NFS_CS_RPCIOD 0 /* - rpciod started */ #define NFS_CS_CALLBACK 1 /* - callback started */ #define NFS_CS_IDMAP 2 /* - idmap started */ +#define NFS_CS_RENEWD 3 /* - renewd started */ struct sockaddr_in cl_addr; /* server identifier */ char * cl_hostname; /* hostname of server */ struct list_head cl_share_link; /* link in global client list */