Blob Blame History Raw
From cd24a1fbd0abbaed922df6f88a4c166642240c18 Mon Sep 17 00:00:00 2001
From: Yannick Cote <ycote@redhat.com>
Date: Mon, 12 Dec 2022 12:34:49 -0500
Subject: [KPATCH CVE-2022-2959] kpatch fixes for CVE-2022-2959

Kernels:
5.14.0-162.6.1.el9_1


Kpatch-MR: https://gitlab.com/redhat/prdsc/rhel/src/kpatch/rhel-9/-/merge_requests/12
Approved-by: Joe Lawrence (@joe.lawrence)
Changes since last build:
[x86_64]:
ax88179_178a.o: changed function: ax88179_rx_fixup
callback_xdr.o: changed function: nfs_callback_dispatch
intel_gt.o: changed function: intel_gt_invalidate_tlbs
nfs3proc.o: changed function: nfsd3_init_dirlist_pages
nfs3proc.o: changed function: nfsd3_proc_read
nfsproc.o: changed function: nfsd_proc_read
nfsproc.o: changed function: nfsd_proc_readdir
nfssvc.o: changed function: nfsd_dispatch
pipe.o: changed function: pipe_resize_ring
svc.o: changed function: nlmsvc_dispatch

[ppc64le]:
ax88179_178a.o: changed function: ax88179_rx_fixup
callback_xdr.o: changed function: nfs_callback_dispatch
nfs3proc.o: changed function: nfsd3_init_dirlist_pages
nfs3proc.o: changed function: nfsd3_proc_read
nfsproc.o: changed function: nfsd_proc_read
nfsproc.o: changed function: nfsd_proc_readdir
nfssvc.o: changed function: nfsd_dispatch
pipe.o: changed function: pipe_resize_ring
svc.o: changed function: nlmsvc_dispatch

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

Modifications: none

commit c08a6dbfd89b0d7dfbc0726eeee3bb7289b59af8
Author: Ian Kent <ikent@redhat.com>
Date:   Tue Nov 15 14:08:25 2022 +0800

    pipe: Fix missing lock in pipe_resize_ring()

    Bugzilla: https://bugzilla.redhat.com/2141631
    CVE: CVE-2022-2959
    Y-Commit: 713162e02d3aca87567fc906879e972491ca045d

    O-Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2141632
    Status: Linus
    O-CVE: CVE-2022-2959

    commit 189b0ddc245139af81198d1a3637cac74f96e13a
    From: David Howells <dhowells@redhat.com>
    Date: 2022-05-26 07:34:52 +0100

            pipe: Fix missing lock in pipe_resize_ring()

            pipe_resize_ring() needs to take the pipe->rd_wait.lock spinlock to
            prevent post_one_notification() from trying to insert into the ring
            whilst the ring is being replaced.

            The occupancy check must be done after the lock is taken, and the lock
            must be taken after the new ring is allocated.

            The bug can lead to an oops looking something like:

             BUG: KASAN: use-after-free in post_one_notification.isra.0+0x62e/0x840
             Read of size 4 at addr ffff88801cc72a70 by task poc/27196
             ...
             Call Trace:
              post_one_notification.isra.0+0x62e/0x840
              __post_watch_notification+0x3b7/0x650
              key_create_or_update+0xb8b/0xd20
              __do_sys_add_key+0x175/0x340
              __x64_sys_add_key+0xbe/0x140
              do_syscall_64+0x5c/0xc0
              entry_SYSCALL_64_after_hwframe+0x44/0xae

            Reported by Selim Enes Karaduman @Enesdex working with Trend Micro Zero
            Day Initiative.

            Fixes: c73be61cede5 ("pipe: Add general notification queue support")
            Reported-by: zdi-disclosures@trendmicro.com # ZDI-CAN-17291
            Signed-off-by: David Howells <dhowells@redhat.com>
            Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

    Signed-off-by: Ian Kent <ikent@redhat.com>
    Signed-off-by: Patrick Talbert <ptalbert@redhat.com>

Signed-off-by: Yannick Cote <ycote@redhat.com>
---
 fs/pipe.c | 31 ++++++++++++++++++-------------
 1 file changed, 18 insertions(+), 13 deletions(-)

diff --git a/fs/pipe.c b/fs/pipe.c
index 751d5b36c84b..4a1c6ef9493d 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -1244,30 +1244,33 @@ unsigned int round_pipe_size(unsigned long size)
 
 /*
  * Resize the pipe ring to a number of slots.
+ *
+ * Note the pipe can be reduced in capacity, but only if the current
+ * occupancy doesn't exceed nr_slots; if it does, EBUSY will be
+ * returned instead.
  */
 int pipe_resize_ring(struct pipe_inode_info *pipe, unsigned int nr_slots)
 {
 	struct pipe_buffer *bufs;
 	unsigned int head, tail, mask, n;
 
-	/*
-	 * We can shrink the pipe, if arg is greater than the ring occupancy.
-	 * Since we don't expect a lot of shrink+grow operations, just free and
-	 * allocate again like we would do for growing.  If the pipe currently
-	 * contains more buffers than arg, then return busy.
-	 */
-	mask = pipe->ring_size - 1;
-	head = pipe->head;
-	tail = pipe->tail;
-	n = pipe_occupancy(pipe->head, pipe->tail);
-	if (nr_slots < n)
-		return -EBUSY;
-
 	bufs = kcalloc(nr_slots, sizeof(*bufs),
 		       GFP_KERNEL_ACCOUNT | __GFP_NOWARN);
 	if (unlikely(!bufs))
 		return -ENOMEM;
 
+	spin_lock_irq(&pipe->rd_wait.lock);
+	mask = pipe->ring_size - 1;
+	head = pipe->head;
+	tail = pipe->tail;
+
+	n = pipe_occupancy(head, tail);
+	if (nr_slots < n) {
+		spin_unlock_irq(&pipe->rd_wait.lock);
+		kfree(bufs);
+		return -EBUSY;
+	}
+
 	/*
 	 * The pipe array wraps around, so just start the new one at zero
 	 * and adjust the indices.
@@ -1299,6 +1302,8 @@ int pipe_resize_ring(struct pipe_inode_info *pipe, unsigned int nr_slots)
 	pipe->tail = tail;
 	pipe->head = head;
 
+	spin_unlock_irq(&pipe->rd_wait.lock);
+
 	/* This might have made more room for writers */
 	wake_up_interruptible(&pipe->wr_wait);
 	return 0;
-- 
2.39.0