lockd: detect and reject lock arguments that overflow
authorJeff Layton <jlayton@kernel.org>
Mon, 1 Aug 2022 19:57:26 +0000 (15:57 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 17 Aug 2022 12:22:47 +0000 (14:22 +0200)
commit 6930bcbfb6ceda63e298c6af6d733ecdf6bd4cde upstream.

lockd doesn't currently vet the start and length in nlm4 requests like
it should, and can end up generating lock requests with arguments that
overflow when passed to the filesystem.

The NLM4 protocol uses unsigned 64-bit arguments for both start and
length, whereas struct file_lock tracks the start and end as loff_t
values. By the time we get around to calling nlm4svc_retrieve_args,
we've lost the information that would allow us to determine if there was
an overflow.

Start tracking the actual start and len for NLM4 requests in the
nlm_lock. In nlm4svc_retrieve_args, vet these values to ensure they
won't cause an overflow, and return NLM4_FBIG if they do.

Link: https://bugzilla.linux-nfs.org/show_bug.cgi?id=392
Reported-by: Jan Kasiak <j.kasiak@gmail.com>
Signed-off-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Cc: <stable@vger.kernel.org> # 5.14+
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/lockd/svc4proc.c
fs/lockd/xdr4.c
include/linux/lockd/xdr.h

index e10ae2c41279e46ef9de58fb04c7b4206afefc28..1c9214801e69e7c8e835272ac21384967f06c5f1 100644 (file)
@@ -32,6 +32,10 @@ nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
        if (!nlmsvc_ops)
                return nlm_lck_denied_nolocks;
 
+       if (lock->lock_start > OFFSET_MAX ||
+           (lock->lock_len && ((lock->lock_len - 1) > (OFFSET_MAX - lock->lock_start))))
+               return nlm4_fbig;
+
        /* Obtain host handle */
        if (!(host = nlmsvc_lookup_host(rqstp, lock->caller, lock->len))
         || (argp->monitor && nsm_monitor(host) < 0))
@@ -50,6 +54,10 @@ nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
                /* Set up the missing parts of the file_lock structure */
                lock->fl.fl_file  = file->f_file[mode];
                lock->fl.fl_pid = current->tgid;
+               lock->fl.fl_start = (loff_t)lock->lock_start;
+               lock->fl.fl_end = lock->lock_len ?
+                                  (loff_t)(lock->lock_start + lock->lock_len - 1) :
+                                  OFFSET_MAX;
                lock->fl.fl_lmops = &nlmsvc_lock_operations;
                nlmsvc_locks_init_private(&lock->fl, host, (pid_t)lock->svid);
                if (!lock->fl.fl_owner) {
index 98e957e4566c27f8a6e91d0ae73711c461572d57..72f7d190fb3b23b251848fd2224e6d0a933d2325 100644 (file)
 
 #include "svcxdr.h"
 
-static inline loff_t
-s64_to_loff_t(__s64 offset)
-{
-       return (loff_t)offset;
-}
-
-
 static inline s64
 loff_t_to_s64(loff_t offset)
 {
@@ -70,8 +63,6 @@ static bool
 svcxdr_decode_lock(struct xdr_stream *xdr, struct nlm_lock *lock)
 {
        struct file_lock *fl = &lock->fl;
-       u64 len, start;
-       s64 end;
 
        if (!svcxdr_decode_string(xdr, &lock->caller, &lock->len))
                return false;
@@ -81,20 +72,14 @@ svcxdr_decode_lock(struct xdr_stream *xdr, struct nlm_lock *lock)
                return false;
        if (xdr_stream_decode_u32(xdr, &lock->svid) < 0)
                return false;
-       if (xdr_stream_decode_u64(xdr, &start) < 0)
+       if (xdr_stream_decode_u64(xdr, &lock->lock_start) < 0)
                return false;
-       if (xdr_stream_decode_u64(xdr, &len) < 0)
+       if (xdr_stream_decode_u64(xdr, &lock->lock_len) < 0)
                return false;
 
        locks_init_lock(fl);
        fl->fl_flags = FL_POSIX;
        fl->fl_type  = F_RDLCK;
-       end = start + len - 1;
-       fl->fl_start = s64_to_loff_t(start);
-       if (len == 0 || end < 0)
-               fl->fl_end = OFFSET_MAX;
-       else
-               fl->fl_end = s64_to_loff_t(end);
 
        return true;
 }
index a98309c0121cb6c34e5fbc65eb61a2868158a544..bed63156b0521eaa19f6151ff7287161bf9a3be9 100644 (file)
@@ -41,6 +41,8 @@ struct nlm_lock {
        struct nfs_fh           fh;
        struct xdr_netobj       oh;
        u32                     svid;
+       u64                     lock_start;
+       u64                     lock_len;
        struct file_lock        fl;
 };