All of lore.kernel.org
 help / color / mirror / Atom feed
From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
To: stable@vger.kernel.org
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	patches@lists.linux.dev, Tj <tj.iam.tj@proton.me>,
	Jeff Layton <jlayton@kernel.org>, NeilBrown <neil@brown.name>,
	Chuck Lever <chuck.lever@oracle.com>,
	Sasha Levin <sashal@kernel.org>
Subject: [PATCH 7.0 03/49] lockd: fix TEST handling when not all permissions are available.
Date: Thu, 25 Jun 2026 14:03:15 +0100	[thread overview]
Message-ID: <20260625125637.974589603@linuxfoundation.org> (raw)
In-Reply-To: <20260625125637.527552689@linuxfoundation.org>

7.0-stable review patch.  If anyone has any objections, please let me know.

------------------

From: NeilBrown <neil@brown.name>

[ Upstream commit 0b474240327cebeff08ad429e8ed3cfc6c8ee816 ]

The F_GETLK fcntl can work with either read access or write access or
both.  It can query F_RDLCK and F_WRLCK locks in either case.

However lockd currently treats F_GETLK similar to F_SETLK in that read
access is required to query an F_RDLCK lock and write access is required
to query a F_WRLCK lock.

This is wrong and can cause problems - e.g.  when qemu accesses a
read-only (e.g. iso) filesystem image over NFS (though why it queries
if it can get a write lock - I don't know.  But it does, and this works
with local filesystems).

So we need TEST requests to be handled differently.  To do this:

- change nlm_do_fopen() to accept O_RDWR as a mode and in that case
  succeed if either a O_RDONLY or O_WRONLY file can be opened.
- change nlm_lookup_file() to accept a mode argument from caller,
  instead of deducing base on lock time, and pass that on to nlm_do_fopen()
- change nlm4svc_retrieve_args() and nlmsvc_retrieve_args() to detect
  TEST requests and pass O_RDWR as a mode to nlm_lookup_file, passing
  the same mode as before for other requests.  Also set
   lock->fl.c.flc_file to whichever file is available for TEST requests.
- change nlmsvc_testlock() to also not calculate the mode, but to use
  whatever was stored in lock->fl.c.flc_file.

This behaviour of lockd - requesting O_WRONLY access to TEST for
exclusive locks - has been present at least since git history began.
However it was hidden until recently because knfsd ignored the access
requested by lockd and required only READ access for all locking
requests (unless the underlying filesystem provided an f_op->open
function which checked access permissions).

The commit mentioned in Fixes: below changed nfsd_permission() to NOT
override the access request for LOCK requests and this exposed the bug
that we are now fixing.

Note that there is another issue that this patch does not address.
The flock(.., LOCK_EX) call is permitted on a read-only file descriptor.
Linux NFS maps this to NLM locking as whole-file byte-range locks.
nfsd will see this as though it were fcntl( F_SETLK (F_WRLCK)) and will
now require write access, which it might not be able to get.
It is not clear if this is a problem in practice, or what the best
solution might be.  So no attempt is made to address it.

Reported-by: Tj <tj.iam.tj@proton.me>
Link: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1128861
Fixes: 4cc9b9f2bf4d ("nfsd: refine and rename NFSD_MAY_LOCK")
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: NeilBrown <neil@brown.name>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 fs/lockd/svc4proc.c         | 13 ++++++++++---
 fs/lockd/svclock.c          |  4 +---
 fs/lockd/svcproc.c          | 15 ++++++++++++---
 fs/lockd/svcsubs.c          | 35 +++++++++++++++++++++++++----------
 include/linux/lockd/lockd.h |  2 +-
 5 files changed, 49 insertions(+), 20 deletions(-)

diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c
index 4b6f18d977343d..75e020a8bfd072 100644
--- a/fs/lockd/svc4proc.c
+++ b/fs/lockd/svc4proc.c
@@ -26,6 +26,8 @@ nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
 	struct nlm_host		*host = NULL;
 	struct nlm_file		*file = NULL;
 	struct nlm_lock		*lock = &argp->lock;
+	bool			is_test = (rqstp->rq_proc == NLMPROC_TEST ||
+					   rqstp->rq_proc == NLMPROC_TEST_MSG);
 	__be32			error = 0;
 
 	/* nfsd callbacks must have been installed for this procedure */
