File linux-2.6-nfs-v4-server-use-after-free.patch of Package kernel
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 */