NFS: Fix 2 use after free issues in the I/O code
authorTrond Myklebust <trond.myklebust@primarydata.com>
Sat, 9 Sep 2017 01:28:11 +0000 (21:28 -0400)
committerTrond Myklebust <trond.myklebust@primarydata.com>
Sat, 9 Sep 2017 02:07:52 +0000 (22:07 -0400)
The writeback code wants to send a commit after processing the pages,
which is why we want to delay releasing the struct path until after
that's done.

Also, the layout code expects that we do not free the inode before
we've put the layout segments in pnfs_writehdr_free() and
pnfs_readhdr_free()

Fixes: 919e3bd9a875 ("NFS: Ensure we commit after writeback is complete")
Fixes: 4714fb51fd03 ("nfs: remove pgio_header refcount, related cleanup")
Cc: stable@vger.kernel.org
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
fs/nfs/internal.h
fs/nfs/pagelist.c
fs/nfs/pnfs.c

index dc456416d2becc960678625da5712a9992595bc4..68cc22083639bb8c90ec8829a713c27a55eef75f 100644 (file)
@@ -251,7 +251,6 @@ int nfs_iocounter_wait(struct nfs_lock_context *l_ctx);
 extern const struct nfs_pageio_ops nfs_pgio_rw_ops;
 struct nfs_pgio_header *nfs_pgio_header_alloc(const struct nfs_rw_ops *);
 void nfs_pgio_header_free(struct nfs_pgio_header *);
-void nfs_pgio_data_destroy(struct nfs_pgio_header *);
 int nfs_generic_pgio(struct nfs_pageio_descriptor *, struct nfs_pgio_header *);
 int nfs_initiate_pgio(struct rpc_clnt *clnt, struct nfs_pgio_header *hdr,
                      struct rpc_cred *cred, const struct nfs_rpc_ops *rpc_ops,
index 23cf4a82d94cbc912fb4cd6b2673cd8b63769f00..bec120ec1967c4f649543852f87b5c3c3e02d8fb 100644 (file)
@@ -501,16 +501,6 @@ struct nfs_pgio_header *nfs_pgio_header_alloc(const struct nfs_rw_ops *ops)
 }
 EXPORT_SYMBOL_GPL(nfs_pgio_header_alloc);
 
-/*
- * nfs_pgio_header_free - Free a read or write header
- * @hdr: The header to free
- */
-void nfs_pgio_header_free(struct nfs_pgio_header *hdr)
-{
-       hdr->rw_ops->rw_free_header(hdr);
-}
-EXPORT_SYMBOL_GPL(nfs_pgio_header_free);
-
 /**
  * nfs_pgio_data_destroy - make @hdr suitable for reuse
  *
@@ -519,14 +509,24 @@ EXPORT_SYMBOL_GPL(nfs_pgio_header_free);
  *
  * @hdr: A header that has had nfs_generic_pgio called
  */
-void nfs_pgio_data_destroy(struct nfs_pgio_header *hdr)
+static void nfs_pgio_data_destroy(struct nfs_pgio_header *hdr)
 {
        if (hdr->args.context)
                put_nfs_open_context(hdr->args.context);
        if (hdr->page_array.pagevec != hdr->page_array.page_array)
                kfree(hdr->page_array.pagevec);
 }
-EXPORT_SYMBOL_GPL(nfs_pgio_data_destroy);
+
+/*
+ * nfs_pgio_header_free - Free a read or write header
+ * @hdr: The header to free
+ */
+void nfs_pgio_header_free(struct nfs_pgio_header *hdr)
+{
+       nfs_pgio_data_destroy(hdr);
+       hdr->rw_ops->rw_free_header(hdr);
+}
+EXPORT_SYMBOL_GPL(nfs_pgio_header_free);
 
 /**
  * nfs_pgio_rpcsetup - Set up arguments for a pageio call
@@ -640,7 +640,6 @@ EXPORT_SYMBOL_GPL(nfs_initiate_pgio);
 static void nfs_pgio_error(struct nfs_pgio_header *hdr)
 {
        set_bit(NFS_IOHDR_REDO, &hdr->flags);
-       nfs_pgio_data_destroy(hdr);
        hdr->completion_ops->completion(hdr);
 }
 
@@ -651,7 +650,6 @@ static void nfs_pgio_error(struct nfs_pgio_header *hdr)
 static void nfs_pgio_release(void *calldata)
 {
        struct nfs_pgio_header *hdr = calldata;
-       nfs_pgio_data_destroy(hdr);
        hdr->completion_ops->completion(hdr);
 }
 
index 3125a9d7b23772f03f9aeae5c4f8547b6f8e1280..7879ed8ceb76b52f1fd9b55ea90462d36554ae49 100644 (file)
@@ -2233,7 +2233,6 @@ pnfs_write_through_mds(struct nfs_pageio_descriptor *desc,
                nfs_pageio_reset_write_mds(desc);
                mirror->pg_recoalesce = 1;
        }
-       nfs_pgio_data_destroy(hdr);
        hdr->release(hdr);
 }
 
@@ -2357,7 +2356,6 @@ pnfs_read_through_mds(struct nfs_pageio_descriptor *desc,
                nfs_pageio_reset_read_mds(desc);
                mirror->pg_recoalesce = 1;
        }
-       nfs_pgio_data_destroy(hdr);
        hdr->release(hdr);
 }