lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <Z1cra8/5H5HvJ5Sw@igm-qws-u22929a.delacy.com>
Date: Mon, 9 Dec 2024 12:39:55 -0500
From: Nikhil Jha <njha@...estreet.com>
To: Trond Myklebust <trondmy@...nel.org>, Anna Schumaker <anna@...nel.org>
Cc: linux-nfs@...r.kernel.org, linux-kernel@...r.kernel.org
Subject: [PATCH] nfs: propagate fileid changed errors back to syscall

Hello! This is the first kernel patch I have tried to upstream. I'm
following along with the kernel newbies guide but apologies if I got
anything wrong.

Currently, if there is a mismatch in the request and response fileids in
an NFS request, the kernel logs an error and attempts to return ESTALE.
However, this error is currently dropped before it makes it all the way
to userspace. This appears to be a mistake, since as far as I can tell
that ESTALE value is never consumed from anywhere.

Callstack for async NFS write, at time of error:

        nfs_update_inode <- returns -ESTALE
        nfs_refresh_inode_locked
        nfs_writeback_update_inode <- error is dropped here
        nfs3_write_done
        nfs_writeback_done
        nfs_pgio_result <- other errors are collected here
        rpc_exit_task
        __rpc_execute
        rpc_async_schedule
        process_one_work
        worker_thread
        kthread
        ret_from_fork

We ran into this issue ourselves, and seeing the -ESTALE in the kernel
source code but not from userspace was surprising.

I tested a rebased version of this patch on an el8 kernel (v6.1.114),
and it seems to correctly propagate this error.

>8------------------------------------------------------8<

If an NFS server returns a response with a different file id to the
response, the kernel currently prints out an error and attempts to
return -ESTALE. However, this -ESTALE value is never surfaced anywhere.

This patch modifies nfs_writeback_update_inode() to propagate these
errors up the call stack, and modifies nfs_pgio_result() to report
the resulting error.

Signed-off-by: Nikhil Jha <njha@...estreet.com>
---
 fs/nfs/filelayout/filelayout.c         | 2 +-
 fs/nfs/flexfilelayout/flexfilelayout.c | 2 +-
 fs/nfs/internal.h                      | 2 +-
 fs/nfs/nfs3proc.c                      | 2 +-
 fs/nfs/nfs4proc.c                      | 2 +-
 fs/nfs/pagelist.c                      | 5 ++++-
 fs/nfs/proc.c                          | 2 +-
 fs/nfs/write.c                         | 9 ++++++---
 8 files changed, 16 insertions(+), 10 deletions(-)

diff --git a/fs/nfs/filelayout/filelayout.c b/fs/nfs/filelayout/filelayout.c
index d39a1f58e18d..4e80a13e9639 100644
--- a/fs/nfs/filelayout/filelayout.c
+++ b/fs/nfs/filelayout/filelayout.c
@@ -335,7 +335,7 @@ static int filelayout_write_done_cb(struct rpc_task *task,
 	/* zero out the fattr */
 	hdr->fattr.valid = 0;
 	if (task->tk_status >= 0)
-		nfs_writeback_update_inode(hdr);
+		return nfs_writeback_update_inode(hdr);
 
 	return 0;
 }
diff --git a/fs/nfs/flexfilelayout/flexfilelayout.c b/fs/nfs/flexfilelayout/flexfilelayout.c
index f78115c6c2c1..d15e3799a351 100644
--- a/fs/nfs/flexfilelayout/flexfilelayout.c
+++ b/fs/nfs/flexfilelayout/flexfilelayout.c
@@ -1503,7 +1503,7 @@ static int ff_layout_write_done_cb(struct rpc_task *task,
 	/* zero out fattr since we don't care DS attr at all */
 	hdr->fattr.valid = 0;
 	if (task->tk_status >= 0)
-		nfs_writeback_update_inode(hdr);
+		return nfs_writeback_update_inode(hdr);
 
 	return 0;
 }
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index e564bd11ba60..5c4e2fa88324 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -592,7 +592,7 @@ void nfs_mark_request_commit(struct nfs_page *req,
 			     struct nfs_commit_info *cinfo,
 			     u32 ds_commit_idx);
 int nfs_write_need_commit(struct nfs_pgio_header *);
