NFSv4: Don't decode fs_locations if we didn't ask for them...
authorTrond Myklebust <Trond.Myklebust@netapp.com>
Mon, 30 Jan 2012 20:43:56 +0000 (15:43 -0500)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Wed, 1 Feb 2012 00:28:23 +0000 (19:28 -0500)
Currently, the server can potentially cause us to Oops by returning an
fs_locations request that we didn't actually request.

Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
fs/nfs/nfs4xdr.c

index 4633d40..ca288d1 100644 (file)
@@ -3561,6 +3561,10 @@ static int decode_attr_fs_locations(struct xdr_stream *xdr, uint32_t *bitmap, st
        status = 0;
        if (unlikely(!(bitmap[0] & FATTR4_WORD0_FS_LOCATIONS)))
                goto out;
+       status = -EIO;
+       /* Ignore borken servers that return unrequested attrs */
+       if (unlikely(res == NULL))
+               goto out;
        dprintk("%s: fsroot ", __func__);
        status = decode_pathname(xdr, &res->fs_path);
        if (unlikely(status != 0))
@@ -4295,6 +4299,7 @@ xdr_error:
 
 static int decode_getfattr_attrs(struct xdr_stream *xdr, uint32_t *bitmap,
                struct nfs_fattr *fattr, struct nfs_fh *fh,
+               struct nfs4_fs_locations *fs_loc,
                const struct nfs_server *server)
 {
        int status;
@@ -4342,9 +4347,7 @@ static int decode_getfattr_attrs(struct xdr_stream *xdr, uint32_t *bitmap,
                goto xdr_error;
        fattr->valid |= status;
 
-       status = decode_attr_fs_locations(xdr, bitmap, container_of(fattr,
-                                               struct nfs4_fs_locations,
-                                               fattr));
+       status = decode_attr_fs_locations(xdr, bitmap, fs_loc);
        if (status < 0)
                goto xdr_error;
        fattr->valid |= status;
@@ -4408,7 +4411,8 @@ xdr_error:
 }
 
 static int decode_getfattr_generic(struct xdr_stream *xdr, struct nfs_fattr *fattr,
-               struct nfs_fh *fh, const struct nfs_server *server)
+               struct nfs_fh *fh, struct nfs4_fs_locations *fs_loc,
+               const struct nfs_server *server)
 {
        __be32 *savep;
        uint32_t attrlen,
@@ -4427,7 +4431,7 @@ static int decode_getfattr_generic(struct xdr_stream *xdr, struct nfs_fattr *fat
        if (status < 0)
                goto xdr_error;
 
-       status = decode_getfattr_attrs(xdr, bitmap, fattr, fh, server);
+       status = decode_getfattr_attrs(xdr, bitmap, fattr, fh, fs_loc, server);
        if (status < 0)
                goto xdr_error;
 
@@ -4440,7 +4444,7 @@ xdr_error:
 static int decode_getfattr(struct xdr_stream *xdr, struct nfs_fattr *fattr,
                const struct nfs_server *server)
 {
-       return decode_getfattr_generic(xdr, fattr, NULL, server);
+       return decode_getfattr_generic(xdr, fattr, NULL, NULL, server);
 }
 
 /*
@@ -6580,8 +6584,9 @@ static int nfs4_xdr_dec_fs_locations(struct rpc_rqst *req,
        if (status)
                goto out;
        xdr_enter_page(xdr, PAGE_SIZE);
-       status = decode_getfattr(xdr, &res->fs_locations->fattr,
-                                res->fs_locations->server);
+       status = decode_getfattr_generic(xdr, &res->fs_locations->fattr,
+                                        NULL, res->fs_locations,
+                                        res->fs_locations->server);
 out:
        return status;
 }
@@ -6961,7 +6966,7 @@ int nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
                goto out_overflow;
 
        if (decode_getfattr_attrs(xdr, bitmap, entry->fattr, entry->fh,
-                                       entry->server) < 0)
+                                 NULL, entry->server) < 0)
                goto out_overflow;
        if (entry->fattr->valid & NFS_ATTR_FATTR_MOUNTED_ON_FILEID)
                entry->ino = entry->fattr->mounted_on_fileid;