@@ -46,15 +48,20 @@ nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
 	if (filp != NULL) {
 		int mode = lock_to_openmode(&lock->fl);
 
+		if (is_test)
+			mode = O_RDWR;
+
 		lock->fl.c.flc_flags = FL_POSIX;
 
-		error = nlm_lookup_file(rqstp, &file, lock);
+		error = nlm_lookup_file(rqstp, &file, lock, mode);
 		if (error)
 			goto no_locks;
 		*filp = file;
-
 		/* Set up the missing parts of the file_lock structure */
-		lock->fl.c.flc_file = file->f_file[mode];
+		if (is_test)
+			lock->fl.c.flc_file = nlmsvc_file_file(file);
+		else
+			lock->fl.c.flc_file = file->f_file[mode];
 		lock->fl.c.flc_pid = current->tgid;
 		lock->fl.fl_start = (loff_t)lock->lock_start;
 		lock->fl.fl_end = lock->lock_len ?
diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c
index abc65dc79f8543..382bf0d93ed1c1 100644
--- a/fs/lockd/svclock.c
+++ b/fs/lockd/svclock.c
@@ -619,7 +619,6 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
 		struct nlm_lock *conflock)
 {
 	int			error;
-	int			mode;
 	__be32			ret;
 
 	dprintk("lockd: nlmsvc_testlock(%s/%ld, ty=%d, %Ld-%Ld)\n",
@@ -637,14 +636,13 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
 		goto out;
 	}
 
-	mode = lock_to_openmode(&lock->fl);
 	locks_init_lock(&conflock->fl);
 	/* vfs_test_lock only uses start, end, and owner, but tests flc_file */
 	conflock->fl.c.flc_file = lock->fl.c.flc_file;
 	conflock->fl.fl_start = lock->fl.fl_start;
 	conflock->fl.fl_end = lock->fl.fl_end;
 	conflock->fl.c.flc_owner = lock->fl.c.flc_owner;
-	error = vfs_test_lock(file->f_file[mode], &conflock->fl);
+	error = vfs_test_lock(lock->fl.c.flc_file, &conflock->fl);
 	if (error) {
 		ret = nlm_lck_denied_nolocks;
 		goto out;
diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c
index 5817ef272332d9..d98e8d684376b7 100644
--- a/fs/lockd/svcproc.c
+++ b/fs/lockd/svcproc.c
@@ -55,6 +55,8 @@ nlmsvc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
 	struct nlm_host		*host = NULL;
 	struct nlm_file		*file = NULL;
 	struct nlm_lock		*lock = &argp->lock;
+	bool			is_test = (rqstp->rq_proc == NLMPROC_TEST ||
+					   rqstp->rq_proc == NLMPROC_TEST_MSG);
 	int			mode;
 	__be32			error = 0;
 
@@ -70,15 +72,22 @@ nlmsvc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
 
 	/* Obtain file pointer. Not used by FREE_ALL call. */
 	if (filp != NULL) {
-		error = cast_status(nlm_lookup_file(rqstp, &file, lock));
+		mode = lock_to_openmode(&lock->fl);
+
+		if (is_test)
+			mode = O_RDWR;
+
+		error = cast_status(nlm_lookup_file(rqstp, &file, lock, mode));
 		if (error != 0)
 			goto no_locks;
 		*filp = file;
 
 		/* Set up the missing parts of the file_lock structure */
-		mode = lock_to_openmode(&lock->fl);
 		lock->fl.c.flc_flags = FL_POSIX;
-		lock->fl.c.flc_file  = file->f_file[mode];
+		if (is_test)
+			lock->fl.c.flc_file = nlmsvc_file_file(file);
+		else
+			lock->fl.c.flc_file = file->f_file[mode];
 		lock->fl.c.flc_pid = current->tgid;
 		lock->fl.fl_lmops = &nlmsvc_lock_operations;
 		nlmsvc_locks_init_private(&lock->fl, host, (pid_t)lock->svid);
diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c
index dd0214dcb69503..b83e6ecd5be34f 100644
--- a/fs/lockd/svcsubs.c
+++ b/fs/lockd/svcsubs.c
@@ -82,18 +82,35 @@ int lock_to_openmode(struct file_lock *lock)
  *
  * We have to make sure we have the right credential to open
  * the file.
+ *
+ * mode can be O_RDONLY(0), O_WRONLY(1) or O_RDWR(2). The latter
+ * means success can be achieved with EITHER O_RDONLY or O_WRONLY.
+ * It does NOT mean both read and write are required.
  */
 static __be32 nlm_do_fopen(struct svc_rqst *rqstp,
 			   struct nlm_file *file, int mode)
 {
-	struct file **fp = &file->f_file[mode];
-	__be32	nfserr;
+	__be32 nfserr = nlm_lck_denied_nolocks;
+	__be32 deferred = 0;
+	struct file **fp;
+	int m;
 
-	if (*fp)
-		return 0;
-	nfserr = nlmsvc_ops->fopen(rqstp, &file->f_handle, fp, mode);
-	if (nfserr)
-		dprintk("lockd: open failed (error %d)\n", nfserr);
+	for (m = O_RDONLY ; m <= O_WRONLY ; m++) {
+		if (mode != O_RDWR && mode != m)
+			continue;
+
+		fp = &file->f_file[m];
+		if (*fp)
+			return 0;
+		nfserr = nlmsvc_ops->fopen(rqstp, &file->f_handle, fp, m);
+		if (!nfserr)
+			return 0;
+		if (nfserr == nlm_drop_reply)
+			deferred = nfserr;
+	}
+	if (deferred)
+		return deferred;
+	dprintk("lockd: open failed (error %d)\n", ntohl(nfserr));
 	return nfserr;
 }
 
@@ -103,17 +120,15 @@ static __be32 nlm_do_fopen(struct svc_rqst *rqstp,
  */
 __be32
 nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
-					struct nlm_lock *lock)
+		struct nlm_lock *lock, int mode)
 {
 	struct nlm_file	*file;
 	unsigned int	hash;
 	__be32		nfserr;
-	int		mode;
 
 	nlm_debug_print_fh("nlm_lookup_file", &lock->fh);
 
 	hash = file_hash(&lock->fh);
-	mode = lock_to_openmode(&lock->fl);
 
 	/* Lock file table */
 	mutex_lock(&nlm_file_mutex);
diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h
index 330e38776bb20d..fe5cdd4d66f484 100644
--- a/include/linux/lockd/lockd.h
+++ b/include/linux/lockd/lockd.h
@@ -294,7 +294,7 @@ void		  nlmsvc_locks_init_private(struct file_lock *, struct nlm_host *, pid_t);
  * File handling for the server personality
  */
 __be32		  nlm_lookup_file(struct svc_rqst *, struct nlm_file **,
-					struct nlm_lock *);
+				  struct nlm_lock *, int);
 void		  nlm_release_file(struct nlm_file *);
 void		  nlmsvc_put_lockowner(struct nlm_lockowner *);
 void		  nlmsvc_release_lockowner(struct nlm_lock *);
-- 
2.53.0




  parent reply	other threads:[~2026-06-25 13:09 UTC|newest]

Thread overview: 61+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-25 13:03 [PATCH 7.0 00/49] 7.0.14-rc1 review Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 01/49] io_uring/net: Avoid msghdr on op_connect/op_bind async data Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 02/49] arm64/entry: Fix arm64-specific rseq brokenness Greg Kroah-Hartman
2026-06-25 13:03 ` Greg Kroah-Hartman [this message]
2026-06-25 13:03 ` [PATCH 7.0 04/49] firmware: exynos-acpm: Count number of commands in acpm_xfer Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 05/49] firmware: exynos-acpm: Count acpm_xfer buffers with __counted_by_ptr Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 06/49] firmware: samsung: acpm: Fix cross-thread RX length corruption Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 07/49] firmware: samsung: acpm: Fix false timeouts and Use-After-Free in polling Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 08/49] firmware: samsung: acpm: Fix missing LKMM barriers in sequence allocator Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 09/49] fuse: re-lock request before replacing page cache folio Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 10/49] Revert "NFSD: Defer sub-object cleanup in export put callbacks" Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 11/49] RDMA/bnxt_re: zero shared page before exposing to userspace Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 12/49] bpf: Fix NULL pointer dereference in bpf_sk_storage_clone and diag paths Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 13/49] i2c: stub: Reject I2C block transfers with invalid length Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 14/49] net: qualcomm: rmnet: fix endpoint use-after-free in rmnet_dellink() Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 15/49] agp/amd64: Fix broken error propagation in agp_amd64_probe() Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 16/49] rose: fix dev_put() leak in rose_loopback_timer() Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 17/49] rose: hold loopback neighbour reference across timer callback Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 18/49] rose: fix race between loopback timer and module removal Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 19/49] rose: clear neighbour pointer after rose_neigh_put() in state machines Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 20/49] rose: guard rose_neigh_put() against NULL in timer expiry Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 21/49] rose: fix netdev double-hold in rose_rx_call_request() Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 22/49] rose: fix notifier unregistered too early in rose_exit() Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 23/49] rose: set SOCK_DESTROY in rose_kill_by_device() for prompt cleanup Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 24/49] rose: disconnect orphaned STATE_2 sockets when device is gone Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 25/49] rose: fix netdev double-hold in rose_make_new() Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 26/49] rose: release netdev ref and destroy orphaned incoming sockets Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 27/49] rose: drop CALL_REQUEST in loopback timer when device is not running Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 28/49] rose: cancel neighbour timers in rose_neigh_put() before freeing Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 29/49] rose: clear neighbour pointer in rose_kill_by_device() Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 30/49] rose: dont free fd-owned sockets when reaping in the heartbeat Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 31/49] net: export netif_open for self_test usage Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 32/49] net: net_failover: Fix the deadlock in slave register Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 33/49] iio: light: veml6075: add bounds check to veml6075_it_ms index Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 34/49] iio: adc: ti-ads1298: add bounds check to pga_settings index Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 35/49] Input: rmi4 - fix register descriptor address calculation Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 36/49] Input: rmi4 - refactor register descriptor parsing Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 37/49] Input: rmi4 - fix type overflow in register counts Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 38/49] Input: rmi4 - fix num_subpackets overflow in register descriptor Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 39/49] Input: rmi4 - fix memory leak in rmi_set_attn_data() Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 40/49] Input: rmi4 - iterative IRQ handler Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 41/49] Input: rmi4 - fix bit count in bitmap_copy() Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 42/49] crypto: qat - remove unused character device and IOCTLs Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 43/49] vc_screen: fix null-ptr-deref in vcs_notifier() during concurrent vcs_write Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 44/49] serial: qcom_geni: Fix RX DMA stall when SE_DMA_RX_LEN_IN is zero Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 45/49] serial: 8250_dw: unregister 8250 port if clk_notifier_register() fails Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 46/49] drivers/base/memory: set mem->altmap after successful device registration Greg Kroah-Hartman
2026-06-25 13:03 ` [PATCH 7.0 47/49] ksmbd: reject non-VALID session in compound request branch Greg Kroah-Hartman
2026-06-25 13:04 ` [PATCH 7.0 48/49] media: vidtv: fix NULL pointer dereference in vidtv_mux_push_si Greg Kroah-Hartman
2026-06-25 13:04 ` [PATCH 7.0 49/49] virtiofs: fix UAF on submount umount Greg Kroah-Hartman
2026-06-25 13:44 ` [PATCH 7.0 00/49] 7.0.14-rc1 review Florian Fainelli
2026-06-25 15:27 ` Brett A C Sheffield
2026-06-25 17:30 ` Justin Forbes
2026-06-25 18:06 ` Peter Schneider
2026-06-25 23:58 ` Shuah Khan
2026-06-26  5:05 ` Ron Economos
2026-06-26 10:54 ` Miguel Ojeda
2026-06-26 11:24 ` Dileep malepu
2026-06-26 11:25 ` Pavel Machek
2026-06-26 11:44   ` Pavel Machek
2026-06-26 13:16 ` Mark Brown

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260625125637.974589603@linuxfoundation.org \
    --to=gregkh@linuxfoundation.org \
    --cc=chuck.lever@oracle.com \
    --cc=jlayton@kernel.org \
    --cc=neil@brown.name \
    --cc=patches@lists.linux.dev \
    --cc=sashal@kernel.org \
    --cc=stable@vger.kernel.org \
    --cc=tj.iam.tj@proton.me \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.