-void nfs_writeback_update_inode(struct nfs_pgio_header *hdr);
+int nfs_writeback_update_inode(struct nfs_pgio_header *hdr);
 int nfs_generic_commit_list(struct inode *inode, struct list_head *head,
 			    int how, struct nfs_commit_info *cinfo);
 void nfs_retry_commit(struct list_head *page_list,
diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c
index 1566163c6d85..42ddbc21fb05 100644
--- a/fs/nfs/nfs3proc.c
+++ b/fs/nfs/nfs3proc.c
@@ -887,7 +887,7 @@ static int nfs3_write_done(struct rpc_task *task, struct nfs_pgio_header *hdr)
 	if (nfs3_async_handle_jukebox(task, inode))
 		return -EAGAIN;
 	if (task->tk_status >= 0)
-		nfs_writeback_update_inode(hdr);
+		return nfs_writeback_update_inode(hdr);
 	return 0;
 }
 
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 405f17e6e0b4..7ec372a1eb98 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -5661,7 +5661,7 @@ static int nfs4_write_done_cb(struct rpc_task *task,
 	}
 	if (task->tk_status >= 0) {
 		renew_lease(NFS_SERVER(inode), hdr->timestamp);
-		nfs_writeback_update_inode(hdr);
+		return nfs_writeback_update_inode(hdr);
 	}
 	return 0;
 }
diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c
index e27c07bd8929..19cb080653e3 100644
--- a/fs/nfs/pagelist.c
+++ b/fs/nfs/pagelist.c
@@ -854,9 +854,12 @@ static void nfs_pgio_result(struct rpc_task *task, void *calldata)
 {
 	struct nfs_pgio_header *hdr = calldata;
 	struct inode *inode = hdr->inode;
+	int status = hdr->rw_ops->rw_done(task, hdr, inode);
 
-	if (hdr->rw_ops->rw_done(task, hdr, inode) != 0)
+	if (status != 0) {
+		nfs_set_pgio_error(hdr, status, hdr->args.offset);
 		return;
+	}
 	if (task->tk_status < 0)
 		nfs_set_pgio_error(hdr, task->tk_status, hdr->args.offset);
 	else
diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c
index 6c09cd090c34..72ffbdfc7ae6 100644
--- a/fs/nfs/proc.c
+++ b/fs/nfs/proc.c
@@ -629,7 +629,7 @@ static int nfs_write_done(struct rpc_task *task, struct nfs_pgio_header *hdr)
 {
 	if (task->tk_status >= 0) {
 		hdr->res.count = hdr->args.count;
-		nfs_writeback_update_inode(hdr);
+		return nfs_writeback_update_inode(hdr);
 	}
 	return 0;
 }
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index 50fa539611f5..151da29175fd 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -1507,22 +1507,25 @@ static void nfs_writeback_check_extend(struct nfs_pgio_header *hdr,
 	fattr->valid |= NFS_ATTR_FATTR_SIZE;
 }
 
-void nfs_writeback_update_inode(struct nfs_pgio_header *hdr)
+int nfs_writeback_update_inode(struct nfs_pgio_header *hdr)
 {
 	struct nfs_fattr *fattr = &hdr->fattr;
 	struct inode *inode = hdr->inode;
+	int ret = 0;
 
 	if (nfs_have_delegated_mtime(inode)) {
 		spin_lock(&inode->i_lock);
 		nfs_set_cache_invalid(inode, NFS_INO_INVALID_BLOCKS);
 		spin_unlock(&inode->i_lock);
-		return;
+		return 0;
 	}
 
 	spin_lock(&inode->i_lock);
 	nfs_writeback_check_extend(hdr, fattr);
-	nfs_post_op_update_inode_force_wcc_locked(inode, fattr);
+	ret = nfs_post_op_update_inode_force_wcc_locked(inode, fattr);
 	spin_unlock(&inode->i_lock);
+
+	return ret;
 }
 EXPORT_SYMBOL_GPL(nfs_writeback_update_inode);
 
-- 
2.39.3


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