6.6.148-rc1 review
Greg Kroah-Hartman
maintainerPATCH 279/484 Bluetooth: RFCOMM: Fix session UAF in set_termios
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chengfeng Ye <[email protected]>
commit c783399efc22d035443f1dfbf2a09bf9562aaa5e upstream.
rfcomm_tty_set_termios() tests dlc->session without rfcomm_mutex and later passes the pointer to rfcomm_send_rpn(). The latter dereferences both session->initiator and session->sock. Meanwhile, krfcommd can unlink the DLC and free the session while holding rfcomm_mutex.
The race can proceed as follows:
TTY ioctl task krfcommd
-------------- --------
load dlc->session
enter rfcomm_send_rpn()
lock rfcomm_mutex
clear dlc->session
free session
unlock rfcomm_mutex read session->initiator
KASAN reported:
BUG: KASAN: slab-use-after-free in rfcomm_send_rpn+0x297/0x2a0 Read of size 4 at addr ffff88810012a850 by task poc/92 Call Trace: rfcomm_send_rpn+0x297/0x2a0 rfcomm_tty_set_termios+0x50d/0x850 tty_set_termios+0x596/0x950 set_termios+0x46a/0x6e0 tty_mode_ioctl+0x152/0xbd0 tty_ioctl+0x915/0x1240 __x64_sys_ioctl+0x134/0x1c0 Allocated by task 92: rfcomm_session_add+0x9e/0x2e0 rfcomm_dlc_open+0x8b1/0xe00 rfcomm_dev_activate+0x85/0x1a0 rfcomm_tty_open+0x90/0x280 Freed by task 68: kfree+0x131/0x3c0 rfcomm_session_del+0x119/0x180 rfcomm_run+0x737/0x4710
Add rfcomm_dlc_send_rpn(), which holds rfcomm_mutex while it verifies that the DLC is still attached and sends the RPN frame. Have the TTY path use the helper and drop its unlocked session check. This keeps the session valid through both the frame construction and socket send.
--- include/net/bluetooth/rfcomm.h | 3 +++ net/bluetooth/rfcomm/core.c | 17 +++++++++++++++++ net/bluetooth/rfcomm/tty.c | 7 +++---- 3 files changed, 23 insertions(+), 4 deletions(-)
include/net/bluetooth/rfcomm.h +3 -0
@@ -229,6 +229,9 @@ int rfcomm_send_rpn(struct rfcomm_sessio u8 bit_rate, u8 data_bits, u8 stop_bits, u8 parity, u8 flow_ctrl_settings, u8 xon_char, u8 xoff_char, u16 param_mask); +int rfcomm_dlc_send_rpn (struct rfcomm_dlc *d,u8 bit_rate,u8 data_bits, +u8 stop_bits,u8 parity,u8 flow_ctrl_settings, +u8 xon_char,u8 xoff_char,u16 param_mask);/* ---- RFCOMM DLCs (channels) ---- */ struct rfcomm_dlc *rfcomm_dlc_alloc (gfp_t prio);
net/bluetooth/rfcomm/core.c +17 -0
@@ -1031,6 +1031,23 @@ int rfcomm_send_rpn(struct rfcomm_sessioreturn rfcomm_send_frame (s, buf, ptr - buf); } +int rfcomm_dlc_send_rpn (struct rfcomm_dlc *d,u8 bit_rate,u8 data_bits, +u8 stop_bits,u8 parity,u8 flow_ctrl_settings, +u8 xon_char,u8 xoff_char,u16 param_mask) +{ +int err = -ENOTCONN ; + +rfcomm_lock (); +if (d->session) + err =rfcomm_send_rpn (d->session,1 , d->dlci, bit_rate, + data_bits, stop_bits, parity, + flow_ctrl_settings, xon_char, xoff_char, + param_mask); +rfcomm_unlock (); + +return err; +} +static int rfcomm_send_rls (struct rfcomm_session *s,int cr,u8 dlci,u8 status) {struct rfcomm_hdr *hdr;
net/bluetooth/rfcomm/tty.c +3 -4
@@ -864,7 +864,7 @@ static void rfcomm_tty_set_termios(strucBT_DBG ("tty %p termios %p" , tty, old); -if (!dev || !dev->dlc || !dev->dlc->session) +if (!dev || !dev->dlc)return ;/* Handle turning off CRTSCTS */
@@ -985,9 +985,8 @@ static void rfcomm_tty_set_termios(struc }if (changes) -rfcomm_send_rpn (dev->dlc->session,1 , dev->dlc->dlci, baud, - data_bits, stop_bits, parity, -RFCOMM_RPN_FLOW_NONE , x_on, x_off, changes); +rfcomm_dlc_send_rpn (dev->dlc, baud, data_bits, stop_bits, parity, +RFCOMM_RPN_FLOW_NONE , x_on, x_off, changes); }static void rfcomm_tty_throttle (struct tty_struct *tty)
Greg Kroah-Hartman
maintainerPATCH 280/484 exec: fix unsigned loop counter wrap in transfer_args_to_stack()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
commit 16cc4f5c1c4b9e45eca7f7deefa5410a292db599 upstream.
The stop value is derived from bprm->p >> PAGE_SHIFT. The index variable is an unsigned long. If bprm->p drops below PAGE_SIZE and stop becomes zero the loop condition index >= stop is always true.
After the index == 0 iteration the decrement wraps to ULONG_MAX and bprm->page[ULONG_MAX] reads sizeof(void *) bytes in front of the array. The pointer has wrapped to -1. That garbage pointer is then passed to kmap_local_page() and PAGE_SIZE bytes are copied from wherever that lands into the stack of the process being created. And the loop doesn't terminate either...
Getting there only requires bprm->p < PAGE_SIZE. On !MMU bprm_set_stack_limit() and bprm_hit_stack_limit() are empty. So the only constraint on how far bprm->p is pushed down is valid_arg_len(), i.e. that each individual string still fits in what is left.
bprm->p starts at PAGE_SIZE * MAX_ARG_PAGES - sizeof(void *) so a single argument or environment string of a little over 31 pages leaves it in the first page:
Oops - load access fault [#1]
CPU: 0 UID: 0 PID: 1 Comm: victim Not tainted 7.2.0-rc4 #1 epc : __memcpy+0xd4/0xf8 ra : transfer_args_to_stack+0xaa/0xae s4 : ffffffffffffffff s2 : 0000000000000000 a1 : ffffffdc98000000 a2 : 0000000000001000 status: 0000000a00001880 badaddr: ffffffdc98000000 cause: 0000000000000005 [<801a5324>] __memcpy+0xd4/0xf8 [<800d5f6a>] load_flat_binary+0x43a/0x65e [<800a2de4>] bprm_execve+0x1d4/0x316 [<800a351a>] do_execveat_common+0x12e/0x138 [<800a3d44>] __riscv_sys_execve+0x38/0x4e Kernel panic - not syncing: Fatal exception in interrupt
This is an arcane bug but we should still fix it.
Count down from MAX_ARG_PAGES so the loop ends when index reaches stop, stop == 0 included. The iterations performed are unchanged for every other value of stop.
Only CONFIG_MMU=n builds are affected, transfer_args_to_stack() is used by binfmt_flat and binfmt_elf_fdpic on nommu only.
The loop predates git history. commit 7e7ec6a93434 ("elf_fdpic_transfer_args_to_stack(): make it generic") only moved it from binfmt_elf_fdpic.c into fs/exec.c and narrowed the copy to the used part of the first page. The condition and the decrement are unchanged from 2.6.12-rc2.
--- fs/exec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/exec.c +1 -1
@@ -882,7 +882,7 @@ int transfer_args_to_stack(struct linux_ stop = bprm->p >>PAGE_SHIFT ; sp = *sp_location; -for (index =MAX_ARG_PAGES -1 ; index >= stop; index--) { +for (index =MAX_ARG_PAGES ; index-- > stop; ) {unsigned int offset = index == stop ? bprm->p & ~PAGE_MASK :0 ;char *src =kmap_local_page (bprm->page[index]) + offset; sp -=PAGE_SIZE - offset;
Greg Kroah-Hartman
maintainerPATCH 281/484 binfmt_misc: set have_execfd only once the interpreter is opened
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
commit bbf5f639918dc011aaf60aab8480218758ee68c5 upstream.
load_misc_binary() raises bprm->have_execfd as soon as it sees the 'O' (or 'C') flag. This happens well before it opens the interpreter. If that open fails the flag stays set on the bprm. binfmt_misc is at the head of the format list so an interpreter open failure that returns -ENOEXEC lets the search fall through to a later format. This means it runs the matched binary directly having never staged an interpreter. So bprm->executable is NULL while have_execfd falsely claims a descriptor is present.
Consequently, begin_new_exec() dereferences the missing executable:
would_dump(bprm, bprm->executable);
and NULL derefs. Had it not, the hand-off later in the same function would have failed anyway. FD_ADD(0, bprm->executable) rejects a NULL file with -ENOMEM. Both sites are past the point of no return so the exec cannot be unwound either way.
This can be reached by unprivileged users as binfmt_misc can be mounted in user namespaces. So a user can register an 'O' entry whose interpreter lives on a FUSE mount, have the FUSE server fail the open with -ENOEXEC and execute a native ELF file that matches the entry.
have_execfd only means anything alongside the executable it describes which is not set until the interpreter has been opened and staged. So lets raise it there, next to execfd_creds, which is already set at that point. An open failure now leaves it clear, so the fallback format derives credentials from the binary and emits no AT_EXECFD, as it would for any native exec. The argv rewrite load_misc_binary() performs before the open is still not undone. This means the binary sees the interpreter path in argv[0] and its own path in argv[1] but that predates this change and only became observable once the exec stopped faulting.
--- fs/binfmt_misc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
fs/binfmt_misc.c +2 -3
@@ -199,9 +199,6 @@ static int load_misc_binary(struct linux goto ret; } -if (fmt->flags &MISC_FMT_OPEN_BINARY ) - bprm->have_execfd =1 ; -/* make argv[1] be the path to the binary */ retval =copy_string_kernel (bprm->interp, bprm);if (retval <0 )
@@ -231,6 +228,8 @@ static int load_misc_binary(struct linux goto ret; bprm->interpreter = interp_file; +if (fmt->flags &MISC_FMT_OPEN_BINARY ) + bprm->have_execfd =1 ;if (fmt->flags &MISC_FMT_CREDENTIALS ) bprm->execfd_creds =1 ;
Greg Kroah-Hartman
maintainerPATCH 282/484 platform/loongarch: laptop: Explicitly reset bl_powered state when suspend
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zixing Liu <[email protected]>
commit 91a70492c03040d51b36f595530d6491d5d6c541 upstream.
On EAECIS NL60R with EC firmware version 1.11, resuming from S3 has a very high chance (>90%) of causing the EC to lose the previous backlight power state. When this happens, the laptop resumes normally from S3, but the backlight remains off (when shining on the screen with a flash light, we can see the screen contents are updating normally).
Since there is no generic way to query the EC's backlight state on Loongson laptop platforms, assume the worst-case scenario and restart the backlight power inside the kernel each time the system resumes.
--- drivers/platform/loongarch/loongson-laptop.c | 1 + 1 file changed, 1 insertion(+)
drivers/platform/loongarch/loongson-laptop.c +1 -0
@@ -189,6 +189,7 @@ static int __init setup_acpi_notify(strustatic int loongson_hotkey_suspend (struct device *dev) { + bl_powered = false;return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 283/484 LoongArch: Fix oops during single-step debugging
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Haoran Jiang <[email protected]>
commit 73555fdab5e1e4f24ca000c41a616b34edf4b55d upstream.
When entering KDB via a breakpoint and then performing single-step debugging, an oops is triggered. Now during single-step debugging, kdb_local() expects the reason to be KDB_REASON_SSTEP, but it is actually KDB_REASON_OOPS. In kdb_stub(), when determining the reason, the ex_vector for single-step should be 0, as already implemented on other architectures such as arm64 and riscv.
Before the patch:
[112]kdb> ss
Entering kdb (current=0x900020009f520000, pid 10661) on processor 112 Oops: (null)
due to oops @ 0x90000000005b57a4
--- arch/loongarch/kernel/kgdb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
arch/loongarch/kernel/kgdb.c +2 -1
@@ -252,7 +252,8 @@ static int kgdb_loongarch_notify(structif (atomic_read (&kgdb_active) !=-1 )kgdb_nmicallback (smp_processor_id (), regs); -if (kgdb_handle_exception (args->trapnr, args->signr, cmd, regs)) +if (kgdb_handle_exception (regs->csr_era == stepped_address ?0 : args->trapnr, + args->signr, cmd, regs))return NOTIFY_DONE ;if (atomic_read (&kgdb_setting_breakpoint))
Greg Kroah-Hartman
maintainerPATCH 284/484 cdrom: fix stack out-of-bounds read in CDROMVOLCTRL
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xu Rao <[email protected]>
commit b27e195d4db8dea263050bdbeb11881b2999c9c6 upstream.
mmc_ioctl_cdrom_volume() first reads the audio control mode page into a 32-byte stack buffer with cgc->buflen set to 24. If the device reports a block descriptor, the function increases cgc->buflen to include that descriptor and reads the page again.
For CDROMVOLCTRL, the function then builds a MODE SELECT parameter list by moving cgc->buffer forward by offset - 8 bytes. This drops the block descriptor from the outgoing payload and leaves a new 8-byte mode parameter header in front of the audio control page. However, cgc->buflen is left unchanged.
With a standard 8-byte block descriptor, cgc->buffer points at buffer + 8 but cgc->buflen remains 32. cdrom_mode_select() therefore asks the low level packet path to write 32 bytes from that adjusted pointer, reading 8 bytes past the end of the 32-byte stack buffer.
This is not hit by CDROMVOLREAD, and CDROMVOLCTRL only triggers it on drives that return a non-zero block descriptor length, which helps explain why it has gone unnoticed. The overread is also sent to the device as extra MODE SELECT payload, so it may not produce an obvious local failure.
Reduce cgc->buflen by the same amount as the buffer pointer adjustment so the MODE SELECT transfer covers only the intended parameter list.
--- drivers/cdrom/cdrom.c | 1 + 1 file changed, 1 insertion(+)
drivers/cdrom/cdrom.c +1 -0
@@ -3191,6 +3191,7 @@ static noinline int mmc_ioctl_cdrom_volu/* set volume */ cgc->buffer = buffer + offset -8 ; + cgc->buflen -= offset -8 ;memset (cgc->buffer,0 ,8 );return cdrom_mode_select (cdi, cgc); }
Greg Kroah-Hartman
maintainerPATCH 285/484 x86/boot/compressed: Disable jump tables
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nathan Chancellor <[email protected]>
commit 4a9ec5ec9555ad62dc5b81a37ac946025c2ea002 upstream.
After a recent upstream LLVM change to start generating jump and lookup tables in switch statements in more instances [1], linking the compressed x86 boot image when CONFIG_KERNEL_ZSTD is enabled fails with:
ld.lld: error: Unexpected run-time relocations (.rela) detected!
Dumping the relocations in misc.o, which is the only file influenced by CONFIG_KERNEL_ZSTD in the decompressor, shows dynamic relocations to some string constants, which correspond to the string literals in the switch statement in handle_zstd_error():
Relocation section '.rela.data.rel.ro' at offset 0x277b0 contains 31 entries:
Offset Info Type Symbol's Value Symbol's Name + Addend
0000000000000000 0000006600000001 R_X86_64_64 0000000000000000 .rodata.str1.1 + 73a 0000000000000008 0000006600000001 R_X86_64_64 0000000000000000 .rodata.str1.1 + 78e 0000000000000010 0000006600000001 R_X86_64_64 0000000000000000 .rodata.str1.1 + 78e 0000000000000018 0000006600000001 R_X86_64_64 0000000000000000 .rodata.str1.1 + 78e
...
This optimization is problematic for the decompressor environment, as it is built as -fPIE without any explicit absolute references (as described at the top of misc.c) while not applying any dynamic relocations, hence the linker assertion. To opt out of this optimization, which is of little value in this special early boot code, and to mirror the other x86 startup code in arch/x86/boot/startup, disable jump tables in the decompressor.
--- arch/x86/boot/compressed/Makefile | 1 + 1 file changed, 1 insertion(+)
arch/x86/boot/compressed/Makefile +1 -0
@@ -36,6 +36,7 @@ targets := vmlinux vmlinux.bin vmlinux.b KBUILD_CFLAGS := -m$(BITS) -O2 $(CLANG_FLAGS) KBUILD_CFLAGS += -std=gnu11 KBUILD_CFLAGS += -fno-strict-aliasing -fPIE +KBUILD_CFLAGS += -fno-jump-tables KBUILD_CFLAGS += -Wundef KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING cflags-$(CONFIG_X86_32) := -march=i386
Greg Kroah-Hartman
maintainerPATCH 286/484 comedi: comedi_parport: deal with premature interrupt
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ian Abbott <[email protected]>
commit 17221216ae8ce6a24e8a4e787382e3ebc81b88a8 upstream.
Syzbot reported a general protection fault in comedi_get_is_subdevice_running(), which was called from the interrupt handler parport_interrupt() in the "comedi_parport" driver, but it does not currently have a C reproducer for the problem. It's probably due to a premature interrupt for one of two reasons:
1. The driver sets up the interrupt handler before the comedi subdevices used by the interrupt handler have been allocated, but does not disable the interrupt in the parallel port's CTRL register first.
2. The driver uses a user-supplied I/O port base address which Syzbot would have supplied, but it might not be backed by real parallel port hardware.
Change the initialization order in the driver's comedi "attach" handler (parport_attach()) so that the hardware registers are initialized before the interrupt handler is requested. This should prevent premature interrupts occurring for real hardware.
Also add a test to the interrupt handler to ensure the comedi device is fully attached and return early if it isn't.
--- drivers/comedi/drivers/comedi_parport.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-)
drivers/comedi/drivers/comedi_parport.c +10 -3
@@ -211,6 +211,13 @@ static irqreturn_t parport_interrupt(intunsigned int ctrl;unsigned short val =0 ; +/* +* Check device is fully attached. Device interrupts should have +* been disabled, but do this in case of bad hardware. +*/ +if (!dev->attached) +return IRQ_NONE ; + ctrl =inb (dev->iobase +PARPORT_CTRL_REG );if (!(ctrl &PARPORT_CTRL_IRQ_ENA ))return IRQ_NONE ;
@@ -231,6 +238,9 @@ static int parport_attach(struct comedi_if (ret)return ret; +outb (0 , dev->iobase +PARPORT_DATA_REG ); +outb (0 , dev->iobase +PARPORT_CTRL_REG ); +if (it->options[1 ]) { ret =request_irq (it->options[1 ], parport_interrupt,0 , dev->board_name, dev);
@@ -286,9 +296,6 @@ static int parport_attach(struct comedi_ s->cancel = parport_intr_cancel; } -outb (0 , dev->iobase +PARPORT_DATA_REG ); -outb (0 , dev->iobase +PARPORT_CTRL_REG ); -return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 287/484 serial: sc16is7xx: implement gpio get_direction() callback
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hugo Villeneuve <[email protected]>
commit af071d9e07e57cfff239e8d09d2f3b05ebc9c667 upstream.
It's strongly recommended for GPIO drivers to always implement the .get_direction() callback - even when the direction is tracked in software. The GPIO core emits a warning when the callback is missing and a user reads the direction of a line, e.g. via /sys/kernel/debug/gpio.
--- drivers/tty/serial/sc16is7xx.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
drivers/tty/serial/sc16is7xx.c +12 -0
@@ -1308,6 +1308,17 @@ static void sc16is7xx_gpio_set(struct gp val ? BIT(offset) : 0); } +static int sc16is7xx_gpio_get_direction (struct gpio_chip *chip,unsigned int offset) +{ +struct sc16is7xx_port *s =gpiochip_get_data (chip); +struct uart_port *port = &s->p[0 ].port; +unsigned int val; + + val =sc16is7xx_port_read (port,SC16IS7XX_IODIR_REG ); + +return val &BIT (offset) ?GPIO_LINE_DIRECTION_OUT :GPIO_LINE_DIRECTION_IN ; +} +static int sc16is7xx_gpio_direction_input (struct gpio_chip *chip,unsigned offset) {
@@ -1385,6 +1396,7 @@ static int sc16is7xx_setup_gpio_chip(str s->gpio.parent = dev; s->gpio.label =dev_name (dev); s->gpio.init_valid_mask = sc16is7xx_gpio_init_valid_mask; + s->gpio.get_direction = sc16is7xx_gpio_get_direction; s->gpio.direction_input = sc16is7xx_gpio_direction_input; s->gpio.get = sc16is7xx_gpio_get; s->gpio.direction_output = sc16is7xx_gpio_direction_output;
Greg Kroah-Hartman
maintainerPATCH 288/484 serial: 8250_mid: Fix NULL function pointer dereference on DNV/ICX-D/SNR platforms
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiangshan Yi <[email protected]>
commit 7fb13fd7e9a59a37cd911efff83abe19e3ee029d upstream.
Commit b1b4efea05a5 ("serial: 8250_mid: Disable DMA for selected platforms") replaced the dnv_board setup and exit callbacks with PTR_IF(false, ...), which evaluates to NULL. However, the three call sites in mid8250_probe() and mid8250_remove() unconditionally dereference these function pointers without NULL checks, causing a NULL pointer dereference (kernel oops) on any Denverton (DNV), Ice Lake Xeon D (ICX-D/CDF), or Snowridge (SNR) platform.
Fix this by adding the missing NULL checks before calling the setup and exit callbacks.
--- drivers/tty/serial/8250/8250_mid.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-)
drivers/tty/serial/8250/8250_mid.c +9 -5
@@ -318,9 +318,11 @@ static int mid8250_probe(struct pci_devif (!uart.port.membase)return -ENOMEM ; - ret = mid->board->setup (mid, &uart.port); -if (ret) -return ret; +if (mid->board->setup) { + ret = mid->board->setup (mid, &uart.port); +if (ret) +return ret; + } ret =mid8250_dma_setup (mid, &uart);if (ret)
@@ -336,7 +338,8 @@ static int mid8250_probe(struct pci_devreturn 0 ; err: - mid->board->exit (mid); +if (mid->board->exit) + mid->board->exit (mid);return ret; }
@@ -346,7 +349,8 @@ static void mid8250_remove(struct pci_deserial8250_unregister_port (mid->line); - mid->board->exit (mid); +if (mid->board->exit) + mid->board->exit (mid); }static const struct mid8250_board pnw_board = {
Greg Kroah-Hartman
maintainerPATCH 289/484 mei: bus: access mei_device under device_lock on cleanup
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexander Usyskin <[email protected]>
commit f112ea910e554d58b4b39a4492b7d302f0f4204f upstream.
Fix couple of problems in mei_cl_bus_dev_release():
mei_cl_flush_queues() is running without lock. bus->file_list access after mei_dev_bus_put(bus) can become a use-after-free if this was the last reference to bus.
Protect queues cleanup and WARN traversal by device lock there to avoid the concurrent access problems.
Move WARN traversal before mei_dev_bus_put(bus).
This file uses bus variable name for mei_device, adjust code of mei_cl_bus_dev_release() to use bus variable too.
--- drivers/misc/mei/bus.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-)
drivers/misc/mei/bus.c +9 -7
@@ -4,6 +4,7 @@* Intel Management Engine Interface (Intel MEI) Linux driver */ +#include <linux/cleanup.h> #include <linux/module.h> #include <linux/device.h> #include <linux/kernel.h>
@@ -1269,15 +1270,16 @@ static void mei_dev_bus_put(struct mei_dstatic void mei_cl_bus_dev_release (struct device *dev) {struct mei_cl_device *cldev =to_mei_cl_device (dev); -struct mei_device *mdev = cldev->cl->dev; +struct mei_device *bus = cldev->bus;struct mei_cl *cl; -mei_cl_flush_queues (cldev->cl,NULL ); -mei_me_cl_put (cldev->me_cl); -mei_dev_bus_put (cldev->bus); - -list_for_each_entry (cl, &mdev->file_list, link) -WARN_ON (cl == cldev->cl); + scoped_guard(mutex, &bus->device_lock) { +mei_cl_flush_queues (cldev->cl,NULL ); +mei_me_cl_put (cldev->me_cl); +list_for_each_entry (cl, &bus->file_list, link) +WARN_ON (cl == cldev->cl); + } +mei_dev_bus_put (bus);kfree (cldev->cl);kfree (cldev);
Greg Kroah-Hartman
maintainerPATCH 290/484 intel_th: fix MSC output device reference leak
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guangshuo Li <[email protected]>
commit 761b785a0cfbce43761227bc42a7f984f31f8921 upstream.
intel_th_output_open() looks up the output device with bus_find_device_by_devt(), which returns the device with a reference that must be dropped after use.
commit 95fc36a234da ("intel_th: fix device leak on output open()") attempted to drop the reference from intel_th_output_release(). However, a successful open replaces file->f_op with the output driver file operations before returning, so close runs the output driver release callback instead.
For MSC outputs, close runs intel_th_msc_release(), which only removes the per-file iterator and does not drop the device reference taken by intel_th_output_open(). Consequently, every successful MSC output open leaks one device reference.
Drop the device reference from intel_th_msc_release(), which is the release path actually used for MSC output files. Remove the now-unused intel_th_output_release() callback from intel_th_output_fops.
--- drivers/hwtracing/intel_th/core.c | 10 ---------- drivers/hwtracing/intel_th/msu.c | 2 ++ 2 files changed, 2 insertions(+), 10 deletions(-)
drivers/hwtracing/intel_th/core.c +0 -10
@@ -843,18 +843,8 @@ out_put_device:return err; } -static int intel_th_output_release (struct inode *inode,struct file *file) -{ -struct intel_th_device *thdev = file->private_data; - -put_device (&thdev->dev); - -return 0 ; -} -static const struct file_operations intel_th_output_fops = { .open = intel_th_output_open, - .release = intel_th_output_release, .llseek = noop_llseek, };
drivers/hwtracing/intel_th/msu.c +2 -0
@@ -1474,8 +1474,10 @@ static int intel_th_msc_release(struct i {struct msc_iter *iter = file->private_data;struct msc *msc = iter->msc; +struct intel_th_device *thdev = msc->thdev;msc_iter_remove (iter, msc); +put_device (&thdev->dev);return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 291/484 tracing: Fix mmiotrace possible NULL dereferencing of hiter->dev
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Steven Rostedt <[email protected]>
commit 144f29e85702234b23d2a62abf723e6a17eb5427 upstream.
If the mmio_pipe_open() fails to find a PCI device, the hiter->dev will be assigned to NULL. The mmiotrace read() function dereferences the hiter->dev if hiter exists.
Change the test of the read to not only check hiter being NULL, but also the hiter->dev before dereferencing it.
--- kernel/trace/trace_mmiotrace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/trace/trace_mmiotrace.c +1 -1
@@ -146,7 +146,7 @@ static ssize_t mmio_read(struct trace_it goto print_out; } -if (!hiter) +if (!hiter || !hiter->dev)return 0 ;mmio_print_pcidev (s, hiter->dev);
Greg Kroah-Hartman
maintainerPATCH 292/484 tracing: Fix resource leak on mmiotrace trace_pipe close
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: deepakraog <[email protected]>
commit c1d87e724ae55e781b7cc7ccafb34d9e668582b2 upstream.
The mmiotrace tracer was added May 12th 2008. At that time, resources created in pipe_open() could not be freed because there was not pipe_close function pointer of the tracer. The pipe_close function pointer was added in December 7th, 2009, but the mmiotrace tracer was not updated.
mmio_pipe_open() allocates a header_iter and takes a pci_dev reference when trace_pipe is opened. mmio_close() frees them, but it was only wired to the tracer's .close callback.
tracing_release_pipe() invokes .pipe_close, not .close, when the trace_pipe file is released. As a result, closing trace_pipe with the mmiotrace tracer active leaked the header_iter allocation and left a stale pci_dev reference.
Set .pipe_close to mmio_close, matching how function_graph wires both callbacks to the same handler.
Note, if the trace_pipe is read to completion, it will clean up the resources, but if one were to run:
# head -n 1 /sys/kernel/tracing/trace_pipe VERSION 20070824
Over and over again, it would trigger a massive leak.
--- kernel/trace/trace_mmiotrace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/trace/trace_mmiotrace.c +1 -1
@@ -109,7 +109,6 @@ static void mmio_pipe_open(struct trace_ iter->private = hiter; } -/* XXX: This is not called when the pipe is closed! */ static void mmio_close (struct trace_iterator *iter) {struct header_iter *hiter = iter->private;
@@ -279,6 +278,7 @@ static struct tracer mmio_tracer __read_ .start = mmio_trace_start, .pipe_open = mmio_pipe_open, .close = mmio_close, + .pipe_close = mmio_close, .read = mmio_read, .print_line = mmio_print_line, .noboot = true,
Greg Kroah-Hartman
maintainerPATCH 293/484 tracing/eprobe: Fix exact system name matching in eprobe_dyn_event_match()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Masami Hiramatsu (Google) <[email protected]>
commit f418d68d71fd4a0a9cef92377bc8c4c3334b5b53 upstream.
eprobe_dyn_event_match() checks if the target event system in argv[0] matches ep->event_system using strncmp(ep->event_system, argv[0], len). However, if ep->event_system is longer than len (e.g. "eprobes" vs "ep/event"), strncmp() still returns 0 because the first len characters match.
Check that ep->event_system[len] is '\0' to ensure exact system name matching.
--- kernel/trace/trace_eprobe.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
kernel/trace/trace_eprobe.c +2 -1
@@ -168,7 +168,8 @@ static bool eprobe_dyn_event_match(constif (!slash)return false; -if (strncmp (ep->event_system, argv[0 ], slash - argv[0 ])) +if (strncmp (ep->event_system, argv[0 ], slash - argv[0 ]) || + ep->event_system[slash - argv[0 ]] !='\0' )return false;if (strcmp (ep->event_name, slash +1 ))return false;
Greg Kroah-Hartman
maintainerPATCH 294/484 tracing/probes: Avoid temporary buffer truncation in trace_probe_match_command_args()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Masami Hiramatsu (Google) <[email protected]>
commit 15f197856d68882af9416fc97516bb55079b7677 upstream.
In trace_probe_match_command_args(), a stack buffer buf[MAX_ARGSTR_LEN + 1] (256 bytes) is used to format "<name>=<comm>". However, since name can be up to 32 bytes (MAX_ARG_NAME_LEN) and comm up to 255 bytes (MAX_ARGSTR_LEN), the formatted string can exceed 256 bytes and get truncated by snprintf(), causing spurious argument matching failures.
Instead of formatting into a temporary buffer on stack, compare the argument name, the '=' delimiter, and the comm expression directly.
--- kernel/trace/trace_probe.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
kernel/trace/trace_probe.c +5 -4
@@ -2105,16 +2105,17 @@ int trace_probe_compare_arg_type(structbool trace_probe_match_command_args (struct trace_probe *tp,int argc,const char **argv) { -char buf[MAX_ARGSTR_LEN +1 ];int i;if (tp->nr_args < argc)return false;for (i =0 ; i < argc; i++) { -snprintf (buf,sizeof (buf),"%s=%s" , - tp->args[i].name, tp->args[i].comm); -if (strcmp (buf, argv[i])) +int len =strlen (tp->args[i].name); + +if (strncmp (argv[i], tp->args[i].name, len) || + argv[i][len] !='=' || +strcmp (argv[i] + len +1 , tp->args[i].comm))return false; }return true;
Greg Kroah-Hartman
maintainerPATCH 295/484 tracing/probes: Fix potential underflow in LEN_OR_ZERO macro
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Masami Hiramatsu (Google) <[email protected]>
commit 8ce20bfba48902e1382187cd1a852f7cf3a1e739 upstream.
In __set_print_fmt(), LEN_OR_ZERO is defined as (len ? len - pos : 0). If len is non-zero but smaller than pos, len - pos evaluates to a negative integer. When passed as a size argument to snprintf(), this negative value is cast to a large unsigned size_t, bypassing buffer size limits.
Ensure len > pos before subtracting to avoid integer underflow.
--- kernel/trace/trace_probe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/trace/trace_probe.c +1 -1
@@ -1779,7 +1779,7 @@ int traceprobe_update_arg(struct probe_a }/* When len=0, we just calculate the needed length */ -#define LEN_OR_ZERO (len ? len - pos : 0) +#define LEN_OR_ZERO (len > pos ? len - pos : 0)static int __set_print_fmt (struct trace_probe *tp,char *buf,int len,enum probe_print_type ptype) {
Greg Kroah-Hartman
maintainerPATCH 296/484 tracing/probes: Prevent out-of-bounds write in __trace_probe_log_err()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Masami Hiramatsu (Google) <[email protected]>
commit a9d6fb284039a5d3858a1d9f9a0d7e46cfb7c2d4 upstream.
If trace_probe_log.argc is 0 in __trace_probe_log_err(), the loop constructing the command string will not execute and p will remain equal to command. Writing to *(p - 1) will cause an out-of-bounds access before command. This should not happen, but better to be treated.
Reject if trace_probe_log.argc is 0.
--- kernel/trace/trace_probe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/trace/trace_probe.c +1 -1
@@ -186,7 +186,7 @@ void __trace_probe_log_err(int offset, ilockdep_assert_held (&dyn_event_ops_mutex); -if (!trace_probe_log.argv) +if (!trace_probe_log.argv || !trace_probe_log.argc)return ;/* Recalculate the length and allocate buffer */
Greg Kroah-Hartman
maintainerPATCH 297/484 arm64: syscall: Ensure saved x0 is kept in-sync with tracer updates
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Will Deacon <[email protected]>
commit e057b94772328221405b067c3a85fe479b915dc8 upstream.
When seccomp support was originally added to arm64 in a1ae65b21941 ("arm64: add seccomp support"), seccomp was erroneously called _before_ the ptrace syscall-enter-stop and therefore the tracer could trivially manipulate the syscall register state after the seccomp check had passed. This was subsequently fixed in a5cd110cb836 ("arm64/ptrace: run seccomp after ptrace") by moving the seccomp check after the tracer has run. Unfortunately, a decade later, that fix has been reported to be incomplete.
On arm64, both the first argument to a syscall and its eventual return value are allocated to register x0. In order to facilitate syscall restarting and querying of syscall arguments on the syscall exit path, the original value of x0 is stashed in 'struct pt_regs::orig_x0' early during the syscall entry path and is returned for the first argument by syscall_get_arguments(). Unlike 32-bit Arm, this stashed value is not directly exposed via ptrace() and so changes to register x0 made by the tracer on a syscall-enter-stop are not reflected in 'orig_x0'. This means that seccomp, syscall tracepoints and audit can observe a stale value for the register compared to the argument that will be observed by the actual syscall.
Re-sync 'orig_x0' from x0 on the syscall entry path following a potential ptrace stop (i.e. PTRACE_EVENTMSG_SYSCALL_ENTRY or SECCOMP_RET_TRACE). This behaviour is limited to native tasks (because compat tasks expose 'orig_r0' to ptrace) where the syscall is not being skipped (because x0 is updated to hold the return value of -ENOSYS in that case).
--- arch/arm64/kernel/ptrace.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+)
arch/arm64/kernel/ptrace.c +29 -0
@@ -2191,6 +2191,21 @@ static void report_syscall(struct pt_reg } } +static void update_syscall_orig_x0_after_ptrace (struct pt_regs *regs) +{ +/* +* Keep orig_x0 authoritative so that seccomp (via +* syscall_get_arguments()), audit and the restart path all see the same +* first argument the syscall is dispatched with, even if it has been +* updated by a tracer. Skip this for NO_SYSCALL (set either by the user +* or the tracer), as regs[0] holds the return value (see the comment in +* el0_svc_common()) and can be unwound using syscall_rollback(). +* For compat tasks, orig_r0 is provided directly through GPR index 17. +*/ +if (!is_compat_task () && regs->syscallno !=NO_SYSCALL ) + regs->orig_x0 = regs->regs[0 ]; +} +int syscall_trace_enter (struct pt_regs *regs) {unsigned long flags =read_thread_flags ();
@@ -2199,12 +2214,26 @@ int syscall_trace_enter(struct pt_regs *report_syscall (regs,PTRACE_SYSCALL_ENTER );if (flags & _TIF_SYSCALL_EMU)return NO_SYSCALL ; + +/* +* Ensure ptrace changes to x0 during a regular +* syscall-enter-stop (PTRACE_SYSCALL) are visible to +* subsequent seccomp checks, tracepoints and audit. +*/ +update_syscall_orig_x0_after_ptrace (regs); }/* Do the secure computing after ptrace; failures should be fast. */ if (secure_computing () ==-1 )return NO_SYSCALL ; +/* +* Ensure tracer changes to x0 during seccomp ptrace exit +* processing (SECCOMP_RET_TRACE) are visible to tracepoints and +* audit. +*/ +update_syscall_orig_x0_after_ptrace (regs); +if (test_thread_flag (TIF_SYSCALL_TRACEPOINT ))trace_sys_enter (regs, regs->syscallno);
Greg Kroah-Hartman
maintainerPATCH 298/484 Revert "arm64: syscall: Ensure saved x0 is kept in-sync with tracer updates"
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Will Deacon <[email protected]>
commit 26b483d52417253d88a3a01262ac85914a7aec8e upstream.
This reverts commit e057b94772328221405b067c3a85fe479b915dc8.
Sashiko points out that updating 'orig_x0' after secure_computing() has returned is too late to handle the case where a seccomp filter is re-evaluated after initially returning SECCOMP_RET_TRACE. This means that a tracer can manipulate the first argument of the syscall behind seccomp's back.
For now, revert the initial fix and we'll have another crack at it soon. Since the incorrect fix was cc'd to stable, do the same here with an appropriate fixes tag.
--- arch/arm64/kernel/ptrace.c | 29 ----------------------------- 1 file changed, 29 deletions(-)
arch/arm64/kernel/ptrace.c +0 -29
@@ -2191,21 +2191,6 @@ static void report_syscall(struct pt_reg } } -static void update_syscall_orig_x0_after_ptrace (struct pt_regs *regs) -{ -/* -* Keep orig_x0 authoritative so that seccomp (via -* syscall_get_arguments()), audit and the restart path all see the same -* first argument the syscall is dispatched with, even if it has been -* updated by a tracer. Skip this for NO_SYSCALL (set either by the user -* or the tracer), as regs[0] holds the return value (see the comment in -* el0_svc_common()) and can be unwound using syscall_rollback(). -* For compat tasks, orig_r0 is provided directly through GPR index 17. -*/ -if (!is_compat_task () && regs->syscallno !=NO_SYSCALL ) - regs->orig_x0 = regs->regs[0 ]; -} -int syscall_trace_enter (struct pt_regs *regs) {unsigned long flags =read_thread_flags ();
@@ -2214,26 +2199,12 @@ int syscall_trace_enter(struct pt_regs *report_syscall (regs,PTRACE_SYSCALL_ENTER );if (flags & _TIF_SYSCALL_EMU)return NO_SYSCALL ; - -/* -* Ensure ptrace changes to x0 during a regular -* syscall-enter-stop (PTRACE_SYSCALL) are visible to -* subsequent seccomp checks, tracepoints and audit. -*/ -update_syscall_orig_x0_after_ptrace (regs); }/* Do the secure computing after ptrace; failures should be fast. */ if (secure_computing () ==-1 )return NO_SYSCALL ; -/* -* Ensure tracer changes to x0 during seccomp ptrace exit -* processing (SECCOMP_RET_TRACE) are visible to tracepoints and -* audit. -*/ -update_syscall_orig_x0_after_ptrace (regs); -if (test_thread_flag (TIF_SYSCALL_TRACEPOINT ))trace_sys_enter (regs, regs->syscallno);
Greg Kroah-Hartman
maintainerPATCH 299/484 mptcp: decrement subflows counter on failed passive join
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chenguang Zhao <[email protected]>
commit f3ca0ee2cc308e33896536789cbc5f3a12ca7b30 upstream.
mptcp_pm_allow_new_subflow() increments extra_subflows before __mptcp_finish_join() on the passive MP_JOIN path.
In case of race conditions, the subflow is dropped without calling mptcp_close_ssk(), so the counter is not rolled back.
Call mptcp_pm_close_subflow() when the join completion fails to decrement the subflows counter.
--- net/mptcp/protocol.c | 1 + 1 file changed, 1 insertion(+)
net/mptcp/protocol.c +1 -0
@@ -3786,6 +3786,7 @@ bool mptcp_finish_join(struct sock *ssk)mptcp_data_unlock (parent);if (!ret) { +mptcp_pm_close_subflow (msk); err_prohibited: subflow->reset_reason =MPTCP_RST_EPROHIBIT ;return false;
Greg Kroah-Hartman
maintainerPATCH 300/484 mptcp: only set DATA_FIN when a mapping is present
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
commit b2ff91b752b0d85e8815e7f44fd85205c4268094 upstream.
mptcp_get_options() clears only the status group of struct mptcp_options_received; data_seq, subflow_seq and data_len are filled in by mptcp_parse_option() exclusively inside the DSS mapping block, which runs only when the DSS M (mapping present) bit is set.
A peer can send a DSS option with the DATA_FIN flag set but the mapping bit clear. The parser then records mp_opt->data_fin while leaving data_len and data_seq uninitialized. For a zero-length segment mptcp_incoming_options() evaluates
if (mp_opt.data_fin && mp_opt.data_len == 1 && mptcp_update_rcv_data_fin(msk, mp_opt.data_seq, mp_opt.dsn64))
which reads the uninitialized data_len and data_seq; KMSAN reports an uninit-value in mptcp_incoming_options(). The stale data_seq can also be fed into the receive-side DATA_FIN sequence tracking.
Record the DATA_FIN flag only when the DSS option carries a mapping, so data_fin is never set without data_seq and data_len also being present. data_fin is part of the status group that mptcp_get_options() clears up front, so on the no-map path it stays zero and the zero-length DATA_FIN branch is simply skipped. A DATA_FIN is always transmitted together with a mapping (mptcp_write_data_fin() sets use_map along with data_seq and data_len), so legitimate DATA_FIN handling is unaffected.
Move the pr_debug() that logs the parsed DSS flags below the mapping block, so it reports the final data_fin value instead of the stale one it would otherwise print before the assignment.
--- net/mptcp/options.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
net/mptcp/options.c +6 -6
@@ -157,17 +157,11 @@ static void mptcp_parse_option(const str ptr++; flags = (*ptr++) &MPTCP_DSS_FLAG_MASK ; - mp_opt->data_fin = (flags &MPTCP_DSS_DATA_FIN ) !=0 ; mp_opt->dsn64 = (flags &MPTCP_DSS_DSN64 ) !=0 ; mp_opt->use_map = (flags &MPTCP_DSS_HAS_MAP ) !=0 ; mp_opt->ack64 = (flags &MPTCP_DSS_ACK64 ) !=0 ; mp_opt->use_ack = (flags &MPTCP_DSS_HAS_ACK ); -pr_debug ("data_fin=%d dsn64=%d use_map=%d ack64=%d use_ack=%d\n" , - mp_opt->data_fin, mp_opt->dsn64, - mp_opt->use_map, mp_opt->ack64, - mp_opt->use_ack); - expected_opsize =TCPOLEN_MPTCP_DSS_BASE ;if (mp_opt->use_ack) {
@@ -178,12 +172,18 @@ static void mptcp_parse_option(const str }if (mp_opt->use_map) { + mp_opt->data_fin = (flags &MPTCP_DSS_DATA_FIN ) !=0 ;if (mp_opt->dsn64) expected_opsize +=TCPOLEN_MPTCP_DSS_MAP64 ;else expected_opsize +=TCPOLEN_MPTCP_DSS_MAP32 ; } +pr_debug ("data_fin=%d dsn64=%d use_map=%d ack64=%d use_ack=%d\n" , + mp_opt->data_fin, mp_opt->dsn64, + mp_opt->use_map, mp_opt->ack64, + mp_opt->use_ack); +/* Always parse any csum presence combination, we will enforce * RFC 8684 Section 3.3.0 checks later in subflow_data_ready */
Greg Kroah-Hartman
maintainerPATCH 301/484 sctp: dont free the ASCONFs own transport in DEL-IP processing
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jun Yang <[email protected]>
commit 9b2854f86f0b56e9027d68e7a3fc909d1a9b566f upstream.
sctp_process_asconf() caches the transport the ASCONF chunk is processed against in asconf->transport (== chunk->transport, set once in sctp_rcv()). For an ASCONF located through its Address Parameter by __sctp_rcv_asconf_lookup(), that cached transport corresponds to the Address Parameter, which need not be the packet's source address.
sctp_process_asconf_param() rejects a DEL-IP for the packet source address (ADDIP D8, SCTP_ERROR_DEL_SRC_IP), but nothing protects asconf->transport. A single ASCONF can therefore carry, in order:
[Address Parameter L] [DEL-IP L] [DEL-IP 0.0.0.0]
where L differs from the source. The DEL-IP for L passes the D8 check and calls sctp_assoc_rm_peer() on the transport that asconf->transport still points at, freeing it (RCU-deferred). The following wildcard DEL-IP then reuses the now-dangling asconf->transport in sctp_assoc_set_primary() and sctp_assoc_del_nonprimary_peers(): set_primary() dereferences the freed transport (->ipaddr, ->state) and plants the dangling pointer into asoc->peer.primary_path / active_path, and del_nonprimary_peers(), keeping only the pointer that is no longer on the list, removes every real transport, leaving the association with a transport_count of 0 and primary_path/active_path pointing at freed memory.
Reject a DEL-IP that targets the transport the ASCONF is being processed against, mirroring the existing source-address guard, so the wildcard branch can never reuse a freed transport.
--- net/sctp/sm_make_chunk.c | 6 ++++++ 1 file changed, 6 insertions(+)
net/sctp/sm_make_chunk.c +6 -0
@@ -3171,6 +3171,12 @@ static __be16 sctp_process_asconf_param(if (!peer)return SCTP_ERROR_DNS_FAILED ; +/* Don't free asconf->transport; a later wildcard DEL-IP +* parameter reuses it. +*/ +if (peer == asconf->transport) +return SCTP_ERROR_REQ_REFUSED ; +sctp_assoc_rm_peer (asoc, peer);break ;case SCTP_PARAM_SET_PRIMARY :
Greg Kroah-Hartman
maintainerPATCH 302/484 ceph: add owner/capability checks for CEPH_IOC_SET_LAYOUT*
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Max Kellermann <[email protected]>
commit cee38bbf5556a8e0a232ccae41649580827d7806 upstream.
These permission checks were already missing in the initial impementation of these ioctls. This Ceph allows any user who owns a file descriptor to manipulate the layout of any file, even if they don't have write permissions.
It might be a good idea to guard other ioctls with permission checks as well or even disallow regular users (even if they own the file) to manipulate layout settings completely, as this may be abused to DoS the Ceph servers, but right now, I find it most urgent to have setter checks at all.
--- fs/ceph/ioctl.c | 6 ++++++ 1 file changed, 6 insertions(+)
fs/ceph/ioctl.c +6 -0
@@ -72,6 +72,9 @@ static long ceph_ioctl_set_layout(structstruct ceph_ioctl_layout nl;int err; +if (!inode_owner_or_capable (&nop_mnt_idmap, inode)) +return -EACCES ; +if (copy_from_user (&l, arg,sizeof (l)))return -EFAULT ;
@@ -142,6 +145,9 @@ static long ceph_ioctl_set_layout_policyint err;struct ceph_mds_client *mdsc =ceph_sb_to_fs_client (inode->i_sb)->mdsc; +if (!inode_owner_or_capable (&nop_mnt_idmap, inode)) +return -EACCES ; +/* copy and validate */ if (copy_from_user (&l, arg,sizeof (l)))return -EFAULT ;
Re: [PATCH 6.6 302/484] ceph: add owner/capability checks for CEPH_IOC_SET_LAYOUT*
Quoted 45 lines · Greg Kroah-Hartman
6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Max Kellermann <[email protected]>
commit
cee38bbf5556a8e0a232ccae41649580827d7806upstream.These permission checks were already missing in the initial impementation of these ioctls. This Ceph allows any user who owns a file descriptor to manipulate the layout of any file, even if they don't have write permissions.
It might be a good idea to guard other ioctls with permission checks as well or even disallow regular users (even if they own the file) to manipulate layout settings completely, as this may be abused to DoS the Ceph servers, but right now, I find it most urgent to have setter checks at all.
Fixes 8f4e91dee2a2 ("ceph: ioctls")Signed-off-by Max Kellermann <[email protected]>Reviewed-by Xiubo Li <[email protected]>Signed-off-by Ilya Dryomov <[email protected]>Signed-off-by Greg Kroah-Hartman <[email protected]>--- fs/ceph/ioctl.c | 6 ++++++ 1 file changed, 6 insertions(+)fs/ceph/ioctl.c +3
-0@@ -72,6 +72,9 @@ static long ceph_ioctl_set_layout(structstruct ceph_ioctl_layout nl;int err; +if (!inode_owner_or_capable (&nop_mnt_idmap, inode)) +return -EACCES ; +if (copy_from_user (&l, arg,sizeof (l)))return -EFAULT ;
fs/ceph/ioctl.c static long ceph_ioctl_set_layout_policy · line 153
Quoted 9 lines
fs/ceph/ioctl.c +3
-0@@ -142,6 +145,9 @@ static long ceph_ioctl_set_layout_policyint err;struct ceph_mds_client *mdsc =ceph_sb_to_fs_client (inode->i_sb)->mdsc; +if (!inode_owner_or_capable (&nop_mnt_idmap, inode)) +return -EACCES ; +/* copy and validate */ if (copy_from_user (&l, arg,sizeof (l)))return -EFAULT ;
Hi Greg,
There is a concern about the correctness of this patch, please drop it for now.
Thanks,
Ilya
Greg Kroah-Hartman
maintainerPATCH 303/484 ceph: fix pre-auth out-of-bounds read on snaptrace in ceph_handle_caps()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bryam Vargas <[email protected]>
commit 4dbc71bcaf9a30abf3920a4e2cc4ed33bba78c02 upstream.
ceph_handle_caps() reads snap_trace_len from the wire-format ceph_mds_caps header and uses it unconditionally to build a fake end pointer (snaptrace + snaptrace_len) that is later handed to ceph_update_snap_trace() in the CEPH_CAP_OP_IMPORT case:
snaptrace = h + 1;
snaptrace_len = le32_to_cpu(h->snap_trace_len);
p = snaptrace + snaptrace_len;
...
case CEPH_CAP_OP_IMPORT:
if (snaptrace_len) {
...
if (ceph_update_snap_trace(mdsc, snaptrace,
snaptrace + snaptrace_len,
false, &realm)) { ... }ceph_update_snap_trace() then decodes a struct ceph_mds_snap_realm from snaptrace using ceph_decode_need(&p, e, sizeof(*ri), bad) with the attacker-supplied fake end e == snaptrace + snaptrace_len. With snaptrace_len == 0xFFFFFFFF the bound check is trivially satisfied, ri = p reads sizeof(struct ceph_mds_snap_realm) past the legitimate msg->front buffer, and ri->num_snaps / ri->num_prior_parent_snaps then drive further out-of-bounds reads of the encoded snap arrays.
The eleven msg_version >= 2 .. msg_version >= 12 decoder blocks above the op switch each catch this OOB through their ceph_decode_*_safe() / ceph_decode_need() helpers, but they sit behind a hdr.version-gated if, so a malicious or compromised MDS that sets msg->hdr.version = 1 reaches the IMPORT path with no version-gated decoder having validated snap_trace_len. The shape has been present since ceph_handle_caps() was introduced.
Validate snap_trace_len against the message front buffer before consuming it, using the canonical ceph_decode_need() / ceph_has_room() helper. The helper bounds the length with subtraction (n <= end - p, guarded by end >= p) rather than pointer addition, so it is wrap-safe for the attacker-controlled u32 length on 32-bit builds where p + snap_trace_len could overflow the address space. This matches the rest of the ceph decode path (e.g. the pool_ns_len check a few lines below), and the existing goto bad cleanup already covers this exit path.
--- fs/ceph/caps.c | 1 + 1 file changed, 1 insertion(+)
fs/ceph/caps.c +1 -0
@@ -4267,6 +4267,7 @@ void ceph_handle_caps(struct ceph_mds_se snaptrace = h +1 ; snaptrace_len =le32_to_cpu (h->snap_trace_len); +ceph_decode_need (&snaptrace, end, snaptrace_len, bad); p = snaptrace + snaptrace_len;if (msg_version >=2 ) {
Greg Kroah-Hartman
maintainerPATCH 304/484 libceph: bound get_version reply decode to front len
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Douya Le <[email protected]>
commit d3c32939fa0e3ee9b883b9a0fd1972c5c444e3d0 upstream.
handle_get_version_reply() uses msg->front_alloc_len as the decode boundary for MON_GET_VERSION_REPLY. That is the size of the reused reply buffer, not the number of bytes actually received.
A truncated reply can therefore pass ceph_decode_need() and decode the second u64 from stale tail bytes left in the buffer by an earlier message, causing an uninitialized memory read.
Use msg->front.iov_len as the receive-side decode boundary, matching other libceph reply handlers and limiting decoding to the bytes that were actually read from the wire.
--- net/ceph/mon_client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/ceph/mon_client.c +1 -1
@@ -821,7 +821,7 @@ static void handle_get_version_reply(strstruct ceph_mon_generic_request *req;u64 tid =le64_to_cpu (msg->hdr.tid);void *p = msg->front.iov_base; -void *end = p + msg->front_alloc_len; +void *const end = p + msg->front.iov_len;u64 handle;dout ("%s msg %p tid %llu\n" , __func__, msg, tid);
Greg Kroah-Hartman
maintainerPATCH 305/484 libceph: Fix multiplication overflow in decode_new_up_state_weight()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Raphael Zimmer <[email protected]>
commit 98917a499ec7064c14fc56d180a4fd636fc2784c upstream.
If a message of type CEPH_MSG_OSD_MAP contains a (maliciously) corrupted osdmap, out-of-bounds memory accesses may occur in decode_new_up_state_weight(). This happens because the bounds check for the new_state part is based on calculating its length depending on a len value read from the incoming message. This calculation may overflow leading to an incorrect bounds check. Subsequently, out-of-bounds reads may occur when decoding this part.
This patch switches the multiplication to use check_mul_overflow() to abort processing the osdmap if an overflow occurred. Therefore, osdmaps/messages containing large values for len that result in a multiplication overflow are treated as invalid.
[ idryomov: rename new_state_len -> new_state_item_size, formatting ]
--- net/ceph/osdmap.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
net/ceph/osdmap.c +4 -1
@@ -1844,6 +1844,8 @@ static int decode_new_up_state_weight(vovoid *new_up_client;void *new_state;void *new_weight_end; +const u32 new_state_item_size = +sizeof (u32) + (struct_v >=5 ?sizeof (u32) :sizeof (u8));u32 len;int ret;int i;
@@ -1864,7 +1866,8 @@ static int decode_new_up_state_weight(vo new_state = *p;ceph_decode_32_safe (p, end, len, e_inval); - len *=sizeof (u32) + (struct_v >=5 ?sizeof (u32) :sizeof (u8)); +if (check_mul_overflow (len, new_state_item_size, &len)) + goto e_inval;ceph_decode_need (p, end, len, e_inval); *p += len;
Greg Kroah-Hartman
maintainerPATCH 306/484 libceph: guard missing CRUSH type name lookup
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhao Zhang <[email protected]>
commit bbeae12fda3384a90fbebc8a19ba9d33f85b5361 upstream.
Localized read selection can walk a parent bucket whose name exists in the CRUSH map while its type has no matching entry in type_names. get_immediate_parent() then dereferences a NULL type_cn and passes an invalid pointer into strcmp(), causing a null-ptr-deref.
Skip such malformed parent buckets unless both the bucket name and type name metadata are present. This keeps malformed hierarchy data from crashing locality lookup and safely falls back to "not local".
[ idryomov: add WARN_ON_ONCE ]
--- net/ceph/osdmap.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
net/ceph/osdmap.c +4 -1
@@ -3060,8 +3060,11 @@ static int get_immediate_parent(struct cif (b->items[j] != id)continue ; - *parent_type_id = b->type; type_cn =lookup_crush_name (&c->type_names, b->type); +if (WARN_ON_ONCE (!type_cn)) +continue ; + + *parent_type_id = b->type; parent_loc->cl_type_name = type_cn->cn_name; parent_loc->cl_name = cn->cn_name;return b->id;
Greg Kroah-Hartman
maintainerPATCH 307/484 libceph: refresh auth->authorizer_buf{,_len} after authorizer update
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shuangpeng Bai <[email protected]>
commit 937d61f86d377a3aa578adae7a3dfcecdddf9d89 upstream.
ceph_x_create_authorizer() caches au->buf->vec.iov_base and au->buf->vec.iov_len in struct ceph_auth_handshake. These cached values are then used by the messenger connect code when sending the authorizer.
ceph_x_update_authorizer() can rebuild the authorizer when a newer service ticket is available. If the rebuilt authorizer no longer fits in the existing buffer, ceph_x_build_authorizer() drops its reference to au->buf and allocates a new one. If this is the final reference, ceph_buffer_put() frees the old ceph_buffer and its vec.iov_base, but auth->authorizer_buf still points at that freed memory.
A subsequent msgr1 reconnect can therefore queue the stale pointer and trigger a KASAN slab-use-after-free in _copy_from_iter() while tcp_sendmsg() copies the authorizer.
Refresh auth->authorizer_buf and auth->authorizer_buf_len after a successful authorizer rebuild so the messenger sends the current buffer.
--- net/ceph/auth_x.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
net/ceph/auth_x.c +8 -1
@@ -781,9 +781,16 @@ static int ceph_x_update_authorizer( au = (struct ceph_x_authorizer *)auth->authorizer;if (au->secret_id < th->secret_id) { +int ret; +dout ("ceph_x_update_authorizer service %u secret %llu < %llu\n" , au->service, au->secret_id, th->secret_id); -return ceph_x_build_authorizer (ac, th, au); + ret =ceph_x_build_authorizer (ac, th, au); +if (ret) +return ret; + + auth->authorizer_buf = au->buf->vec.iov_base; + auth->authorizer_buf_len = au->buf->vec.iov_len; }return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 308/484 libceph: Reject monmaps advertising zero monitors
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Raphael Zimmer <[email protected]>
commit 40480eee361ed9676b3f844d532ac28b47251634 upstream.
A message of type CEPH_MSG_MON_MAP contains a monmap that is sent from a monitor to the client. This monmap contains information about the existing monitors in the cluster. Currently, a monmap indicating that there are zero monitors in the cluster is treated as valid. However, it is impossible to have zero monitors in the cluster and still receive a valid monmap from a monitor. Therefore, such a monmap must be corrupted and should be treated as invalid. Furthermore, a monmap with a monitor count of zero can subsequently crash the client when attempting to open a session with a monitor in __open_session(). This happens because the "BUG_ON(monc->monmap->num_mon < 1)" assertion in pick_new_mon() is triggered.
This patch extends a check in ceph_monmap_decode() to also reject arriving mon_maps with num_mon == 0 rather than only with num_mon > CEPH_MAX_MON.
[ idryomov: drop "log output for unusual values of num_mon" part ]
--- net/ceph/mon_client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/ceph/mon_client.c +1 -1
@@ -114,7 +114,7 @@ static struct ceph_monmap *ceph_monmap_ddout ("%s fsid %pU epoch %u num_mon %u\n" , __func__, &fsid, epoch, num_mon); -if (num_mon >CEPH_MAX_MON ) +if (num_mon ==0 || num_mon >CEPH_MAX_MON ) goto e_inval; monmap =kmalloc (struct_size (monmap, mon_inst, num_mon),GFP_NOIO );
Greg Kroah-Hartman
maintainerPATCH 309/484 libceph: reject zero bucket types in crush_decode
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Douya Le <[email protected]>
commit 05f90284223381005d6bcddab3fda4a97f9c3401 upstream.
CRUSH bucket type 0 is reserved for devices. The mapper relies on that invariant and uses type 0 to identify leaf devices.
If crush_decode() accepts a bucket with type 0, a malformed CRUSH map can make the mapper treat a negative bucket ID as a device and pass it to is_out(), which then indexes the OSD weight array with a negative value.
Reject zero bucket types while decoding the CRUSH map so the invalid state never reaches the mapper.
--- net/ceph/osdmap.c | 2 ++ 1 file changed, 2 insertions(+)
net/ceph/osdmap.c +2 -0
@@ -520,6 +520,8 @@ static struct crush_map *crush_decode(voceph_decode_need (p, end,4 *sizeof (u32), bad); b->id =ceph_decode_32 (p); b->type =ceph_decode_16 (p); +if (b->type ==0 ) + goto bad; b->alg =ceph_decode_8 (p);if (b->alg != alg) { b->alg =0 ;
Greg Kroah-Hartman
maintainerPATCH 310/484 libceph: remove debugfs files before client teardown
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Douya Le <[email protected]>
commit e4c804726c4afce3ba648b982d564f6af2cfa328 upstream.
ceph_destroy_client() tears down the monitor client before removing the per-client debugfs files. A concurrent read of the monmap debugfs file can enter monmap_show() after ceph_monc_stop() has freed monc->monmap, triggering a use-after-free.
Remove the debugfs files before stopping the OSD and monitor clients. debugfs_remove() drains active handlers and prevents new accesses, so the debugfs callbacks can no longer race the rest of client teardown.
--- net/ceph/ceph_common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
net/ceph/ceph_common.c +2 -2
@@ -763,13 +763,13 @@ void ceph_destroy_client(struct ceph_cliatomic_set (&client->msgr.stopping,1 ); +ceph_debugfs_client_cleanup (client); +/* unmount */ ceph_osdc_stop (&client->osdc);ceph_monc_stop (&client->monc);ceph_messenger_fini (&client->msgr); -ceph_debugfs_client_cleanup (client); -ceph_destroy_options (client->options);kfree (client);
Greg Kroah-Hartman
maintainerPATCH 311/484 binfmt_elf_fdpic: only honour the first PT_INTERP
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
commit 3349ef6a366a61d631f6a263d12cea240957719d upstream.
The program header scan handles PT_INTERP from a switch nested in the scan loop, so its break leaves the switch and not the loop. A binary carrying more than one PT_INTERP runs the case again and overwrites both interpreter_name and interpreter. The previous name allocation leaks and so does the previous interpreter reference, along with the write denial open_exec() took on it. The denial is never released, so the file stays unwritable for as long as the system runs.
An unprivileged caller reaches this with a crafted binary and repeats it at will. binfmt_elf stops at the first PT_INTERP. Do the same here.
The flaw dates back to the driver's introduction in the pre-git history tree introduced in v2.6.11 by 91808d6ebe39 ("[PATCH] FRV: Add FDPIC ELF binary format driver").
--- fs/binfmt_elf_fdpic.c | 4 ++++ 1 file changed, 4 insertions(+)
fs/binfmt_elf_fdpic.c +4 -0
@@ -231,6 +231,10 @@ static int load_elf_fdpic_binary(structfor (i =0 ; i < exec_params.hdr.e_phnum; i++, phdr++) {switch (phdr->p_type) {case PT_INTERP : +/* elf ABI allows only one interpreter */ +if (interpreter_name) +continue ; + retval = -ENOMEM ;if (phdr->p_filesz >PATH_MAX ) goto error;
Greg Kroah-Hartman
maintainerPATCH 312/484 fs: preserve ACL_DONT_CACHE state in forget_cached_acl()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Amir Goldstein <[email protected]>
commit 4b9a5458d02e214ef2b384124ca626e3e381d778 upstream.
The ACL_DONT_CACHE state is meant to be a constant state for the inode for filesystems that want to opt out of posix acl caching.
Commit facd61053cff1 ("fuse: fixes after adapting to new posix acl api") used this facility to opt out of posix acl caching for fuse inodes with fuse server that does not negotiate FUSE_POSIX_ACL (fc->posix_acl).
The commit also takes care to gate the forget_all_cached_acls() call in fuse_set_acl() on fc->posix_acl because there is no need for it, but there are other placed in fuse code which call forget_all_cached_acls() unconditional to fc->posix_acl and those cause the loss of the ACL_DONT_CACHE state.
This is not only a functional bug. Properly timed, a get_acl() from this fuse filesystem can return a stale cached value, as was observed in tests, because set_acl() does not invalidate the unintentional acl cache.
We could fix this in fuse, but it actually makes no sense for the vfs helper forget_cached_acl() to invalidate the ACL_DONT_CACHE state, so let it not do that to fix fuse and future users of ACL_DONT_CACHE.
--- fs/posix_acl.c | 7 +++++++ 1 file changed, 7 insertions(+)
fs/posix_acl.c +7 -0
@@ -94,6 +94,13 @@ static void __forget_cached_acl(struct p {struct posix_acl *old; +/* +* ACL_DONT_CACHE is expected to be a "const" value and xchg it with +* ACL_NOT_CACHED would enable acl caching for the inode - +* clearly not what the caller has intended. +*/ +if (READ_ONCE (*p) ==ACL_DONT_CACHE ) +return ; old =xchg (p,ACL_NOT_CACHED );if (!is_uncached_acl (old))posix_acl_release (old);
Greg Kroah-Hartman
maintainerPATCH 313/484 fscrypt: Add missing superblock check in find_or_insert_direct_key()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Biggers <[email protected]>
commit b5fa40226e71c17847b9ff2816c6ca4133d0d994 upstream.
The legacy 'fscrypt_direct_keys' table caches master keys that are used by v1 encryption policies that have FSCRYPT_POLICY_FLAG_DIRECT_KEY. It's just a global table for all filesystems (since the keys can be provided by the legacy process-subscribed keyrings mechanism, which makes it difficult to reuse super_block::s_master_keys).
The entries in it ('struct fscrypt_direct_key') do contain a super_block pointer, though, for passing to fscrypt_destroy_inline_crypt_key() when the last inode that references the key is evicted.
However, when finding the fscrypt_direct_key for an inode, we weren't actually comparing the super_block pointer. As a result, inodes with different super_blocks could point to the same fscrypt_direct_key. That could extend the lifetime of a fscrypt_direct_key beyond the super_block it points to, causing a use-after-free later.
Fix this by creating distinct fscrypt_direct_key structs for distinct super_block structs.
Note that this problem doesn't exist in the v2 policy equivalent ("per-mode keys"), since the data structures there are per super_block.
--- fs/crypto/keysetup_v1.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
fs/crypto/keysetup_v1.c +7 -1
@@ -199,13 +199,19 @@ find_or_insert_direct_key(struct fscryptif (memcmp (ci->ci_policy.v1.master_key_descriptor, dk->dk_descriptor,FSCRYPT_KEY_DESCRIPTOR_SIZE ) !=0 )continue ; +/* The sb is used at eviction time, so it must be the same. */ +if (ci->ci_inode->i_sb != dk->dk_sb) +continue ;if (ci->ci_mode != dk->dk_mode)continue ;if (!fscrypt_is_key_prepared (&dk->dk_key, ci))continue ;if (crypto_memneq (raw_key, dk->dk_raw, ci->ci_mode->keysize))continue ; -/* using existing tfm with same (descriptor, mode, raw_key) */ +/* +* Use an existing prepared key with the same (descriptor, sb, +* mode, inlinecrypt, raw_key) combination. +*/ refcount_inc (&dk->dk_refcount);spin_unlock (&fscrypt_direct_keys_lock);free_direct_key (to_insert);
Greg Kroah-Hartman
maintainerPATCH 314/484 ftrace: Add global mutex to serialize trace_parser access
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tengda Wu <[email protected]>
commit 7720b63bcef3f54c7fe288774b720a227d54a306 upstream.
In ftrace, the trace_parser structure is allocated and initialized when a trace file is opened, and is subsequently used across write and release handlers to parse user input.
The affected handler paths and their specific functions are:
- Open paths: ftrace_regex_open(), ftrace_graph_open()
- Write paths: ftrace_regex_write(), ftrace_graph_write()
- Release paths: ftrace_regex_release(), ftrace_graph_release()
If userspace opens a trace file descriptor and shares it across multiple threads, concurrent write calls will race on the parser's internal state, specifically the 'idx', 'cont', and 'buffer' fields, leading to corrupted input or undefined behavior.
Fix this by adding a global mutex, parser_lock, to serialize all access to trace_parser across write and release paths, preventing concurrent corruption of parser state.
--- kernel/trace/ftrace.c | 13 +++++++++++++ 1 file changed, 13 insertions(+)
kernel/trace/ftrace.c +13 -0
@@ -1068,6 +1068,12 @@ struct ftrace_ops global_ops = { };/* +* parser_lock - Protects trace_parser state against concurrent operations. +* Held across trace_get_user() and subsequent buffer parsing to prevent races. +*/ +static DEFINE_MUTEX (parser_lock); + +/* * Used by the stack unwinder to know about dynamic ftrace trampolines. */ struct ftrace_ops *ftrace_ops_trampoline (unsigned long addr)
@@ -5205,6 +5211,8 @@ ftrace_regex_write(struct file *file, co/* iter->hash is a local copy, so we don't need regex_lock */ parser = &iter->parser; + +guard (mutex)(&parser_lock); read =trace_get_user (parser, ubuf, cnt, ppos); if (read >= 0 && trace_parser_loaded(parser) &&
@@ -5930,12 +5938,14 @@ int ftrace_regex_release(struct inode *i iter = file->private_data; parser = &iter->parser; +mutex_lock (&parser_lock);if (trace_parser_loaded (parser)) {int enable = !(iter->flags &FTRACE_ITER_NOTRACE );ftrace_process_regex (iter, parser->buffer, parser->idx, enable); } +mutex_unlock (&parser_lock);trace_parser_put (parser);
@@ -6267,10 +6277,12 @@ ftrace_graph_release(struct inode *inode parser = &fgd->parser; +mutex_lock (&parser_lock);if (trace_parser_loaded ((parser))) { ret =ftrace_graph_set_hash (fgd->new_hash, parser->buffer); } +mutex_unlock (&parser_lock);trace_parser_put (parser);
@@ -6390,6 +6402,7 @@ ftrace_graph_write(struct file *file, co parser = &fgd->parser; +guard (mutex)(&parser_lock); read =trace_get_user (parser, ubuf, cnt, ppos); if (read >= 0 && trace_parser_loaded(parser) &&
Greg Kroah-Hartman
maintainerPATCH 315/484 iommu/vt-d: Disallow SVA if page walk is not coherent
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lu Baolu <[email protected]>
commit 780dfed688622ea01be3c9c2c55eec2207f05e04 upstream.
Hardware implementations report Scalable-Mode Page-walk Coherency Support via the SMPWCS field in the extended capability register. If the hardware does not support page-walk coherency, a clflush is required every time the page table entries (which are walked by the IOMMU hardware) are updated.
In the SVA case, page tables are managed by the CPU mm core, not by the IOMMU driver. Because the IOMMU driver has no way of knowing whether the CPU page table management code has ensured coherency via clflush, the driver must deny SVA if the hardware does not support coherent paging.
--- drivers/iommu/intel/svm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/iommu/intel/svm.c +1 -1
@@ -149,7 +149,7 @@ int intel_svm_finish_prq(struct intel_iovoid intel_svm_check (struct intel_iommu *iommu) { -if (!pasid_supported (iommu)) +if (!pasid_supported (iommu) || !ecap_smpwc (iommu->ecap))return ; if (cpu_feature_enabled(X86_FEATURE_GBPAGES) &&
Greg Kroah-Hartman
maintainerPATCH 316/484 phonet: pep: fix use-after-free in pep_get_sb()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Breno Leitao <[email protected]>
commit 0f71f852a96af9685858ce59fda34ecbf85c283d upstream.
pep_get_sb() doesn't consider that pskb_may_pull() might have relocated the skb data, and continue to access the older pointer, causing UAF.
Reproduced under KASAN:
BUG: KASAN: slab-use-after-free in pep_get_sb+0x234/0x3b0 Read of size 1 at addr ff11000105510f50 by task repro/157 pep_get_sb+0x234/0x3b0 pipe_handler_do_rcv+0x5f7/0xa10 pep_do_rcv+0x203/0x410 __sk_receive_skb+0x471/0x4a0 phonet_rcv+0x5b3/0x6c0 __netif_receive_skb+0xcc/0x1d0
Refetch the header with skb_header_pointer() after pskb_may_pull(), so the possibly stale pointer is no longer dereferenced. There are better ways to solve this, but, this is the less instrusive one.
--- net/phonet/pep.c | 2 ++ 1 file changed, 2 insertions(+)
net/phonet/pep.c +2 -0
@@ -55,6 +55,8 @@ static unsigned char *pep_get_sb(struct ph =skb_header_pointer (skb,0 ,2 , &h);if (ph ==NULL || ph->sb_len <2 || !pskb_may_pull (skb, ph->sb_len))return NULL ; +/* pskb_may_pull() may have reallocated the head; refetch ph. */ + ph =skb_header_pointer (skb,0 ,2 , &h); ph->sb_len -=2 ; *ptype = ph->sb_type; *plen = ph->sb_len;
Greg Kroah-Hartman
maintainerPATCH 317/484 vxlan: require CAP_NET_ADMIN in the device netns for changelink
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Doruk Tan Ozturk <[email protected]>
commit 3a61bd9637f3d929aa846e4eb3d98b48c26fcb0e upstream.
A tunnel changelink() operates on at most two netns, dev_net(dev) and the sticky underlay netns vxlan->net. They differ once the device is created in or moved to a netns other than the one the request runs in. The rtnl changelink path checks CAP_NET_ADMIN only against dev_net(dev), so a caller privileged there but not in vxlan->net can rewrite a vxlan device whose underlay lives in vxlan->net.
vxlan_changelink() validates and applies the new configuration against vxlan->net (vxlan_config_validate(vxlan->net, ...)) and can reopen the underlay socket in that netns, so the same reasoning as the tunnel changelink series applies here.
Gate vxlan_changelink() with rtnl_dev_link_net_capable(), at the top of the op before any attribute is parsed, matching ipgre_changelink() and the rest of the "require CAP_NET_ADMIN in the device netns for changelink" series.
Found by 0sec automated security-research tooling (https://0sec.ai).
--- drivers/net/vxlan/vxlan_core.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/net/vxlan/vxlan_core.c +3 -0
@@ -4257,6 +4257,9 @@ static int vxlan_changelink(struct net_dstruct vxlan_rdst *dst;int err; +if (!rtnl_dev_link_net_capable (dev, vxlan->net)) +return -EPERM ; + dst = &vxlan->default_dst; err =vxlan_nl2conf (tb, data, dev, &conf, true, extack);if (err)
Greg Kroah-Hartman
maintainerPATCH 318/484 net: slip: serialize receive against buffer reallocation
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sungmin Kang <[email protected]>
commit ee7f9bb9320add61f7b367d7e6cd55e3a3a4d65d upstream.
sl_realloc_bufs() replaces rbuff and updates buffsize while holding sl->lock. slip_receive_buf() reads those fields and writes through rbuff without holding the lock.
An MTU change can therefore race with receive processing. An MTU shrink can expose the new smaller rbuff with the old larger bound, causing an out-of-bounds write. A receive callback which already loaded the old rbuff can instead continue writing after that buffer has been freed.
Serialize receive processing with sl_realloc_bufs() by holding sl->lock while consuming each receive batch.
--- drivers/net/slip/slip.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/net/slip/slip.c +4 -0
@@ -693,6 +693,8 @@ static void slip_receive_buf(struct tty_if (!sl || sl->magic !=SLIP_MAGIC || !netif_running (sl->dev))return ; +spin_lock_bh (&sl->lock); +/* Read the characters out of the buffer */ while (count--) {if (fp && *fp++) {
@@ -708,6 +710,8 @@ static void slip_receive_buf(struct tty_#endif slip_unesc (sl, *cp++); } + +spin_unlock_bh (&sl->lock); }/************************************
Greg Kroah-Hartman
maintainerPATCH 319/484 geneve: require CAP_NET_ADMIN in the device netns for changelink
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Doruk Tan Ozturk <[email protected]>
commit 8efb8f8bbb353b8f2fdf4f37534c6d96c9f69e01 upstream.
A tunnel changelink() operates on at most two netns, dev_net(dev) and the sticky underlay netns geneve->net. They differ once the device is created in or moved to a netns other than the one the request runs in. The rtnl changelink path checks CAP_NET_ADMIN only against dev_net(dev), so a caller privileged there but not in geneve->net can rewrite a geneve device whose underlay lives in geneve->net.
geneve_changelink() applies the new configuration against geneve->net:geneve_link_config() and the geneve_quiesce()/geneve_unquiesce() pair reopen the underlay sockets in that netns (geneve_sock_add() uses geneve->net), so the same reasoning as the tunnel changelink series applies here.
Gate geneve_changelink() with rtnl_dev_link_net_capable(), at the top of the op before any attribute is parsed, matching ipgre_changelink() and the rest of the "require CAP_NET_ADMIN in the device netns for changelink" series.
Found by 0sec automated security-research tooling (https://0sec.ai).
--- drivers/net/geneve.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/net/geneve.c +3 -0
@@ -1755,6 +1755,9 @@ static int geneve_changelink(struct net_struct geneve_config cfg;int err; +if (!rtnl_dev_link_net_capable (dev, geneve->net)) +return -EPERM ; +/* If the geneve device is configured for metadata (or externally * controlled, for example, OVS), then nothing can be changed. */
Greg Kroah-Hartman
maintainerPATCH 320/484 net/af_iucv: fix NULL deref in afiucv_hs_callback_syn()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hidayath Khan <[email protected]>
commit 47a5116e56a6b6fe1e909f244e39cd0fc26ceee4 upstream.
afiucv_hs_callback_syn() allocates the child socket with GFP_ATOMIC. If the allocation fails, nsk is NULL.
The connection-refused path is entered when the listen state check fails, the accept backlog is full, or nsk is NULL. The code unconditionally calls iucv_sock_kill(nsk) in that path.
iucv_sock_kill() does not accept a NULL socket pointer and immediately dereferences sk via sock_flag(sk, SOCK_ZAPPED). When nsk is NULL, calling iucv_sock_kill(nsk) results in a NULL pointer dereference.
Only call iucv_sock_kill() when a child socket was successfully allocated.
--- net/iucv/af_iucv.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
net/iucv/af_iucv.c +2 -1
@@ -1873,7 +1873,8 @@ static int afiucv_hs_callback_syn(structafiucv_swap_src_dest (skb); trans_hdr->flags =AF_IUCV_FLAG_SYN |AF_IUCV_FLAG_FIN ; err =dev_queue_xmit (skb); -iucv_sock_kill (nsk); +if (nsk) +iucv_sock_kill (nsk);bh_unlock_sock (sk); goto out; }
Greg Kroah-Hartman
maintainerPATCH 321/484 net/iucv: fix use-after-free of a severed iucv_path
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bryam Vargas <[email protected]>
commit be7cc4656eb1f54029610e82d1f0fdd3f9b5ec0a upstream.
af_iucv queues not-yet-received message notifications on iucv->message_q, each holding a raw pointer to the connection's iucv_path. When the peer severs the connection, iucv_sever_path() frees that path with iucv_path_free() but leaves the notifications queued. A later recvmsg() drains message_q via iucv_process_message_q() and hands the stale path to message_receive() -- a use-after-free of the freed iucv_path.
Drop the queued notifications when the path is severed; once the path is gone they can no longer be received. This also frees the notifications leaked when a socket is closed with messages still queued.
--- net/iucv/af_iucv.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
net/iucv/af_iucv.c +14 -0
@@ -334,6 +334,7 @@ static void iucv_sever_path(struct sockunsigned char user_data[16 ];struct iucv_sock *iucv =iucv_sk (sk);struct iucv_path *path = iucv->path; +struct sock_msg_q *p, *n;/* Whoever resets the path pointer, must sever and free it. */ if (xchg (&iucv->path,NULL )) {
@@ -345,6 +346,19 @@ static void iucv_sever_path(struct sock }else pr_iucv->path_sever (path,NULL );iucv_path_free (path); + +/* +* Message notifications queued on message_q still reference +* the now freed path; drop them, otherwise a later recvmsg() +* would pass the freed iucv_path to message_receive() via +* iucv_process_message_q(). +*/ +spin_lock_bh (&iucv->message_q.lock); +list_for_each_entry_safe (p, n, &iucv->message_q.list, list) { +list_del (&p->list); +kfree (p); + } +spin_unlock_bh (&iucv->message_q.lock); } }
Greg Kroah-Hartman
maintainerPATCH 322/484 net/x25: fix use-after-free in x25_kill_by_neigh()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Lee <[email protected]>
commit 5499e0602d2faafd42c580d25f615903c3fbe11b upstream.
x25_kill_by_neigh() walks the global X.25 socket list looking for sockets attached to a terminating neighbour. x25_list_lock protects list membership while the lookup is in progress, but it does not pin a socket's lifetime after the lock is dropped.
The function currently drops x25_list_lock before calling lock_sock(s). A concurrent close can run x25_release(), remove the same socket from x25_list, and drop the last socket reference in that window. The neighbour teardown path can then lock or inspect a freed struct sock/struct x25_sock.
Take sock_hold(s) while x25_list_lock still proves that the list entry is live, then drop the temporary reference after the socket has been locked, rechecked, and released. Recheck x25_sk(s)->neighbour after lock_sock(), because another path may have disconnected the socket before this path acquired the socket lock. Restart the list walk after each disconnect because the list lock was dropped and the previous iterator state may no longer be valid.
A QEMU/KASAN run against origin/master reproduced a slab-use-after-free in x25_kill_by_neigh().
--- net/x25/af_x25.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
net/x25/af_x25.c +6 -2
@@ -1772,15 +1772,19 @@ void x25_kill_by_neigh(struct x25_neigh {struct sock *s; +again:write_lock_bh (&x25_list_lock);sk_for_each (s, &x25_list) {if (x25_sk (s)->neighbour == nb) { +sock_hold (s);write_unlock_bh (&x25_list_lock);lock_sock (s); -x25_disconnect (s,ENETUNREACH ,0 ,0 ); +if (x25_sk (s)->neighbour == nb) +x25_disconnect (s,ENETUNREACH ,0 ,0 );release_sock (s); -write_lock_bh (&x25_list_lock); +sock_put (s); + goto again; } }write_unlock_bh (&x25_list_lock);
Greg Kroah-Hartman
maintainerPATCH 323/484 net: hip04: fix RX buffer leak on build_skb failure
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fan Wu <[email protected]>
commit 14fa65d10f5696b063a7d8d26e8291ea84a2c6ed upstream.
When build_skb() fails in hip04_rx_poll(), the driver jumps to the refill path without releasing the current RX buffer and its DMA mapping. Installing a replacement buffer then overwrites the slot references and leaks both resources.
Keep the current slot intact and return budget so NAPI retries the same buffer. Also free a newly allocated RX fragment when dma_map_single() fails.
This issue was found by an in-house static analysis tool.
--- drivers/net/ethernet/hisilicon/hip04_eth.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
drivers/net/ethernet/hisilicon/hip04_eth.c +8 -3
@@ -594,7 +594,11 @@ static int hip04_rx_poll(struct napi_str skb =build_skb (buf, priv->rx_buf_size);if (unlikely (!skb)) {net_dbg_ratelimited ("build_skb failed\n" ); - goto refill; +/* Retain the slot; return budget so NAPI retries this +* buffer. Refill would overwrite rx_buf[]/rx_phys[] +* and leak them. +*/ +return budget; } dma_unmap_single(priv->dev, priv->rx_phys[priv->rx_head],
@@ -622,14 +626,15 @@ static int hip04_rx_poll(struct napi_str rx++; } -refill: buf =netdev_alloc_frag (priv->rx_buf_size);if (!buf) goto done; phys =dma_map_single (priv->dev, buf,RX_BUF_SIZE ,DMA_FROM_DEVICE ); -if (dma_mapping_error (priv->dev, phys)) +if (dma_mapping_error (priv->dev, phys)) { +skb_free_frag (buf); goto done; + } priv->rx_buf[priv->rx_head] = buf; priv->rx_phys[priv->rx_head] = phys;hip04_set_recv_desc (priv, phys);
Greg Kroah-Hartman
maintainerPATCH 324/484 proc: Fix broken error paths for namespace links
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jann Horn <[email protected]>
commit 425224c2d700391729be7fe6929a88ef4e2d7a4e upstream.
Don't return the return value of down_read_killable() (0) when a ptrace access check fails, return -EACCES as intended.
--- fs/proc/namespaces.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
fs/proc/namespaces.c +3 -1
@@ -46,7 +46,7 @@ static const char *proc_ns_get_link(struconst struct proc_ns_operations *ns_ops =PROC_I (inode)->ns_ops;struct task_struct *task;struct path ns_path; -int error = -EACCES ; +int error;if (!dentry)return ERR_PTR (-ECHILD );
@@ -59,6 +59,7 @@ static const char *proc_ns_get_link(struif (error) goto out_put_task; + error = -EACCES ;if (!ptrace_may_access (task,PTRACE_MODE_READ_FSCREDS )) goto out;
@@ -90,6 +91,7 @@ static int proc_ns_readlink(struct dentrif (res) goto out_put_task; + res = -EACCES ;if (ptrace_may_access (task,PTRACE_MODE_READ_FSCREDS )) { res =ns_get_name (name,sizeof (name), task, ns_ops);if (res >=0 )
Greg Kroah-Hartman
maintainerPATCH 325/484 selftests/ftrace: Reset triggers at top level before instance loop
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Masami Hiramatsu (Google) <[email protected]>
commit 1a087033a6bad73b4140020b40e819b0933aafc3 upstream.
When running instance tests, 'ftracetest' creates a new ftrace instance and runs the tests inside it. Before starting each test, it executes 'initialize_system()' to reset the ftrace state to initial-state.
However, since 'initialize_system()' is executed in the context of the instance directory, it only cleans up triggers and filters of that instance.
Any triggers or dynamic events left behind in the top-level instance by previous failed top-level tests, are left completely untouched. These top-level leftovers can cause subsequent instance-based tests to fail or even crash the kernel.
Fix this by executing 'initialize_system()' in the top-level tracing directory once before entering the instance loop.
--- tools/testing/selftests/ftrace/ftracetest | 1 + 1 file changed, 1 insertion(+)
tools/testing/selftests/ftrace/ftracetest +1 -0
@@ -477,6 +477,7 @@ for t in $TEST_CASES; do done # Test on instance loop +(cd $TRACING_DIR; initialize_system) INSTANCE=" (instance) " for t in $TEST_CASES; do test_on_instance $t || continue
Greg Kroah-Hartman
maintainerPATCH 326/484 rbd: Reset positive result codes to zero in object map update path
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Raphael Zimmer <[email protected]>
commit a6c4250b81bd30beae94e1b7a4b26fa1193ad2e4 upstream.
In a reply message to an RBD request, a positive result code indicates a data payload, which is not allowed for writes. While rbd_osd_req_callback() already resets a positive result code for writes to zero, rbd_object_map_callback() does not. This allows a corrupted reply to an object map update to trigger the rbd_assert(*result < 0) in __rbd_obj_handle_request(). This happens, because rbd_object_map_callback() calls rbd_obj_handle_request() -> __rbd_obj_handle_request() and passes this positive result code. From __rbd_obj_handle_request(), rbd_obj_advance_write() is called, which leaves the positive result code unchanged and returns true. Therefore, the if(done && *result) branch is executed in __rbd_obj_handle_request() and the assertion triggers.
This patch fixes the issue by adjusting the logic in the rbd_object_map_callback() path. A positive result code for an object map update is now reset to zero (similar to rbd_osd_req_callback()), and the message is subsequently handled the same way as if the result code was zero from the beginning. Additionally, a WARN_ON_ONCE() is added for this case.
--- drivers/block/rbd.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
drivers/block/rbd.c +6 -1
@@ -1957,10 +1957,15 @@ static int rbd_object_map_update_finish(bool has_current_state;void *p; -if (osd_req->r_result) +if (osd_req->r_result <0 )return osd_req->r_result;/* +* Writes aren't allowed to return a data payload. +*/ +WARN_ON_ONCE (osd_req->r_result >0 ); + +/* * Nothing to do for a snapshot object map. */ if (osd_req->r_num_ops ==1 )
Greg Kroah-Hartman
maintainerPATCH 327/484 ksmbd: defer destroy_previous_session() until after NTLM authentication
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: James Montgomery <[email protected]>
commit c74801ee524f477c174a1899782b6c3b6918d407 upstream.
In ntlm_authenticate(), destroy_previous_session() is called using a user pointer resolved from the client-supplied NTLM blob username field before the NTLMv2 response is validated. An authenticated attacker can set the NTLM blob username to match a victim account and set PreviousSessionId to the victim's session ID; destroy_previous_session() destroys the victim's session while ksmbd_decode_ntlmssp_auth_blob() subsequently rejects the request with -EPERM.
Move destroy_previous_session() and the prev_id assignment to after ksmbd_decode_ntlmssp_auth_blob() returns success and use sess->user rather than the pre-authentication lookup result. This matches the ordering already used by krb5_authenticate(), where destroy_previous_session() is called only after ksmbd_krb5_authenticate() returns success.
--- fs/smb/server/smb2pdu.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-)
fs/smb/server/smb2pdu.c +4 -5
@@ -1483,11 +1483,6 @@ static int ntlm_authenticate(struct ksmbreturn -EPERM ; } -/* Check for previous session */ - prev_id =le64_to_cpu (req->PreviousSessionId); -if (prev_id && prev_id != sess->id) -destroy_previous_session (conn, user, prev_id); -if (sess->state ==SMB2_SESSION_VALID ) {/* * Reuse session if anonymous try to connect
@@ -1525,6 +1520,10 @@ static int ntlm_authenticate(struct ksmb } } + prev_id =le64_to_cpu (req->PreviousSessionId); +if (prev_id && prev_id != sess->id) +destroy_previous_session (conn, sess->user, prev_id); +/* * If session state is SMB2_SESSION_VALID, We can assume * that it is reauthentication. And the user/password
Greg Kroah-Hartman
maintainerPATCH 328/484 ice: use READ_ONCE() to access cached PHC time
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sergey Temerkhanov <[email protected]>
commit 2915681b89f817677ab9f1166d95b595bc144f5f upstream.
ptp.cached_phc_time is a 64-bit value updated by a periodic work item on one CPU and read locklessly on another. On 32-bit or non-atomic architectures this can result in a torn read. Use READ_ONCE() to enforce a single atomic load.
--- drivers/net/ethernet/intel/ice/ice_ptp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/ethernet/intel/ice/ice_ptp.c +1 -1
@@ -595,7 +595,7 @@ static u64 ice_ptp_extend_40b_ts(structreturn 0 ; } -return ice_ptp_extend_32b_ts (pf->ptp.cached_phc_time, +return ice_ptp_extend_32b_ts (READ_ONCE (pf->ptp.cached_phc_time), (in_tstamp >>8 ) & mask); }
Greg Kroah-Hartman
maintainerPATCH 329/484 ila: reload IPv6 header after pskb_may_pull in checksum adjust
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
commit 92d3817649df2b0b6a008a686c8275c88d7ef594 upstream.
ila_csum_adjust_transport() caches ip6h = ipv6_hdr(skb) before calling pskb_may_pull(). On a non-linear skb whose transport header sits in a page fragment, pskb_may_pull() can call __pskb_pull_tail() / pskb_expand_head() and free the old skb head, leaving ip6h dangling; the following get_csum_diff(ip6h, p) then reads freed memory. ila_update_ipv6_locator() uses ip6h (and the iaddr derived from it) again after the csum-adjust call and additionally writes the new locator through that pointer.
Impact: a remote IPv6 packet routed through a configured ILA csum-adjust-transport route or receive-side mapping triggers a slab-use-after-free in ila_update_ipv6_locator() (KASAN). The route or mapping requires CAP_NET_ADMIN to configure, but trigger packets are unauthenticated once it exists.
Reload ip6h after each pskb_may_pull() in ila_csum_adjust_transport() before the csum-diff read. In ila_update_ipv6_locator() only the ILA_CSUM_ADJUST_TRANSPORT case pulls the skb, so reload ip6h and iaddr in that case alone before the destination-address write; the neutral-map modes never pull and keep their cached pointers.
--- net/ipv6/ila/ila_common.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
net/ipv6/ila/ila_common.c +12 -0
@@ -84,6 +84,7 @@ static void ila_csum_adjust_transport(ststruct tcphdr *th = (struct tcphdr *) (skb_network_header (skb) + nhoff); + ip6h =ipv6_hdr (skb); diff =get_csum_diff (ip6h, p);inet_proto_csum_replace_by_diff (&th->check, skb, diff, true, true);
@@ -95,6 +96,7 @@ static void ila_csum_adjust_transport(st (skb_network_header (skb) + nhoff);if (uh->check || skb->ip_summed ==CHECKSUM_PARTIAL ) { + ip6h =ipv6_hdr (skb); diff =get_csum_diff (ip6h, p);inet_proto_csum_replace_by_diff (&uh->check, skb, diff, true, true);
@@ -109,6 +111,7 @@ static void ila_csum_adjust_transport(ststruct icmp6hdr *ih = (struct icmp6hdr *) (skb_network_header (skb) + nhoff); + ip6h =ipv6_hdr (skb); diff =get_csum_diff (ip6h, p);inet_proto_csum_replace_by_diff (&ih->icmp6_cksum, skb, diff, true, true);
@@ -126,6 +129,15 @@ void ila_update_ipv6_locator(struct sk_bswitch (p->csum_mode) {case ILA_CSUM_ADJUST_TRANSPORT :ila_csum_adjust_transport (skb, p); +/* +* ila_csum_adjust_transport() calls pskb_may_pull(), which can +* reallocate the skb head and leave ip6h (and the iaddr derived +* from it) dangling; reload both before the write below. The +* other csum modes do not pull, so their cached pointers stay +* valid. +*/ + ip6h =ipv6_hdr (skb); + iaddr =ila_a2i (&ip6h->daddr);break ;case ILA_CSUM_NEUTRAL_MAP :if (sir2ila) {
Greg Kroah-Hartman
maintainerPATCH 330/484 mac802154: hold an interface reference across the scan worker
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ibrahim Hashimov <[email protected]>
commit 234e5e898b713bc0b3a631b6f002897f43d046c8 upstream.
mac802154_scan_worker() captures the scanning sub-interface under RCU and then keeps dereferencing sdata->dev after rcu_read_unlock() and outside the rtnl -- in the failure traces, in mac802154_transmit_beacon_req() (skb->dev = sdata->dev), and in the end_scan cleanup. Nothing keeps that netdev alive across the worker iteration.
A concurrent DEL_INTERFACE or PHY removal can unregister the interface once the worker drops the rtnl between its two drv_set_channel() sections. unregister_netdevice() frees the netdev asynchronously from netdev_run_todo() with the rtnl already dropped, so neither holding the rtnl nor the per-PHY IEEE802154_IS_SCANNING flag prevents a stale worker iteration from dereferencing the freed netdev -- a KASAN slab-use-after-free, reachable by racing TRIGGER_SCAN against DEL_INTERFACE (both CAP_NET_ADMIN).
Pin the netdev with netdev_hold() while the RCU read lock is still held, and release it at every worker exit.
--- net/mac802154/scan.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
net/mac802154/scan.c +11 -0
@@ -179,6 +179,7 @@ void mac802154_scan_worker(struct work_senum nl802154_scan_types scan_req_type;struct ieee802154_sub_if_data *sdata;unsigned int scan_duration =0 ; +netdevice_tracker dev_tracker;struct wpan_phy *wpan_phy;u8 scan_req_duration;u8 page, channel;
@@ -209,6 +210,14 @@ void mac802154_scan_worker(struct work_sreturn ; } +/* +* sdata->dev is dereferenced below after rcu_read_unlock() and outside +* the rtnl, and a concurrent DEL_INTERFACE / PHY teardown can free it +* asynchronously from netdev_run_todo(). Pin it with a reference taken +* while the RCU read lock is still held, and drop it at every exit. +*/ +netdev_hold (sdata->dev, &dev_tracker,GFP_ATOMIC ); + wpan_phy = scan_req->wpan_phy; scan_req_type = scan_req->type; scan_req_duration = scan_req->duration;
@@ -262,12 +271,14 @@ void mac802154_scan_worker(struct work_s"Scan page %u channel %u for %ums\n" , page, channel, jiffies_to_msecs(scan_duration));queue_delayed_work (local->mac_wq, &local->scan_work, scan_duration); +netdev_put (sdata->dev, &dev_tracker);return ; end_scan:rtnl_lock ();mac802154_scan_cleanup_locked (local, sdata, false);rtnl_unlock (); +netdev_put (sdata->dev, &dev_tracker); } int mac802154_trigger_scan_locked(struct ieee802154_sub_if_data *sdata,
Greg Kroah-Hartman
maintainerPATCH 331/484 mac802154: llsec: reject frames shorter than the authentication tag
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Doruk Tan Ozturk <[email protected]>
commit fd3a3f28ed60c6af4b2a39933b151d6b27842c3b upstream.
llsec_do_decrypt_auth() computes the associated-data length for the AEAD request as
assoclen += datalen - authlen;
where datalen is the number of bytes after the MAC header and authlen (4, 8 or 16) is the length of the authentication tag. Nothing verifies that the frame actually carries at least authlen payload bytes. A secured frame whose payload is shorter than the tag makes datalen - authlen negative; assoclen is then passed to aead_request_set_ad() as an unsigned value close to 4 GiB, so crypto_aead_decrypt() walks far off the end of the scatterlist that only spans the real frame.
The frame is fully attacker-controlled and reaches this path from any IEEE 802.15.4 peer in radio range. Reject frames whose payload is shorter than the authentication tag before the subtraction.
Dynamically reproduced on a KASAN kernel as a general-protection-fault in the AEAD scatterwalk, and the fix confirmed.
--- net/mac802154/llsec.c | 5 +++++ 1 file changed, 5 insertions(+)
net/mac802154/llsec.c +5 -0
@@ -891,6 +891,11 @@ llsec_do_decrypt_auth(struct sk_buff *sk data =skb_mac_header (skb) + skb->mac_len; datalen =skb_tail_pointer (skb) - data; +if (datalen < authlen) { +kfree_sensitive (req); +return -EBADMSG ; + } +sg_init_one (&sg,skb_mac_header (skb), assoclen + datalen);if (!(hdr->sec.level &IEEE802154_SCF_SECLEVEL_ENC )) {
Greg Kroah-Hartman
maintainerPATCH 332/484 mctp: serial: handle zero-length frames to prevent rx buffer overflow
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Doruk Tan Ozturk <[email protected]>
commit 793b9b729f1e8de57be8c8daf1a9838be96cabed upstream.
The MCTP serial receive state machine reads a frame length byte in mctp_serial_push_header() case 2 and validates it upper-bound-only:
if (c > MCTP_SERIAL_FRAME_MTU) {
dev->rxstate = STATE_ERR;
} else {
dev->rxlen = c;
dev->rxpos = 0;
dev->rxstate = STATE_DATA;
...
}A length of zero passes this check, so rxlen is set to 0 and the state machine advances to STATE_DATA. In mctp_serial_push() STATE_DATA, the incoming byte is stored and rxpos incremented before the terminator is tested:
dev->rxbuf[dev->rxpos] = c;
dev->rxpos++;
dev->rxstate = STATE_DATA;
if (dev->rxpos == dev->rxlen) {
dev->rxpos = 0;
dev->rxstate = STATE_TRAILER;
}With rxlen == 0 the "rxpos == rxlen" terminator can never fire (rxpos is already 1 on the first data byte), so subsequent bytes are written past the end of the fixed 74-byte rxbuf, which is the last member of the netdev private area. Every following data byte is an attacker-controlled 1-byte out-of-bounds heap write, and the overflow continues until a frame (0x7e) or escape byte resets the parser -- effectively unbounded.
Reaching this requires CAP_NET_ADMIN to attach the N_MCTP line discipline and bring the resulting mctpserialN netdev up, after which the bytes arrive via the tty receive path.
Route a zero-length frame straight to STATE_TRAILER instead of STATE_DATA. The trailer/framing bytes are still consumed, and the frame resolves to a zero-length skb that the MCTP core rejects; the parser never enters STATE_DATA with rxlen == 0, so the out-of-bounds write can no longer occur.
KASAN, on a frame of 0x7e 0x01 0x00 followed by data bytes (before this change):
UBSAN: array-index-out-of-bounds in drivers/net/mctp/mctp-serial.c:370 index 74 is out of range for type 'u8 [74]' BUG: KASAN: slab-out-of-bounds in mctp_serial_tty_receive_buf Write of size 1 at addr ... by task kworker/u16:0 mctp_serial_tty_receive_buf tty_ldisc_receive_buf flush_to_ldisc Allocated by task 152: alloc_netdev_mqs mctp_serial_open
v2: route zero-length frames to STATE_TRAILER instead of STATE_ERR so
the trailer/framing bytes are still consumed (Jeremy Kerr).
Found by 0sec automated security-research tooling (https://0sec.ai).
--- drivers/net/mctp/mctp-serial.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/mctp/mctp-serial.c +1 -1
@@ -316,7 +316,7 @@ static void mctp_serial_push_header(stru }else { dev->rxlen = c; dev->rxpos =0 ; - dev->rxstate =STATE_DATA ; + dev->rxstate = c >0 ?STATE_DATA :STATE_TRAILER ; dev->rxfcs =crc_ccitt_byte (dev->rxfcs, c); }break ;
Greg Kroah-Hartman
maintainerPATCH 333/484 pppoe: reload header pointer after dev_hard_header()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Asim Viladi Oglu Manizada <[email protected]>
commit e9c238f6fe42fb1b4dba3a578277de32cb487937 upstream.
pppoe_sendmsg() saves a pointer to the PPPoE header before calling dev_hard_header(). Device header callbacks are allowed to reallocate the skb head, invalidating pointers into it.
This can happen when a send is blocked in copy_from_user() while the first non-Ethernet port is added to an empty team device. The team's delegated GRE header callback then expands the skb head. PPPoE subsequently writes six bytes through the stale pointer into the freed head.
Reload the PPPoE header through the skb's network-header offset after device header creation. pskb_expand_head() updates that offset when it relocates the head.
--- drivers/net/ppp/pppoe.c | 1 + 1 file changed, 1 insertion(+)
drivers/net/ppp/pppoe.c +1 -0
@@ -900,6 +900,7 @@ static int pppoe_sendmsg(struct socket *dev_hard_header (skb, dev,ETH_P_PPP_SES , po->pppoe_pa.remote,NULL , total_len); + ph =pppoe_hdr (skb);memcpy (ph, &hdr,sizeof (struct pppoe_hdr )); ph->length =htons (total_len);
Greg Kroah-Hartman
maintainerPATCH 334/484 tipc: clear sock->sk on the failed-insert path in tipc_sk_create()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Daehyeon Ko <[email protected]>
commit ba0533fc163f905fe817cfabdf8ed4058da44800 upstream.
When tipc_sk_create() fails to insert the new socket (tipc_sk_insert() returns non-zero), its error path frees the sk with sk_free() but leaves sock->sk pointing at the freed object:
if (tipc_sk_insert(tsk)) {
sk_free(sk);
pr_warn("Socket create failed; port number exhausted\n");
return -EINVAL;
}This is harmless for plain socket(): the syscall layer clears sock->ops before releasing, so tipc_release() is never called. It is not harmless on the accept() path. tipc_accept() creates the pre-allocated child socket with tipc_sk_create(net, new_sock, 0, kern); on failure it leaves new_sock->sk dangling and new_sock->ops non-NULL, and do_accept() then fput()s the new file, so __sock_release() -> tipc_release() runs lock_sock(new_sock->sk) on the freed sk -- a use-after-free write of the sk_lock spinlock.
tipc_release() already guards this exact "failed accept() releases a pre-allocated child" case with "if (sk == NULL) return 0;", but the guard is bypassed because tipc_sk_create() left sock->sk non-NULL (dangling) rather than NULL.
Clear sock->sk on the failed-insert path so the existing tipc_release() NULL check fires and the use-after-free is avoided.
The tipc_sk_insert() failure is reached when the per-netns socket rhashtable hits its max_size (tsk_rht_params.max_size = 1048576, ~2M elements) -- i.e. once a netns holds ~2M TIPC sockets every insert returns -E2BIG.
BUG: KASAN: slab-use-after-free in lock_sock_nested (net/core/sock.c:3839) Write of size 8 at addr ffff8880047cdc38 by task init/1 lock_sock_nested (net/core/sock.c:3839) tipc_release (net/tipc/socket.c:638) __sock_release (net/socket.c:710) sock_close (net/socket.c:1501) __fput (fs/file_table.c:512) Allocated by task 1: sk_alloc (net/core/sock.c:2308) tipc_sk_create (net/tipc/socket.c:487) tipc_accept (net/tipc/socket.c:2744) do_accept (net/socket.c:2034) Freed by task 1: __sk_destruct (net/core/sock.c:2391) tipc_sk_create (net/tipc/socket.c:504) tipc_accept (net/tipc/socket.c:2744) do_accept (net/socket.c:2034)
--- net/tipc/socket.c | 1 + 1 file changed, 1 insertion(+)
net/tipc/socket.c +1 -0
@@ -504,6 +504,7 @@ static int tipc_sk_create(struct net *netipc_set_sk_state (sk,TIPC_OPEN );if (tipc_sk_insert (tsk)) {sk_free (sk); + sock->sk =NULL ;pr_warn ("Socket create failed; port number exhausted\n" );return -EINVAL ; }
Greg Kroah-Hartman
maintainerPATCH 335/484 vxlan: mdb: Fix source list corruption on a failed replace
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: James Raphael Tiovalen <[email protected]>
commit dcd9b465965422b9654f6026e8a2fa8984f74c3c upstream.
When replacing the source list of an MDB remote entry, all existing sources are first marked for deletion and vxlan_mdb_remote_srcs_add() is then called to add the new source list. Sources present in the new list have their deletion mark cleared, and any sources left marked afterwards are removed.
If vxlan_mdb_remote_srcs_add() fails partway through, its error path deletes all entries on the remote's source list. That rollback is only correct for its other caller, vxlan_mdb_remote_add(), where the remote was just allocated and the list contains solely entries added during the call. On the replace path the list also holds pre-existing sources, so a failed replace tears them down together with their (S, G) forwarding entries instead of leaving the entry unchanged.
This is reachable from an existing (*, G) remote. An EXCLUDE filter that loses sources starts forwarding traffic that should be blocked, while an INCLUDE filter that loses sources drops traffic that should be forwarded.
Mark entries created during the current pass with a new VXLAN_SGRP_F_NEW flag. On failure, delete only those entries and clear the deletion mark on the pre-existing ones, so a failed replace leaves the source list untouched. Retain the flag until the whole operation succeeds and then clear it. Also stop vxlan_mdb_remote_src_add() from deleting a pre-existing entry it only looked up when adding that entry's forwarding entry fails.
--- drivers/net/vxlan/vxlan_mdb.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-)
drivers/net/vxlan/vxlan_mdb.c +18 -12
@@ -42,6 +42,7 @@ struct vxlan_mdb_remote { };#define VXLAN_SGRP_F_DELETE BIT(0) +#define VXLAN_SGRP_F_NEW BIT(1)struct vxlan_mdb_src_entry {struct hlist_node node;
@@ -838,6 +839,7 @@ vxlan_mdb_remote_src_add(const struct vx ent =vxlan_mdb_remote_src_entry_add (remote, &src->addr);if (!ent)return -ENOMEM ; + ent->flags |=VXLAN_SGRP_F_NEW ; }else if (!(cfg->nlflags &NLM_F_REPLACE )) {NL_SET_ERR_MSG_MOD (extack,"Source entry already exists" );return -EEXIST ;
@@ -847,15 +849,16 @@ vxlan_mdb_remote_src_add(const struct vxif (err) goto err_src_del; -/* Clear flags in case source entry was marked for deletion as part of -* replace flow. +/* Clear the deletion mark so the entry survives the replace sweep. +* The new mark is retained until the whole operation succeeds. */ - ent->flags =0 ; + ent->flags &= ~VXLAN_SGRP_F_DELETE ;return 0 ; err_src_del: -vxlan_mdb_remote_src_entry_del (ent); +if (ent->flags &VXLAN_SGRP_F_NEW ) +vxlan_mdb_remote_src_entry_del (ent);return err; }
@@ -883,11 +886,19 @@ static int vxlan_mdb_remote_srcs_add(con goto err_src_del; } +hlist_for_each_entry (ent, &remote->src_list, node) + ent->flags &= ~VXLAN_SGRP_F_NEW ; +return 0 ; err_src_del: -hlist_for_each_entry_safe (ent, tmp, &remote->src_list, node) -vxlan_mdb_remote_src_del (cfg->vxlan, &cfg->group, remote, ent); +hlist_for_each_entry_safe (ent, tmp, &remote->src_list, node) { +if (ent->flags &VXLAN_SGRP_F_NEW ) +vxlan_mdb_remote_src_del (cfg->vxlan, &cfg->group, remote, + ent); +else + ent->flags &= ~VXLAN_SGRP_F_DELETE ; + }return err; }
@@ -1053,7 +1064,7 @@ vxlan_mdb_remote_srcs_replace(const stru err =vxlan_mdb_remote_srcs_add (cfg, remote, extack);if (err) - goto err_clear_delete; +return err;hlist_for_each_entry_safe (ent, tmp, &remote->src_list, node) {if (ent->flags &VXLAN_SGRP_F_DELETE )
@@ -1062,11 +1073,6 @@ vxlan_mdb_remote_srcs_replace(const stru }return 0 ; - -err_clear_delete: -hlist_for_each_entry (ent, &remote->src_list, node) - ent->flags &= ~VXLAN_SGRP_F_DELETE ; -return err; } static int vxlan_mdb_remote_replace(const struct vxlan_mdb_config *cfg,
Greg Kroah-Hartman
maintainerPATCH 336/484 drm/amd/pm: make pp_features read-only when scpm is enabled
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yang Wang <[email protected]>
commit 53c78ab388bfc1a4d72e756815d0db0a842c812e upstream.
SCPM owns power feature control when enabled.
Make pp_features read-only during sysfs setup by clearing its write bits and store callback.
--- drivers/gpu/drm/amd/pm/amdgpu_pm.c | 5 +++++ 1 file changed, 5 insertions(+)
drivers/gpu/drm/amd/pm/amdgpu_pm.c +5 -0
@@ -2055,6 +2055,11 @@ static int default_attr_update(struct am gc_ver != IP_VERSION(9, 4, 3)) || gc_ver < IP_VERSION(9, 0, 0)) *states =ATTR_STATE_UNSUPPORTED ; + +if (adev->scpm_enabled) { + dev_attr->attr.mode &= ~S_IWUGO ; + dev_attr->store =NULL ; + } }else if (DEVICE_ATTR_IS (gpu_metrics)) {if (gc_ver <IP_VERSION (9 ,1 ,0 )) *states =ATTR_STATE_UNSUPPORTED ;
Greg Kroah-Hartman
maintainerPATCH 337/484 drm/amdgpu/gfx10: replace BUG_ON() with WARN_ON()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alex Deucher <[email protected]>
commit d06c4173a7c38c7a39e98859f839ce714c7af2c9 upstream.
There's no need to crash the kernel for these cases.
--- drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-)
drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c +5 -8
@@ -3755,7 +3755,7 @@ static void gfx_v10_0_wait_reg_mem(struc WAIT_REG_MEM_ENGINE(eng_sel)));if (mem_space) -BUG_ON (addr0 &0x3 );/* Dword align */ +WARN_ON (addr0 &0x3 );/* Dword align */ amdgpu_ring_write (ring, addr0);amdgpu_ring_write (ring, addr1);amdgpu_ring_write (ring, ref);
@@ -8311,7 +8311,7 @@ static void gfx_v10_0_ring_emit_ib_gfx(s control |=0x400000 ;amdgpu_ring_write (ring, header); -BUG_ON (ib->gpu_addr &0x3 );/* Dword align */ +WARN_ON (ib->gpu_addr &0x3 );/* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) |
@@ -8350,7 +8350,7 @@ static void gfx_v10_0_ring_emit_ib_compu }amdgpu_ring_write (ring,PACKET3 (PACKET3_INDIRECT_BUFFER ,2 )); -BUG_ON (ib->gpu_addr &0x3 );/* Dword align */ +WARN_ON (ib->gpu_addr &0x3 );/* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) |
@@ -8383,9 +8383,9 @@ static void gfx_v10_0_ring_emit_fence(st* aligned if only send 32bit data low (discard data high) */ if (write64bit) -BUG_ON (addr &0x7 ); +WARN_ON (addr &0x7 );else -BUG_ON (addr &0x3 ); +WARN_ON (addr &0x3 );amdgpu_ring_write (ring,lower_32_bits (addr));amdgpu_ring_write (ring,upper_32_bits (addr));amdgpu_ring_write (ring,lower_32_bits (seq));
@@ -8437,9 +8437,6 @@ static void gfx_v10_0_ring_emit_fence_ki {struct amdgpu_device *adev = ring->adev; -/* we only allocate 32bit for each seq wb address */ -BUG_ON (flags &AMDGPU_FENCE_FLAG_64BIT ); -/* write fence seq to the "addr" */ amdgpu_ring_write (ring,PACKET3 (PACKET3_WRITE_DATA ,3 )); amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) |
Greg Kroah-Hartman
maintainerPATCH 338/484 drm/amdgpu/gfx11: replace BUG_ON() with WARN_ON()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alex Deucher <[email protected]>
commit 0eebcab1ea2a77f086a04108f386f82ee3496022 upstream.
There's no need to crash the kernel for these cases.
--- drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-)
drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c +5 -8
@@ -310,7 +310,7 @@ static void gfx_v11_0_wait_reg_mem(struc WAIT_REG_MEM_ENGINE(eng_sel)));if (mem_space) -BUG_ON (addr0 &0x3 );/* Dword align */ +WARN_ON (addr0 &0x3 );/* Dword align */ amdgpu_ring_write (ring, addr0);amdgpu_ring_write (ring, addr1);amdgpu_ring_write (ring, ref);
@@ -5306,7 +5306,7 @@ static void gfx_v11_0_ring_emit_ib_gfx(s control |=0x400000 ;amdgpu_ring_write (ring, header); -BUG_ON (ib->gpu_addr &0x3 );/* Dword align */ +WARN_ON (ib->gpu_addr &0x3 );/* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) |
@@ -5345,7 +5345,7 @@ static void gfx_v11_0_ring_emit_ib_compu }amdgpu_ring_write (ring,PACKET3 (PACKET3_INDIRECT_BUFFER ,2 )); -BUG_ON (ib->gpu_addr &0x3 );/* Dword align */ +WARN_ON (ib->gpu_addr &0x3 );/* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) |
@@ -5382,9 +5382,9 @@ static void gfx_v11_0_ring_emit_fence(st* aligned if only send 32bit data low (discard data high) */ if (write64bit) -BUG_ON (addr &0x7 ); +WARN_ON (addr &0x7 );else -BUG_ON (addr &0x3 ); +WARN_ON (addr &0x3 );amdgpu_ring_write (ring,lower_32_bits (addr));amdgpu_ring_write (ring,upper_32_bits (addr));amdgpu_ring_write (ring,lower_32_bits (seq));
@@ -5436,9 +5436,6 @@ static void gfx_v11_0_ring_emit_fence_ki {struct amdgpu_device *adev = ring->adev; -/* we only allocate 32bit for each seq wb address */ -BUG_ON (flags &AMDGPU_FENCE_FLAG_64BIT ); -/* write fence seq to the "addr" */ amdgpu_ring_write (ring,PACKET3 (PACKET3_WRITE_DATA ,3 )); amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) |
Greg Kroah-Hartman
maintainerPATCH 339/484 drm/amdgpu/gfx8: drop unecessary BUG_ON()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alex Deucher <[email protected]>
commit 84a1a8a952ab4b8c23c5dd1f2eea4049cb4914f5 upstream.
There's no need to crash the kernel for this case.
--- drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c | 3 --- 1 file changed, 3 deletions(-)
drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c +0 -3
@@ -6267,9 +6267,6 @@ static void gfx_v8_0_ring_emit_fence_comstatic void gfx_v8_0_ring_emit_fence_kiq (struct amdgpu_ring *ring,u64 addr,u64 seq,unsigned int flags) { -/* we only allocate 32bit for each seq wb address */ -BUG_ON (flags &AMDGPU_FENCE_FLAG_64BIT ); -/* write fence seq to the "addr" */ amdgpu_ring_write (ring,PACKET3 (PACKET3_WRITE_DATA ,3 )); amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) |
Greg Kroah-Hartman
maintainerPATCH 340/484 drm/amdgpu/gfx9.4.3: replace BUG_ON() with WARN_ON()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alex Deucher <[email protected]>
commit 00f4050f7c367d7bdce347ca279ce467c434cf15 upstream.
There's no need to crash the kernel for these cases.
--- drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-)
drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c +4 -7
@@ -236,7 +236,7 @@ static void gfx_v9_4_3_wait_reg_mem(stru WAIT_REG_MEM_ENGINE(eng_sel)));if (mem_space) -BUG_ON (addr0 &0x3 );/* Dword align */ +WARN_ON (addr0 &0x3 );/* Dword align */ amdgpu_ring_write (ring, addr0);amdgpu_ring_write (ring, addr1);amdgpu_ring_write (ring, ref);
@@ -2529,7 +2529,7 @@ static void gfx_v9_4_3_ring_emit_ib_comp }amdgpu_ring_write (ring,PACKET3 (PACKET3_INDIRECT_BUFFER ,2 )); -BUG_ON (ib->gpu_addr &0x3 );/* Dword align */ +WARN_ON (ib->gpu_addr &0x3 );/* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) |
@@ -2563,9 +2563,9 @@ static void gfx_v9_4_3_ring_emit_fence(s* aligned if only send 32bit data low (discard data high) */ if (write64bit) -BUG_ON (addr &0x7 ); +WARN_ON (addr &0x7 );else -BUG_ON (addr &0x3 ); +WARN_ON (addr &0x3 );amdgpu_ring_write (ring,lower_32_bits (addr));amdgpu_ring_write (ring,upper_32_bits (addr));amdgpu_ring_write (ring,lower_32_bits (seq));
@@ -2625,9 +2625,6 @@ static void gfx_v9_4_3_ring_emit_fence_k {struct amdgpu_device *adev = ring->adev; -/* we only allocate 32bit for each seq wb address */ -BUG_ON (flags &AMDGPU_FENCE_FLAG_64BIT ); -/* write fence seq to the "addr" */ amdgpu_ring_write (ring,PACKET3 (PACKET3_WRITE_DATA ,3 )); amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) |
Greg Kroah-Hartman
maintainerPATCH 341/484 drm/amdgpu/gfx9: replace BUG_ON() with WARN_ON()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alex Deucher <[email protected]>
commit 6302be10b521f5106ce01eb5a724b9e7945a5061 upstream.
There's no need to crash the kernel for these cases.
--- drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c +5 -5
@@ -983,7 +983,7 @@ static void gfx_v9_0_wait_reg_mem(struct WAIT_REG_MEM_ENGINE(eng_sel)));if (mem_space) -BUG_ON (addr0 &0x3 );/* Dword align */ +WARN_ON (addr0 &0x3 );/* Dword align */ amdgpu_ring_write (ring, addr0);amdgpu_ring_write (ring, addr1);amdgpu_ring_write (ring, ref);
@@ -5166,7 +5166,7 @@ static void gfx_v9_0_ring_emit_ib_gfx(st }amdgpu_ring_write (ring, header); -BUG_ON (ib->gpu_addr &0x3 );/* Dword align */ +WARN_ON (ib->gpu_addr &0x3 );/* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) |
@@ -5278,7 +5278,7 @@ static void gfx_v9_0_ring_emit_ib_comput }amdgpu_ring_write (ring,PACKET3 (PACKET3_INDIRECT_BUFFER ,2 )); -BUG_ON (ib->gpu_addr &0x3 );/* Dword align */ +WARN_ON (ib->gpu_addr &0x3 );/* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) |
@@ -5319,9 +5319,9 @@ static void gfx_v9_0_ring_emit_fence(str* aligned if only send 32bit data low (discard data high) */ if (write64bit) -BUG_ON (addr &0x7 ); +WARN_ON (addr &0x7 );else -BUG_ON (addr &0x3 ); +WARN_ON (addr &0x3 );amdgpu_ring_write (ring,lower_32_bits (addr));amdgpu_ring_write (ring,upper_32_bits (addr));amdgpu_ring_write (ring,lower_32_bits (seq));
Greg Kroah-Hartman
maintainerPATCH 342/484 drm/amdgpu/sdma4.4.2: replace BUG_ON() with WARN_ON()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alex Deucher <[email protected]>
commit 40cdbe9fa424cc6264a7aed93a04bd7d69109d9e upstream.
There's no need to crash the kernel for these cases.
--- drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c +2 -2
@@ -393,7 +393,7 @@ static void sdma_v4_4_2_ring_emit_fence(/* write the fence */ amdgpu_ring_write (ring,SDMA_PKT_HEADER_OP (SDMA_OP_FENCE ));/* zero in first two bits */ -BUG_ON (addr &0x3 ); +WARN_ON (addr &0x3 );amdgpu_ring_write (ring,lower_32_bits (addr));amdgpu_ring_write (ring,upper_32_bits (addr));amdgpu_ring_write (ring,lower_32_bits (seq));
@@ -403,7 +403,7 @@ static void sdma_v4_4_2_ring_emit_fence( addr +=4 ;amdgpu_ring_write (ring,SDMA_PKT_HEADER_OP (SDMA_OP_FENCE ));/* zero in first two bits */ -BUG_ON (addr &0x3 ); +WARN_ON (addr &0x3 );amdgpu_ring_write (ring,lower_32_bits (addr));amdgpu_ring_write (ring,upper_32_bits (addr));amdgpu_ring_write (ring,upper_32_bits (seq));
Greg Kroah-Hartman
maintainerPATCH 343/484 drm/amdgpu/vce: fix integer overflow in image size
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Boyuan Zhang <[email protected]>
commit 186bfdc4e26d019b2e7570cb121964a1d89b2e5b upstream.
Fix a security vulnerability where malicious VCE command streams with oversized dimensions (e.g. 65536×65536) cause 32-bit integer overflow, wrapping the calculated buffer size to 0. This bypasses validation and allows GPU firmware to perform out-of-bound memory access.
The fix uses 64-bit arithmetic to detect overflow and rejects invalid dimensions before they reach the hardware.
V2: remove redundant check
V3: modify max height value
V4: remove size64
--- drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c +14 -3
@@ -852,9 +852,20 @@ int amdgpu_vce_ring_parse_cs(struct amdg goto out; } - *size =amdgpu_ib_get_value (ib, idx +8 ) * -amdgpu_ib_get_value (ib, idx +10 ) * -8 *3 /2 ; +uint32_t width, height; + width =amdgpu_ib_get_value (ib, idx +8 ); + height =amdgpu_ib_get_value (ib, idx +10 ); + +if (width ==0 || height ==0 || + width >4096 || height >2304 ) { +DRM_ERROR ("invalid VCE image size: %ux%u\n" , + width, height); + r = -EINVAL ; + goto out; + } + + *size = width * height *8 *3 /2 ; +break ;case 0x04000001 :/* config extension */
Greg Kroah-Hartman
maintainerPATCH 344/484 drm/amdgpu/vcn4: avoid rereading IB param length
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Boyuan Zhang <[email protected]>
commit 3b4082fabc67c9780b06eb959e59dd92fa79c0f0 upstream.
Reuse the parameter length returned by vcn_v4_0_enc_find_ib_param() instead of rereading it from the IB.
This avoids a potential TOCTOU issue if the IB contents change between reads.
--- drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-)
drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c +9 -6
@@ -1769,14 +1769,17 @@ out:#define RENCODE_IB_PARAM_SESSION_INIT 0x00000003/* return the offset in ib if id is found, -1 otherwise */ -static int vcn_v4_0_enc_find_ib_param (struct amdgpu_ib *ib,uint32_t id,int start) +static int vcn_v4_0_enc_find_ib_param (struct amdgpu_ib *ib,uint32_t id,int start,uint32_t *length) {int i;uint32_t len;for (i = start; (len =amdgpu_ib_get_value (ib, i)) >=8 ; i += len /4 ) { -if (amdgpu_ib_get_value (ib, i +1 ) == id) +if (amdgpu_ib_get_value (ib, i +1 ) == id) { +if (length) + *length = len;return i; + } }return -1 ; }
@@ -1786,14 +1789,14 @@ static int vcn_v4_0_ring_patch_cs_in_pla struct amdgpu_ib *ib) {struct amdgpu_ring *ring =amdgpu_job_ring (job); -uint32_t val; +uint32_t val, len;int idx =0 , sidx;/* The first instance can decode anything */ if (!ring->me)return 0 ; -while ((idx =vcn_v4_0_enc_find_ib_param (ib,RADEON_VCN_ENGINE_INFO , idx)) >=0 ) { +while ((idx =vcn_v4_0_enc_find_ib_param (ib,RADEON_VCN_ENGINE_INFO , idx, &len)) >=0 ) { val =amdgpu_ib_get_value (ib, idx +2 );/* RADEON_VCN_ENGINE_TYPE */ if (val ==RADEON_VCN_ENGINE_TYPE_DECODE ) {uint32_t valid_buf_flag =amdgpu_ib_get_value (ib, idx +6 );
@@ -1806,12 +1809,12 @@ static int vcn_v4_0_ring_patch_cs_in_plaamdgpu_ib_get_value (ib, idx +8 );return vcn_v4_0_dec_msg (p, job, msg_buffer_addr); }else if (val ==RADEON_VCN_ENGINE_TYPE_ENCODE ) { - sidx =vcn_v4_0_enc_find_ib_param (ib,RENCODE_IB_PARAM_SESSION_INIT , idx); + sidx =vcn_v4_0_enc_find_ib_param (ib,RENCODE_IB_PARAM_SESSION_INIT , idx,NULL );if (sidx >=0 &&amdgpu_ib_get_value (ib, sidx +2 ) ==RENCODE_ENCODE_STANDARD_AV1 )return vcn_v4_0_limit_sched (p, job); } - idx +=amdgpu_ib_get_value (ib, idx) /4 ; + idx += len /4 ; }return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 345/484 drm/dp_mst: Handle torn-down topology gracefully in drm_dp_mst_topology_queue_probe()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luca Coelho <[email protected]>
commit 613059875958e7b217b250ed14c3b189f9488421 upstream.
A hotplug or link-loss event can tear down the MST topology (setting mgr->mst_state = false and mgr->mst_primary = NULL) concurrently with a caller invoking drm_dp_mst_topology_queue_probe(). Since the check is already performed under mgr->lock, the condition is not a programming error but a valid race -- the topology was valid when the caller decided to call this function, but was torn down before the lock was acquired.
Replace the drm_WARN_ON() with a graceful early return. This eliminates spurious kernel warnings and the resulting compositor crashes observed when connecting/disconnecting DP MST monitors, while keeping the correct behavior of doing nothing when MST is not active. A drm_dbg_mst() trace is added so the skipped probe remains observable under MST debug logging.
The existing WARN_ON(mgr->mst_primary) in drm_dp_mst_topology_mgr_set_mst() already catches the case where the topology is initialized twice, so no diagnostic coverage is lost.
--- drivers/gpu/drm/display/drm_dp_mst_topology.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/gpu/drm/display/drm_dp_mst_topology.c +3 -1
@@ -3703,8 +3703,10 @@ void drm_dp_mst_topology_queue_probe(str {mutex_lock (&mgr->lock); -if (drm_WARN_ON (mgr->dev, !mgr->mst_state || !mgr->mst_primary)) +if (!mgr->mst_state || !mgr->mst_primary) { +drm_dbg_kms (mgr->dev,"queue_probe skipped: topology torn down\n" ); goto out_unlock; + }drm_dp_mst_topology_mgr_invalidate_mstb (mgr->mst_primary);drm_dp_mst_queue_probe_work (mgr);
Greg Kroah-Hartman
maintainerPATCH 346/484 drm/amdgpu: fix division by zero with invalid uvd dimensions
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Boyuan Zhang <[email protected]>
commit 0c01c811be47e6b146552dd59bfedbea8f09b8f4 upstream.
When width or height is less than 16, width_in_mb or height_in_mb becomes 0, leading to fs_in_mb being 0. This causes a division by zero when calculating num_dpb_buffer in H264 and H264 Perf decode paths.
Add validation to reject frames with width < 16 or height < 16 before performing any calculations that depend on these values.
V2: Format change - move up all vaiable definitions.
V3: Use warn_once to avoid spam.
--- drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c | 8 ++++++++ 1 file changed, 8 insertions(+)
drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +8 -0
@@ -647,6 +647,14 @@ static int amdgpu_uvd_cs_msg_decode(struunsigned int image_size, tmp, min_dpb_size, num_dpb_buffer;unsigned int min_ctx_size = ~0 ; +/* Reject invalid dimensions to prevent division by zero */ +if (width <16 || height <16 ) { +dev_WARN_ONCE (adev->dev,1 , +"Invalid UVD decoding dimensions (%dx%d)!\n" , + width, height); +return -EINVAL ; + } + image_size = width * height; image_size += image_size /2 ; image_size =ALIGN (image_size,1024 );
Greg Kroah-Hartman
maintainerPATCH 347/484 drm/amdgpu: invoke pm_genpd_remove() before freeing genpd
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ce Sun <[email protected]>
commit 28c9b3c5dc35cc790d11e26ca3fc6e068be63998 upstream.
Call pm_genpd_remove() to unregister from global list prior to releasing acp_genpd memory, and clear the pointer after free.
--- drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c +2 -0
@@ -559,7 +559,9 @@ static int acp_hw_fini(void *handle)mfd_remove_devices (adev->acp.parent);kfree (adev->acp.acp_res); +pm_genpd_remove (&adev->acp.acp_genpd->gpd);kfree (adev->acp.acp_genpd); + adev->acp.acp_genpd =NULL ;kfree (adev->acp.acp_cell);return 0 ;
Greg Kroah-Hartman
maintainerPATCH 348/484 drm/amdgpu: fix aperture mapping leak
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Asad Kamal <[email protected]>
commit ea772a440d56b285f4d491affac50ecd41f6b402 upstream.
amdgpu_pci_remove() calls drm_dev_unplug() before invoking the driver fini routines. This causes drm_dev_enter() in amdgpu_ttm_fini() to always return false, so iounmap(aper_base_kaddr) never runs on normal driver unload, leaving an orphaned entry in the x86 PAT interval tree.
On connected_to_cpu hardware, the aperture is mapped write-back (WB) via ioremap_cache(). On reload, IP discovery calls memremap(..., MEMREMAP_WC) over the same range. The WC vs WB conflict causes:
ioremap error for 0x..., requested 0x1, got 0x0
amdgpu: discovery failed: -2
Fix by switching to devres-managed mappings so cleanup is guaranteed regardless of drm_dev_enter() state:
- connected_to_cpu path: devm_memremap(MEMREMAP_WB). For IORESOURCE_SYSTEM_RAM ranges this takes the try_ram_remap() shortcut, returning __va(offset) from the existing kernel direct map. No new ioremap VA or PAT entry is created, so there is nothing to orphan.
- dGPU path: devm_ioremap_wc() registers iounmap() as a devres action, guaranteeing cleanup at device_del() time.
Also remove iounmap(aper_base_kaddr) from amdgpu_device_unmap_mmio() since the mapping is now devres-owned.
v2: Remove redundant x86_64 guard (Lijo)
--- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 2 - drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 36 ++++++++++++----------------- 2 files changed, 16 insertions(+), 22 deletions(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +0 -2
@@ -3977,8 +3977,6 @@ static void amdgpu_device_unmap_mmio(striounmap (adev->rmmio); adev->rmmio =NULL ; -if (adev->mman.aper_base_kaddr) -iounmap (adev->mman.aper_base_kaddr); adev->mman.aper_base_kaddr =NULL ;/* Memory manager related */
drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +16 -20
@@ -1887,18 +1887,23 @@ int amdgpu_ttm_init(struct amdgpu_device/* Change the size here instead of the init above so only lpfn is affected */ amdgpu_ttm_set_buffer_funcs_status (adev, false);#ifdef CONFIG_64BIT -#ifdef CONFIG_X86 -if (adev->gmc.xgmi.connected_to_cpu) - adev->mman.aper_base_kaddr =ioremap_cache (adev->gmc.aper_base, - adev->gmc.visible_vram_size); - -else if (adev->gmc.is_app_apu) +if (adev->gmc.xgmi.connected_to_cpu) { +void *kaddr =devm_memremap (adev->dev, adev->gmc.aper_base, + adev->gmc.visible_vram_size, +MEMREMAP_WB ); +if (IS_ERR (kaddr)) +return PTR_ERR (kaddr); + adev->mman.aper_base_kaddr = (__force void __iomem *)kaddr; + }else if (adev->gmc.is_app_apu) {DRM_DEBUG_DRIVER ("No need to ioremap when real vram size is 0\n" ); - else -#endif - adev->mman.aper_base_kaddr =ioremap_wc (adev->gmc.aper_base, - adev->gmc.visible_vram_size); + }else { + adev->mman.aper_base_kaddr =devm_ioremap_wc (adev->dev, + adev->gmc.aper_base, + adev->gmc.visible_vram_size); +if (!adev->mman.aper_base_kaddr) +return -ENOMEM ; + }#endif /*
@@ -2033,8 +2038,6 @@ int amdgpu_ttm_init(struct amdgpu_device*/ void amdgpu_ttm_fini (struct amdgpu_device *adev) { -int idx; -if (!adev->mman.initialized)return ;
@@ -2057,14 +2060,7 @@ void amdgpu_ttm_fini(struct amdgpu_devicamdgpu_ttm_fw_reserve_vram_fini (adev);amdgpu_ttm_drv_reserve_vram_fini (adev); -if (drm_dev_enter (adev_to_drm (adev), &idx)) { - -if (adev->mman.aper_base_kaddr) -iounmap (adev->mman.aper_base_kaddr); - adev->mman.aper_base_kaddr =NULL ; - -drm_dev_exit (idx); - } + adev->mman.aper_base_kaddr =NULL ;amdgpu_vram_mgr_fini (adev);amdgpu_gtt_mgr_fini (adev);
Greg Kroah-Hartman
maintainerPATCH 349/484 i40e: remove read access to debugfs files
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jacob Keller <[email protected]>
[ Upstream commit 9fcdb1c3c4ba134434694c001dbff343f1ffa319 ]
The 'command' and 'netdev_ops' debugfs files are a legacy debugging interface supported by the i40e driver since its early days by commit 02e9c290814c ("i40e: debugfs interface").
Both of these debugfs files provide a read handler which is mostly useless, and which is implemented with questionable logic. They both use a static 256 byte buffer which is initialized to the empty string. In the case of the 'command' file this buffer is literally never used and simply wastes space. In the case of the 'netdev_ops' file, the last command written is saved here.
On read, the files contents are presented as the name of the device followed by a colon and then the contents of their respective static buffer. For 'command' this will always be "<device>: ". For 'netdev_ops', this will be "<device>: <last command written>". But note the buffer is shared between all devices operated by this module. At best, it is mostly meaningless information, and at worse it could be accessed simultaneously as there doesn't appear to be any locking mechanism.
We have also recently received multiple reports for both read functions about their use of snprintf and potential overflow that could result in reading arbitrary kernel memory. For the 'command' file, this is definitely impossible, since the static buffer is always zero and never written to. For the 'netdev_ops' file, it does appear to be possible, if the user carefully crafts the command input, it will be copied into the buffer, which could be large enough to cause snprintf to truncate, which then causes the copy_to_user to read beyond the length of the buffer allocated by kzalloc.
A minimal fix would be to replace snprintf() with scnprintf() which would cap the return to the number of bytes written, preventing an overflow. A more involved fix would be to drop the mostly useless static buffers, saving 512 bytes and modifying the read functions to stop needing those as input.
Instead, lets just completely drop the read access to these files. These are debug interfaces exposed as part of debugfs, and I don't believe that dropping read access will break any script, as the provided output is pretty useless. You can find the netdev name through other more standard interfaces, and the 'netdev_ops' interface can easily result in garbage if you issue simultaneous writes to multiple devices at once.
In order to properly remove the i40e_dbg_netdev_ops_buf, we need to refactor its write function to avoid using the static buffer. Instead, use the same logic as the i40e_dbg_command_write, with an allocated buffer. Update the code to use this instead of the static buffer, and ensure we free the buffer on exit. This fixes simultaneous writes to 'netdev_ops' on multiple devices, and allows us to remove the now unused static buffer along with removing the read access.
vs i40e_pf_get_main_vsi(pf) in the removed read functions; resolution is simply to delete the 6.6 version of those functions.]
--- .../net/ethernet/intel/i40e/i40e_debugfs.c | 121 +++--------------- 1 file changed, 19 insertions(+), 102 deletions(-)
drivers/net/ethernet/intel/i40e/i40e_debugfs.c +19 -102
@@ -58,47 +58,6 @@ static struct i40e_veb *i40e_dbg_find_veb(struct i40e_pf *pf, int seid)* setup, adding or removing filters, or other things. Many of * these will be useful for some forms of unit testing. **************************************************************/ -static char i40e_dbg_command_buf[256 ] ="" ; - -/** -* i40e_dbg_command_read - read for command datum -* @filp: the opened file -* @buffer: where to write the data for the user to read -* @count: the size of the user's buffer -* @ppos: file position offset -**/ -static ssize_t i40e_dbg_command_read (struct file *filp,char __user *buffer, -size_t count,loff_t *ppos) -{ -struct i40e_pf *pf = filp->private_data; -int bytes_not_copied; -int buf_size =256 ; -char *buf; -int len; - -/* don't allow partial reads */ -if (*ppos !=0 ) -return 0 ; -if (count < buf_size) -return -ENOSPC ; - - buf =kzalloc (buf_size,GFP_KERNEL ); -if (!buf) -return -ENOSPC ; - - len =snprintf (buf, buf_size,"%s: %s\n" , - pf->vsi[pf->lan_vsi]->netdev->name, - i40e_dbg_command_buf); - - bytes_not_copied =copy_to_user (buffer, buf, len); -kfree (buf); - -if (bytes_not_copied) -return -EFAULT ; - - *ppos = len; -return len; -}static char *i40e_filter_state_string[] = {"INVALID" ,
@@ -1637,7 +1596,6 @@ static ssize_t i40e_dbg_command_write(struct file *filp,static const struct file_operations i40e_dbg_command_fops = { .owner =THIS_MODULE , .open = simple_open, - .read = i40e_dbg_command_read, .write = i40e_dbg_command_write, };
@@ -1646,47 +1604,6 @@ static const struct file_operations i40e_dbg_command_fops = {* The netdev_ops entry in debugfs is for giving the driver commands * to be executed from the netdev operations. **************************************************************/ -static char i40e_dbg_netdev_ops_buf[256 ] ="" ; - -/** -* i40e_dbg_netdev_ops_read - read for netdev_ops datum -* @filp: the opened file -* @buffer: where to write the data for the user to read -* @count: the size of the user's buffer -* @ppos: file position offset -**/ -static ssize_t i40e_dbg_netdev_ops_read (struct file *filp,char __user *buffer, -size_t count,loff_t *ppos) -{ -struct i40e_pf *pf = filp->private_data; -int bytes_not_copied; -int buf_size =256 ; -char *buf; -int len; - -/* don't allow partal reads */ -if (*ppos !=0 ) -return 0 ; -if (count < buf_size) -return -ENOSPC ; - - buf =kzalloc (buf_size,GFP_KERNEL ); -if (!buf) -return -ENOSPC ; - - len =snprintf (buf, buf_size,"%s: %s\n" , - pf->vsi[pf->lan_vsi]->netdev->name, - i40e_dbg_netdev_ops_buf); - - bytes_not_copied =copy_to_user (buffer, buf, len); -kfree (buf); - -if (bytes_not_copied) -return -EFAULT ; - - *ppos = len; -return len; -}/** * i40e_dbg_netdev_ops_write - write into netdev_ops datum
@@ -1700,35 +1617,36 @@ static ssize_t i40e_dbg_netdev_ops_write(struct file *filp, size_t count, loff_t *ppos) { struct i40e_pf *pf = filp->private_data; +char *cmd_buf, *buf_tmp;int bytes_not_copied;struct i40e_vsi *vsi; -char *buf_tmp;int vsi_seid;int i, cnt;/* don't allow partial writes */ if (*ppos !=0 )return 0 ; -if (count >=sizeof (i40e_dbg_netdev_ops_buf)) -return -ENOSPC ; -memset (i40e_dbg_netdev_ops_buf,0 ,sizeof (i40e_dbg_netdev_ops_buf)); - bytes_not_copied =copy_from_user (i40e_dbg_netdev_ops_buf, - buffer, count); -if (bytes_not_copied) + cmd_buf =kzalloc (count +1 ,GFP_KERNEL ); +if (!cmd_buf) +return count; + bytes_not_copied =copy_from_user (cmd_buf, buffer, count); +if (bytes_not_copied) { +kfree (cmd_buf);return -EFAULT ; - i40e_dbg_netdev_ops_buf[count] ='\0' ; + } + cmd_buf[count] ='\0' ; - buf_tmp =strchr (i40e_dbg_netdev_ops_buf,'\n' ); + buf_tmp =strchr (cmd_buf,'\n' );if (buf_tmp) { *buf_tmp ='\0' ; - count = buf_tmp - i40e_dbg_netdev_ops_buf +1 ; + count = buf_tmp - cmd_buf +1 ; } -if (strncmp (i40e_dbg_netdev_ops_buf,"change_mtu" ,10 ) ==0 ) { +if (strncmp (cmd_buf,"change_mtu" ,10 ) ==0 ) {int mtu; - cnt =sscanf (&i40e_dbg_netdev_ops_buf[11 ],"%i %i" , + cnt =sscanf (&cmd_buf[11 ],"%i %i" , &vsi_seid, &mtu);if (cnt !=2 ) {dev_info (&pf->pdev->dev,"change_mtu <vsi_seid> <mtu>\n" );
@@ -1750,8 +1668,8 @@ static ssize_t i40e_dbg_netdev_ops_write(struct file *filp,dev_info (&pf->pdev->dev,"Could not acquire RTNL - please try again\n" ); } - }else if (strncmp (i40e_dbg_netdev_ops_buf,"set_rx_mode" ,11 ) ==0 ) { - cnt =sscanf (&i40e_dbg_netdev_ops_buf[11 ],"%i" , &vsi_seid); + }else if (strncmp (cmd_buf,"set_rx_mode" ,11 ) ==0 ) { + cnt =sscanf (&cmd_buf[11 ],"%i" , &vsi_seid);if (cnt !=1 ) {dev_info (&pf->pdev->dev,"set_rx_mode <vsi_seid>\n" ); goto netdev_ops_write_done;
@@ -1771,8 +1689,8 @@ static ssize_t i40e_dbg_netdev_ops_write(struct file *filp,dev_info (&pf->pdev->dev,"Could not acquire RTNL - please try again\n" ); } - }else if (strncmp (i40e_dbg_netdev_ops_buf,"napi" ,4 ) ==0 ) { - cnt =sscanf (&i40e_dbg_netdev_ops_buf[4 ],"%i" , &vsi_seid); + }else if (strncmp (cmd_buf,"napi" ,4 ) ==0 ) { + cnt =sscanf (&cmd_buf[4 ],"%i" , &vsi_seid);if (cnt !=1 ) {dev_info (&pf->pdev->dev,"napi <vsi_seid>\n" ); goto netdev_ops_write_done;
@@ -1790,21 +1708,20 @@ static ssize_t i40e_dbg_netdev_ops_write(struct file *filp,dev_info (&pf->pdev->dev,"napi called\n" ); } }else { -dev_info (&pf->pdev->dev,"unknown command '%s'\n" , - i40e_dbg_netdev_ops_buf); +dev_info (&pf->pdev->dev,"unknown command '%s'\n" , cmd_buf);dev_info (&pf->pdev->dev,"available commands\n" );dev_info (&pf->pdev->dev," change_mtu <vsi_seid> <mtu>\n" );dev_info (&pf->pdev->dev," set_rx_mode <vsi_seid>\n" );dev_info (&pf->pdev->dev," napi <vsi_seid>\n" ); } netdev_ops_write_done: +kfree (cmd_buf);return count; }static const struct file_operations i40e_dbg_netdev_ops_fops = { .owner =THIS_MODULE , .open = simple_open, - .read = i40e_dbg_netdev_ops_read, .write = i40e_dbg_netdev_ops_write, };
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 350/484 ipv6: ndisc: fix NULL deref in accept_untracked_na()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Weiming Shi <[email protected]>
commit d186e942365acece7c56d39da05dd63bf95b280a upstream.
accept_untracked_na() re-fetches the inet6_dev with __in6_dev_get(dev) and dereferences idev->cnf.accept_untracked_na without a NULL check, even though its only caller ndisc_recv_na() already fetched and NULL-checked idev for the same device.
Both reads of dev->ip6_ptr run in the same RCU read-side critical section, but a concurrent addrconf_ifdown() can clear dev->ip6_ptr between them: lowering the MTU below IPV6_MIN_MTU calls addrconf_ifdown() without the synchronize_net() that orders the unregister path, so the re-fetch returns NULL and oopses:
BUG: KASAN: null-ptr-deref in ndisc_recv_na (net/ipv6/ndisc.c:974) Read of size 4 at addr 0000000000000364 Call Trace: <IRQ> ndisc_recv_na (net/ipv6/ndisc.c:974) icmpv6_rcv (net/ipv6/icmp.c:1193) ip6_protocol_deliver_rcu (net/ipv6/ip6_input.c:479) ip6_input_finish (net/ipv6/ip6_input.c:534) ip6_input (net/ipv6/ip6_input.c:545) ip6_mc_input (net/ipv6/ip6_input.c:635) ipv6_rcv (net/ipv6/ip6_input.c:351) </IRQ>
It is reachable by an unprivileged user via a network namespace.
Pass the caller's already validated idev instead of re-fetching it; the idev stays alive for the whole RCU critical section, so it is safe even after dev->ip6_ptr has been cleared.
--- net/ipv6/ndisc.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-)
net/ipv6/ndisc.c +3 -5
@@ -973,10 +973,8 @@ static enum skb_drop_reason ndisc_recv_ns(struct sk_buff *skb)return reason; } -static int accept_untracked_na (struct net_device *dev,struct in6_addr *saddr) +static int accept_untracked_na (struct inet6_dev *idev,struct in6_addr *saddr) { -struct inet6_dev *idev =__in6_dev_get (dev); -switch (READ_ONCE (idev->cnf.accept_untracked_na)) {case 0 :/* Don't accept untracked na (absent in neighbor cache) */ return 0 ;
@@ -986,7 +984,7 @@ static int accept_untracked_na(struct net_device *dev, struct in6_addr *saddr)* same subnet as an address configured on the interface that * received the na */ -return !!ipv6_chk_prefix (saddr, dev); +return !!ipv6_chk_prefix (saddr, idev->dev);default :return 0 ; }
@@ -1085,7 +1083,7 @@ static enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb)*/ new_state = msg->icmph.icmp6_solicited ?NUD_REACHABLE :NUD_STALE ;if (!neigh && lladdr && idev && idev->cnf.forwarding) { -if (accept_untracked_na (dev, saddr)) { +if (accept_untracked_na (idev, saddr)) { neigh =neigh_create (&nd_tbl, &msg->target, dev); new_state =NUD_STALE ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 351/484 net: qrtr: ns: Raise node count limit to 512
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Youssef Samir <[email protected]>
commit ff194cffd586cbd4cc49eccb002c65f2a902a277 upstream.
The current node limit of 64 breaks the functionality for a number of AI200 deployments that have up to 384 nodes. Raise the limit to 512.
--- net/qrtr/ns.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
net/qrtr/ns.c +2 -2
@@ -85,9 +85,9 @@ struct qrtr_node {/* Max nodes limit is chosen based on the current platform requirements. * If the requirement changes in the future, this value can be increased. */ -#define QRTR_NS_MAX_NODES 64 +#define QRTR_NS_MAX_NODES 512 -static u8 node_count; +static u16 node_count;static struct qrtr_node *node_get (unsigned int node_id) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 352/484 ksmbd: validate num_subauth when copying ACE in set_ntacl_dacl
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Haofeng Li <[email protected]>
commit 47f0b34f6bc98ed85bfdc293e8f3e432ec24958d upstream.
set_ntacl_dacl() copies each ACE from the attacker-controlled stored security descriptor verbatim into the response DACL without checking sid.num_subauth. The ACE bytes (including an unchecked num_subauth) originate from an authenticated SMB2_SET_INFO(SecInfo=DACL) that is stored raw via ksmbd_vfs_set_sd_xattr(); parse_dacl() rejects a bad ACE with break rather than an error, so parse_sec_desc() still returns success and the malformed SD reaches the xattr intact.
On a subsequent SMB2_QUERY_INFO(SecInfo=DACL) for an inode carrying a POSIX access ACL, build_sec_desc() -> set_ntacl_dacl() -> set_posix_acl_entries_dacl() walks the copied ACEs and reads
ntace->sid.sub_auth[ntace->sid.num_subauth - 1]
with num_subauth taken straight from the stored SD. Since sub_auth[] is fixed at SID_MAX_SUB_AUTHORITIES (15), a crafted num_subauth (e.g.
255) drives an out-of-bounds heap read of ~1 KB with an offset fully controlled by an authenticated client.
The sibling functions already gate this field:
parse_dacl() -- num_subauth == 0 || > SID_MAX_SUB_AUTHORITIES parse_sid() -- num_subauth > SID_MAX_SUB_AUTHORITIES smb_copy_sid() -- min_t(u8, num_subauth, SID_MAX_SUB_AUTHORITIES)
set_ntacl_dacl() is the lone inconsistent path that omits the check.
Add the same num_subauth validation in set_ntacl_dacl() before copying the ACE, matching the gate already enforced by parse_dacl().
--- fs/smb/server/smbacl.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
fs/smb/server/smbacl.c +7 -1
@@ -745,12 +745,18 @@ static void set_ntacl_dacl(struct mnt_idmap *idmap,if (nt_ace_size > aces_size)break ; +if (ntace->sid.num_subauth ==0 || + ntace->sid.num_subauth >SID_MAX_SUB_AUTHORITIES ) + goto next_ace; +memcpy ((char *)pndace + size, ntace, nt_ace_size);if (check_add_overflow (size, nt_ace_size, &size))break ; + num_aces++; + +next_ace: aces_size -= nt_ace_size; ntace = (struct smb_ace *)((char *)ntace + nt_ace_size); - num_aces++; } }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 353/484 ksmbd: restore DACL size on check_add_overflow() to avoid malformed ACL
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wentao Guan <[email protected]>
commit bbf0a8e931204ecdab494a88d43b0a24a04285c5 upstream.
check_add_overflow() unconditionally writes the truncated sum into *d even on overflow, per its contract in include/linux/overflow.h. The four check_add_overflow() guards in set_posix_acl_entries_dacl() and set_ntacl_dacl() break out of the ACE-building loops on overflow, but the truncated *size is then consumed downstream at the end of set_ntacl_dacl():
pndacl->size = cpu_to_le16(le16_to_cpu(pndacl->size) + size);
This produces an on-wire NT ACL whose pndacl->size under-reports the bytes actually written by the preceding fill_ace_for_sid()/memcpy() calls, yielding a malformed ACL that can trigger out-of-bounds reads when re-parsed by clients or ksmbd itself.
Restore *size to its pre-addition value on each overflow branch (via *size -= ace_sz / size -= nt_ace_size) so that after the break, *size once again holds the cumulative size of the successfully-written ACEs. The committed ACL is then truncated-but-self-consistent rather than malformed.
The ksmbd DACL builders are the only check_add_overflow() sites found where an overflow path breaks out of a loop and the destination value is consumed afterward. The other nearby break-style cases either return -EINVAL on overflow (transport_ipc.c) or break without consuming the overflowed destination value afterward (buildid.c).
--- fs/smb/server/smbacl.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
fs/smb/server/smbacl.c +6 -1
@@ -649,6 +649,7 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap, ace_sz =fill_ace_for_sid (ntace, sid,ACCESS_ALLOWED , flags, pace->e_perm,0777 );if (check_add_overflow (*size, ace_sz, size)) { + *size -= ace_sz;kfree (sid);break ; }
@@ -663,6 +664,7 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap, ace_sz =fill_ace_for_sid (ntace, sid,ACCESS_ALLOWED ,0x03 , pace->e_perm,0777 );if (check_add_overflow (*size, ace_sz, size)) { + *size -= ace_sz;kfree (sid);break ; }
@@ -708,6 +710,7 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap, ace_sz =fill_ace_for_sid (ntace, sid,ACCESS_ALLOWED ,0x0b , pace->e_perm,0777 );if (check_add_overflow (*size, ace_sz, size)) { + *size -= ace_sz;kfree (sid);break ; }
@@ -750,8 +753,10 @@ static void set_ntacl_dacl(struct mnt_idmap *idmap, goto next_ace;memcpy ((char *)pndace + size, ntace, nt_ace_size); -if (check_add_overflow (size, nt_ace_size, &size)) +if (check_add_overflow (size, nt_ace_size, &size)) { + size -= nt_ace_size;break ; + } num_aces++; next_ace:
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 354/484 ksmbd: bound DACL dedup walk to copied ACEs
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Namjae Jeon <[email protected]>
commit 58d97fcd0bf1aee694e244cc28635b9df95b543b upstream.
set_ntacl_dacl() can stop copying ACEs before consuming the full input DACL when size accounting overflows.
When that happens, num_aces reflects only the ACEs that were actually copied into the output DACL, but set_posix_acl_entries_dacl() still receives nt_num_aces and uses it to walk the existing ACE array during dedup.
That makes the dedup walk scan past the copied ACE array and inspect buffer tail that does not contain valid ACEs.
Split the two meanings currently carried by the NT ACE count. Pass the number of copied NT ACEs to bound the dedup walk, and preserve the original "input DACL had NT ACEs" state separately for the Everyone/default ACL fallback.
This keeps the dedup walk aligned with the ACEs that are actually present in the rebuilt DACL.
--- fs/smb/server/smbacl.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-)
fs/smb/server/smbacl.c +10 -6
@@ -595,7 +595,8 @@ static void parse_dacl(struct mnt_idmap *idmap,static void set_posix_acl_entries_dacl (struct mnt_idmap *idmap,struct smb_ace *pndace,struct smb_fattr *fattr,u16 *num_aces, -u16 *size,u32 nt_aces_num) +u16 *size,u16 existing_nt_aces, +bool had_nt_aces) {struct posix_acl_entry *pace;struct smb_sid *sid;
@@ -627,14 +628,14 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap, gid =posix_acl_gid_translate (idmap, pace);id_to_sid (gid,SIDUNIX_GROUP , sid); - }else if (pace->e_tag ==ACL_OTHER && !nt_aces_num) { + }else if (pace->e_tag ==ACL_OTHER && !had_nt_aces) {smb_copy_sid (sid, &sid_everyone); }else {kfree (sid);continue ; } ntace = pndace; -for (j =0 ; j < nt_aces_num; j++) { +for (j =0 ; j < existing_nt_aces; j++) {if (ntace->sid.sub_auth[ntace->sid.num_subauth -1 ] == sid->sub_auth[sid->num_subauth -1 ]) goto pass_same_sid;
@@ -678,7 +679,7 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap,kfree (sid); } -if (nt_aces_num) +if (had_nt_aces)return ; posix_default_acl:
@@ -732,6 +733,7 @@ static void set_ntacl_dacl(struct mnt_idmap *idmap, {struct smb_ace *ntace, *pndace;u16 nt_num_aces =le16_to_cpu (nt_dacl->num_aces), num_aces =0 ; +u16 copied_nt_aces;unsigned short size =0 ;int i;
@@ -765,8 +767,10 @@ static void set_ntacl_dacl(struct mnt_idmap *idmap, } } + copied_nt_aces = num_aces;set_posix_acl_entries_dacl (idmap, pndace, fattr, - &num_aces, &size, nt_num_aces); + &num_aces, &size, copied_nt_aces, + nt_num_aces !=0 ); pndacl->num_aces =cpu_to_le16 (num_aces); pndacl->size =cpu_to_le16 (le16_to_cpu (pndacl->size) + size); }
@@ -784,7 +788,7 @@ static void set_mode_dacl(struct mnt_idmap *idmap,if (fattr->cf_acls) {set_posix_acl_entries_dacl (idmap, pndace, fattr, - &num_aces, &size, num_aces); + &num_aces, &size, num_aces, false); goto out; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 355/484 ksmbd: validate ACE size against SID sub-authorities
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Namjae Jeon <[email protected]>
commit 5152c6d49e3fd4e9f2e857c57527aead752f1f87 upstream.
set_ntacl_dacl() validates sid.num_subauth before copying an ACE, but does not verify that the declared ACE size contains all sub-authorities described by that field. An undersized ACE can therefore be copied and later make the POSIX ACL deduplication walk inspect data beyond the copied ACE boundary.
The existing initial bound check is also too small. It only ensures that the ACE size field is accessible before set_ntacl_dacl() reads sid.num_subauth farther into the input buffer.
Require enough input for the fixed SID header before accessing num_subauth, reject ACEs smaller than that header, and skip ACEs whose declared size cannot contain the complete SID. This makes the validation consistent with the other ACE walk paths.
--- fs/smb/server/smbacl.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-)
fs/smb/server/smbacl.c +10 -3
@@ -743,15 +743,22 @@ static void set_ntacl_dacl(struct mnt_idmap *idmap,for (i =0 ; i < nt_num_aces; i++) {unsigned short nt_ace_size; -if (offsetof(struct smb_ace , access_req) > aces_size) +if (aces_size < offsetof(struct smb_ace , sid) + +CIFS_SID_BASE_SIZE )break ; nt_ace_size =le16_to_cpu (ntace->size); -if (nt_ace_size > aces_size) +if (nt_ace_size > aces_size || + nt_ace_size < offsetof(struct smb_ace , sid) + +CIFS_SID_BASE_SIZE )break ;if (ntace->sid.num_subauth ==0 || - ntace->sid.num_subauth >SID_MAX_SUB_AUTHORITIES ) + ntace->sid.num_subauth >SID_MAX_SUB_AUTHORITIES || + nt_ace_size < offsetof(struct smb_ace , sid) + +CIFS_SID_BASE_SIZE + +sizeof (__le32) * + ntace->sid.num_subauth) goto next_ace;memcpy ((char *)pndace + size, ntace, nt_ace_size);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 356/484 openvswitch: fix GSO userspace truncation underflow
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kyle Zeng <[email protected]>
[ Upstream commit 4032f8ed10fcb84d41c508dfb04be96589f78dfe ]
OVS_ACTION_ATTR_TRUNC currently stores a delta from the original skb length in OVS_CB(skb)->cutlen. When a later userspace action segments a GSO skb, queue_gso_packets() reuses that delta for each smaller segment. A segment can then reach queue_userspace_packet() with cutlen greater than skb->len, underflowing the length passed to skb_zerocopy().
Store the maximum preserved length instead and bound each consumer against the current skb length. Use U32_MAX as the no-truncation sentinel so the value remains valid if skb geometry changes before a consumer handles it.
--- net/openvswitch/actions.c | 15 +++++---------- net/openvswitch/datapath.c | 25 ++++++++++++++----------- net/openvswitch/datapath.h | 2 +- net/openvswitch/vport.c | 2 +- 4 files changed, 21 insertions(+), 23 deletions(-)
net/openvswitch/actions.c +5 -10
@@ -861,12 +861,8 @@ static void do_output(struct datapath *dp, struct sk_buff *skb, int out_port,u16 mru =OVS_CB (skb)->mru;u32 cutlen =OVS_CB (skb)->cutlen; -if (unlikely (cutlen >0 )) { -if (skb->len - cutlen >ovs_mac_header_len (key)) -pskb_trim (skb, skb->len - cutlen); -else -pskb_trim (skb,ovs_mac_header_len (key)); - } +if (unlikely (cutlen < skb->len)) +pskb_trim (skb,max (cutlen,ovs_mac_header_len (key)));if (likely (!mru || (skb->len <= mru + vport->dev->hard_header_len))) {
@@ -1258,22 +1254,21 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, clone =skb_clone (skb,GFP_ATOMIC );if (clone)do_output (dp, clone, port, key); -OVS_CB (skb)->cutlen =0 ; +OVS_CB (skb)->cutlen =U32_MAX ;break ; }case OVS_ACTION_ATTR_TRUNC : {struct ovs_action_trunc *trunc =nla_data (a); -if (skb->len > trunc->max_len) -OVS_CB (skb)->cutlen = skb->len - trunc->max_len; +OVS_CB (skb)->cutlen = trunc->max_len;break ; }case OVS_ACTION_ATTR_USERSPACE :output_userspace (dp, skb, key, a, attr, len,OVS_CB (skb)->cutlen); -OVS_CB (skb)->cutlen =0 ; +OVS_CB (skb)->cutlen =U32_MAX ;if (nla_is_last (a, rem)) {consume_skb (skb);return 0 ;
net/openvswitch/datapath.c +14 -11
@@ -273,7 +273,7 @@ void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key) upcall.portid =ovs_vport_find_upcall_portid (p, skb); upcall.mru =OVS_CB (skb)->mru; - error =ovs_dp_upcall (dp, skb, key, &upcall,0 ); + error =ovs_dp_upcall (dp, skb, key, &upcall,U32_MAX );switch (error) {case 0 :case -EAGAIN :
@@ -438,7 +438,8 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,struct sk_buff *nskb =NULL ;struct sk_buff *user_skb =NULL ;/* to be queued to userspace */ struct nlattr *nla; -size_t len; +size_t msg_size; +size_t skb_len;unsigned int hlen;int err, dp_ifindex;u64 hash;
@@ -459,7 +460,8 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb, skb = nskb; } -if (nla_attr_size (skb->len) >USHRT_MAX ) { + skb_len =min (skb->len, cutlen); +if (nla_attr_size (skb_len) >USHRT_MAX ) { err = -EFBIG ; goto out; }
@@ -474,13 +476,13 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,* padding logic. Only perform zerocopy if padding is not required. */ if (dp->user_features &OVS_DP_F_UNALIGNED ) - hlen =skb_zerocopy_headlen (skb); + hlen =min (skb_zerocopy_headlen (skb), cutlen);else - hlen = skb->len; + hlen = skb_len; - len =upcall_msg_size (upcall_info, hlen - cutlen, -OVS_CB (skb)->acts_origlen); - user_skb =genlmsg_new (len,GFP_ATOMIC ); + msg_size =upcall_msg_size (upcall_info, hlen, +OVS_CB (skb)->acts_origlen); + user_skb =genlmsg_new (msg_size,GFP_ATOMIC );if (!user_skb) { err = -ENOMEM ; goto out;
@@ -541,7 +543,7 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb, }/* Add OVS_PACKET_ATTR_LEN when packet is truncated */ -if (cutlen >0 && +if (skb_len < skb->len &&nla_put_u32 (user_skb,OVS_PACKET_ATTR_LEN , skb->len)) { err = -ENOBUFS ; goto out;
@@ -566,9 +568,9 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb, err = -ENOBUFS ; goto out; } - nla->nla_len =nla_attr_size (skb->len - cutlen); + nla->nla_len =nla_attr_size (skb_len); - err =skb_zerocopy (user_skb, skb, skb->len - cutlen, hlen); + err =skb_zerocopy (user_skb, skb, skb_len, hlen);if (err) goto out;
@@ -625,6 +627,7 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info) packet->ignore_df =1 ; }OVS_CB (packet)->mru = mru; +OVS_CB (packet)->cutlen =U32_MAX ;if (a[OVS_PACKET_ATTR_HASH ]) { hash =nla_get_u64 (a[OVS_PACKET_ATTR_HASH ]);
net/openvswitch/datapath.h +1 -1
@@ -114,7 +114,7 @@ struct datapath {* @mru: The maximum received fragement size; 0 if the packet is not * fragmented. * @acts_origlen: The netlink size of the flow actions applied to this skb. -* @cutlen: The number of bytes from the packet end to be removed. +* @cutlen: The number of bytes in the packet to preserve on output. */ struct ovs_skb_cb {struct vport *input_vport;
net/openvswitch/vport.c +1 -1
@@ -503,7 +503,7 @@ int ovs_vport_receive(struct vport *vport, struct sk_buff *skb,OVS_CB (skb)->input_vport = vport;OVS_CB (skb)->mru =0 ; -OVS_CB (skb)->cutlen =0 ; +OVS_CB (skb)->cutlen =U32_MAX ;if (unlikely (dev_net (skb->dev) !=ovs_dp_get_net (vport->dp))) {u32 mark;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 357/484 fscrypt: Avoid dynamic allocation in fscrypt_get_devices()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Biggers <[email protected]>
commit 6fe4e4b8259e1330945b5f3c9476e08473b8e0e8 upstream.
When a blk_crypto_key starts being used or is evicted, fs/crypto/ calls fscrypt_get_devices() to get the filesystem's list of block devices, then iterates over them and calls blk_crypto_config_supported(), blk_crypto_start_using_key(), or blk_crypto_evict_key() on each one.
Currently, the block device pointers are placed in a dynamically allocated array. This dynamic allocation is problematic because:
- It can fail, especially at the fscrypt_destroy_inline_crypt_key() call site when it's invoked for inode eviction under direct reclaim.
- fscrypt_destroy_inline_crypt_key() doesn't handle the failure. It just zeroizes and frees the blk_crypto_key without calling blk_crypto_evict_key(). That causes a use-after-free.
For now, let's fix this in the straightforward and easily-backportable way by switching to an on-stack array. Currently the fscrypt multi-device functionality is used only by f2fs, which has a hardcoded limit of 8 block devices. An on-stack array works fine for that.
(Of course, this solution won't scale up to large number of block devices. For that we'd need a different solution, like moving the block device iteration into the filesystem. Or in the case of btrfs, which will only support blk-crypto-fallback, we should make it just call blk-crypto-fallback directly, so the block devices won't be needed.)
--- fs/crypto/inline_crypt.c | 57 ++++++++++++++-------------------------- fs/f2fs/super.c | 25 ++++++++++-------- include/linux/fscrypt.h | 18 +++++++------ 3 files changed, 44 insertions(+), 56 deletions(-)
fs/crypto/inline_crypt.c +20 -37
@@ -21,22 +21,14 @@#include "fscrypt_private.h" -static struct block_device **fscrypt_get_devices (struct super_block *sb, -unsigned int *num_devs) +static unsigned int +fscrypt_get_devices (struct super_block *sb, +struct block_device *devs[FSCRYPT_MAX_DEVICES ]) { -struct block_device **devs; - -if (sb->s_cop->get_devices) { - devs = sb->s_cop->get_devices (sb, num_devs); -if (devs) -return devs; - } - devs =kmalloc (sizeof (*devs),GFP_KERNEL ); -if (!devs) -return ERR_PTR (-ENOMEM ); +if (sb->s_cop->get_devices) +return sb->s_cop->get_devices (sb, devs); devs[0 ] = sb->s_bdev; - *num_devs =1 ; -return devs; +return 1 ; }static unsigned int fscrypt_get_dun_bytes (const struct fscrypt_info *ci)
@@ -95,7 +87,7 @@ int fscrypt_select_encryption_impl(struct fscrypt_info *ci)const struct inode *inode = ci->ci_inode;struct super_block *sb = inode->i_sb;struct blk_crypto_config crypto_cfg; -struct block_device **devs; +struct block_device *devs[FSCRYPT_MAX_DEVICES ];unsigned int num_devs;unsigned int i;
@@ -132,20 +124,15 @@ int fscrypt_select_encryption_impl(struct fscrypt_info *ci) crypto_cfg.data_unit_size = sb->s_blocksize; crypto_cfg.dun_bytes =fscrypt_get_dun_bytes (ci); - devs =fscrypt_get_devices (sb, &num_devs); -if (IS_ERR (devs)) -return PTR_ERR (devs); - + num_devs =fscrypt_get_devices (sb, devs);for (i =0 ; i < num_devs; i++) {if (!blk_crypto_config_supported (devs[i], &crypto_cfg)) - goto out_free_devs; +return 0 ; }fscrypt_log_blk_crypto_impl (ci->ci_mode, devs, num_devs, &crypto_cfg); ci->ci_inlinecrypt = true; -out_free_devs: -kfree (devs);return 0 ; }
@@ -158,7 +145,7 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,struct super_block *sb = inode->i_sb;enum blk_crypto_mode_num crypto_mode = ci->ci_mode->blk_crypto_mode;struct blk_crypto_key *blk_key; -struct block_device **devs; +struct block_device *devs[FSCRYPT_MAX_DEVICES ];unsigned int num_devs;unsigned int i;int err;
@@ -175,17 +162,12 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, }/* Start using blk-crypto on all the filesystem's block devices. */ - devs =fscrypt_get_devices (sb, &num_devs); -if (IS_ERR (devs)) { - err =PTR_ERR (devs); - goto fail; - } + num_devs =fscrypt_get_devices (sb, devs);for (i =0 ; i < num_devs; i++) { err =blk_crypto_start_using_key (devs[i], blk_key);if (err)break ; } -kfree (devs);if (err) {fscrypt_err (inode,"error %d starting to use blk-crypto" , err); goto fail;
@@ -209,20 +191,21 @@ void fscrypt_destroy_inline_crypt_key(struct super_block *sb, struct fscrypt_prepared_key *prep_key) {struct blk_crypto_key *blk_key = prep_key->blk_key; -struct block_device **devs; +struct block_device *devs[FSCRYPT_MAX_DEVICES ];unsigned int num_devs;unsigned int i;if (!blk_key)return ; -/* Evict the key from all the filesystem's block devices. */ - devs =fscrypt_get_devices (sb, &num_devs); -if (!IS_ERR (devs)) { -for (i =0 ; i < num_devs; i++) -blk_crypto_evict_key (devs[i], blk_key); -kfree (devs); - } +/* +* Evict the key from all the filesystem's block devices. +* This *must* be done before the key is freed. +*/ + num_devs =fscrypt_get_devices (sb, devs); +for (i =0 ; i < num_devs; i++) +blk_crypto_evict_key (devs[i], blk_key); +kfree_sensitive (blk_key); }
fs/f2fs/super.c +14 -11
@@ -3248,24 +3248,27 @@ static void f2fs_get_ino_and_lblk_bits(struct super_block *sb, *lblk_bits_ret =8 *sizeof (block_t); } -static struct block_device **f2fs_get_devices (struct super_block *sb, -unsigned int *num_devs) +static unsigned int +f2fs_get_devices (struct super_block *sb, +struct block_device *devs[FSCRYPT_MAX_DEVICES ]) {struct f2fs_sb_info *sbi =F2FS_SB (sb); -struct block_device **devs; +int ndevs;int i; -if (!f2fs_is_multi_device (sbi)) -return NULL ; +static_assert (MAX_DEVICES <=FSCRYPT_MAX_DEVICES ); - devs =kmalloc_array (sbi->s_ndevs,sizeof (*devs),GFP_KERNEL ); -if (!devs) -return ERR_PTR (-ENOMEM ); +if (!f2fs_is_multi_device (sbi)) { + devs[0 ] = sb->s_bdev; +return 1 ; + } + ndevs = sbi->s_ndevs; +if (WARN_ON_ONCE (ndevs >FSCRYPT_MAX_DEVICES )) + ndevs =FSCRYPT_MAX_DEVICES ; -for (i =0 ; i < sbi->s_ndevs; i++) +for (i =0 ; i < ndevs; i++) devs[i] =FDEV (i).bdev; - *num_devs = sbi->s_ndevs; -return devs; +return ndevs; }static const struct fscrypt_operations f2fs_cryptops = {
include/linux/fscrypt.h +10 -8
@@ -57,6 +57,9 @@ struct fscrypt_name {/* Maximum value for the third parameter of fscrypt_operations.set_context(). */ #define FSCRYPT_SET_CONTEXT_MAX_SIZE 40 +/* Maximum supported number of block devices per filesystem */ +#define FSCRYPT_MAX_DEVICES 8 +#ifdef CONFIG_FS_ENCRYPTION /*
@@ -161,21 +164,20 @@ struct fscrypt_operations { int *ino_bits_ret, int *lblk_bits_ret);/* -* Return an array of pointers to the block devices to which the -* filesystem may write encrypted file contents, NULL if the filesystem -* only has a single such block device, or an ERR_PTR() on error. +* Retrieve the list of block devices to which the filesystem may write +* encrypted file contents. * -* On successful non-NULL return, *num_devs is set to the number of -* devices in the returned array. The caller must free the returned -* array using kfree(). +* This writes the block_device pointers to @devs and returns the count +* (between 1 and FSCRYPT_MAX_DEVICES inclusively). * * If the filesystem can use multiple block devices (other than block * devices that aren't used for encrypted file contents, such as * external journal devices), and wants to support inline encryption, * then it must implement this function. Otherwise it's not needed. */ -struct block_device **(*get_devices)(struct super_block *sb, -unsigned int *num_devs); +unsigned int (*get_devices)( +struct super_block *sb, +struct block_device *devs[FSCRYPT_MAX_DEVICES ]); };static inline struct fscrypt_info *fscrypt_get_info (const struct inode *inode)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 358/484 exfat: validate cluster allocation bits of the allocation bitmap
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Namjae Jeon <[email protected]>
[ Upstream commit 79c1587b6cda74deb0c86fc7ba194b92958c793c ]
syzbot created an exfat image with cluster bits not set for the allocation bitmap. exfat-fs reads and uses the allocation bitmap without checking this. The problem is that if the start cluster of the allocation bitmap is 6, cluster 6 can be allocated when creating a directory with mkdir. exfat zeros out this cluster in exfat_mkdir, which can delete existing entries. This can reallocate the allocated entries. In addition, the allocation bitmap is also zeroed out, so cluster 6 can be reallocated. This patch adds exfat_test_bitmap_range to validate that clusters used for the allocation bitmap are correctly marked as in-use.
[Adapted to 6.6: replaced __le_long/lel_to_cpu word-level bitmap access
with per-bit test_bit_le() calls, as __le_long and lel_to_cpu do not exist in 6.6. Uses same test_bit_le API as rest of exfat bitmap code.]
--- fs/exfat/balloc.c | 54 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 42 insertions(+), 12 deletions(-)
fs/exfat/balloc.c +42 -12
@@ -45,12 +45,37 @@ static const unsigned char used_bit[] = {/* * Allocation Bitmap Management Functions */ +static bool exfat_test_bitmap_range (struct super_block *sb,unsigned int clu, +unsigned int count) +{ +struct exfat_sb_info *sbi =EXFAT_SB (sb); +unsigned int start = clu; +unsigned int end = clu + count; +unsigned int ent_idx, i, b; + +if (!is_valid_cluster (sbi, start) || !is_valid_cluster (sbi, end -1 )) +return false; + +while (start < end) { + ent_idx =CLUSTER_TO_BITMAP_ENT (start); + i =BITMAP_OFFSET_SECTOR_INDEX (sb, ent_idx); + b =BITMAP_OFFSET_BIT_IN_SECTOR (sb, ent_idx); + +if (!test_bit_le (b, sbi->vol_amap[i]->b_data)) +return false; + + start++; + } + +return true; +} +static int exfat_allocate_bitmap (struct super_block *sb,struct exfat_dentry *ep) {struct exfat_sb_info *sbi =EXFAT_SB (sb);long long map_size; -unsigned int i, need_map_size; +unsigned int i, j, need_map_size;sector_t sector; sbi->map_clu =le32_to_cpu (ep->dentry.bitmap.start_clu);
@@ -77,20 +102,25 @@ static int exfat_allocate_bitmap(struct super_block *sb, sector =exfat_cluster_to_sector (sbi, sbi->map_clu);for (i =0 ; i < sbi->map_sectors; i++) { sbi->vol_amap[i] =sb_bread (sb, sector + i); -if (!sbi->vol_amap[i]) { -/* release all buffers and free vol_amap */ -int j =0 ; - -while (j < i) -brelse (sbi->vol_amap[j++]); - -kvfree (sbi->vol_amap); - sbi->vol_amap =NULL ; -return -EIO ; - } +if (!sbi->vol_amap[i]) + goto err_out; } +if (exfat_test_bitmap_range (sb, sbi->map_clu, +EXFAT_B_TO_CLU_ROUND_UP (map_size, sbi)) == false) + goto err_out; +return 0 ; + +err_out: + j =0 ; +/* release all buffers and free vol_amap */ +while (j < i) +brelse (sbi->vol_amap[j++]); + +kvfree (sbi->vol_amap); + sbi->vol_amap =NULL ; +return -EIO ; }int exfat_load_bitmap (struct super_block *sb)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 359/484 drm/amd/display: Fix dcn32 DTB DTO update breaking live pixel rate sources
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Harry Wentland <[email protected]>
commit 76a2db58e95e328007043f54ac3c7336ccbee440 upstream.
dcn32_update_clocks_update_dtb_dto() reprograms the DTB DTO of every timing generator in the context whenever the DTBCLK reference changes, passing a zeroed pixel rate and never setting is_hdmi. dccg32_set_dtbclk_dto() treats a zero pixel rate as a disable request, and that branch drives PIPE_DTO_SRC_SEL to the DP DTO source. A timing generator actively scanning out an HDMI stream therefore has its pixel rate source re-muxed out from under the live raster, and the OTG stops on the spot.
Two displays where only one runs a 128b/132b link hit this reliably. is_dtbclk_required() holds the DTBCLK reference high while both are active, and the moment the 128b/132b stream is torn down (compositor switch, display disable, hot-unplug) the next safe_to_lower pass drops the reference to the lowest DPM level and the DTO walk freezes the surviving screen. On Navi31 the DAL mailbox then goes deaf on the DISPCLK hard-min that follows the walk in dcn32_update_clocks(), stranding both SMU mailboxes until reboot.
Set is_hdmi for HDMI and DVI signals so the disable path leaves the pixel rate source selection on the HDMI path, and pass the real pixel rate for 128b/132b streams so a reference change rescales their DTO instead of disabling it.
[ mschwartz: dcn32 clk_mgr hunk only, as 6.6.y predates the dcn35
clk_mgr. The rest is HDMI FRL enablement, absent before 7.2, so the FRL conditions and the req_audio_dtbclk_khz assignment they guard are dropped and the FRL-centric changelog is rewritten. Added the pipe_ctx->stream check the new dereferences need. ]
--- .../gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c +8 -1
@@ -255,13 +255,20 @@ static void dcn32_update_clocks_update_dtb_dto(struct clk_mgr_internal *clk_mgr,struct dtbclk_dto_params dto_params = {0 };/* use mask to program DTO once per tg */ -if (pipe_ctx->stream_res.tg && +if (pipe_ctx->stream && pipe_ctx->stream_res.tg && !(tg_mask & (1 << pipe_ctx->stream_res.tg->inst))) { tg_mask |= (1 << pipe_ctx->stream_res.tg->inst); dto_params.otg_inst = pipe_ctx->stream_res.tg->inst; dto_params.ref_dtbclk_khz = ref_dtbclk_khz; +if (dccg->ctx->dc->link_srv->dp_is_128b_132b_signal (pipe_ctx)) + dto_params.pixclk_khz = pipe_ctx->stream->timing.pix_clk_100hz /10 ; + +if (dc_is_hdmi_signal (pipe_ctx->stream->signal) || +dc_is_dvi_signal (pipe_ctx->stream->signal)) + dto_params.is_hdmi = true; + dccg->funcs->set_dtbclk_dto (clk_mgr->dccg, &dto_params);//dccg->funcs->set_audio_dtbclk_dto(clk_mgr->dccg, &dto_params); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 360/484 io_uring/rw: fix missing ERESTARTSYS conversion in read paths
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yitang Yang <[email protected]>
commit ab05caca123c6d0b41850b7c05b246e4dca4a770 upstream.
Both read and write may receive internal restart error codes from the filesystem layer and should be converted to -EINTR. However, when multishot read support was added, the error code normalization was lost for both io_read() and io_read_mshot().
Extract the conversion into io_fixup_restart_res() and apply it in all three locations: io_rw_done(), io_read(), and io_read_mshot().
[ 6.6 doesn't have multishot read, drop the io_read_mshot() hunk and
adapt io_rw_done() to the older calling convention ]
--- io_uring/rw.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-)
io_uring/rw.c +19 -9
@@ -140,27 +140,37 @@ void io_readv_writev_cleanup(struct io_kiocb *req)kfree (io->free_iovec); } -static inline void io_rw_done (struct kiocb *kiocb,ssize_t ret) +static inline ssize_t io_fixup_restart_res (ssize_t ret) {switch (ret) { -case -EIOCBQUEUED : -break ;case -ERESTARTSYS :case -ERESTARTNOINTR :case -ERESTARTNOHAND :case -ERESTART_RESTARTBLOCK :/* * We can't just restart the syscall, since previously -* submitted sqes may already be in progress. Just fail this -* IO with EINTR. +* submitted sqes may already be in progress. Just fail +* this IO with EINTR. */ - ret = -EINTR ; - fallthrough; +return -EINTR ;default : - kiocb->ki_complete (kiocb, ret); +return ret; } } +static inline void io_rw_done (struct kiocb *kiocb,ssize_t ret) +{ +/* IO was queued async, completion will happen later */ +if (ret == -EIOCBQUEUED ) +return ; + +/* transform internal restart error codes */ +if (unlikely (ret <0 )) + ret =io_fixup_restart_res (ret); + + kiocb->ki_complete (kiocb, ret); +} +static inline loff_t *io_kiocb_update_pos (struct io_kiocb *req) { struct io_rw *rw = io_kiocb_to_cmd(req, struct io_rw);
@@ -885,7 +895,7 @@ int io_read(struct io_kiocb *req, unsigned int issue_flags)if (ret >=0 )return kiocb_done (req, ret, issue_flags); -return ret; +return io_fixup_restart_res (ret); }static bool io_kiocb_start_write (struct io_kiocb *req,struct kiocb *kiocb)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 361/484 net: pcs: xpcs: fix SGMII state reading
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Coia Prant <[email protected]>
commit def9a4745e105145133e442dd8a1c126caf0f553 upstream.
Commit 2a22b7ae2fa3 ("net: pcs: xpcs: adapt Wangxun NICs for SGMII mode") added a path in xpcs_get_state_c37_sgmii() that reads speed/duplex from BMCR after AN completes. However, BMCR does not reflect the negotiated result on the hardware where this has been tested:
- On RK3568 (MAC side SGMII), BMCR returns a fixed hardware reset value
- Wangxun engineer Jiawen Wu confirmed that on their side, "BMCR looks like it only wants to be return as 0" [0]
The correct information is available in CL37_ANSGM_STS, which contains the actual link status and negotiated speed/duplex.
This bug was previously masked by phylink core, which overrides the PCS link state with the PHY state when a PHY is present:
/* If we have a phy, the "up" state is the union of both the
* PHY and the MAC
*/
if (phy)
link_state.link &= pl->phy_state.link;Thus, when the link is down, the PHY's link_down state is applied on top of whatever the PCS reports, hiding the broken PCS state reading path.
Modify xpcs_get_state_c37_sgmii() to:
1. Read link state from CL37_ANSGM_STS
2. If link is up, report speed/duplex from CL37_ANSGM_STS
3. Remove the broken BMCR reading path entirely
Also properly set state->an_complete to reflect the AN completion status, and clear CL37_ANCMPLT_INTR when link is down to avoid stale state.
[0] https://lore.kernel.org/all/[email protected]/
--- drivers/net/pcs/pcs-xpcs.c | 32 +++++++------------------------- 1 file changed, 7 insertions(+), 25 deletions(-)
drivers/net/pcs/pcs-xpcs.c +7 -25
@@ -994,6 +994,7 @@ static int xpcs_get_state_c37_sgmii(struct dw_xpcs *xpcs,/* Reset link_state */ state->link = false; + state->an_complete = false; state->speed =SPEED_UNKNOWN ; state->duplex =DUPLEX_UNKNOWN ; state->pause =0 ;
@@ -1005,6 +1006,8 @@ static int xpcs_get_state_c37_sgmii(struct dw_xpcs *xpcs,if (ret <0 )return ret; + state->an_complete = ret &DW_VR_MII_AN_STS_C37_ANCMPLT_INTR ; +if (ret &DW_VR_MII_C37_ANSGM_SP_LNKSTS ) {int speed_value;
@@ -1023,34 +1026,13 @@ static int xpcs_get_state_c37_sgmii(struct dw_xpcs *xpcs, state->duplex =DUPLEX_FULL ; else state->duplex = DUPLEX_HALF; - }else if (ret ==DW_VR_MII_AN_STS_C37_ANCMPLT_INTR ) { -int speed, duplex; - - state->link = true; - - speed =xpcs_read (xpcs,MDIO_MMD_VEND2 ,MDIO_CTRL1 ); -if (speed <0 ) -return speed; - - speed &=SGMII_SPEED_SS13 |SGMII_SPEED_SS6 ; -if (speed ==SGMII_SPEED_SS6 ) - state->speed =SPEED_1000 ; -else if (speed ==SGMII_SPEED_SS13 ) - state->speed =SPEED_100 ; -else if (speed ==0 ) - state->speed =SPEED_10 ; - - duplex =xpcs_read (xpcs,MDIO_MMD_VEND2 ,MII_ADVERTISE ); -if (duplex <0 ) -return duplex; -if (duplex &DW_FULL_DUPLEX ) - state->duplex =DUPLEX_FULL ; -else if (duplex &DW_HALF_DUPLEX ) - state->duplex =DUPLEX_HALF ; +return 0 ; + } +/* Clear AN complete status or interrupt */ +if (state->an_complete)xpcs_write (xpcs,MDIO_MMD_VEND2 ,DW_VR_MII_AN_INTR_STS ,0 ); - }return 0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 362/484 bpf: drop bpf_lsm_getselfattr from hook list
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wentao Guan <[email protected]>
Backport ("bpf, lsm: Add disabled BPF LSM hook list") for v6.6.y bring the warning "WARN: resolve_btfids: unresolved symbol bpf_lsm_getselfattr".
The lsm_getselfattr from commit a04a1198088a
("LSM: syscalls for current process attributes"), no need to backport the huge patch, simply drop the entry to fix the noise.
This is a fix for stable v6.6.145 backport commit, so no upstream commit.
--- kernel/bpf/bpf_lsm.c | 1 - 1 file changed, 1 deletion(-)
kernel/bpf/bpf_lsm.c +0 -1
@@ -42,7 +42,6 @@ BTF_ID(func, bpf_lsm_inode_need_killpriv) BTF_ID(func, bpf_lsm_inode_getsecurity) BTF_ID(func, bpf_lsm_inode_listsecurity) BTF_ID(func, bpf_lsm_inode_copy_up_xattr) -BTF_ID(func, bpf_lsm_getselfattr) BTF_ID(func, bpf_lsm_getprocattr) BTF_ID(func, bpf_lsm_setprocattr)#ifdef CONFIG_KEYS
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 363/484 bpf: Reject BPF_MAP_TYPE_INODE_STORAGE creation if BPF LSM is uninitialized
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Matt Bobrowski <[email protected]>
[ Upstream commit a6f0643e4f63cfaa0d5d4a69de4f132eac4b8fe4 ]
When CONFIG_BPF_LSM=y is set, BPF inode storage maps (BPF_MAP_TYPE_INODE_STORAGE) are compiled into the kernel. However, if the BPF LSM is not explicitly enabled at boot time (e.g. omitted from the "lsm=" boot parameter), lsm_prepare() is never executed for the BPF LSM.
Consequently, the BPF inode security blob offset (bpf_lsm_blob_sizes.lbs_inode) is never initialized and remains at its default compiled size of 8 bytes instead of being updated to a valid offset past the reserved struct rcu_head (typically 16 bytes or more).
When a privileged user creates and updates a BPF_MAP_TYPE_INODE_STORAGE map, bpf_inode() evaluates inode->i_security + 8. This erroneously aliases the struct rcu_head.func callback pointer at the beginning of the inode->i_security blob. During subsequent map element cleanup or inode destruction, writing NULL to owner_storage clears the queued RCU callback pointer. When rcu_do_batch() later executes the queued callback, it attempts an instruction fetch at address 0x0, triggering an immediate kernel panic.
Fix this by introducing a global bpf_lsm_initialized boolean flag marked with __ro_after_init. Set this flag to true inside bpf_lsm_init() when the LSM framework successfully registers the BPF LSM. Gate map allocation in inode_storage_map_alloc() on this flag, returning -EOPNOTSUPP if the BPF LSM is in turn uninitialized.
This fail-fast approach prevents userspace from allocating inode storage maps when the supporting BPF LSM infrastructure is absent, avoiding zombie map states.
--- include/linux/bpf_lsm.h | 4 ++++ kernel/bpf/bpf_inode_storage.c | 9 +++++++++ security/bpf/hooks.c | 3 +++ 3 files changed, 16 insertions(+)
include/linux/bpf_lsm.h +4 -0
@@ -13,6 +13,8 @@#ifdef CONFIG_BPF_LSM +extern bool bpf_lsm_initialized__ro_after_init ; +#define LSM_HOOK (RET ,DEFAULT ,NAME , ...) \ RET bpf_lsm_##NAME(__VA_ARGS__);#include <linux/lsm_hook_defs.h>
@@ -47,6 +49,8 @@ void bpf_lsm_find_cgroup_shim(const stru#else /* !CONFIG_BPF_LSM */ +#define bpf_lsm_initialized false +static inline bool bpf_lsm_is_sleepable_hook (u32 btf_id) {return false;
kernel/bpf/bpf_inode_storage.c +9 -0
@@ -191,6 +191,15 @@ static int notsupp_get_next_key(struct bstatic struct bpf_map *inode_storage_map_alloc (union bpf_attr *attr) { +/* +* Do not allow allocation of BPF_MAP_TYPE_INODE_STORAGE if the BPF LSM +* was not initialized by the LSM framework at boot. Without proper +* initialization, the BPF inode security blob offset remains unprepared, +* causing bpf_inode() to calculate an invalid memory offset and corrupt +* inode->i_security. +*/ +if (!bpf_lsm_initialized) +return ERR_PTR (-EOPNOTSUPP );return bpf_local_storage_map_alloc (attr, &inode_cache, false); }
security/bpf/hooks.c +3 -0
@@ -6,6 +6,8 @@#include <linux/lsm_hooks.h> #include <linux/bpf_lsm.h> +bool bpf_lsm_initialized__ro_after_init ; +static struct security_hook_list bpf_lsm_hooks[]__ro_after_init = { #define LSM_HOOK(RET, DEFAULT, NAME, ...) \ LSM_HOOK_INIT(NAME, bpf_lsm_##NAME),
@@ -18,6 +20,7 @@ static struct security_hook_list bpf_lsmstatic int __init bpf_lsm_init (void ) {security_add_hooks (bpf_lsm_hooks,ARRAY_SIZE (bpf_lsm_hooks),"bpf" ); + bpf_lsm_initialized = true;pr_info ("LSM support for eBPF active\n" );return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 364/484 iommu/amd: Dont split flush for amd_iommu_domain_flush_all()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Weinan Liu <[email protected]>
[ Upstream commit 69fe699afe1afcb730164b86c228483c2da05f94 ]
We have observed multiple full invalidations occurring during device detach when we are done using the vfio-device.
blocked_domain_attach_device()
-> detach_device()
-> amd_iommu_domain_flush_all()
-> amd_iommu_domain_flush_pages(..., CMD_INV_IOMMU_ALL_PAGES_ADDRESS)
while (size != 0) {
-> __domain_flush_pages( flush_size /* power of 2 flush_size */)
-> domain_flush_pages_v1()
-> build_inv_iommu_pages()
-> build_inv_address()
}build_inv_address() will trigger a full invalidation if the chunk size > (1 << 51). Consequently, the guest will issue multiple full invalidations for a single call to amd_iommu_domain_flush_all()
Without this patch, we will see 10 time instead of 1 time full invalidations for every amd_iommu_domain_flush_all().
--- drivers/iommu/amd/iommu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/iommu/amd/iommu.c +2 -1
@@ -1441,7 +1441,8 @@ static void __domain_flush_pages(structstatic void domain_flush_pages (struct protection_domain *domain,u64 address,size_t size,int pde) { -if (likely (!amd_iommu_np_cache)) { +if (likely (!amd_iommu_np_cache) || + size >= (1ULL <<52 )) {__domain_flush_pages (domain, address, size, pde);return ; }
Greg Kroah-Hartman
maintainerPATCH 365/484 udmabuf: Do not create malformed scatterlists
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jason Gunthorpe <[email protected]>
[ Upstream commit 5bf888673e0dda5a53220fa0c4956271a46c353c ]
Using a sg_set_folio() loop for every 4K results in a malformed scatterlist because sg_set_folio() has an issue with offsets > PAGE_SIZE and because scatterlist expects the creator to build a list which consolidates any physical contiguity.
sg_alloc_table_from_pages() creates a valid scatterlist directly from a struct page array, so go back to that.
Remove the offsets allocation and just store an array of tail pages as it did before the below commit. Everything wants that anyhow.
Stable-dep-of: 504e2b4ab97a ("dma-buf/udmabuf: skip redundant cpu sync to fix cacheline EEXIST warning")
--- drivers/dma-buf/udmabuf.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
drivers/dma-buf/udmabuf.c +5 -3
@@ -100,14 +100,16 @@ static struct sg_table *get_sg_table(str0 , ubuf->pagecount <<PAGE_SHIFT , GFP_KERNEL);if (ret <0 ) - goto err; + goto err_alloc; + ret =dma_map_sgtable (dev, sg, direction,0 );if (ret <0 ) - goto err; + goto err_map;return sg; -err: +err_map:sg_free_table (sg); +err_alloc:kfree (sg);return ERR_PTR (ret); }
Greg Kroah-Hartman
maintainerPATCH 366/484 dma-buf/udmabuf: skip redundant cpu sync to fix cacheline EEXIST warning
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mikhail Gavrilov <[email protected]>
[ Upstream commit 504e2b4ab97a51d56d966cd36d0997ad30b65b2d ]
When CONFIG_DMA_API_DEBUG_SG is enabled, importing a udmabuf into a DRM driver (e.g. amdgpu for video playback in GNOME Videos / Showtime) triggers a spurious warning:
DMA-API: amdgpu 0000:03:00.0: cacheline tracking EEXIST, \
overlapping mappings aren't supported
WARNING: kernel/dma/debug.c:619 at add_dma_entry+0x473/0x5f0The call chain is:
amdgpu_cs_ioctl
-> amdgpu_ttm_backend_bind
-> dma_buf_map_attachment
-> [udmabuf] map_udmabuf -> get_sg_table
-> dma_map_sgtable(dev, sg, direction, 0) // attrs=0
-> debug_dma_map_sg -> add_dma_entry -> EEXISTThis happens because udmabuf builds a per-page scatter-gather list via sg_set_folio(). When begin_cpu_udmabuf() has already created an sg table mapped for the misc device, and an importer such as amdgpu maps the same pages for its own device via map_udmabuf(), the DMA debug infrastructure sees two active mappings whose physical addresses share cacheline boundaries and warns about the overlap.
The DMA_ATTR_SKIP_CPU_SYNC flag suppresses this check in add_dma_entry() because it signals that no CPU cache maintenance is performed at map/unmap time, making the cacheline overlap harmless.
All other major dma-buf exporters already pass this flag:
- drm_gem_map_dma_buf() passes DMA_ATTR_SKIP_CPU_SYNC
- amdgpu_dma_buf_map() passes DMA_ATTR_SKIP_CPU_SYNC
The CPU sync at map/unmap time is also redundant for udmabuf:begin_cpu_udmabuf() and end_cpu_udmabuf() already perform explicit cache synchronization via dma_sync_sgtable_for_cpu/device() when CPU access is requested through the dma-buf interface.
Pass DMA_ATTR_SKIP_CPU_SYNC to dma_map_sgtable() and dma_unmap_sgtable() in udmabuf to suppress the spurious warning and skip the redundant sync.
--- drivers/dma-buf/udmabuf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/dma-buf/udmabuf.c +2 -2
@@ -102,7 +102,7 @@ static struct sg_table *get_sg_table(strif (ret <0 ) goto err_alloc; - ret =dma_map_sgtable (dev, sg, direction,0 ); + ret =dma_map_sgtable (dev, sg, direction,DMA_ATTR_SKIP_CPU_SYNC );if (ret <0 ) goto err_map;return sg;
@@ -117,7 +117,7 @@ err_alloc:static void put_sg_table (struct device *dev,struct sg_table *sg,enum dma_data_direction direction) { -dma_unmap_sgtable (dev, sg, direction,0 ); +dma_unmap_sgtable (dev, sg, direction,DMA_ATTR_SKIP_CPU_SYNC );sg_free_table (sg);kfree (sg); }
Greg Kroah-Hartman
maintainerPATCH 367/484 fpga: dfl-afu: validate DMA mapping length in afu_dma_map_region()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sebastian Alba Vives <[email protected]>
[ Upstream commit fc3b071a7c8dc0f5d56defddf6e6fd5aaa3e1e27 ]
afu_ioctl_dma_map() accepts a 64-bit length from userspace via DFL_FPGA_PORT_DMA_MAP ioctl without an upper bound check. The value is passed to afu_dma_pin_pages() where npages is derived as length >> PAGE_SHIFT and passed to pin_user_pages_fast() which takes int nr_pages, causing implicit truncation if length is very large.
Validate map.length at the ioctl entry point before calling afu_dma_map_region(), rejecting values whose page count exceeds INT_MAX.
--- drivers/fpga/dfl-afu-main.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/fpga/dfl-afu-main.c +3 -0
@@ -720,6 +720,9 @@ afu_ioctl_dma_map(struct dfl_feature_plaif (map.argsz < minsz || map.flags)return -EINVAL ; +if (map.length >>PAGE_SHIFT > (u64 )INT_MAX ) +return -EINVAL ; + ret =afu_dma_map_region (pdata, map.user_addr, map.length, &map.iova);if (ret)return ret;
Greg Kroah-Hartman
maintainerPATCH 368/484 i2c: davinci: Unregister cpufreq notifier on probe failure
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Haoxiang Li <[email protected]>
[ Upstream commit e43f32816a1b1fe5a86279411626fe3a9be56d45 ]
davinci_i2c_probe() registers a cpufreq transition notifier before adding the I2C adapter. If i2c_add_numbered_adapter() fails, the probe error path releases the device resources without unregistering the notifier.
Add a dedicated error path to unregister the cpufreq notifier after i2c_add_numbered_adapter() fails.
--- drivers/i2c/busses/i2c-davinci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/i2c/busses/i2c-davinci.c +3 -1
@@ -866,13 +866,15 @@ static int davinci_i2c_probe(struct plat adap->nr = pdev->id; r =i2c_add_numbered_adapter (adap);if (r) - goto err_unuse_clocks; + goto err_cpufreq;pm_runtime_mark_last_busy (dev->dev);pm_runtime_put_autosuspend (dev->dev);return 0 ; +err_cpufreq: +i2c_davinci_cpufreq_deregister (dev); err_unuse_clocks:pm_runtime_dont_use_autosuspend (dev->dev);pm_runtime_put_sync (dev->dev);
Greg Kroah-Hartman
maintainerPATCH 369/484 VFS/audit: introduce kern_path_parent() for audit
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: NeilBrown <[email protected]>
[ Upstream commit 76a53de6f7ff0641570364234fb4489f4d4fc8e9 ]
audit_alloc_mark() and audit_get_nd() both need to perform a path lookup getting the parent dentry (which must exist) and the final target (following a LAST_NORM name) which sometimes doesn't need to exist.
They don't need the parent to be locked, but use kern_path_locked() or kern_path_locked_negative() anyway. This is somewhat misleading to the casual reader.
This patch introduces a more targeted function, kern_path_parent(), which returns not holding locks. On success the "path" will be set to the parent, which must be found, and the return value is the dentry of the target, which might be negative.
This will clear the way to rename kern_path_locked() which is otherwise only used to prepare for removing something.
It also allows us to remove kern_path_locked_negative(), which is transformed into the new kern_path_parent().
Stable-dep-of: 81905b5acbe7 ("audit: fix recursive locking deadlock in audit_dupe_exe()")
--- fs/namei.c | 43 +++++++++++++++++++++++++++++++++++++++++++ include/linux/namei.h | 1 + kernel/audit.h | 4 ++-- kernel/audit_fsnotify.c | 9 +++------ kernel/audit_watch.c | 9 ++++++--- 5 files changed, 55 insertions(+), 11 deletions(-)
fs/namei.c +43 -0
@@ -2603,6 +2603,49 @@ static struct dentry *__kern_path_lockedreturn d; } +/** +* kern_path_parent: lookup path returning parent and target +* @name: path name +* @path: path to store parent in +* +* The path @name should end with a normal component, not "." or ".." or "/". +* A lookup is performed and if successful the parent information +* is store in @parent and the dentry is returned. +* +* The dentry maybe negative, the parent will be positive. +* +* Returns: dentry or error. +*/ +struct dentry *kern_path_parent (const char *name,struct path *path) +{ +struct filename *filename =getname_kernel (name); +struct path parent_path; +struct dentry *d; +struct qstr last; +int type, error; + + error =filename_parentat (AT_FDCWD , filename,0 , &parent_path, &last, &type); +if (error) { + d =ERR_PTR (error); + goto out; + } +if (unlikely (type !=LAST_NORM )) { +path_put (&parent_path); + d =ERR_PTR (-EINVAL ); + goto out; + } + + d =lookup_one_len_unlocked (last.name, parent_path.dentry, last.len); +if (IS_ERR (d)) { +path_put (&parent_path); + goto out; + } + *path = parent_path; +out: +putname (filename); +return d; +} +struct dentry *kern_path_locked (const char *name,struct path *path) {struct filename *filename =getname_kernel (name);
include/linux/namei.h +1 -0
@@ -61,6 +61,7 @@ struct dentry *lookup_one_qstr_excl(consstruct dentry *base, unsigned int flags);extern int kern_path (const char *,unsigned ,struct path *); +struct dentry *kern_path_parent (const char *name,struct path *parent);extern struct dentry *kern_path_create (int ,const char *,struct path *,unsigned int );extern struct dentry *user_path_create (int ,const char __user *,struct path *,unsigned int );
kernel/audit.h +2 -2
@@ -279,8 +279,8 @@ extern struct audit_fsnotify_mark *auditextern char *audit_mark_path (struct audit_fsnotify_mark *mark);extern void audit_remove_mark (struct audit_fsnotify_mark *audit_mark);extern void audit_remove_mark_rule (struct audit_krule *krule); -extern int audit_mark_compare (struct audit_fsnotify_mark *mark, -unsigned long ino,dev_t dev); +extern int audit_mark_compare (struct audit_fsnotify_mark *mark,u64 ino, +dev_t dev);extern int audit_dupe_exe (struct audit_krule *new,struct audit_krule *old);extern int audit_exe_compare (struct task_struct *tsk,struct audit_fsnotify_mark *mark);
kernel/audit_fsnotify.c +3 -6
@@ -57,7 +57,7 @@ char *audit_mark_path(struct audit_fsnotreturn mark->path; } -int audit_mark_compare (struct audit_fsnotify_mark *mark,unsigned long ino,dev_t dev) +int audit_mark_compare (struct audit_fsnotify_mark *mark,u64 ino,dev_t dev) {if (mark->ino ==AUDIT_INO_UNSET )return 0 ;
@@ -76,17 +76,14 @@ struct audit_fsnotify_mark *audit_alloc_struct audit_fsnotify_mark *audit_mark;struct path path;struct dentry *dentry; -struct inode *inode;int ret;if (pathname[0 ] !='/' || pathname[len-1 ] =='/' )return ERR_PTR (-EINVAL ); - dentry =kern_path_locked (pathname, &path); + dentry =kern_path_parent (pathname, &path);if (IS_ERR (dentry))return ERR_CAST (dentry);/* returning an error */ - inode = path.dentry->d_inode; -inode_unlock (inode); audit_mark =kzalloc (sizeof (*audit_mark),GFP_KERNEL );if (unlikely (!audit_mark)) {
@@ -100,7 +97,7 @@ struct audit_fsnotify_mark *audit_alloc_audit_update_mark (audit_mark, dentry->d_inode); audit_mark->rule = krule; - ret =fsnotify_add_inode_mark (&audit_mark->mark, inode,0 ); + ret =fsnotify_add_inode_mark (&audit_mark->mark, path.dentry->d_inode,0 );if (ret <0 ) { audit_mark->path =NULL ;fsnotify_put_mark (&audit_mark->mark);
kernel/audit_watch.c +6 -3
@@ -244,7 +244,7 @@ static void audit_watch_log_rule_change(/* Update inode info in audit rules based on filesystem event. */ static void audit_update_watch (struct audit_parent *parent,const struct qstr *dname,dev_t dev, -unsigned long ino,unsigned invalidating) +u64 ino,unsigned int invalidating) {struct audit_watch *owatch, *nwatch, *nextw;struct audit_krule *r, *nextr;
@@ -347,15 +347,18 @@ static void audit_remove_parent_watches(/* Get path information necessary for adding watches. */ static int audit_get_nd (struct audit_watch *watch,struct path *parent) { -struct dentry *d =kern_path_locked (watch->path, parent); +struct dentry *d; + + d =kern_path_parent (watch->path, parent);if (IS_ERR (d))return PTR_ERR (d); +if (d_is_positive (d)) {/* update watch filter fields */ watch->dev = d->d_sb->s_dev; watch->ino =d_backing_inode (d)->i_ino; } -inode_unlock (d_backing_inode (parent->dentry)); +dput (d);return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 370/484 audit: widen ino fields to u64
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jeff Layton <[email protected]>
[ Upstream commit 125dfa218134df7cc112667e92984de9d8cd0bf6 ]
inode->i_ino is being widened from unsigned long to u64. The audit subsystem uses unsigned long ino in struct fields, function parameters, and local variables that store inode numbers from arbitrary filesystems. On 32-bit platforms this truncates inode numbers that exceed 32 bits, which will cause incorrect audit log entries and broken watch/mark comparisons.
Widen all audit ino fields, parameters, and locals to u64, and update the inode format string from %lu to %llu to match.
Stable-dep-of: 81905b5acbe7 ("audit: fix recursive locking deadlock in audit_dupe_exe()")
--- include/linux/audit.h | 2 +- kernel/audit.h | 9 ++++----- kernel/audit_fsnotify.c | 2 +- kernel/audit_watch.c | 10 +++++----- kernel/auditsc.c | 4 ++-- 5 files changed, 13 insertions(+), 14 deletions(-)
include/linux/audit.h +1 -1
@@ -16,7 +16,7 @@#include <uapi/linux/netfilter/nf_tables.h> #include <uapi/linux/fanotify.h> -#define AUDIT_INO_UNSET ((unsigned long)-1) +#define AUDIT_INO_UNSET ((u64)-1)#define AUDIT_DEV_UNSET ((dev_t)-1)struct audit_sig_info {
kernel/audit.h +4 -5
@@ -75,7 +75,7 @@ struct audit_names {int name_len;/* number of chars to log */ bool hidden;/* don't log this record */ -unsigned long ino; +u64 ino;dev_t dev;umode_t mode;kuid_t uid;
@@ -219,9 +219,9 @@ extern int auditd_test_task(struct task_#define AUDIT_INODE_BUCKETS 32extern struct list_head audit_inode_hash[AUDIT_INODE_BUCKETS ]; -static inline int audit_hash_ino (u32 ino) +static inline int audit_hash_ino (u64 ino) { -return (ino & (AUDIT_INODE_BUCKETS -1 )); +return ((u32 )ino & (AUDIT_INODE_BUCKETS -1 )); }/* Indicates that audit should log the full pathname. */
@@ -271,8 +271,7 @@ extern int audit_to_watch(struct audit_kextern int audit_add_watch (struct audit_krule *krule,struct list_head **list);extern void audit_remove_watch_rule (struct audit_krule *krule);extern char *audit_watch_path (struct audit_watch *watch); -extern int audit_watch_compare (struct audit_watch *watch,unsigned long ino, -dev_t dev); +extern int audit_watch_compare (struct audit_watch *watch,u64 ino,dev_t dev);extern struct audit_fsnotify_mark *audit_alloc_mark (struct audit_krule *krule,char *pathname,int len);
kernel/audit_fsnotify.c +1 -1
@@ -25,7 +25,7 @@*/ struct audit_fsnotify_mark {dev_t dev;/* associated superblock device */ -unsigned long ino;/* associated inode number */ +u64 ino;/* associated inode number */ char *path;/* insertion path */ struct fsnotify_mark mark;/* fsnotify mark on the inode */ struct audit_krule *rule;
kernel/audit_watch.c +5 -5
@@ -37,7 +37,7 @@ struct audit_watch {refcount_t count;/* reference count */ dev_t dev;/* associated superblock device */ char *path;/* insertion path */ -unsigned long ino;/* associated inode number */ +u64 ino;/* associated inode number */ struct audit_parent *parent;/* associated parent */ struct list_head wlist;/* entry in parent->watches list */ struct list_head rules;/* anchor for krule->rlist */
@@ -125,7 +125,7 @@ char *audit_watch_path(struct audit_watcreturn watch->path; } -int audit_watch_compare (struct audit_watch *watch,unsigned long ino,dev_t dev) +int audit_watch_compare (struct audit_watch *watch,u64 ino,dev_t dev) {return (watch->ino !=AUDIT_INO_UNSET ) && (watch->ino == ino) &&
@@ -285,7 +285,7 @@ static void audit_update_watch(struct aulist_del (&oentry->rule.list);audit_panic ("error updating watch, removing" ); }else { -int h =audit_hash_ino ((u32 )ino); +int h =audit_hash_ino (ino);/* * nentry->rule.watch == oentry->rule.watch so
@@ -439,7 +439,7 @@ int audit_add_watch(struct audit_krule *audit_add_to_parent (krule, parent); - h =audit_hash_ino ((u32 )watch->ino); + h =audit_hash_ino (watch->ino); *list = &audit_inode_hash[h]; error:path_put (&parent_path);
@@ -527,7 +527,7 @@ int audit_dupe_exe(struct audit_krule *nint audit_exe_compare (struct task_struct *tsk,struct audit_fsnotify_mark *mark) {struct file *exe_file; -unsigned long ino; +u64 ino;dev_t dev;/* only do exe filtering if we are recording @current events/records */
kernel/auditsc.c +2 -2
@@ -884,7 +884,7 @@ static int audit_filter_inode_name(strucstruct audit_names *n, struct audit_context *ctx) { - int h = audit_hash_ino((u32)n->ino); + int h = audit_hash_ino(n->ino);struct list_head *list = &audit_inode_hash[h];return __audit_filter_op (tsk, ctx, list, n, ctx->major);
@@ -1549,7 +1549,7 @@ static void audit_log_name(struct audit_audit_log_format (ab," name=(null)" );if (n->ino !=AUDIT_INO_UNSET ) - audit_log_format(ab, " inode=%lu dev=%02x:%02x mode=%#ho ouid=%u ogid=%u rdev=%02x:%02x", + audit_log_format(ab, " inode=%llu dev=%02x:%02x mode=%#ho ouid=%u ogid=%u rdev=%02x:%02x", n->ino, MAJOR(n->dev), MINOR(n->dev),
Greg Kroah-Hartman
maintainerPATCH 371/484 audit: use unsigned int instead of unsigned
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ricardo Robaina <[email protected]>
[ Upstream commit 8b226771014beab1292081151a99530886ce54b4 ]
Address checkpatch.pl warning below, across the audit subsystem:
WARNING: Prefer 'unsigned int' to bare use of 'unsigned'
Minor cleanup, no functional changes.
Stable-dep-of: 81905b5acbe7 ("audit: fix recursive locking deadlock in audit_dupe_exe()")
--- include/linux/audit.h | 4 ++-- include/linux/audit_arch.h | 12 ++++++------ kernel/audit.c | 2 +- kernel/audit.h | 2 +- kernel/audit_tree.c | 2 +- kernel/auditfilter.c | 8 ++++---- kernel/auditsc.c | 2 +- lib/compat_audit.c | 12 ++++++------ 8 files changed, 22 insertions(+), 22 deletions(-)
include/linux/audit.h +2 -2
@@ -122,8 +122,8 @@ enum audit_nfcfgop { AUDIT_NFT_OP_INVALID, }; -extern int __init audit_register_class (int class,unsigned *list); -extern int audit_classify_syscall (int abi,unsigned syscall); +extern int __init audit_register_class (int class,unsigned int *list); +extern int audit_classify_syscall (int abi,unsigned int syscall);extern int audit_classify_arch (int arch);/* audit_names->type values */
include/linux/audit_arch.h +6 -6
@@ -21,13 +21,13 @@ enum auditsc_class_t {AUDITSC_NVALS /* count */ }; -extern int audit_classify_compat_syscall (int abi,unsigned syscall); +extern int audit_classify_compat_syscall (int abi,unsigned int syscall);/* only for compat system calls */ -extern unsigned compat_write_class[]; -extern unsigned compat_read_class[]; -extern unsigned compat_dir_class[]; -extern unsigned compat_chattr_class[]; -extern unsigned compat_signal_class[]; +extern unsigned int compat_write_class[]; +extern unsigned int compat_read_class[]; +extern unsigned int compat_dir_class[]; +extern unsigned int compat_chattr_class[]; +extern unsigned int compat_signal_class[];#endif
kernel/audit.c +1 -1
@@ -1989,7 +1989,7 @@ static void audit_log_vformat(struct aud* here and AUDIT_BUFSIZ is at least 1024, then we can * log everything that printk could have logged. */ avail =audit_expand (ab, -max_t (unsigned,AUDIT_BUFSIZ ,1 +len-avail)); + max_t(unsigned int, AUDIT_BUFSIZ, 1+len-avail));if (!avail) goto out_va_end; len =vsnprintf (skb_tail_pointer (skb), avail, fmt, args2);
kernel/audit.h +1 -1
@@ -227,7 +227,7 @@ static inline int audit_hash_ino(u64 ino/* Indicates that audit should log the full pathname. */ #define AUDIT_NAME_FULL -1 -extern int audit_match_class (int class,unsigned syscall); +extern int audit_match_class (int class,unsigned int syscall);extern int audit_comparator (const u32 left,const u32 op,const u32 right);extern int audit_uid_comparator (kuid_t left,u32 op,kuid_t right);extern int audit_gid_comparator (kgid_t left,u32 op,kgid_t right);
kernel/audit_tree.c +1 -1
@@ -33,7 +33,7 @@ struct audit_chunk {struct audit_node {struct list_head list;struct audit_tree *owner; -unsigned index;/* index; upper bit indicates 'will prune' */ +unsigned int index;/* index; upper bit indicates 'will prune' */ } owners[]; };
kernel/auditfilter.c +4 -4
@@ -165,13 +165,13 @@ static inline int audit_to_inode(structstatic __u32 *classes[AUDIT_SYSCALL_CLASSES ]; -int __init audit_register_class (int class,unsigned *list) +int __init audit_register_class (int class,unsigned int *list) {__u32 *p =kcalloc (AUDIT_BITMASK_SIZE ,sizeof (__u32),GFP_KERNEL );if (!p)return -ENOMEM ;while (*list != ~0U ) { -unsigned n = *list++; +unsigned int n = *list++;if (n >=AUDIT_BITMASK_SIZE *32 -AUDIT_SYSCALL_CLASSES ) {kfree (p);return -EINVAL ;
@@ -186,7 +186,7 @@ int __init audit_register_class(int clasreturn 0 ; } -int audit_match_class (int class,unsigned syscall) +int audit_match_class (int class,unsigned int syscall) {if (unlikely (syscall >=AUDIT_BITMASK_SIZE *32 ))return 0 ;
@@ -237,7 +237,7 @@ static int audit_match_signal(struct aud/* Common user-space to kernel rule translation. */ static inline struct audit_entry *audit_to_entry_common (struct audit_rule_data *rule) { -unsigned listnr; +unsigned int listnr;struct audit_entry *entry;int i, err;
kernel/auditsc.c +1 -1
@@ -150,7 +150,7 @@ static const struct audit_nfcfgop_tab austatic int audit_match_perm (struct audit_context *ctx,int mask) { -unsigned n; +unsigned int n;if (unlikely (!ctx))return 0 ;
lib/compat_audit.c +6 -6
@@ -4,32 +4,32 @@#include <linux/audit_arch.h> #include <asm/unistd32.h> -unsigned compat_dir_class[] = { +unsigned int compat_dir_class[] = { #include <asm-generic/audit_dir_write.h> ~0U }; -unsigned compat_read_class[] = { +unsigned int compat_read_class[] = { #include <asm-generic/audit_read.h> ~0U }; -unsigned compat_write_class[] = { +unsigned int compat_write_class[] = { #include <asm-generic/audit_write.h> ~0U }; -unsigned compat_chattr_class[] = { +unsigned int compat_chattr_class[] = { #include <asm-generic/audit_change_attr.h> ~0U }; -unsigned compat_signal_class[] = { +unsigned int compat_signal_class[] = { #include <asm-generic/audit_signal.h> ~0U }; -int audit_classify_compat_syscall (int abi,unsigned syscall) +int audit_classify_compat_syscall (int abi,unsigned int syscall) {switch (syscall) {#ifdef __NR_open
Greg Kroah-Hartman
maintainerPATCH 372/484 audit: fix recursive locking deadlock in audit_dupe_exe()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ricardo Robaina <[email protected]>
[ Upstream commit 81905b5acbe77284734438df3fbec1158e6429a3 ]
A deadlock occurs in the audit subsystem when duplicating executable-related rules.
When a file is moved (e.g., via do_renameat2()), the VFS layer locks the parent directory (I_MUTEX_PARENT), which synchronously triggers an fsnotify_move event. If an existing executable audit rule matches the file being moved, the audit subsystem catches this event and calls audit_dupe_exe() to duplicate the watch and update the rule. Then, audit_alloc_mark() would call kern_path_parent() to resolve the path, leading to a blind attempt to acquire the exact same I_MUTEX_PARENT lock already held by the task, resulting in the following recursive locking deadlock:
============================================
WARNING: possible recursive locking detected 6.12.0-55.27.1.el10_0.x86_64+debug #1 Not tainted
--------------------------------------------
mv/5099 is trying to acquire lock:
ffff888132845358 (&inode->i_sb->s_type->i_mutex_dir_key/1){+.+.}-{3:3},
at: __kern_path_locked+0x10a/0x2f0
but task is already holding lock:
ffff888132846b58 (&inode->i_sb->s_type->i_mutex_dir_key/1){+.+.}-{3:3},
at: lock_two_directories+0x13f/0x2b0
other info that might help us debug this:
Possible unsafe locking scenario:
CPU0
---- lock(&inode->i_sb->s_type->i_mutex_dir_key/1);
lock(&inode->i_sb->s_type->i_mutex_dir_key/1);
*** DEADLOCK ***
May be due to missing lock nesting notation
6 locks held by mv/5099:
#0: ffff888112a9c440 (sb_writers#13)
at: do_renameat2+0x34c/0xbc0
#1: ffff888112a9c790 (&type->s_vfs_rename_key#3)
at: do_renameat2+0x415/0xbc0
#2: ffff888132846b58 (&inode->i_sb->s_type->i_mutex_dir_key/1)
at: lock_two_directories+0x13f/0x2b0
#3: ffff888132845358 (&inode->i_sb->s_type->i_mutex_dir_key/5)
at: lock_two_directories+0x175/0x2b0
#4: ffffffffb3a1fb10 (&fsnotify_mark_srcu)
at: fsnotify+0x454/0x28a0
#5: ffffffffaf886230 (audit_filter_mutex)
at: audit_update_watch+0x36/0x11e0
stack backtrace: Call Trace: <TASK> dump_stack_lvl+0x6f/0xb0 print_deadlock_bug.cold+0xbd/0xca validate_chain+0x83a/0xf00 __lock_acquire+0xcac/0x1d20 lock_acquire.part.0+0x11b/0x360 down_write_nested+0x9f/0x230 __kern_path_locked+0x10a/0x2f0 kern_path_locked+0x26/0x40 audit_alloc_mark+0xfb/0x4f0 audit_dupe_exe+0x6c/0xe0 audit_dupe_rule+0x6c2/0xc00 audit_update_watch+0x4cc/0x11e0 audit_watch_handle_event+0x12c/0x1b0 send_to_group+0x5d0/0x8b0 fsnotify+0x615/0x28a0 fsnotify_move+0x1d8/0x630 vfs_rename+0xdcd/0x1df0 do_renameat2+0x9d4/0xbc0 __x64_sys_renameat+0x192/0x260 do_syscall_64+0x92/0x180 entry_SYSCALL_64_after_hwframe+0x76/0x7e RIP: 0033:0x7f0491fe8c4e Code: 0f 1f 40 00 48 8b 15 c1 e1 16 00 f7 d8 64 89 02 b8 ff ff ff ff c3 66 0f 1f 44 00 00 f3 0f 1e fa 49 89 ca b8 08 01 00 00 0f 05 <48> 3d 00 f0 ff ff 77 0a c3 66 0f 1f 84 00 00 00 00 00 48 8b 15 89 RSP: 002b:00007ffc7210bf38 EFLAGS: 00000246 ORIG_RAX: 0000000000000108 RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f0491fe8c4e RDX: 0000000000000003 RSI: 00007ffc7210e6c8 RDI: 00000000ffffff9c RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000001 R10: 00005575eb2dae2a R11: 0000000000000246 R12: 00005575eb2dae2a R13: 00007ffc7210e6c8 R14: 0000000000000003 R15: 00000000ffffff9c </TASK>
The aforementioned deadlock can be consistently reproduced by running the script below:
audit-dupe-exe-deadlock.sh
--------------------------
#!/bin/bash
auditctl -D
mkdir -p /tmp/foo
touch /tmp/file
auditctl -a always,exit -F exe=/tmp/file -F path=/tmp/file -S all -k dr mv /tmp/file /tmp/foo/file
rm -Rf /tmp/foo
This patch fixes the issue by introducing struct audit_watch_ctx to pass the fsnotify event context down to audit_alloc_mark(). By utilizing the already-resolved directory inode provided by the event, we bypass the kern_path_parent() path resolution entirely, safely avoiding the recursive lock. Furthermore, it explicitly allows duplicate fsnotify marks (allow_dups = 1) during the rename update, allowing the new rule's mark to safely coexist with the old rule's mark until the old rule is freed.
P.S.: This issue was identified and reproduced during a comprehensive code coverage analysis of the audit subsystem. The full report is available at the link below:
https://people.redhat.com/rrobaina/audit-code-coverage-analysis.pdf
P.P.S: With the permission of both Ricardo and Nathan, I've squashed a fixup patch from Nathan that addresses a compile time error when CONFIG_AUDITSYSCALL=n.
--- kernel/audit.h | 17 ++++++++++++----- kernel/audit_fsnotify.c | 32 +++++++++++++++++++++++--------- kernel/audit_watch.c | 25 +++++++++++++++++-------- kernel/auditfilter.c | 9 +++++---- 4 files changed, 57 insertions(+), 26 deletions(-)
kernel/audit.h +12 -5
@@ -250,8 +250,13 @@ extern int audit_del_rule(struct audit_eextern void audit_free_rule_rcu (struct rcu_head *head);extern struct list_head audit_filter_list[]; -extern struct audit_entry *audit_dupe_rule (struct audit_krule *old); +struct audit_watch_ctx { +struct inode *dir; +struct inode *child; +}; +extern struct audit_entry *audit_dupe_rule (struct audit_krule *old, +struct audit_watch_ctx *ctx);extern void audit_log_d_path_exe (struct audit_buffer *ab,struct mm_struct *mm);
@@ -274,13 +279,15 @@ extern char *audit_watch_path(struct audextern int audit_watch_compare (struct audit_watch *watch,u64 ino,dev_t dev);extern struct audit_fsnotify_mark *audit_alloc_mark (struct audit_krule *krule, -char *pathname,int len); +char *pathname,int len, +struct audit_watch_ctx *ctx);extern char *audit_mark_path (struct audit_fsnotify_mark *mark);extern void audit_remove_mark (struct audit_fsnotify_mark *audit_mark);extern void audit_remove_mark_rule (struct audit_krule *krule);extern int audit_mark_compare (struct audit_fsnotify_mark *mark,u64 ino,dev_t dev); -extern int audit_dupe_exe (struct audit_krule *new,struct audit_krule *old); +extern int audit_dupe_exe (struct audit_krule *new,struct audit_krule *old, +struct audit_watch_ctx *ctx);extern int audit_exe_compare (struct task_struct *tsk,struct audit_fsnotify_mark *mark);
@@ -311,13 +318,13 @@ extern struct list_head *audit_killed_tr#define audit_watch_path (w) ""#define audit_watch_compare (w, i, d) 0 -#define audit_alloc_mark (k, p, l) (ERR_PTR(-EINVAL)) +#define audit_alloc_mark (k, p, l, c) (ERR_PTR(-EINVAL))#define audit_mark_path (m) ""#define audit_remove_mark (m) do { } while (0)#define audit_remove_mark_rule (k) do { } while (0)#define audit_mark_compare (m, i, d) 0#define audit_exe_compare (t, m) (-EINVAL) -#define audit_dupe_exe (n, o) (-EINVAL) +#define audit_dupe_exe (n, o, c) (-EINVAL)#define audit_remove_tree_rule (rule) BUG()#define audit_add_tree_rule (rule) -EINVAL
kernel/audit_fsnotify.c +23 -9
@@ -71,19 +71,30 @@ static void audit_update_mark(struct aud audit_mark->ino = inode ? inode->i_ino :AUDIT_INO_UNSET ; } -struct audit_fsnotify_mark *audit_alloc_mark (struct audit_krule *krule,char *pathname,int len) +struct audit_fsnotify_mark *audit_alloc_mark (struct audit_krule *krule,char *pathname, +int len,struct audit_watch_ctx *ctx) {struct audit_fsnotify_mark *audit_mark;struct path path;struct dentry *dentry; -int ret; +struct inode *dir, *child; +int ret, allow_dups;if (pathname[0 ] !='/' || pathname[len-1 ] =='/' )return ERR_PTR (-EINVAL ); - dentry =kern_path_parent (pathname, &path); -if (IS_ERR (dentry)) -return ERR_CAST (dentry);/* returning an error */ +if (!ctx) { + dentry =kern_path_parent (pathname, &path); +if (IS_ERR (dentry)) +return ERR_CAST (dentry);/* returning an error */ + dir =d_inode (path.dentry); + child =d_inode (dentry); + allow_dups =0 ; + }else { + dir = ctx->dir; + child = ctx->child; + allow_dups =1 ; + } audit_mark =kzalloc (sizeof (*audit_mark),GFP_KERNEL );if (unlikely (!audit_mark)) {
@@ -94,18 +105,21 @@ struct audit_fsnotify_mark *audit_alloc_fsnotify_init_mark (&audit_mark->mark, audit_fsnotify_group); audit_mark->mark.mask =AUDIT_FS_EVENTS ; audit_mark->path = pathname; -audit_update_mark (audit_mark, dentry->d_inode); audit_mark->rule = krule; - ret =fsnotify_add_inode_mark (&audit_mark->mark, path.dentry->d_inode,0 ); +audit_update_mark (audit_mark, child); + ret =fsnotify_add_inode_mark (&audit_mark->mark, dir, allow_dups); +if (ret <0 ) { audit_mark->path =NULL ;fsnotify_put_mark (&audit_mark->mark); audit_mark =ERR_PTR (ret); } out: -dput (dentry); -path_put (&path); +if (!ctx) { +dput (dentry); +path_put (&path); + }return audit_mark; }
kernel/audit_watch.c +17 -8
@@ -244,7 +244,8 @@ static void audit_watch_log_rule_change(/* Update inode info in audit rules based on filesystem event. */ static void audit_update_watch (struct audit_parent *parent,const struct qstr *dname,dev_t dev, -u64 ino,unsigned int invalidating) +u64 ino,unsigned int invalidating, +struct audit_watch_ctx *ctx) {struct audit_watch *owatch, *nwatch, *nextw;struct audit_krule *r, *nextr;
@@ -280,7 +281,7 @@ static void audit_update_watch(struct aulist_del (&oentry->rule.rlist);list_del_rcu (&oentry->list); - nentry =audit_dupe_rule (&oentry->rule); + nentry =audit_dupe_rule (&oentry->rule, ctx);if (IS_ERR (nentry)) {list_del (&oentry->rule.list);audit_panic ("error updating watch, removing" );
@@ -479,10 +480,17 @@ static int audit_watch_handle_event(struif (WARN_ON_ONCE (inode_mark->group != audit_watch_group))return 0 ; -if (mask & (FS_CREATE |FS_MOVED_TO ) && inode) -audit_update_watch (parent, dname, inode->i_sb->s_dev, inode->i_ino,0 ); -else if (mask & (FS_DELETE |FS_MOVED_FROM )) -audit_update_watch (parent, dname,AUDIT_DEV_UNSET ,AUDIT_INO_UNSET ,1 ); +if (mask & (FS_CREATE |FS_MOVED_TO ) && inode) { +struct audit_watch_ctx ctx = { .dir = dir, .child = inode }; + +audit_update_watch (parent, dname, inode->i_sb->s_dev, inode->i_ino,0 , + &ctx); + }else if (mask & (FS_DELETE |FS_MOVED_FROM )) { +struct audit_watch_ctx ctx = { .dir = dir, .child =NULL }; + +audit_update_watch (parent, dname,AUDIT_DEV_UNSET ,AUDIT_INO_UNSET ,1 , + &ctx); + }else if (mask & (FS_DELETE_SELF |FS_UNMOUNT |FS_MOVE_SELF ))audit_remove_parent_watches (parent);
@@ -505,7 +513,8 @@ static int __init audit_watch_init(void) }device_initcall (audit_watch_init); -int audit_dupe_exe (struct audit_krule *new,struct audit_krule *old) +int audit_dupe_exe (struct audit_krule *new,struct audit_krule *old, +struct audit_watch_ctx *ctx) {struct audit_fsnotify_mark *audit_mark;char *pathname;
@@ -514,7 +523,7 @@ int audit_dupe_exe(struct audit_krule *nif (!pathname)return -ENOMEM ; - audit_mark =audit_alloc_mark (new, pathname,strlen (pathname)); + audit_mark =audit_alloc_mark (new, pathname,strlen (pathname), ctx);if (IS_ERR (audit_mark)) {kfree (pathname);return PTR_ERR (audit_mark);
kernel/auditfilter.c +5 -4
@@ -590,7 +590,7 @@ static struct audit_entry *audit_data_to err =PTR_ERR (str); goto exit_free; } - audit_mark =audit_alloc_mark (&entry->rule, str, f_val); + audit_mark =audit_alloc_mark (&entry->rule, str, f_val,NULL );if (IS_ERR (audit_mark)) {kfree (str); err =PTR_ERR (audit_mark);
@@ -818,7 +818,8 @@ static inline int audit_dupe_lsm_field(s* rule with the new rule in the filterlist, then free the old rule. * The rlist element is undefined; list manipulations are handled apart from * the initial copy. */ -struct audit_entry *audit_dupe_rule (struct audit_krule *old) +struct audit_entry *audit_dupe_rule (struct audit_krule *old, +struct audit_watch_ctx *ctx) {u32 fcount = old->field_count;struct audit_entry *entry;
@@ -877,7 +878,7 @@ struct audit_entry *audit_dupe_rule(stru new->filterkey = fk;break ;case AUDIT_EXE : - err =audit_dupe_exe (new, old); + err =audit_dupe_exe (new, old, ctx);break ; }if (err) {
@@ -1408,7 +1409,7 @@ static int update_lsm_rule(struct audit_if (!security_audit_rule_known (r))return 0 ; - nentry =audit_dupe_rule (r); + nentry =audit_dupe_rule (r,NULL );if (entry->rule.exe)audit_remove_mark (entry->rule.exe);if (IS_ERR (nentry)) {
Greg Kroah-Hartman
maintainerPATCH 373/484 i2c: i801: fix hardware state machine corruption in error path
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mingyu Wang <[email protected]>
[ Upstream commit 10dd1a736d557e310a77117832874729a0175d57 ]
A severe livelock and subsequent Hung Task panic were observed in the i2c-i801 driver during concurrent Fuzzing. The crash is caused by an unconditional hardware register cleanup in the error handling path of i801_access().
When i801_check_pre() fails (e.g., returning -EBUSY because the SMBus controller is actively used by BIOS/ACPI), the kernel does not actually acquire the hardware ownership. However, the code jumps to the 'out' label and executes:
iowrite8(SMBHSTSTS_INUSE_STS | STATUS_FLAGS, SMBHSTSTS(priv));
This forcefully clears the INUSE_STS lock and resets the hardware status flags without owning the controller. Doing so interrupts ongoing BIOS/ACPI transactions and totally corrupts the SMBus hardware state machine.
Consequently, all subsequent i801_access() calls fail at the pre-check stage, triggering an endless stream of "SMBus is busy, can't use it!" error logs. Over a slow serial console, this printk flood monopolizes the CPU (Console Livelock), starving other processes trying to acquire the mmap_lock down_read semaphore, ultimately triggering the hung task watchdog.
Fix this by moving the 'out' label below the hardware register cleanup. If i801_check_pre() fails, we safely bypass the iowrite8() and only release the software locks (pm_runtime and mutex), strictly adhering to the rule of not releasing resources that were never acquired.
--- drivers/i2c/busses/i2c-i801.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/i2c/busses/i2c-i801.c +1 -1
@@ -918,13 +918,13 @@ static s32 i801_access(struct i2c_adapte*/ if (hwpec)outb_p (inb_p (SMBAUXCTL (priv)) & ~SMBAUXCTL_CRC ,SMBAUXCTL (priv)); -out:/* * Unlock the SMBus device for use by BIOS/ACPI, * and clear status flags if not done already. */ outb_p (SMBHSTSTS_INUSE_STS |STATUS_FLAGS ,SMBHSTSTS (priv)); +out:pm_runtime_mark_last_busy (&priv->pci_dev->dev);pm_runtime_put_autosuspend (&priv->pci_dev->dev);mutex_unlock (&priv->acpi_lock);
Greg Kroah-Hartman
maintainerPATCH 374/484 ALSA: hda: conexant: Remove mic bias threshold override
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhang Heng <[email protected]>
[ Upstream commit f52524da7084c1a54683ae9fbc73e93fff19dd64 ]
Remove the mic bias current comparator threshold override (NID 0x1c, verb 0x320, value 0x010) from Conexant codec driver.
This override was originally intended to support volume up/down controls on headsets with inline remote controls, but it causes microphone detection failures on some headsets with impedance less than 1k ohm.
After consulting with the vendor's engineers, it was confirmed that this setting is board-specific and should be handled by BIOS/firmware rather than the generic codec driver, especially since inline remote support is not currently implemented.
--- sound/pci/hda/patch_conexant.c | 3 --- 1 file changed, 3 deletions(-)
sound/pci/hda/patch_conexant.c +0 -3
@@ -168,9 +168,6 @@ static void cx_fixup_headset_recog(struc {unsigned int mic_present; -/* fix some headset type recognize fail issue, such as EDIFIER headset */ -/* set micbias output current comparator threshold from 66% to 55%. */ -snd_hda_codec_write (codec,0x1c ,0 ,0x320 ,0x010 );/* set OFF voltage for DFET from -1.2V to -0.8V, set headset micbias register * value adjustment trim from 2.2K ohms to 2.0K ohms. */
Greg Kroah-Hartman
maintainerPATCH 375/484 ALSA: hda: Fix cached processing coefficient verbs
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xu Rao <[email protected]>
[ Upstream commit f67be28fdf8b5d31ac1cc1152bb17250f9f8f513 ]
Intel HD Audio defines Coefficient Index and Processing Coefficient as separate audio widget controls in the Audio Widget Verb Definitions:
Coefficient Index selects the coefficient slot, while Processing Coefficient accesses the value at the selected slot.
hda_reg_read_coef() selects the slot with AC_VERB_SET_COEF_INDEX, but then uses AC_VERB_GET_COEF_INDEX for the value read. That reads back the selected index instead of the coefficient value. hda_reg_write_coef() has the same issue and builds the value write from AC_VERB_GET_COEF_INDEX instead of AC_VERB_SET_PROC_COEF.
This only affects the regmap coefficient cache path used by codecs that set codec->cache_coef. Direct coefficient helpers already use the normal SET_COEF_INDEX followed by GET_PROC_COEF or SET_PROC_COEF sequence, which is likely why this has not been noticed widely.
Use AC_VERB_GET_PROC_COEF for cached coefficient reads and AC_VERB_SET_PROC_COEF for cached coefficient writes.
--- sound/hda/hdac_regmap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
sound/hda/hdac_regmap.c +2 -2
@@ -214,7 +214,7 @@ static int hda_reg_read_coef(struct hdac err =snd_hdac_exec_verb (codec, verb,0 ,NULL );if (err <0 )return err; - verb = (reg & ~0xfffff ) | (AC_VERB_GET_COEF_INDEX <<8 ); + verb = (reg & ~0xfffff ) | (AC_VERB_GET_PROC_COEF <<8 );return snd_hdac_exec_verb (codec, verb,0 , val); }
@@ -232,7 +232,7 @@ static int hda_reg_write_coef(struct hda err =snd_hdac_exec_verb (codec, verb,0 ,NULL );if (err <0 )return err; - verb = (reg & ~0xfffff ) | (AC_VERB_GET_COEF_INDEX <<8 ) | + verb = (reg & ~0xfffff ) | (AC_VERB_SET_PROC_COEF <<8 ) | (val &0xffff );return snd_hdac_exec_verb (codec, verb,0 ,NULL ); }
Greg Kroah-Hartman
maintainerPATCH 376/484 serial: max310x: replace bare use of unsigned with unsigned int (checkpatch)
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hugo Villeneuve <[email protected]>
[ Upstream commit 79b69eb09cf5b6a77e621b2838b7e0d38113debb ]
Fixes the following checkpatch warnings:
WARNING: Prefer 'unsigned int' to bare use of 'unsigned'
With this change, the affected functions now match the prototypes in struct gpio_chip.
Stable-dep-of: a483b1a91b33 ("serial: max310x: implement gpio_chip::get_direction()")
--- drivers/tty/serial/max310x.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/tty/serial/max310x.c +4 -4
@@ -1209,7 +1209,7 @@ static int __maybe_unused max310x_resume static SIMPLE_DEV_PM_OPS(max310x_pm_ops, max310x_suspend, max310x_resume);#ifdef CONFIG_GPIOLIB -static int max310x_gpio_get (struct gpio_chip *chip,unsigned offset) +static int max310x_gpio_get (struct gpio_chip *chip,unsigned int offset) {unsigned int val;struct max310x_port *s =gpiochip_get_data (chip);
@@ -1220,7 +1220,7 @@ static int max310x_gpio_get(struct gpio_return !!((val >>4 ) & (1 << (offset %4 ))); } -static void max310x_gpio_set (struct gpio_chip *chip,unsigned offset,int value) +static void max310x_gpio_set (struct gpio_chip *chip,unsigned int offset,int value) {struct max310x_port *s =gpiochip_get_data (chip);struct uart_port *port = &s->p[offset /4 ].port;
@@ -1229,7 +1229,7 @@ static void max310x_gpio_set(struct gpio value ? 1 << (offset % 4) : 0); } -static int max310x_gpio_direction_input (struct gpio_chip *chip,unsigned offset) +static int max310x_gpio_direction_input (struct gpio_chip *chip,unsigned int offset) {struct max310x_port *s =gpiochip_get_data (chip);struct uart_port *port = &s->p[offset /4 ].port;
@@ -1240,7 +1240,7 @@ static int max310x_gpio_direction_input( }static int max310x_gpio_direction_output (struct gpio_chip *chip, -unsigned offset,int value) +unsigned int offset,int value) {struct max310x_port *s =gpiochip_get_data (chip);struct uart_port *port = &s->p[offset /4 ].port;
Greg Kroah-Hartman
maintainerPATCH 377/484 serial: max310x: implement gpio_chip::get_direction()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tapio Reijonen <[email protected]>
[ Upstream commit a483b1a91b33b7533280e7c3efd2bc1275caef18 ]
It's strongly recommended for GPIO drivers to always implement the .get_direction() callback - even when the direction is tracked in software. The GPIO core emits a warning when the callback is missing and a user reads the direction of a line, e.g. via /sys/kernel/debug/gpio.
The MAX310X keeps the GPIO direction in the GPIOCFG register (a set bit selects output), which the existing direction_input/output callbacks already program, so the current direction can be read back directly.
--- drivers/tty/serial/max310x.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
drivers/tty/serial/max310x.c +12 -0
@@ -1229,6 +1229,17 @@ static void max310x_gpio_set(struct gpio value ? 1 << (offset % 4) : 0); } +static int max310x_gpio_get_direction (struct gpio_chip *chip,unsigned int offset) +{ +struct max310x_port *s =gpiochip_get_data (chip); +struct uart_port *port = &s->p[offset /4 ].port; +unsigned int val; + + val =max310x_port_read (port,MAX310X_GPIOCFG_REG ); + +return val &BIT (offset %4 ) ?GPIO_LINE_DIRECTION_OUT :GPIO_LINE_DIRECTION_IN ; +} +static int max310x_gpio_direction_input (struct gpio_chip *chip,unsigned int offset) {struct max310x_port *s =gpiochip_get_data (chip);
@@ -1442,6 +1453,7 @@ static int max310x_probe(struct device * s->gpio.owner =THIS_MODULE ; s->gpio.parent = dev; s->gpio.label = devtype->name; + s->gpio.get_direction = max310x_gpio_get_direction; s->gpio.direction_input = max310x_gpio_direction_input; s->gpio.get = max310x_gpio_get; s->gpio.direction_output= max310x_gpio_direction_output;
Greg Kroah-Hartman
maintainerPATCH 378/484 afs: Annotate struct afs_addr_list with __counted_by
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kees Cook <[email protected]>
[ Upstream commit 2d26302bdff1300029a67931739f5770eac22493 ]
Prepare for the coming implementation by GCC and Clang of the __counted_by attribute. Flexible array members annotated with __counted_by can have their accesses bounds-checked at run-time checking via CONFIG_UBSAN_BOUNDS (for array indexing) and CONFIG_FORTIFY_SOURCE (for strcpy/memcpy-family functions).
As found with Coccinelle[1], add __counted_by for struct afs_addr_list.
[1] https://github.com/kees/kernel-tools/blob/trunk/coccinelle/examples/counted_by.cocci
Stable-dep-of: dc175389b18c ("rxrpc: serialize kernel accept preallocation with socket teardown")
--- fs/afs/internal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/afs/internal.h +1 -1
@@ -87,7 +87,7 @@ struct afs_addr_list { enum dns_lookup_status status:8;unsigned long failed;/* Mask of addrs that failed locally/ICMP */ unsigned long responded;/* Mask of addrs that responded */ -struct sockaddr_rxrpc addrs[]; + struct sockaddr_rxrpc addrs[] __counted_by(max_addrs);#define AFS_MAX_ADDRESSES ((unsigned int)(sizeof(unsigned long) * 8)) };
Greg Kroah-Hartman
maintainerPATCH 379/484 afs: Turn the afs_addr_list address array into an array of structs
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit 07f3502b33a260f873e35708d2fa693eb52225cb ]
Turn the afs_addr_list address array into an array of structs, thereby allowing per-address (such as RTT) info to be added.
Stable-dep-of: dc175389b18c ("rxrpc: serialize kernel accept preallocation with socket teardown")
--- fs/afs/addr_list.c | 10 +++++----- fs/afs/fs_probe.c | 6 +++--- fs/afs/internal.h | 6 +++++- fs/afs/proc.c | 4 ++-- fs/afs/rotate.c | 2 +- fs/afs/rxrpc.c | 4 ++-- fs/afs/server.c | 4 ++-- fs/afs/vl_alias.c | 4 ++-- fs/afs/vl_probe.c | 6 +++--- fs/afs/vl_rotate.c | 2 +- 10 files changed, 26 insertions(+), 22 deletions(-)
fs/afs/addr_list.c +5 -5
@@ -45,7 +45,7 @@ struct afs_addr_list *afs_alloc_addrlist alist->max_addrs = nr;for (i =0 ; i < nr; i++) { -struct sockaddr_rxrpc *srx = &alist->addrs[i]; +struct sockaddr_rxrpc *srx = &alist->addrs[i].srx; srx->srx_family =AF_RXRPC ; srx->srx_service = service; srx->transport_type =SOCK_DGRAM ;
@@ -281,7 +281,7 @@ void afs_merge_fs_addr4(struct afs_addr_return ;for (i =0 ; i < alist->nr_ipv4; i++) { -struct sockaddr_in *a = &alist->addrs[i].transport.sin; +struct sockaddr_in *a = &alist->addrs[i].srx.transport.sin;u32 a_addr =ntohl (a->sin_addr.s_addr);u16 a_port =ntohs (a->sin_port);
@@ -298,7 +298,7 @@ void afs_merge_fs_addr4(struct afs_addr_ alist->addrs + i, sizeof(alist->addrs[0]) * (alist->nr_addrs - i)); - srx = &alist->addrs[i]; + srx = &alist->addrs[i].srx; srx->srx_family =AF_RXRPC ; srx->transport_type =SOCK_DGRAM ; srx->transport_len =sizeof (srx->transport.sin);
@@ -321,7 +321,7 @@ void afs_merge_fs_addr6(struct afs_addr_return ;for (i = alist->nr_ipv4; i < alist->nr_addrs; i++) { -struct sockaddr_in6 *a = &alist->addrs[i].transport.sin6; +struct sockaddr_in6 *a = &alist->addrs[i].srx.transport.sin6;u16 a_port =ntohs (a->sin6_port); diff =memcmp (xdr, &a->sin6_addr,16 );
@@ -338,7 +338,7 @@ void afs_merge_fs_addr6(struct afs_addr_ alist->addrs + i, sizeof(alist->addrs[0]) * (alist->nr_addrs - i)); - srx = &alist->addrs[i]; + srx = &alist->addrs[i].srx; srx->srx_family =AF_RXRPC ; srx->transport_type =SOCK_DGRAM ; srx->transport_len =sizeof (srx->transport.sin6);
fs/afs/fs_probe.c +3 -3
@@ -153,12 +153,12 @@ responded:if (call->service_id ==YFS_FS_SERVICE ) { server->probe.is_yfs = true;set_bit (AFS_SERVER_FL_IS_YFS , &server->flags); - alist->addrs[index].srx_service = call->service_id; + alist->addrs[index].srx.srx_service = call->service_id; }else { server->probe.not_yfs = true;if (!server->probe.is_yfs) {clear_bit (AFS_SERVER_FL_IS_YFS , &server->flags); - alist->addrs[index].srx_service = call->service_id; + alist->addrs[index].srx.srx_service = call->service_id; } cap0 =ntohl (call->tmp);if (cap0 &AFS3_VICED_CAPABILITY_64BITFILES )
@@ -182,7 +182,7 @@ out:spin_unlock (&server->probe_lock);_debug ("probe %pU [%u] %pISpc rtt=%u ret=%d" , - &server->uuid, index, &alist->addrs[index].transport, + &server->uuid, index, &alist->addrs[index].srx.transport, rtt_us, ret);return afs_done_one_fs_probe (call->net, server);
fs/afs/internal.h +5 -1
@@ -87,7 +87,9 @@ struct afs_addr_list { enum dns_lookup_status status:8;unsigned long failed;/* Mask of addrs that failed locally/ICMP */ unsigned long responded;/* Mask of addrs that responded */ - struct sockaddr_rxrpc addrs[] __counted_by(max_addrs); +struct { +struct sockaddr_rxrpc srx; + } addrs[] __counted_by(max_addrs);#define AFS_MAX_ADDRESSES ((unsigned int)(sizeof(unsigned long) * 8)) };
@@ -972,6 +974,8 @@ extern void afs_put_addrlist(struct afs_extern struct afs_vlserver_list *afs_parse_text_addrs (struct afs_net *,const char *,size_t ,char ,unsigned short ,unsigned short ); +bool afs_addr_list_same (const struct afs_addr_list *a, +const struct afs_addr_list *b);extern struct afs_vlserver_list *afs_dns_query (struct afs_cell *,time64_t *);extern bool afs_iterate_addresses (struct afs_addr_cursor *);extern int afs_end_cursor (struct afs_addr_cursor *);
fs/afs/proc.c +2 -2
@@ -307,7 +307,7 @@ static int afs_proc_cell_vlservers_show(for (i =0 ; i < alist->nr_addrs; i++)seq_printf (m," %c %pISpc\n" , alist->preferred == i ?'>' :'-' , - &alist->addrs[i].transport); + &alist->addrs[i].srx.transport); }seq_printf (m," info: fl=%lx rtt=%d\n" , vlserver->flags, vlserver->rtt); seq_printf(m, " probe: fl=%x e=%d ac=%d out=%d\n",
@@ -399,7 +399,7 @@ static int afs_proc_servers_show(struct alist->version, alist->responded, alist->failed);for (i =0 ; i < alist->nr_addrs; i++)seq_printf (m," [%x] %pISpc%s\n" , - i, &alist->addrs[i].transport, + i, &alist->addrs[i].srx.transport, alist->preferred == i ?"*" :"" );return 0 ; }
fs/afs/rotate.c +1 -1
@@ -409,7 +409,7 @@ iterate_address:_debug ("address [%u] %u/%u %pISp" , op->index, op->ac.index, op->ac.alist->nr_addrs, - &op->ac.alist->addrs[op->ac.index].transport); + &op->ac.alist->addrs[op->ac.index].srx.transport);_leave (" = t" );return true;
fs/afs/rxrpc.c +2 -2
@@ -302,7 +302,7 @@ static void afs_notify_end_request_tx(st*/ void afs_make_call (struct afs_addr_cursor *ac,struct afs_call *call,gfp_t gfp) { -struct sockaddr_rxrpc *srx = &ac->alist->addrs[ac->index]; +struct sockaddr_rxrpc *srx = &ac->alist->addrs[ac->index].srx;struct rxrpc_call *rxcall;struct msghdr msg;struct kvec iov[1 ];
@@ -474,7 +474,7 @@ static void afs_log_error(struct afs_cal max = m +1 ;pr_notice ("kAFS: Peer reported %s failure on %s [%pISp]\n" , msg, call->type->name, - &call->alist->addrs[call->addr_ix].transport); + &call->alist->addrs[call->addr_ix].srx.transport); } }
fs/afs/server.c +2 -2
@@ -43,7 +43,7 @@ struct afs_server *afs_find_server(struchlist_for_each_entry_rcu (server, &net->fs_addresses6, addr6_link) { alist =rcu_dereference (server->addresses);for (i = alist->nr_ipv4; i < alist->nr_addrs; i++) { - b = &alist->addrs[i].transport.sin6; + b = &alist->addrs[i].srx.transport.sin6; diff = ((u16 __force )a->sin6_port - (u16 __force )b->sin6_port);if (diff ==0 )
@@ -59,7 +59,7 @@ struct afs_server *afs_find_server(struchlist_for_each_entry_rcu (server, &net->fs_addresses4, addr4_link) { alist =rcu_dereference (server->addresses);for (i =0 ; i < alist->nr_ipv4; i++) { - b = &alist->addrs[i].transport.sin; + b = &alist->addrs[i].srx.transport.sin; diff = ((u16 __force )a->sin_port - (u16 __force )b->sin_port);if (diff ==0 )
fs/afs/vl_alias.c +2 -2
@@ -94,8 +94,8 @@ static int afs_compare_fs_alists(const s lb =rcu_dereference (server_b->addresses);while (a < la->nr_addrs && b < lb->nr_addrs) { -const struct sockaddr_rxrpc *srx_a = &la->addrs[a]; -const struct sockaddr_rxrpc *srx_b = &lb->addrs[b]; +const struct sockaddr_rxrpc *srx_a = &la->addrs[a].srx; +const struct sockaddr_rxrpc *srx_b = &lb->addrs[b].srx;int diff =afs_compare_addrs (srx_a, srx_b);if (diff <0 ) {
fs/afs/vl_probe.c +3 -3
@@ -106,12 +106,12 @@ responded:if (call->service_id ==YFS_VL_SERVICE ) { server->probe.flags |=AFS_VLSERVER_PROBE_IS_YFS ;set_bit (AFS_VLSERVER_FL_IS_YFS , &server->flags); - alist->addrs[index].srx_service = call->service_id; + alist->addrs[index].srx.srx_service = call->service_id; }else { server->probe.flags |=AFS_VLSERVER_PROBE_NOT_YFS ;if (!(server->probe.flags &AFS_VLSERVER_PROBE_IS_YFS )) {clear_bit (AFS_VLSERVER_FL_IS_YFS , &server->flags); - alist->addrs[index].srx_service = call->service_id; + alist->addrs[index].srx.srx_service = call->service_id; } }
@@ -131,7 +131,7 @@ out:spin_unlock (&server->probe_lock);_debug ("probe [%u][%u] %pISpc rtt=%u ret=%d" , - server_index, index, &alist->addrs[index].transport, rtt_us, ret); + server_index, index, &alist->addrs[index].srx.transport, rtt_us, ret);afs_done_one_vl_probe (server, have_result); }
fs/afs/vl_rotate.c +1 -1
@@ -249,7 +249,7 @@ iterate_address:_debug ("VL address %d/%d" , vc->ac.index, vc->ac.alist->nr_addrs); -_leave (" = t %pISpc" , &vc->ac.alist->addrs[vc->ac.index].transport); +_leave (" = t %pISpc" , &vc->ac.alist->addrs[vc->ac.index].srx.transport);return true; next_server:
Greg Kroah-Hartman
maintainerPATCH 380/484 rxrpc: Pull out certain app callback funcs into an ops table
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit 23738cc8048322cf324f330cd697380fb3455da5 ]
A number of functions separately furnish an AF_RXRPC socket with callback function pointers into a kernel app (such as the AFS filesystem) that is using it. Replace most of these with an ops table for the entire socket. This makes it easier to add more callback functions.
Note that the call incoming data processing callback is retaind as that gets set to different things, depending on the type of op.
Stable-dep-of: dc175389b18c ("rxrpc: serialize kernel accept preallocation with socket teardown")
--- fs/afs/rxrpc.c | 11 ++++++++--- include/net/af_rxrpc.h | 25 +++++++++++++++---------- net/rxrpc/af_rxrpc.c | 20 ++++++++------------ net/rxrpc/ar-internal.h | 3 +-- net/rxrpc/call_accept.c | 34 ++++++++++++++++------------------ net/rxrpc/rxperf.c | 10 +++++++--- 6 files changed, 55 insertions(+), 48 deletions(-)
fs/afs/rxrpc.c +8 -3
@@ -23,8 +23,15 @@ static void afs_wake_up_async_call(strucstatic void afs_process_async_call (struct work_struct *);static void afs_rx_new_call (struct sock *,struct rxrpc_call *,unsigned long );static void afs_rx_discard_new_call (struct rxrpc_call *,unsigned long ); +static void afs_rx_attach (struct rxrpc_call *rxcall,unsigned long user_call_ID);static int afs_deliver_cm_op_id (struct afs_call *); +static const struct rxrpc_kernel_ops afs_rxrpc_callback_ops = { + .notify_new_call = afs_rx_new_call, + .discard_new_call = afs_rx_discard_new_call, + .user_attach_call = afs_rx_attach, +}; +/* asynchronous incoming call initial processing */ static const struct afs_call_type afs_RXCMxxxx = { .name ="CB.xxxx" ,
@@ -83,8 +90,7 @@ int afs_open_socket(struct afs_net *net)* it sends back to us. */ -rxrpc_kernel_new_call_notification (socket, afs_rx_new_call, - afs_rx_discard_new_call); +rxrpc_kernel_set_notifications (socket, &afs_rxrpc_callback_ops); ret =kernel_listen (socket,INT_MAX );if (ret <0 )
@@ -748,7 +754,6 @@ void afs_charge_preallocation(struct worif (rxrpc_kernel_charge_accept (net->socket, afs_wake_up_async_call, - afs_rx_attach, (unsigned long )call,GFP_KERNEL , call->debug_id) <0 )
include/net/af_rxrpc.h +15 -10
@@ -28,18 +28,23 @@ enum rxrpc_interruptibility {*/ extern atomic_t rxrpc_debug_id; +/* +* Operations table for rxrpc to call out to a kernel application (e.g. kAFS). +*/ +struct rxrpc_kernel_ops { +void (*notify_new_call)(struct sock *sk,struct rxrpc_call *call, +unsigned long user_call_ID); +void (*discard_new_call)(struct rxrpc_call *call,unsigned long user_call_ID); +void (*user_attach_call)(struct rxrpc_call *call,unsigned long user_call_ID); +}; +typedef void (*rxrpc_notify_rx_t )(struct sock *,struct rxrpc_call *,unsigned long );typedef void (*rxrpc_notify_end_tx_t )(struct sock *,struct rxrpc_call *,unsigned long ); -typedef void (*rxrpc_notify_new_call_t )(struct sock *,struct rxrpc_call *, -unsigned long ); -typedef void (*rxrpc_discard_new_call_t )(struct rxrpc_call *,unsigned long ); -typedef void (*rxrpc_user_attach_call_t )(struct rxrpc_call *,unsigned long ); -void rxrpc_kernel_new_call_notification (struct socket *, -rxrpc_notify_new_call_t , -rxrpc_discard_new_call_t ); +void rxrpc_kernel_set_notifications (struct socket *sock, +const struct rxrpc_kernel_ops *app_ops); struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *sock, struct sockaddr_rxrpc *srx, struct key *key,
@@ -63,9 +68,9 @@ void rxrpc_kernel_put_call(struct socketvoid rxrpc_kernel_get_peer (struct socket *,struct rxrpc_call *,struct sockaddr_rxrpc *);bool rxrpc_kernel_get_srtt (struct socket *,struct rxrpc_call *,u32 *); -int rxrpc_kernel_charge_accept (struct socket *,rxrpc_notify_rx_t , -rxrpc_user_attach_call_t ,unsigned long ,gfp_t , -unsigned int ); +int rxrpc_kernel_charge_accept (struct socket *sock,rxrpc_notify_rx_t notify_rx, +unsigned long user_call_ID,gfp_t gfp, +unsigned int debug_id);void rxrpc_kernel_set_tx_length (struct socket *,struct rxrpc_call *,s64 );bool rxrpc_kernel_check_life (const struct socket *,const struct rxrpc_call *);u32 rxrpc_kernel_get_epoch (struct socket *,struct rxrpc_call *);
net/rxrpc/af_rxrpc.c +8 -12
@@ -418,24 +418,20 @@ u32 rxrpc_kernel_get_epoch(struct socketEXPORT_SYMBOL (rxrpc_kernel_get_epoch);/** -* rxrpc_kernel_new_call_notification - Get notifications of new calls -* @sock: The socket to intercept received messages on -* @notify_new_call: Function to be called when new calls appear -* @discard_new_call: Function to discard preallocated calls +* rxrpc_kernel_set_notifications - Set table of callback operations +* @sock: The socket to install table upon +* @app_ops: Callback operation table to set * -* Allow a kernel service to be given notifications about new calls. +* Allow a kernel service to set a table of event notifications on a socket. */ -void rxrpc_kernel_new_call_notification ( -struct socket *sock, -rxrpc_notify_new_call_t notify_new_call, -rxrpc_discard_new_call_t discard_new_call) +void rxrpc_kernel_set_notifications (struct socket *sock, +const struct rxrpc_kernel_ops *app_ops) {struct rxrpc_sock *rx =rxrpc_sk (sock->sk); - rx->notify_new_call = notify_new_call; - rx->discard_new_call = discard_new_call; + rx->app_ops = app_ops; } -EXPORT_SYMBOL (rxrpc_kernel_new_call_notification); +EXPORT_SYMBOL (rxrpc_kernel_set_notifications);/** * rxrpc_kernel_set_max_life - Set maximum lifespan on a call
net/rxrpc/ar-internal.h +1 -2
@@ -141,8 +141,7 @@ struct rxrpc_backlog {struct rxrpc_sock {/* WARNING: sk has to be the first member */ struct sock sk; -rxrpc_notify_new_call_t notify_new_call;/* Func to notify of new call */ -rxrpc_discard_new_call_t discard_new_call;/* Func to discard a new call */ +const struct rxrpc_kernel_ops *app_ops;/* Table of kernel app notification funcs */ struct rxrpc_local *local;/* local endpoint */ struct rxrpc_backlog *backlog;/* Preallocation for services */ spinlock_t incoming_lock;/* Incoming call vs service shutdown lock */
net/rxrpc/call_accept.c +16 -18
@@ -34,7 +34,6 @@ static void rxrpc_dummy_notify(struct sostatic int rxrpc_service_prealloc_one (struct rxrpc_sock *rx,struct rxrpc_backlog *b,rxrpc_notify_rx_t notify_rx, -rxrpc_user_attach_call_t user_attach_call,unsigned long user_call_ID,gfp_t gfp,unsigned int debug_id) {
@@ -123,9 +122,10 @@ static int rxrpc_service_prealloc_one(st call->user_call_ID = user_call_ID; call->notify_rx = notify_rx; -if (user_attach_call) { +if (rx->app_ops && + rx->app_ops->user_attach_call) {rxrpc_get_call (call, rxrpc_call_get_kernel_service); -user_attach_call (call, user_call_ID); + rx->app_ops->user_attach_call (call, user_call_ID); }rxrpc_get_call (call, rxrpc_call_get_userid);
@@ -221,9 +221,10 @@ void rxrpc_discard_prealloc(struct rxrpcstruct rxrpc_call *call = b->call_backlog[tail];rxrpc_see_call (call, rxrpc_call_see_discard);rcu_assign_pointer (call->socket, rx); -if (rx->discard_new_call) { +if (rx->app_ops && + rx->app_ops->discard_new_call) {_debug ("discard %lx" , call->user_call_ID); - rx->discard_new_call (call, call->user_call_ID); + rx->app_ops->discard_new_call (call, call->user_call_ID);if (call->notify_rx) call->notify_rx = rxrpc_dummy_notify;rxrpc_put_call (call, rxrpc_call_put_kernel);
@@ -392,8 +393,9 @@ bool rxrpc_new_incoming_call(struct rxrprxrpc_incoming_call (rx, call, skb); conn = call->conn; -if (rx->notify_new_call) - rx->notify_new_call (&rx->sk, call, call->user_call_ID); +if (rx->app_ops && + rx->app_ops->notify_new_call) + rx->app_ops->notify_new_call (&rx->sk, call, call->user_call_ID);spin_lock (&conn->state_lock);if (conn->state ==RXRPC_CONN_SERVICE_UNSECURED ) {
@@ -445,8 +447,7 @@ int rxrpc_user_charge_accept(struct rxrpif (rx->sk.sk_state ==RXRPC_CLOSE )return -ESHUTDOWN ; -return rxrpc_service_prealloc_one (rx, b,NULL ,NULL , user_call_ID, -GFP_KERNEL , +return rxrpc_service_prealloc_one (rx, b,NULL , user_call_ID,GFP_KERNEL ,atomic_inc_return (&rxrpc_debug_id)); }
@@ -454,20 +455,18 @@ int rxrpc_user_charge_accept(struct rxrp* rxrpc_kernel_charge_accept - Charge up socket with preallocated calls * @sock: The socket on which to preallocate * @notify_rx: Event notification function for the call -* @user_attach_call: Func to attach call to user_call_ID * @user_call_ID: The tag to attach to the preallocated call * @gfp: The allocation conditions. * @debug_id: The tracing debug ID. * -* Charge up the socket with preallocated calls, each with a user ID. A -* function should be provided to effect the attachment from the user's side. -* The user is given a ref to hold on the call. +* Charge up the socket with preallocated calls, each with a user ID. The +* ->user_attach_call() callback function should be provided to effect the +* attachment from the user's side. The user is given a ref to hold on the +* call. * * Note that the call may be come connected before this function returns. */ -int rxrpc_kernel_charge_accept (struct socket *sock, -rxrpc_notify_rx_t notify_rx, -rxrpc_user_attach_call_t user_attach_call, +int rxrpc_kernel_charge_accept (struct socket *sock,rxrpc_notify_rx_t notify_rx,unsigned long user_call_ID,gfp_t gfp,unsigned int debug_id) {
@@ -477,8 +476,7 @@ int rxrpc_kernel_charge_accept(struct soif (sock->sk->sk_state ==RXRPC_CLOSE )return -ESHUTDOWN ; -return rxrpc_service_prealloc_one (rx, b, notify_rx, - user_attach_call, user_call_ID, +return rxrpc_service_prealloc_one (rx, b, notify_rx, user_call_ID, gfp, debug_id); }EXPORT_SYMBOL (rxrpc_kernel_charge_accept);
net/rxrpc/rxperf.c +7 -3
@@ -136,6 +136,12 @@ static void rxperf_notify_end_reply_tx(s RXPERF_CALL_SV_AWAIT_ACK); } +static const struct rxrpc_kernel_ops rxperf_rxrpc_callback_ops = { + .notify_new_call = rxperf_rx_new_call, + .discard_new_call = rxperf_rx_discard_new_call, + .user_attach_call = rxperf_rx_attach, +}; +/* * Charge the incoming call preallocation. */
@@ -161,7 +167,6 @@ static void rxperf_charge_preallocation(if (rxrpc_kernel_charge_accept (rxperf_socket, rxperf_notify_rx, - rxperf_rx_attach, (unsigned long )call,GFP_KERNEL , call->debug_id) <0 )
@@ -209,8 +214,7 @@ static int rxperf_open_socket(void)if (ret <0 ) goto error_2; -rxrpc_kernel_new_call_notification (socket, rxperf_rx_new_call, - rxperf_rx_discard_new_call); +rxrpc_kernel_set_notifications (socket, &rxperf_rxrpc_callback_ops); ret =kernel_listen (socket,INT_MAX );if (ret <0 )
Greg Kroah-Hartman
maintainerPATCH 381/484 rxrpc: serialize kernel accept preallocation with socket teardown
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Li Daming <[email protected]>
[ Upstream commit dc175389b18c29a5303ee83169ec653adfae3e17 ]
rxrpc_kernel_charge_accept() reads rx->backlog without any socket/backlog synchronization and passes that raw pointer into rxrpc_service_prealloc_one(). A concurrent rxrpc_discard_prealloc() sets rx->backlog = NULL and frees the backlog rings, so a kernel preallocation worker can keep using a freed struct rxrpc_backlog while updating *_backlog_head/tail and array slots.
Serialize the state check and backlog lookup with the socket lock, and reject kernel preallocation once teardown has disabled listening or discarded the service backlog.
--- net/rxrpc/call_accept.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-)
net/rxrpc/call_accept.c +19 -6
@@ -470,13 +470,26 @@ int rxrpc_kernel_charge_accept(struct so unsigned long user_call_ID, gfp_t gfp, unsigned int debug_id) { - struct rxrpc_sock *rx = rxrpc_sk(sock->sk); -struct rxrpc_backlog *b = rx->backlog; + struct rxrpc_backlog *b; +struct rxrpc_sock *rx; +struct sock *sk; +int ret; -if (sock->sk->sk_state ==RXRPC_CLOSE ) -return -ESHUTDOWN ; + sk = sock->sk; + rx =rxrpc_sk (sk); -return rxrpc_service_prealloc_one (rx, b, notify_rx, user_call_ID, - gfp, debug_id); +lock_sock (sk); +if (sk->sk_state !=RXRPC_SERVER_LISTENING || !rx->backlog) { + ret = -ESHUTDOWN ; + goto out; + } + + b = rx->backlog; + ret =rxrpc_service_prealloc_one (rx, b, notify_rx, user_call_ID, + gfp, debug_id); + +out: +release_sock (sk); +return ret; }EXPORT_SYMBOL (rxrpc_kernel_charge_accept);
Greg Kroah-Hartman
maintainerPATCH 382/484 fbcon: Rename struct fbcon_ops to struct fbcon_par
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thomas Zimmermann <[email protected]>
[ Upstream commit a6adbbc4c32a016146e117b1e9e5242724a75e10 ]
The type struct fbcon_ops contains fbcon state and callbacks. As the callbacks will be removed from struct fbcon_ops, rename the data type to struct fbcon_par. Also rename the variables from ops to par.
The _par postfix ("private access registers") is used throughout the fbdev subsystem for per-driver state. The fbcon pointer within struct fb_info is also named fbcon_par. Hence, the new naming fits existing practice.
v2:
- rename struct fbcon_ops to struct fbcon_par
- fix build for CONFIG_FB_TILEBITTING=n (kernel test robot)
- fix indention
Stable-dep-of: 84202754fb17 ("fbcon: Use correct type for vc_resize() return value")
--- drivers/video/fbdev/core/bitblit.c | 122 ++++----- drivers/video/fbdev/core/fbcon.c | 417 +++++++++++++++----------------- drivers/video/fbdev/core/fbcon.h | 6 drivers/video/fbdev/core/fbcon_ccw.c | 148 +++++------ drivers/video/fbdev/core/fbcon_cw.c | 148 +++++------ drivers/video/fbdev/core/fbcon_rotate.c | 44 +-- drivers/video/fbdev/core/fbcon_rotate.h | 6 drivers/video/fbdev/core/fbcon_ud.c | 164 ++++++------ drivers/video/fbdev/core/softcursor.c | 20 - drivers/video/fbdev/core/tileblit.c | 28 +- 10 files changed, 547 insertions(+), 556 deletions(-)
drivers/video/fbdev/core/bitblit.c +61 -61
@@ -261,10 +261,10 @@ static void bit_cursor(struct vc_data *v int fg, int bg) {struct fb_cursor cursor; -struct fbcon_ops *ops = info->fbcon_par; +struct fbcon_par *par = info->fbcon_par;unsigned short charmask = vc->vc_hi_font_mask ?0x1ff :0xff ;int w =DIV_ROUND_UP (vc->vc_font.width,8 ), c; -int y =real_y (ops->p, vc->state.y); +int y =real_y (par->p, vc->state.y);int attribute, use_sw = vc->vc_cursor_type &CUR_SW ;int err =1 ;char *src;
@@ -278,10 +278,10 @@ static void bit_cursor(struct vc_data *v attribute =get_attribute (info, c); src = vc->vc_font.data + ((c & charmask) * (w * vc->vc_font.height)); -if (ops->cursor_state.image.data != src || - ops->cursor_reset) { - ops->cursor_state.image.data = src; - cursor.set |=FB_CUR_SETIMAGE ; +if (par->cursor_state.image.data != src || + par->cursor_reset) { + par->cursor_state.image.data = src; + cursor.set |=FB_CUR_SETIMAGE ; }if (attribute) {
@@ -290,46 +290,46 @@ static void bit_cursor(struct vc_data *v dst =kmalloc_array (w, vc->vc_font.height,GFP_ATOMIC );if (!dst)return ; -kfree (ops->cursor_data); - ops->cursor_data = dst; +kfree (par->cursor_data); + par->cursor_data = dst;update_attr (dst, src, attribute, vc); src = dst; } -if (ops->cursor_state.image.fg_color != fg || - ops->cursor_state.image.bg_color != bg || - ops->cursor_reset) { - ops->cursor_state.image.fg_color = fg; - ops->cursor_state.image.bg_color = bg; +if (par->cursor_state.image.fg_color != fg || + par->cursor_state.image.bg_color != bg || + par->cursor_reset) { + par->cursor_state.image.fg_color = fg; + par->cursor_state.image.bg_color = bg; cursor.set |=FB_CUR_SETCMAP ; } -if ((ops->cursor_state.image.dx != (vc->vc_font.width * vc->state.x)) || - (ops->cursor_state.image.dy != (vc->vc_font.height * y)) || - ops->cursor_reset) { - ops->cursor_state.image.dx = vc->vc_font.width * vc->state.x; - ops->cursor_state.image.dy = vc->vc_font.height * y; +if ((par->cursor_state.image.dx != (vc->vc_font.width * vc->state.x)) || + (par->cursor_state.image.dy != (vc->vc_font.height * y)) || + par->cursor_reset) { + par->cursor_state.image.dx = vc->vc_font.width * vc->state.x; + par->cursor_state.image.dy = vc->vc_font.height * y; cursor.set |=FB_CUR_SETPOS ; } -if (ops->cursor_state.image.height != vc->vc_font.height || - ops->cursor_state.image.width != vc->vc_font.width || - ops->cursor_reset) { - ops->cursor_state.image.height = vc->vc_font.height; - ops->cursor_state.image.width = vc->vc_font.width; +if (par->cursor_state.image.height != vc->vc_font.height || + par->cursor_state.image.width != vc->vc_font.width || + par->cursor_reset) { + par->cursor_state.image.height = vc->vc_font.height; + par->cursor_state.image.width = vc->vc_font.width; cursor.set |=FB_CUR_SETSIZE ; } -if (ops->cursor_state.hot.x || ops->cursor_state.hot.y || - ops->cursor_reset) { - ops->cursor_state.hot.x = cursor.hot.y =0 ; +if (par->cursor_state.hot.x || par->cursor_state.hot.y || + par->cursor_reset) { + par->cursor_state.hot.x = cursor.hot.y =0 ; cursor.set |=FB_CUR_SETHOT ; }if (cursor.set &FB_CUR_SETSIZE || - vc->vc_cursor_type != ops->p->cursor_shape || - ops->cursor_state.mask ==NULL || - ops->cursor_reset) { + vc->vc_cursor_type != par->p->cursor_shape || + par->cursor_state.mask ==NULL || + par->cursor_reset) {char *mask =kmalloc_array (w, vc->vc_font.height,GFP_ATOMIC );int cur_height, size, i =0 ;u8 msk =0xff ;
@@ -337,13 +337,13 @@ static void bit_cursor(struct vc_data *vif (!mask)return ; -kfree (ops->cursor_state.mask); - ops->cursor_state.mask = mask; +kfree (par->cursor_state.mask); + par->cursor_state.mask = mask; - ops->p->cursor_shape = vc->vc_cursor_type; + par->p->cursor_shape = vc->vc_cursor_type; cursor.set |=FB_CUR_SETSHAPE ; -switch (CUR_SIZE (ops->p->cursor_shape)) { +switch (CUR_SIZE (par->p->cursor_shape)) {case CUR_NONE : cur_height =0 ;break ;
@@ -374,26 +374,26 @@ static void bit_cursor(struct vc_data *vswitch (mode) {case CM_ERASE : - ops->cursor_state.enable =0 ; + par->cursor_state.enable =0 ;break ;case CM_DRAW :case CM_MOVE :default : - ops->cursor_state.enable = (use_sw) ?0 :1 ; + par->cursor_state.enable = (use_sw) ?0 :1 ;break ; } cursor.image.data = src; - cursor.image.fg_color = ops->cursor_state.image.fg_color; - cursor.image.bg_color = ops->cursor_state.image.bg_color; - cursor.image.dx = ops->cursor_state.image.dx; - cursor.image.dy = ops->cursor_state.image.dy; - cursor.image.height = ops->cursor_state.image.height; - cursor.image.width = ops->cursor_state.image.width; - cursor.hot.x = ops->cursor_state.hot.x; - cursor.hot.y = ops->cursor_state.hot.y; - cursor.mask = ops->cursor_state.mask; - cursor.enable = ops->cursor_state.enable; + cursor.image.fg_color = par->cursor_state.image.fg_color; + cursor.image.bg_color = par->cursor_state.image.bg_color; + cursor.image.dx = par->cursor_state.image.dx; + cursor.image.dy = par->cursor_state.image.dy; + cursor.image.height = par->cursor_state.image.height; + cursor.image.width = par->cursor_state.image.width; + cursor.hot.x = par->cursor_state.hot.x; + cursor.hot.y = par->cursor_state.hot.y; + cursor.mask = par->cursor_state.mask; + cursor.enable = par->cursor_state.enable; cursor.image.depth =1 ; cursor.rop =ROP_XOR ;
@@ -403,31 +403,31 @@ static void bit_cursor(struct vc_data *vif (err)soft_cursor (info, &cursor); - ops->cursor_reset =0 ; + par->cursor_reset =0 ; }static int bit_update_start (struct fb_info *info) { -struct fbcon_ops *ops = info->fbcon_par; +struct fbcon_par *par = info->fbcon_par;int err; - err =fb_pan_display (info, &ops->var); - ops->var.xoffset = info->var.xoffset; - ops->var.yoffset = info->var.yoffset; - ops->var.vmode = info->var.vmode; + err =fb_pan_display (info, &par->var); + par->var.xoffset = info->var.xoffset; + par->var.yoffset = info->var.yoffset; + par->var.vmode = info->var.vmode;return err; } -void fbcon_set_bitops (struct fbcon_ops *ops) +void fbcon_set_bitops (struct fbcon_par *par) { - ops->bmove = bit_bmove; - ops->clear = bit_clear; - ops->putcs = bit_putcs; - ops->clear_margins = bit_clear_margins; - ops->cursor = bit_cursor; - ops->update_start = bit_update_start; - ops->rotate_font =NULL ; + par->bmove = bit_bmove; + par->clear = bit_clear; + par->putcs = bit_putcs; + par->clear_margins = bit_clear_margins; + par->cursor = bit_cursor; + par->update_start = bit_update_start; + par->rotate_font =NULL ; -if (ops->rotate) -fbcon_set_rotate (ops); +if (par->rotate) +fbcon_set_rotate (par); }
drivers/video/fbdev/core/fbcon.c +204 -213
1,057 lines not on this page · raw message
drivers/video/fbdev/core/fbcon.h +3 -3
@@ -50,7 +50,7 @@ struct fbcon_display {const struct fb_videomode *mode; }; -struct fbcon_ops { +struct fbcon_par {void (*bmove)(struct vc_data *vc,struct fb_info *info,int sy,int sx,int dy,int dx,int height,int width); void (*clear)(struct vc_data *vc, struct fb_info *info, int sy,
@@ -185,7 +185,7 @@ static inline u_short fb_scrollmode(stru#ifdef CONFIG_FB_TILEBLITTING extern void fbcon_set_tileops (struct vc_data *vc,struct fb_info *info);#endif -extern void fbcon_set_bitops (struct fbcon_ops *ops); +extern void fbcon_set_bitops (struct fbcon_par *par);extern int soft_cursor (struct fb_info *info,struct fb_cursor *cursor);#define FBCON_ATTRIBUTE_UNDERLINE 1
@@ -224,7 +224,7 @@ static inline int get_attribute(struct f (i == FB_ROTATE_UR || i == FB_ROTATE_UD) ? _r : _v; })#ifdef CONFIG_FRAMEBUFFER_CONSOLE_ROTATION -extern void fbcon_set_rotate (struct fbcon_ops *ops); +extern void fbcon_set_rotate (struct fbcon_par *par);#else #define fbcon_set_rotate (x) do {} while(0)#endif /* CONFIG_FRAMEBUFFER_CONSOLE_ROTATION */
drivers/video/fbdev/core/fbcon_ccw.c +74 -74
269 lines not on this page · raw message
drivers/video/fbdev/core/fbcon_cw.c +74 -74
269 lines not on this page · raw message
drivers/video/fbdev/core/fbcon_rotate.c +22 -22
90 lines not on this page · raw message
drivers/video/fbdev/core/fbcon_rotate.h +3 -3
@@ -90,7 +90,7 @@ static inline void rotate_ccw(const char } } -extern void fbcon_rotate_cw (struct fbcon_ops *ops); -extern void fbcon_rotate_ud (struct fbcon_ops *ops); -extern void fbcon_rotate_ccw (struct fbcon_ops *ops); +extern void fbcon_rotate_cw (struct fbcon_par *par); +extern void fbcon_rotate_ud (struct fbcon_par *par); +extern void fbcon_rotate_ccw (struct fbcon_par *par);#endif
drivers/video/fbdev/core/fbcon_ud.c +82 -82
299 lines not on this page · raw message
drivers/video/fbdev/core/softcursor.c +10 -10
@@ -21,7 +21,7 @@int soft_cursor (struct fb_info *info,struct fb_cursor *cursor) { -struct fbcon_ops *ops = info->fbcon_par; +struct fbcon_par *par = info->fbcon_par;unsigned int scan_align = info->pixmap.scan_align -1 ;unsigned int buf_align = info->pixmap.buf_align -1 ;unsigned int i, size, dsize, s_pitch, d_pitch;
@@ -34,19 +34,19 @@ int soft_cursor(struct fb_info *info, st s_pitch = (cursor->image.width +7 ) >>3 ; dsize = s_pitch * cursor->image.height; -if (dsize +sizeof (struct fb_image ) != ops->cursor_size) { -kfree (ops->cursor_src); - ops->cursor_size = dsize +sizeof (struct fb_image ); - - ops->cursor_src =kmalloc (ops->cursor_size,GFP_ATOMIC ); -if (!ops->cursor_src) { - ops->cursor_size =0 ; +if (dsize +sizeof (struct fb_image ) != par->cursor_size) { +kfree (par->cursor_src); + par->cursor_size = dsize +sizeof (struct fb_image ); + + par->cursor_src =kmalloc (par->cursor_size,GFP_ATOMIC ); +if (!par->cursor_src) { + par->cursor_size =0 ;return -ENOMEM ; } } - src = ops->cursor_src +sizeof (struct fb_image ); - image = (struct fb_image *)ops->cursor_src; + src = par->cursor_src +sizeof (struct fb_image ); + image = (struct fb_image *)par->cursor_src; *image = cursor->image; d_pitch = (s_pitch + scan_align) & ~scan_align;
drivers/video/fbdev/core/tileblit.c +14 -14
@@ -151,34 +151,34 @@ static void tile_cursor(struct vc_data *static int tile_update_start (struct fb_info *info) { -struct fbcon_ops *ops = info->fbcon_par; +struct fbcon_par *par = info->fbcon_par;int err; - err =fb_pan_display (info, &ops->var); - ops->var.xoffset = info->var.xoffset; - ops->var.yoffset = info->var.yoffset; - ops->var.vmode = info->var.vmode; + err =fb_pan_display (info, &par->var); + par->var.xoffset = info->var.xoffset; + par->var.yoffset = info->var.yoffset; + par->var.vmode = info->var.vmode;return err; }void fbcon_set_tileops (struct vc_data *vc,struct fb_info *info) {struct fb_tilemap map; -struct fbcon_ops *ops = info->fbcon_par; +struct fbcon_par *par = info->fbcon_par; - ops->bmove = tile_bmove; - ops->clear = tile_clear; - ops->putcs = tile_putcs; - ops->clear_margins = tile_clear_margins; - ops->cursor = tile_cursor; - ops->update_start = tile_update_start; + par->bmove = tile_bmove; + par->clear = tile_clear; + par->putcs = tile_putcs; + par->clear_margins = tile_clear_margins; + par->cursor = tile_cursor; + par->update_start = tile_update_start; -if (ops->p) { +if (par->p) { map.width = vc->vc_font.width; map.height = vc->vc_font.height; map.depth =1 ; map.length = vc->vc_font.charcount; - map.data = ops->p->fontdata; + map.data = par->p->fontdata; info->tileops->fb_settile (info, &map); } }
Greg Kroah-Hartman
maintainerPATCH 383/484 fbcon: Use correct type for vc_resize() return value
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiacheng Yu <[email protected]>
[ Upstream commit 84202754fb1727dc3ee87f47104e4162ecc8ba3a ]
The return value of vc_resize() is int, but fbcon_set_disp() stores it in an unsigned long variable. While the !ret check happens to work correctly by coincidence (negative values become large positive values), the types should match. Use int instead.
Eliminates the following W=3 warning:
drivers/video/fbdev/core/fbcon.c: In function 'fbcon_set_disp':
drivers/video/fbdev/core/fbcon.c:1494:14: warning: implicit conversion from 'int' to 'unsigned long' [-Wconversion]
--- drivers/video/fbdev/core/fbcon.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/video/fbdev/core/fbcon.c +1 -2
@@ -1356,8 +1356,7 @@ static void fbcon_set_disp(struct fb_infstruct vc_data **default_mode, *vc;struct vc_data *svc;struct fbcon_par *par = info->fbcon_par; -int rows, cols; -unsigned long ret =0 ; +int rows, cols, ret; p = &fb_display[unit];
Greg Kroah-Hartman
maintainerPATCH 384/484 tipc: restrict socket queue dumps in enqueue tracepoints
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Li Xiasong <[email protected]>
[ Upstream commit acd7df8d955480a6f6e5bb809da67b1500cc3cf4 ]
tipc_sk_enqueue() runs with sk->sk_lock.slock held while the socket is owned by user context. The spinlock protects the backlog queue in this path, but it does not serialize against the socket owner consuming or purging sk_receive_queue.
KASAN reported:
CPU: 14 UID: 0 PID: 1050 Comm: tipc3 Not tainted 7.1.0-rc6+ #126 PREEMPT(lazy)
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014
Call Trace:
<TASK>
dump_stack_lvl+0x76/0xa0 lib/dump_stack.c:123
print_report+0xce/0x5b0 mm/kasan/report.c:482
kasan_report+0xc6/0x100 mm/kasan/report.c:597
__asan_report_load4_noabort+0x14/0x30 mm/kasan/report_generic.c:380
tipc_skb_dump+0x1327/0x16f0 net/tipc/trace.c:73
tipc_list_dump+0x208/0x2e0 net/tipc/trace.c:187
tipc_sk_dump+0xaf6/0xd60 net/tipc/socket.c:3996
trace_event_raw_event_tipc_sk_class+0x312/0x5a0 net/tipc/trace.h:188
tipc_sk_rcv+0xb1d/0x1d50 net/tipc/socket.c:2497
tipc_node_xmit+0x1c3/0x1440 net/tipc/node.c:1689
__tipc_sendmsg+0x97a/0x1440 net/tipc/socket.c:1512
tipc_sendmsg+0x52/0x80 net/tipc/socket.c:1400
sock_sendmsg+0x2f6/0x3e0 net/socket.c:825
splice_to_socket+0x7f9/0x1010 fs/splice.c:884
do_splice+0xe21/0x2330 fs/splice.c:936
__do_splice+0x153/0x260 fs/splice.c:1431
__x64_sys_splice+0x150/0x230 fs/splice.c:1616
x64_sys_call+0xeb5/0x2790 arch/x86/entry/syscall_64.c:41
do_syscall_64+0xf3/0x620 arch/x86/entry/syscall_64.c:63
entry_SYSCALL_64_after_hwframe+0x76/0x7e arch/x86/entry/entry_64.S:130
RIP: 0033:0x71624e8aafe2
Code: 08 0f 85 71 3a ff ff 49 89 fb 48 89 f0 48 89 d7 48 89 ce 4c 89 c2 4d 89 ca 4c 8b 44 24 08 4c 8b 4c 24 10 4c 89 5c 24 08 0f 05 <c3> 66 2e 0f 1f 84 00 00 00 00 00 66 2e 0f 1f 84 00 00 00 00 00 66
RSP: 002b:0000716157ffed68 EFLAGS: 00000246 ORIG_RAX: 0000000000000113
RAX: ffffffffffffffda RBX: 0000716157fff6c0 RCX: 000071624e8aafe2
RDX: 000000000000005f RSI: 0000000000000000 RDI: 0000000000000066
RBP: 0000716157ffed90 R08: 0000000000008000 R09: 0000000000000001
R10: 0000000000000000 R11: 0000000000000246 R12: ffffffffffffff00
R13: 0000000000000021 R14: 0000000000000000 R15: 00007fff89799c40
</TASK>The TIPC_DUMP_ALL tracepoints in tipc_sk_enqueue() also dump sk_receive_queue and can therefore dereference skbs that the socket owner has already dequeued or freed. Restrict these dumps to TIPC_DUMP_SK_BKLGQ, which matches the queue protected by the held spinlock.
Keep the change limited to the enqueue path, where the unsafe queue dump is reachable while the socket is owned by user context.
--- net/tipc/socket.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
net/tipc/socket.c +3 -3
@@ -2461,17 +2461,17 @@ static void tipc_sk_enqueue(struct sk_buatomic_set (dcnt,0 ); lim =rcvbuf_limit (sk, skb) +atomic_read (dcnt);if (likely (!sk_add_backlog (sk, skb, lim))) { -trace_tipc_sk_overlimit1 (sk, skb,TIPC_DUMP_ALL , +trace_tipc_sk_overlimit1 (sk, skb,TIPC_DUMP_SK_BKLGQ ,"bklg & rcvq >90% allocated!" );continue ; } -trace_tipc_sk_dump (sk, skb,TIPC_DUMP_ALL ,"err_overload!" ); +trace_tipc_sk_dump (sk, skb,TIPC_DUMP_SK_BKLGQ ,"err_overload!" );/* Overload => reject message back to sender */ onode =tipc_own_addr (sock_net (sk));atomic_inc (&sk->sk_drops);if (tipc_msg_reverse (onode, &skb,TIPC_ERR_OVERLOAD )) { -trace_tipc_sk_rej_msg (sk, skb,TIPC_DUMP_ALL , +trace_tipc_sk_rej_msg (sk, skb,TIPC_DUMP_SK_BKLGQ ,"@sk_enqueue!" );__skb_queue_tail (xmitq, skb); }
Greg Kroah-Hartman
maintainerPATCH 385/484 rxrpc: Dont need barrier for ->tx_bottom and ->acks_hard_ack
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit 6396b48ac0a77165f9c2c40ab03d6c8188c89739 ]
We don't need a barrier for the ->tx_bottom value (which indicates the lowest sequence still in the transmission queue) and the ->acks_hard_ack value (which tracks the DATA packets hard-ack'd by the latest ACK packet received and thus indicates which DATA packets can now be discarded) as the app thread doesn't use either value as a reference to memory to access. Rather, the app thread merely uses these as a guide to how much space is available in the transmission queue
Change the code to use READ/WRITE_ONCE() instead.
Also, change rxrpc_check_tx_space() to use the same value for tx_bottom throughout.
Stable-dep-of: e66f8f32f501 ("rxrpc: Fix socket notification race")
--- net/rxrpc/sendmsg.c | 8 +++++--- net/rxrpc/txbuf.c | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-)
net/rxrpc/sendmsg.c +5 -3
@@ -93,9 +93,11 @@ no_wait:*/ static bool rxrpc_check_tx_space (struct rxrpc_call *call,rxrpc_seq_t *_tx_win) { +rxrpc_seq_t tx_bottom =READ_ONCE (call->tx_bottom); +if (_tx_win) - *_tx_win = call->tx_bottom; -return call->tx_prepared - call->tx_bottom <256 ; + *_tx_win = tx_bottom; +return call->tx_prepared - tx_bottom <256 ; }/*
@@ -137,7 +139,7 @@ static int rxrpc_wait_for_tx_window_wait rtt =2 ; timeout = rtt; - tx_start =smp_load_acquire (&call->acks_hard_ack); + tx_start =READ_ONCE (call->acks_hard_ack);for (;;) {set_current_state (TASK_UNINTERRUPTIBLE );
net/rxrpc/txbuf.c +2 -2
@@ -112,14 +112,14 @@ void rxrpc_shrink_call_tx_buffer(structwhile ((txb =list_first_entry_or_null (&call->tx_buffer, struct rxrpc_txbuf, call_link))) { - hard_ack =smp_load_acquire (&call->acks_hard_ack); + hard_ack = call->acks_hard_ack;if (before (hard_ack, txb->seq))break ;if (txb->seq != call->tx_bottom +1 )rxrpc_see_txbuf (txb, rxrpc_txbuf_see_out_of_step); ASSERTCMP(txb->seq, ==, call->tx_bottom + 1); -smp_store_release (&call->tx_bottom, call->tx_bottom +1 ); +WRITE_ONCE (call->tx_bottom, call->tx_bottom +1 );list_del_rcu (&txb->call_link);trace_rxrpc_txqueue (call, rxrpc_txqueue_dequeue);
Greg Kroah-Hartman
maintainerPATCH 386/484 rxrpc: Use irq-disabling spinlocks between app and I/O thread
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit a2ea9a9072607c2fd6442bd1ffb4dbdbf882aed7 ]
Where a spinlock is used by both the application thread and the I/O thread, use irq-disabling locking so that an interrupt taken on the app thread doesn't also slow down the I/O thread.
Stable-dep-of: e66f8f32f501 ("rxrpc: Fix socket notification race")
--- net/rxrpc/af_rxrpc.c | 4 ++-- net/rxrpc/call_accept.c | 20 ++++++++++---------- net/rxrpc/call_object.c | 14 +++++++------- net/rxrpc/conn_client.c | 12 ++++++------ net/rxrpc/conn_event.c | 14 +++++++------- net/rxrpc/conn_object.c | 8 ++++---- net/rxrpc/input.c | 5 +---- net/rxrpc/peer_event.c | 8 ++++---- net/rxrpc/peer_object.c | 1 + net/rxrpc/recvmsg.c | 22 +++++++++++----------- net/rxrpc/security.c | 4 ++-- 11 files changed, 55 insertions(+), 57 deletions(-)
net/rxrpc/af_rxrpc.c +2 -2
@@ -362,9 +362,9 @@ void rxrpc_kernel_shutdown_call(struct s/* Make sure we're not going to call back into a kernel service */ if (call->notify_rx) { -spin_lock (&call->notify_lock); +spin_lock_irq (&call->notify_lock); call->notify_rx = rxrpc_dummy_notify_rx; -spin_unlock (&call->notify_lock); +spin_unlock_irq (&call->notify_lock); } }mutex_unlock (&call->user_mutex);
net/rxrpc/call_accept.c +10 -10
@@ -189,8 +189,8 @@ void rxrpc_discard_prealloc(struct rxrpc/* Make sure that there aren't any incoming calls in progress before we * clear the preallocation buffers. */ -spin_lock (&rx->incoming_lock); -spin_unlock (&rx->incoming_lock); +spin_lock_irq (&rx->incoming_lock); +spin_unlock_irq (&rx->incoming_lock); head = b->peer_backlog_head; tail = b->peer_backlog_tail;
@@ -349,7 +349,7 @@ bool rxrpc_new_incoming_call(struct rxrpif (sp->hdr.type !=RXRPC_PACKET_TYPE_DATA )return rxrpc_protocol_error (skb, rxrpc_eproto_no_service_call); -read_lock (&local->services_lock); +read_lock_irq (&local->services_lock);/* Weed out packets to services we're not offering. Packets that would * begin a call are explicitly rejected and the rest are just
@@ -406,12 +406,12 @@ bool rxrpc_new_incoming_call(struct rxrpspin_unlock (&conn->state_lock);spin_unlock (&rx->incoming_lock); -read_unlock (&local->services_lock); +read_unlock_irq (&local->services_lock);if (hlist_unhashed (&call->error_link)) { -spin_lock (&call->peer->lock); +spin_lock_irq (&call->peer->lock);hlist_add_head (&call->error_link, &call->peer->error_targets); -spin_unlock (&call->peer->lock); +spin_unlock_irq (&call->peer->lock); }_leave (" = %p{%d}" , call, call->debug_id);
@@ -420,20 +420,20 @@ bool rxrpc_new_incoming_call(struct rxrpreturn true; unsupported_service: -read_unlock (&local->services_lock); +read_unlock_irq (&local->services_lock);return rxrpc_direct_abort (skb, rxrpc_abort_service_not_offered,RX_INVALID_OPERATION , -EOPNOTSUPP ); unsupported_security: -read_unlock (&local->services_lock); +read_unlock_irq (&local->services_lock);return rxrpc_direct_abort (skb, rxrpc_abort_service_not_offered,RX_INVALID_OPERATION , -EKEYREJECTED ); no_call:spin_unlock (&rx->incoming_lock); -read_unlock (&local->services_lock); +read_unlock_irq (&local->services_lock);_leave (" = f [%u]" , skb->mark);return false; discard: -read_unlock (&local->services_lock); +read_unlock_irq (&local->services_lock);return true; }
net/rxrpc/call_object.c +7 -7
@@ -48,7 +48,7 @@ void rxrpc_poke_call(struct rxrpc_call *bool busy;if (!test_bit (RXRPC_CALL_DISCONNECTED , &call->flags)) { -spin_lock_bh (&local->lock); +spin_lock_irq (&local->lock); busy = !list_empty (&call->attend_link);trace_rxrpc_poke_call (call, busy, what);if (!busy && !rxrpc_try_get_call (call, rxrpc_call_get_poke))
@@ -56,7 +56,7 @@ void rxrpc_poke_call(struct rxrpc_call *if (!busy) {list_add_tail (&call->attend_link, &local->call_attend_q); } -spin_unlock_bh (&local->lock); +spin_unlock_irq (&local->lock);if (!busy)rxrpc_wake_up_io_thread (local); }
@@ -311,9 +311,9 @@ static int rxrpc_connect_call(struct rxrtrace_rxrpc_client (NULL ,-1 , rxrpc_client_queue_new_call);rxrpc_get_call (call, rxrpc_call_get_io_thread); -spin_lock (&local->client_call_lock); +spin_lock_irq (&local->client_call_lock);list_add_tail (&call->wait_link, &local->new_client_calls); -spin_unlock (&local->client_call_lock); +spin_unlock_irq (&local->client_call_lock);rxrpc_wake_up_io_thread (local);return 0 ;
@@ -439,7 +439,7 @@ error_attached_to_socket:/* * Set up an incoming call. call->conn points to the connection. -* This is called in BH context and isn't allowed to fail. +* This is called with interrupts disabled and isn't allowed to fail. */ void rxrpc_incoming_call(struct rxrpc_sock *rx, struct rxrpc_call *call,
@@ -563,7 +563,7 @@ void rxrpc_release_call(struct rxrpc_socrxrpc_put_call_slot (call);/* Make sure we don't get any more notifications */ -spin_lock (&rx->recvmsg_lock); +spin_lock_irq (&rx->recvmsg_lock);if (!list_empty (&call->recvmsg_link)) { _debug("unlinking once-pending call %p { e=%lx f=%lx }",
@@ -576,7 +576,7 @@ void rxrpc_release_call(struct rxrpc_soc call->recvmsg_link.next =NULL ; call->recvmsg_link.prev =NULL ; -spin_unlock (&rx->recvmsg_lock); +spin_unlock_irq (&rx->recvmsg_lock);if (put)rxrpc_put_call (call, rxrpc_call_put_unnotify);
net/rxrpc/conn_client.c +6 -6
@@ -505,10 +505,10 @@ void rxrpc_connect_client_calls(struct r ) {struct rxrpc_bundle *bundle = call->bundle; -spin_lock (&local->client_call_lock); +spin_lock_irq (&local->client_call_lock);list_move_tail (&call->wait_link, &bundle->waiting_calls);rxrpc_see_call (call, rxrpc_call_see_waiting_call); -spin_unlock (&local->client_call_lock); +spin_unlock_irq (&local->client_call_lock);if (rxrpc_bundle_has_space (bundle))rxrpc_activate_channels (bundle);
@@ -536,9 +536,9 @@ void rxrpc_expose_client_call(struct rxrset_bit (RXRPC_CONN_DONT_REUSE , &conn->flags);trace_rxrpc_client (conn, channel, rxrpc_client_exposed); -spin_lock (&call->peer->lock); +spin_lock_irq (&call->peer->lock);hlist_add_head (&call->error_link, &call->peer->error_targets); -spin_unlock (&call->peer->lock); +spin_unlock_irq (&call->peer->lock); } }
@@ -579,9 +579,9 @@ void rxrpc_disconnect_client_call(struct ASSERTCMP(call->call_id, ==, 0);ASSERT (!test_bit (RXRPC_CALL_EXPOSED , &call->flags));/* May still be on ->new_client_calls. */ -spin_lock (&local->client_call_lock); +spin_lock_irq (&local->client_call_lock);list_del_init (&call->wait_link); -spin_unlock (&local->client_call_lock); +spin_unlock_irq (&local->client_call_lock);return ; }
net/rxrpc/conn_event.c +7 -7
@@ -26,7 +26,7 @@ static bool rxrpc_set_conn_aborted(strucbool aborted = false;if (conn->state !=RXRPC_CONN_ABORTED ) { -spin_lock (&conn->state_lock); +spin_lock_irq (&conn->state_lock);if (conn->state !=RXRPC_CONN_ABORTED ) { conn->abort_code = abort_code; conn->error = err;
@@ -37,7 +37,7 @@ static bool rxrpc_set_conn_aborted(strucset_bit (RXRPC_CONN_EV_ABORT_CALLS , &conn->events); aborted = true; } -spin_unlock (&conn->state_lock); +spin_unlock_irq (&conn->state_lock); }return aborted;
@@ -268,12 +268,12 @@ static int rxrpc_process_event(struct rxreturn conn->security->respond_to_challenge (conn, skb);case RXRPC_PACKET_TYPE_RESPONSE : -spin_lock (&conn->state_lock); +spin_lock_irq (&conn->state_lock);if (conn->state !=RXRPC_CONN_SERVICE_CHALLENGING ) { -spin_unlock (&conn->state_lock); +spin_unlock_irq (&conn->state_lock);return 0 ; } -spin_unlock (&conn->state_lock); +spin_unlock_irq (&conn->state_lock); ret =rxrpc_verify_response (conn, skb);if (ret <0 )
@@ -284,12 +284,12 @@ static int rxrpc_process_event(struct rxif (ret <0 )return ret; -spin_lock (&conn->state_lock); +spin_lock_irq (&conn->state_lock);if (conn->state ==RXRPC_CONN_SERVICE_CHALLENGING ) { conn->state =RXRPC_CONN_SERVICE ; secured = true; } -spin_unlock (&conn->state_lock); +spin_unlock_irq (&conn->state_lock);if (secured) {/* Offload call state flipping to the I/O thread. As
net/rxrpc/conn_object.c +4 -4
@@ -31,13 +31,13 @@ void rxrpc_poke_conn(struct rxrpc_connecif (WARN_ON_ONCE (!local))return ; -spin_lock_bh (&local->lock); +spin_lock_irq (&local->lock); busy = !list_empty (&conn->attend_link);if (!busy) {rxrpc_get_connection (conn, why);list_add_tail (&conn->attend_link, &local->conn_attend_q); } -spin_unlock_bh (&local->lock); +spin_unlock_irq (&local->lock);rxrpc_wake_up_io_thread (local); }
@@ -196,9 +196,9 @@ void rxrpc_disconnect_call(struct rxrpc_ call->peer->cong_ssthresh = call->cong_ssthresh;if (!hlist_unhashed (&call->error_link)) { -spin_lock (&call->peer->lock); +spin_lock_irq (&call->peer->lock);hlist_del_init (&call->error_link); -spin_unlock (&call->peer->lock); +spin_unlock_irq (&call->peer->lock); }if (rxrpc_is_client_call (call)) {
net/rxrpc/input.c +1 -4
@@ -366,7 +366,7 @@ static void rxrpc_input_queue_data(strucstruct rxrpc_skb_priv *sp =rxrpc_skb (skb);bool last = sp->hdr.flags &RXRPC_LAST_PACKET ; -__skb_queue_tail (&call->recvmsg_queue, skb); +skb_queue_tail (&call->recvmsg_queue, skb);rxrpc_input_update_ack_window (call, window, wtop);trace_rxrpc_receive (call, last ? why +1 : why, sp->hdr.serial, sp->hdr.seq);if (last)
@@ -443,7 +443,6 @@ static void rxrpc_input_data_one(structrxrpc_get_skb (skb, rxrpc_skb_get_to_recvmsg); -spin_lock (&call->recvmsg_queue.lock);rxrpc_input_queue_data (call, skb, window, wtop, rxrpc_receive_queue); *_notify = true;
@@ -465,8 +464,6 @@ static void rxrpc_input_data_one(struct rxrpc_receive_queue_oos); } -spin_unlock (&call->recvmsg_queue.lock); - call->ackr_sack_base = sack; }else {unsigned int slot;
net/rxrpc/peer_event.c +4 -4
@@ -205,23 +205,23 @@ static void rxrpc_distribute_error(strucstruct rxrpc_call *call;HLIST_HEAD (error_targets); -spin_lock (&peer->lock); +spin_lock_irq (&peer->lock);hlist_move_list (&peer->error_targets, &error_targets);while (!hlist_empty (&error_targets)) { call =hlist_entry (error_targets.first, struct rxrpc_call, error_link);hlist_del_init (&call->error_link); -spin_unlock (&peer->lock); +spin_unlock_irq (&peer->lock);rxrpc_see_call (call, rxrpc_call_see_distribute_error);rxrpc_set_call_completion (call, compl,0 , -err);rxrpc_input_call_event (call, skb); -spin_lock (&peer->lock); +spin_lock_irq (&peer->lock); } -spin_unlock (&peer->lock); +spin_unlock_irq (&peer->lock); }/*
net/rxrpc/peer_object.c +1 -0
@@ -302,6 +302,7 @@ static void rxrpc_free_peer(struct rxrpc* Set up a new incoming peer. There shouldn't be any other matching peers * since we've already done a search in the list from the non-reentrant context * (the data_ready handler) that is the only place we can add new peers. +* Called with interrupts disabled. */ void rxrpc_new_incoming_peer (struct rxrpc_local *local,struct rxrpc_peer *peer) {
net/rxrpc/recvmsg.c +11 -11
@@ -36,16 +36,16 @@ void rxrpc_notify_socket(struct rxrpc_ca sk = &rx->sk;if (rx && sk->sk_state <RXRPC_CLOSE ) {if (call->notify_rx) { -spin_lock (&call->notify_lock); +spin_lock_irq (&call->notify_lock); call->notify_rx (sk, call, call->user_call_ID); -spin_unlock (&call->notify_lock); +spin_unlock_irq (&call->notify_lock); }else { -spin_lock (&rx->recvmsg_lock); +spin_lock_irq (&rx->recvmsg_lock);if (list_empty (&call->recvmsg_link)) {rxrpc_get_call (call, rxrpc_call_get_notify_socket);list_add_tail (&call->recvmsg_link, &rx->recvmsg_q); } -spin_unlock (&rx->recvmsg_lock); +spin_unlock_irq (&rx->recvmsg_lock);if (!sock_flag (sk,SOCK_DEAD )) {_debug ("call %ps" , sk->sk_data_ready);
@@ -379,14 +379,14 @@ try_again:* We also want to weed out calls that got requeued whilst we were * shovelling data out. */ -spin_lock (&rx->recvmsg_lock); +spin_lock_irq (&rx->recvmsg_lock); l = rx->recvmsg_q.next; call = list_entry(l, struct rxrpc_call, recvmsg_link);if (!rxrpc_call_is_complete (call) &&skb_queue_empty (&call->recvmsg_queue)) {list_del_init (&call->recvmsg_link); -spin_unlock (&rx->recvmsg_lock); +spin_unlock_irq (&rx->recvmsg_lock);release_sock (&rx->sk);trace_rxrpc_recvmsg (call->debug_id, rxrpc_recvmsg_unqueue,0 );rxrpc_put_call (call, rxrpc_call_put_recvmsg);
@@ -407,7 +407,7 @@ try_again:list_del_init (&call->recvmsg_link); elserxrpc_get_call (call ,rxrpc_call_get_recvmsg ); -spin_unlock (&rx->recvmsg_lock); +spin_unlock_irq (&rx->recvmsg_lock); call_debug_id = call->debug_id;trace_rxrpc_recvmsg (call_debug_id, rxrpc_recvmsg_dequeue,0 );
@@ -502,17 +502,17 @@ error_unlock_call: error_requeue_call:if (!(flags &MSG_PEEK )) { -spin_lock (&rx->recvmsg_lock); +spin_lock_irq (&rx->recvmsg_lock);if (list_empty (&call->recvmsg_link)) {list_add (&call->recvmsg_link, &rx->recvmsg_q);rxrpc_see_call (call, rxrpc_call_see_recvmsg_requeue); -spin_unlock (&rx->recvmsg_lock); +spin_unlock_irq (&rx->recvmsg_lock); }else if (list_is_first (&call->recvmsg_link, &rx->recvmsg_q)) { -spin_unlock (&rx->recvmsg_lock); +spin_unlock_irq (&rx->recvmsg_lock);rxrpc_put_call (call, rxrpc_call_see_recvmsg_requeue_first); }else {list_move (&call->recvmsg_link, &rx->recvmsg_q); -spin_unlock (&rx->recvmsg_lock); +spin_unlock_irq (&rx->recvmsg_lock);rxrpc_put_call (call, rxrpc_call_see_recvmsg_requeue_move); }trace_rxrpc_recvmsg (call_debug_id, rxrpc_recvmsg_requeue,0 );
net/rxrpc/security.c +2 -2
@@ -114,10 +114,10 @@ found:if (conn->state ==RXRPC_CONN_CLIENT_UNSECURED ) { ret = conn->security->init_connection_security (conn, token);if (ret ==0 ) { -spin_lock (&conn->state_lock); +spin_lock_irq (&conn->state_lock);if (conn->state ==RXRPC_CONN_CLIENT_UNSECURED ) conn->state =RXRPC_CONN_CLIENT ; -spin_unlock (&conn->state_lock); +spin_unlock_irq (&conn->state_lock); } }mutex_unlock (&conn->security_lock);
Greg Kroah-Hartman
maintainerPATCH 387/484 rxrpc: Fix notification vs call-release vs recvmsg
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit 2fd895842d49c23137ae48252dd211e5d6d8a3ed ]
When a call is released, rxrpc takes the spinlock and removes it from ->recvmsg_q in an effort to prevent racing recvmsg() invocations from seeing the same call. Now, rxrpc_recvmsg() only takes the spinlock when actually removing a call from the queue; it doesn't, however, take it in the lead up to that when it checks to see if the queue is empty. It *does* hold the socket lock, which prevents a recvmsg/recvmsg race - but this doesn't prevent sendmsg from ending the call because sendmsg() drops the socket lock and relies on the call->user_mutex.
Fix this by firstly removing the bit in rxrpc_release_call() that dequeues the released call and, instead, rely on recvmsg() to simply discard released calls (done in a preceding fix).
Secondly, rxrpc_notify_socket() is abandoned if the call is already marked as released rather than trying to be clever by setting both pointers in call->recvmsg_link to NULL to trick list_empty(). This isn't perfect and can still race, resulting in a released call on the queue, but recvmsg() will now clean that up.
Stable-dep-of: e66f8f32f501 ("rxrpc: Fix socket notification race")
--- include/trace/events/rxrpc.h | 3 ++- net/rxrpc/call_object.c | 28 ++++++++++++---------------- net/rxrpc/recvmsg.c | 4 ++++ 3 files changed, 18 insertions(+), 17 deletions(-)
include/trace/events/rxrpc.h +2 -1
@@ -271,10 +271,10 @@ EM(rxrpc_call_put_poke, "PUT poke ") \ EM(rxrpc_call_put_recvmsg, "PUT recvmsg ") \ EM(rxrpc_call_put_recvmsg_peek_nowait, "PUT peek-nwt") \ + EM(rxrpc_call_put_release_recvmsg_q, "PUT rls-rcmq") \ EM(rxrpc_call_put_release_sock, "PUT rls-sock") \ EM(rxrpc_call_put_release_sock_tba, "PUT rls-sk-a") \ EM(rxrpc_call_put_sendmsg, "PUT sendmsg ") \ - EM(rxrpc_call_put_unnotify, "PUT unnotify") \ EM(rxrpc_call_put_userid_exists, "PUT u-exists") \ EM(rxrpc_call_put_userid, "PUT user-id ") \EM (rxrpc_call_see_accept,"SEE accept " ) \
@@ -287,6 +287,7 @@ EM(rxrpc_call_see_disconnected, "SEE disconn ") \ EM(rxrpc_call_see_distribute_error, "SEE dist-err") \ EM(rxrpc_call_see_input, "SEE input ") \ + EM(rxrpc_call_see_notify_released, "SEE nfy-rlsd") \ EM(rxrpc_call_see_recvmsg, "SEE recvmsg ") \ EM(rxrpc_call_see_recvmsg_requeue, "SEE recv-rqu") \EM (rxrpc_call_see_recvmsg_requeue_first,"SEE recv-rqF" ) \
net/rxrpc/call_object.c +12 -16
@@ -550,7 +550,7 @@ static void rxrpc_cleanup_ring(struct rxvoid rxrpc_release_call (struct rxrpc_sock *rx,struct rxrpc_call *call) {struct rxrpc_connection *conn = call->conn; -bool put = false, putu = false; +bool putu = false;_enter ("{%d,%d}" , call->debug_id,refcount_read (&call->ref));
@@ -562,23 +562,13 @@ void rxrpc_release_call(struct rxrpc_socrxrpc_put_call_slot (call); -/* Make sure we don't get any more notifications */ +/* Note that at this point, the call may still be on or may have been +* added back on to the socket receive queue. recvmsg() must discard +* released calls. The CALL_RELEASED flag should prevent further +* notifications. +*/ spin_lock_irq (&rx->recvmsg_lock); - -if (!list_empty (&call->recvmsg_link)) { -_debug ("unlinking once-pending call %p { e=%lx f=%lx }" , - call, call->events, call->flags); -list_del (&call->recvmsg_link); - put = true; - } - -/* list_empty() must return false in rxrpc_notify_socket() */ - call->recvmsg_link.next =NULL ; - call->recvmsg_link.prev =NULL ; -spin_unlock_irq (&rx->recvmsg_lock); -if (put) -rxrpc_put_call (call, rxrpc_call_put_unnotify);write_lock (&rx->call_lock);
@@ -627,6 +617,12 @@ void rxrpc_release_calls_on_socket(strucrxrpc_put_call (call, rxrpc_call_put_release_sock); } +while ((call =list_first_entry_or_null (&rx->recvmsg_q, + struct rxrpc_call, recvmsg_link))) { +list_del_init (&call->recvmsg_link); +rxrpc_put_call (call, rxrpc_call_put_release_recvmsg_q); + } +_leave ("" ); }
net/rxrpc/recvmsg.c +4 -0
@@ -29,6 +29,10 @@ void rxrpc_notify_socket(struct rxrpc_caif (!list_empty (&call->recvmsg_link))return ; +if (test_bit (RXRPC_CALL_RELEASED , &call->flags)) { +rxrpc_see_call (call, rxrpc_call_see_notify_released); +return ; + }rcu_read_lock ();
Greg Kroah-Hartman
maintainerPATCH 388/484 rxrpc: Fix socket notification race
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit e66f8f32f50116670dbbee5bc9e692cd2cd0c8f8 ]
There's a race between rxrpc_recvmsg() and rxrpc_notify_socket(), whereby the latter's attempt to avoid disabling interrupts and taking the socket's recvmsg_lock if the call is already queued may happen simultaneously with the former's discarding of a call that has nothing queued.
Fix this by removing the shortcut. Note that this only affects userspace's use of AF_RXRPC; the AFS filesystem driver doesn't use the socket queue.
--- net/rxrpc/recvmsg.c | 2 -- 1 file changed, 2 deletions(-)
net/rxrpc/recvmsg.c +0 -2
@@ -27,8 +27,6 @@ void rxrpc_notify_socket(struct rxrpc_ca_enter ("%d" , call->debug_id); -if (!list_empty (&call->recvmsg_link)) -return ;if (test_bit (RXRPC_CALL_RELEASED , &call->flags)) {rxrpc_see_call (call, rxrpc_call_see_notify_released);return ;
Greg Kroah-Hartman
maintainerPATCH 389/484 vduse: Use fixed 4KB bounce pages for non-4KB page size
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sheng Zhao <[email protected]>
[ Upstream commit 3fc3068e7247c94dec08e93fea422a1bb649bfe5 ]
The allocation granularity of bounce pages is PAGE_SIZE. This may cause even small IO requests to occupy an entire bounce page exclusively. The kind of memory waste will be more significant when PAGE_SIZE is larger than 4KB (e.g. arm64 with 64KB pages).
So, optimize it by using fixed 4KB bounce maps and iova allocation granularity. A single IO request occupies at least a 4KB bounce page instead of the entire memory page of PAGE_SIZE.
Message-Id: <[email protected]>
Stable-dep-of: 9c1523803445 ("VDUSE: avoid leaking information to userspace")
--- drivers/vdpa/vdpa_user/iova_domain.c | 134 +++++++++++++++++++++++++---------- drivers/vdpa/vdpa_user/iova_domain.h | 5 + 2 files changed, 101 insertions(+), 38 deletions(-)
drivers/vdpa/vdpa_user/iova_domain.c +96 -38
@@ -103,19 +103,38 @@ void vduse_domain_clear_map(struct vdusestatic int vduse_domain_map_bounce_page (struct vduse_iova_domain *domain,u64 iova,u64 size,u64 paddr) { -struct vduse_bounce_map *map; +struct vduse_bounce_map *map, *head_map; +struct page *tmp_page;u64 last = iova + size -1 ;while (iova <= last) { - map = &domain->bounce_maps[iova >>PAGE_SHIFT ]; +/* +* When PAGE_SIZE is larger than 4KB, multiple adjacent bounce_maps will +* point to the same memory page of PAGE_SIZE. Since bounce_maps originate +* from IO requests, we may not be able to guarantee that the orig_phys +* values of all IO requests within the same 64KB memory page are contiguous. +* Therefore, we need to store them separately. +* +* Bounce pages are allocated on demand. As a result, it may occur that +* multiple bounce pages corresponding to the same 64KB memory page attempt +* to allocate memory simultaneously, so we use cmpxchg to handle this +* concurrency. +*/ + map = &domain->bounce_maps[iova >>BOUNCE_MAP_SHIFT ];if (!map->bounce_page) { - map->bounce_page =alloc_page (GFP_ATOMIC ); -if (!map->bounce_page) -return -ENOMEM ; + head_map = &domain->bounce_maps[(iova &PAGE_MASK ) >>BOUNCE_MAP_SHIFT ]; +if (!head_map->bounce_page) { + tmp_page =alloc_page (GFP_ATOMIC ); +if (!tmp_page) +return -ENOMEM ; +if (cmpxchg (&head_map->bounce_page,NULL , tmp_page)) +__free_page (tmp_page); + } + map->bounce_page = head_map->bounce_page; } map->orig_phys = paddr; - paddr +=PAGE_SIZE ; - iova +=PAGE_SIZE ; + paddr +=BOUNCE_MAP_SIZE ; + iova +=BOUNCE_MAP_SIZE ; }return 0 ; }
@@ -127,12 +146,17 @@ static void vduse_domain_unmap_bounce_pau64 last = iova + size -1 ;while (iova <= last) { - map = &domain->bounce_maps[iova >>PAGE_SHIFT ]; + map = &domain->bounce_maps[iova >>BOUNCE_MAP_SHIFT ]; map->orig_phys =INVALID_PHYS_ADDR ; - iova +=PAGE_SIZE ; + iova +=BOUNCE_MAP_SIZE ; } } +static unsigned int offset_in_bounce_page (dma_addr_t addr) +{ +return (addr & ~BOUNCE_MAP_MASK ); +} +static void do_bounce (phys_addr_t orig,void *addr,size_t size,enum dma_data_direction dir) {
@@ -162,7 +186,7 @@ static void vduse_domain_bounce(struct v enum dma_data_direction dir) {struct vduse_bounce_map *map; -unsigned int offset; +unsigned int offset, head_offset;void *addr;size_t sz;
@@ -170,16 +194,17 @@ static void vduse_domain_bounce(struct vreturn ;while (size) { - map = &domain->bounce_maps[iova >>PAGE_SHIFT ]; - offset =offset_in_page (iova); - sz =min_t (size_t,PAGE_SIZE - offset, size); + map = &domain->bounce_maps[iova >>BOUNCE_MAP_SHIFT ]; + head_offset =offset_in_page (iova); + offset =offset_in_bounce_page (iova); + sz =min_t (size_t,BOUNCE_MAP_SIZE - offset, size);if (WARN_ON (!map->bounce_page || map->orig_phys ==INVALID_PHYS_ADDR ))return ; addr =kmap_local_page (map->bounce_page); -do_bounce (map->orig_phys + offset, addr + offset, sz, dir); +do_bounce (map->orig_phys + offset, addr + head_offset, sz, dir);kunmap_local (addr); size -= sz; iova += sz;
@@ -214,7 +239,7 @@ vduse_domain_get_bounce_page(struct vdusstruct page *page =NULL ;read_lock (&domain->bounce_lock); - map = &domain->bounce_maps[iova >>PAGE_SHIFT ]; + map = &domain->bounce_maps[iova >>BOUNCE_MAP_SHIFT ];if (domain->user_bounce_pages || !map->bounce_page) goto out;
@@ -232,7 +257,7 @@ vduse_domain_free_kernel_bounce_pages(ststruct vduse_bounce_map *map;unsigned long pfn, bounce_pfns; - bounce_pfns = domain->bounce_size >>PAGE_SHIFT ; + bounce_pfns = domain->bounce_size >>BOUNCE_MAP_SHIFT ;for (pfn =0 ; pfn < bounce_pfns; pfn++) { map = &domain->bounce_maps[pfn];
@@ -242,7 +267,8 @@ vduse_domain_free_kernel_bounce_pages(stif (!map->bounce_page)continue ; -__free_page (map->bounce_page); +if (!((pfn <<BOUNCE_MAP_SHIFT ) & ~PAGE_MASK )) +__free_page (map->bounce_page); map->bounce_page =NULL ; } }
@@ -250,8 +276,12 @@ vduse_domain_free_kernel_bounce_pages(stint vduse_domain_add_user_bounce_pages (struct vduse_iova_domain *domain,struct page **pages,int count) { -struct vduse_bounce_map *map; -int i, ret; +struct vduse_bounce_map *map, *head_map; +int i, j, ret; +int inner_pages =PAGE_SIZE /BOUNCE_MAP_SIZE ; +int bounce_pfns = domain->bounce_size >>BOUNCE_MAP_SHIFT ; +struct page *head_page =NULL ; +bool need_copy;/* Now we don't support partial mapping */ if (count != (domain->bounce_size >>PAGE_SHIFT ))
@@ -263,17 +293,25 @@ int vduse_domain_add_user_bounce_pages(s goto out;for (i =0 ; i < count; i++) { - map = &domain->bounce_maps[i]; -if (map->bounce_page) { + need_copy = false; + head_map = &domain->bounce_maps[(i * inner_pages)]; + head_page = head_map->bounce_page; +for (j =0 ; j < inner_pages; j++) { +if ((i * inner_pages + j) >= bounce_pfns) +break ; + map = &domain->bounce_maps[(i * inner_pages + j)];/* Copy kernel page to user page if it's in use */ -if (map->orig_phys !=INVALID_PHYS_ADDR ) -memcpy_to_page (pages[i],0 , -page_address (map->bounce_page), -PAGE_SIZE ); -__free_page (map->bounce_page); +if ((head_page) && (map->orig_phys !=INVALID_PHYS_ADDR )) + need_copy = true; + map->bounce_page = pages[i]; } - map->bounce_page = pages[i];get_page (pages[i]); +if ((head_page) && (need_copy)) +memcpy_to_page (pages[i],0 , +page_address (head_page), +PAGE_SIZE ); +if (head_page) +__free_page (head_page); } domain->user_bounce_pages = true; ret =0 ;
@@ -285,8 +323,12 @@ out:void vduse_domain_remove_user_bounce_pages (struct vduse_iova_domain *domain) { -struct vduse_bounce_map *map; -unsigned long i, count; +struct vduse_bounce_map *map, *head_map; +unsigned long i, j, count; +int inner_pages =PAGE_SIZE /BOUNCE_MAP_SIZE ; +int bounce_pfns = domain->bounce_size >>BOUNCE_MAP_SHIFT ; +struct page *head_page =NULL ; +bool need_copy;write_lock (&domain->bounce_lock);if (!domain->user_bounce_pages)
@@ -296,18 +338,34 @@ void vduse_domain_remove_user_bounce_pagfor (i =0 ; i < count; i++) {struct page *page =NULL ; - map = &domain->bounce_maps[i]; -if (WARN_ON (!map->bounce_page)) + need_copy = false; + head_map = &domain->bounce_maps[(i * inner_pages)]; +if (WARN_ON (!head_map->bounce_page))continue ; + head_page = head_map->bounce_page; -/* Copy user page to kernel page if it's in use */ -if (map->orig_phys !=INVALID_PHYS_ADDR ) { +for (j =0 ; j < inner_pages; j++) { +if ((i * inner_pages + j) >= bounce_pfns) +break ; + map = &domain->bounce_maps[(i * inner_pages + j)]; +if (WARN_ON (!map->bounce_page)) +continue ; +/* Copy user page to kernel page if it's in use */ +if (map->orig_phys !=INVALID_PHYS_ADDR ) + need_copy = true; + } +if (need_copy) { page =alloc_page (GFP_ATOMIC | __GFP_NOFAIL);memcpy_from_page (page_address (page), - map->bounce_page,0 ,PAGE_SIZE ); + head_page,0 ,PAGE_SIZE ); + } +for (j =0 ; j < inner_pages; j++) { +if ((i * inner_pages + j) >= bounce_pfns) +break ; + map = &domain->bounce_maps[(i * inner_pages + j)]; + map->bounce_page = page; } -put_page (map->bounce_page); - map->bounce_page = page; +put_page (head_page); } domain->user_bounce_pages = false; out:
@@ -557,7 +615,7 @@ vduse_domain_create(unsigned long iova_lunsigned long pfn, bounce_pfns;int ret; - bounce_pfns =PAGE_ALIGN (bounce_size) >>PAGE_SHIFT ; + bounce_pfns =PAGE_ALIGN (bounce_size) >>BOUNCE_MAP_SHIFT ;if (iova_limit <= bounce_size)return NULL ;
@@ -589,7 +647,7 @@ vduse_domain_create(unsigned long iova_lrwlock_init (&domain->bounce_lock);spin_lock_init (&domain->iotlb_lock);init_iova_domain (&domain->stream_iovad, -PAGE_SIZE ,IOVA_START_PFN ); +BOUNCE_MAP_SIZE ,IOVA_START_PFN ); ret =iova_domain_init_rcaches (&domain->stream_iovad);if (ret) goto err_iovad_stream;
drivers/vdpa/vdpa_user/iova_domain.h +5 -0
@@ -19,6 +19,11 @@#define INVALID_PHYS_ADDR (~(phys_addr_t)0) +#define BOUNCE_MAP_SHIFT 12 +#define BOUNCE_MAP_SIZE (1 << BOUNCE_MAP_SHIFT) +#define BOUNCE_MAP_MASK (~(BOUNCE_MAP_SIZE - 1)) +#define BOUNCE_MAP_ALIGN (addr) (((addr) + BOUNCE_MAP_SIZE - 1) & ~(BOUNCE_MAP_SIZE - 1)) +struct vduse_bounce_map {struct page *bounce_page;u64 orig_phys;
Greg Kroah-Hartman
maintainerPATCH 390/484 vduse: remove unused vaddr parameter of vduse_domain_free_coherent
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eugenio Pérez <[email protected]>
[ Upstream commit 766e1749c0ef6a09651be9b8a8283d508c322b58 ]
We will modify the function in next patches so let's clean it first.
Message-Id: <[email protected]>
Stable-dep-of: 9c1523803445 ("VDUSE: avoid leaking information to userspace")
--- drivers/vdpa/vdpa_user/iova_domain.c | 3 +-- drivers/vdpa/vdpa_user/iova_domain.h | 3 +-- drivers/vdpa/vdpa_user/vduse_dev.c | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-)
drivers/vdpa/vdpa_user/iova_domain.c +1 -2
@@ -514,8 +514,7 @@ err: }void vduse_domain_free_coherent (struct vduse_iova_domain *domain,size_t size, -void *vaddr,dma_addr_t dma_addr, -unsigned long attrs) +dma_addr_t dma_addr,unsigned long attrs) {struct iova_domain *iovad = &domain->consistent_iovad;struct vhost_iotlb_map *map;
drivers/vdpa/vdpa_user/iova_domain.h +1 -2
@@ -63,8 +63,7 @@ void *vduse_domain_alloc_coherent(struct gfp_t flag, unsigned long attrs);void vduse_domain_free_coherent (struct vduse_iova_domain *domain,size_t size, -void *vaddr,dma_addr_t dma_addr, -unsigned long attrs); +dma_addr_t dma_addr,unsigned long attrs);void vduse_domain_reset_bounce_map (struct vduse_iova_domain *domain);
drivers/vdpa/vdpa_user/vduse_dev.c +1 -1
@@ -872,7 +872,7 @@ static void vduse_dev_free_coherent(strustruct vduse_dev *vdev =dev_to_vduse (dev);struct vduse_iova_domain *domain = vdev->domain; -vduse_domain_free_coherent (domain, size, vaddr, dma_addr, attrs); +vduse_domain_free_coherent (domain, size, dma_addr, attrs); }static size_t vduse_dev_max_mapping_size (struct device *dev)
Greg Kroah-Hartman
maintainerPATCH 391/484 vduse: take out allocations from vduse_dev_alloc_coherent
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eugenio Pérez <[email protected]>
[ Upstream commit 489d76520612abf9a4ede4344349105406c91a73 ]
The function vduse_dev_alloc_coherent will be called under rwlock in next patches. Make it out of the lock to avoid increasing its fail rate.
Message-Id: <[email protected]>
Stable-dep-of: 9c1523803445 ("VDUSE: avoid leaking information to userspace")
--- drivers/vdpa/vdpa_user/iova_domain.c | 24 +++++++----------------- drivers/vdpa/vdpa_user/iova_domain.h | 5 ++--- drivers/vdpa/vdpa_user/vduse_dev.c | 14 ++++++++++---- 3 files changed, 19 insertions(+), 24 deletions(-)
drivers/vdpa/vdpa_user/iova_domain.c +7 -17
@@ -479,17 +479,15 @@ void vduse_domain_unmap_page(struct vdusvduse_domain_free_iova (iovad, dma_addr, size); } -void *vduse_domain_alloc_coherent (struct vduse_iova_domain *domain, -size_t size,dma_addr_t *dma_addr, -gfp_t flag,unsigned long attrs) +dma_addr_t vduse_domain_alloc_coherent (struct vduse_iova_domain *domain, +size_t size,void *orig) {struct iova_domain *iovad = &domain->consistent_iovad;unsigned long limit = domain->iova_limit;dma_addr_t iova =vduse_domain_alloc_iova (iovad, size, limit); -void *orig =alloc_pages_exact (size, flag); -if (!iova || !orig) - goto err; +if (!iova) +return DMA_MAPPING_ERROR ;spin_lock (&domain->iotlb_lock); if (vduse_iotlb_add_range(domain, (u64)iova, (u64)iova + size - 1,
@@ -500,17 +498,12 @@ void *vduse_domain_alloc_coherent(struct }spin_unlock (&domain->iotlb_lock); - *dma_addr = iova; +return iova; -return orig; err: - *dma_addr =DMA_MAPPING_ERROR ; -if (orig) -free_pages_exact (orig, size); -if (iova) -vduse_domain_free_iova (iovad, iova, size); +vduse_domain_free_iova (iovad, iova, size); -return NULL ; +return DMA_MAPPING_ERROR ; } void vduse_domain_free_coherent(struct vduse_iova_domain *domain, size_t size,
@@ -519,7 +512,6 @@ void vduse_domain_free_coherent(struct vstruct iova_domain *iovad = &domain->consistent_iovad;struct vhost_iotlb_map *map;struct vdpa_map_file *map_file; -phys_addr_t pa;spin_lock (&domain->iotlb_lock); map = vhost_iotlb_itree_first(domain->iotlb, (u64)dma_addr,
@@ -531,12 +523,10 @@ void vduse_domain_free_coherent(struct v map_file = (struct vdpa_map_file *)map->opaque;fput (map_file->file);kfree (map_file); - pa = map->addr;vhost_iotlb_map_free (domain->iotlb, map);spin_unlock (&domain->iotlb_lock);vduse_domain_free_iova (iovad, dma_addr, size); -free_pages_exact (phys_to_virt (pa), size); }static vm_fault_t vduse_domain_mmap_fault (struct vm_fault *vmf)
drivers/vdpa/vdpa_user/iova_domain.h +2 -3
@@ -58,9 +58,8 @@ void vduse_domain_unmap_page(struct vdus dma_addr_t dma_addr, size_t size, enum dma_data_direction dir, unsigned long attrs); -void *vduse_domain_alloc_coherent (struct vduse_iova_domain *domain, -size_t size,dma_addr_t *dma_addr, -gfp_t flag,unsigned long attrs); +dma_addr_t vduse_domain_alloc_coherent (struct vduse_iova_domain *domain, +size_t size,void *orig);void vduse_domain_free_coherent (struct vduse_iova_domain *domain,size_t size,dma_addr_t dma_addr,unsigned long attrs);
drivers/vdpa/vdpa_user/vduse_dev.c +10 -4
@@ -851,18 +851,23 @@ static void *vduse_dev_alloc_coherent(st {struct vduse_dev *vdev =dev_to_vduse (dev);struct vduse_iova_domain *domain = vdev->domain; -unsigned long iova;void *addr; *dma_addr =DMA_MAPPING_ERROR ; - addr =vduse_domain_alloc_coherent (domain, size, - (dma_addr_t *)&iova, flag, attrs); + + addr =alloc_pages_exact (size, flag);if (!addr)return NULL ; - *dma_addr = (dma_addr_t )iova; + *dma_addr =vduse_domain_alloc_coherent (domain, size, addr); +if (*dma_addr ==DMA_MAPPING_ERROR ) + goto err;return addr; + +err: +free_pages_exact (addr, size); +return NULL ; } static void vduse_dev_free_coherent(struct device *dev, size_t size,
@@ -873,6 +878,7 @@ static void vduse_dev_free_coherent(strustruct vduse_iova_domain *domain = vdev->domain;vduse_domain_free_coherent (domain, size, dma_addr, attrs); +free_pages_exact (vaddr, size); }static size_t vduse_dev_max_mapping_size (struct device *dev)
Greg Kroah-Hartman
maintainerPATCH 392/484 VDUSE: avoid leaking information to userspace
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jason Wang <[email protected]>
[ Upstream commit 9c1523803445ee0348f62b77793266dd981596e0 ]
The bounceing is not necessarily page aligned, so current VDUSE can leak kernel information through mapping bounce pages to userspace. Allocate bounce pages with __GFP_ZERO to avoid leaking information to userspace.
Message-ID: <[email protected]>
--- drivers/vdpa/vdpa_user/iova_domain.c | 2 +- drivers/vdpa/vdpa_user/vduse_dev.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
drivers/vdpa/vdpa_user/iova_domain.c +1 -1
@@ -124,7 +124,7 @@ static int vduse_domain_map_bounce_page(if (!map->bounce_page) { head_map = &domain->bounce_maps[(iova &PAGE_MASK ) >>BOUNCE_MAP_SHIFT ];if (!head_map->bounce_page) { - tmp_page =alloc_page (GFP_ATOMIC ); + tmp_page =alloc_page (GFP_ATOMIC | __GFP_ZERO);if (!tmp_page)return -ENOMEM ;if (cmpxchg (&head_map->bounce_page,NULL , tmp_page))
drivers/vdpa/vdpa_user/vduse_dev.c +1 -1
@@ -855,7 +855,7 @@ static void *vduse_dev_alloc_coherent(st *dma_addr =DMA_MAPPING_ERROR ; - addr =alloc_pages_exact (size, flag); + addr =alloc_pages_exact (size, flag | __GFP_ZERO);if (!addr)return NULL ;
Greg Kroah-Hartman
maintainerPATCH 393/484 octeontx2: Annotate mmio regions as __iomem
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Subbaraya Sundeep <[email protected]>
[ Upstream commit d0976b43956ee8c8bd093223df9115bfcf63dfe5 ]
This patch removes unnecessary typecasts by marking the mbox_regions array as __iomem since it is used to store pointers to memory-mapped I/O (MMIO) regions. Also simplified the call to readq() in PF driver by removing redundant type casts.
Stable-dep-of: f918554fb724 ("octeontx2-pf: clear stale mailbox IRQ state before request_irq()")
--- drivers/net/ethernet/marvell/octeontx2/af/rvu.c | 12 ++++++------ drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 3 +-- 2 files changed, 7 insertions(+), 8 deletions(-)
drivers/net/ethernet/marvell/octeontx2/af/rvu.c +6 -6
@@ -2330,7 +2330,7 @@ static inline void rvu_afvf_mbox_up_hand__rvu_mbox_up_handler (mwork,TYPE_AFVF ); } -static int rvu_get_mbox_regions (struct rvu *rvu,void **mbox_addr, +static int rvu_get_mbox_regions (struct rvu *rvu,void __iomem **mbox_addr,int num,int type,unsigned long *pf_bmap) {struct rvu_hwinfo *hw = rvu->hw;
@@ -2355,7 +2355,7 @@ static int rvu_get_mbox_regions(struct r bar4 =rvupf_read64 (rvu,RVU_PF_VF_BAR4_ADDR ); bar4 += region *MBOX_SIZE ; } - mbox_addr[region] = (void *)ioremap_wc (bar4,MBOX_SIZE ); + mbox_addr[region] =ioremap_wc (bar4,MBOX_SIZE );if (!mbox_addr[region]) goto error; }
@@ -2378,7 +2378,7 @@ static int rvu_get_mbox_regions(struct r RVU_AF_PF_BAR4_ADDR); bar4 += region *MBOX_SIZE ; } - mbox_addr[region] = (void *)ioremap_wc (bar4,MBOX_SIZE ); + mbox_addr[region] =ioremap_wc (bar4,MBOX_SIZE );if (!mbox_addr[region]) goto error; }
@@ -2386,7 +2386,7 @@ static int rvu_get_mbox_regions(struct r error:while (region--) -iounmap ((void __iomem *)mbox_addr[region]); +iounmap (mbox_addr[region]);return -ENOMEM ; }
@@ -2396,10 +2396,10 @@ static int rvu_mbox_init(struct rvu *rvu void (mbox_up_handler)(struct work_struct *)) {int err = -EINVAL , i, dir, dir_up; +void __iomem **mbox_regions;void __iomem *reg_base;struct rvu_work *mwork;unsigned long *pf_bmap; -void **mbox_regions;const char *name;u64 cfg;
@@ -2422,7 +2422,7 @@ static int rvu_mbox_init(struct rvu *rvumutex_init (&rvu->mbox_lock); - mbox_regions =kcalloc (num,sizeof (void *),GFP_KERNEL ); + mbox_regions =kcalloc (num,sizeof (void __iomem *),GFP_KERNEL );if (!mbox_regions) { err = -ENOMEM ; goto free_bitmap;
drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +1 -2
@@ -597,8 +597,7 @@ static int otx2_pfvf_mbox_init(struct ot base =pci_resource_start (pf->pdev,PCI_MBOX_BAR_NUM ) +MBOX_SIZE ; else - base =readq ((void __iomem *)((u64 )pf->reg_base + -RVU_PF_VF_BAR4_ADDR )); + base =readq (pf->reg_base +RVU_PF_VF_BAR4_ADDR ); hwbase =ioremap_wc (base,MBOX_SIZE * pf->total_vfs);if (!hwbase) {
Greg Kroah-Hartman
maintainerPATCH 394/484 octeontx2-pf: clear stale mailbox IRQ state before request_irq()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Runyu Xiao <[email protected]>
[ Upstream commit f918554fb7246e89b98ef90abe80801f038258b3 ]
otx2_register_mbox_intr() currently installs the PF mailbox IRQ handler before clearing stale mailbox interrupt state. The function itself then comments that the local interrupt bits must be cleared first to avoid spurious interrupts, but that clear happens only after request_irq() has already exposed the handler to irq delivery.
A running system can reach this during PF mailbox interrupt registration while stale or latched RVU_PF_INT state is still present. If delivery happens in the request_irq()-to-clear window, otx2_pfaf_mbox_intr_handler() can run before local quiesce and touch the same pf->mbox and pf->mbox_wq carrier that probe and teardown later reuse or destroy.
Move the stale mailbox interrupt clear ahead of request_irq(), but keep interrupt enabling after the handler is installed. This closes the pre-clear early-IRQ window without creating a new enable-before-handler window.
--- drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +4 -4
@@ -1027,6 +1027,9 @@ static int otx2_register_mbox_intr(strucchar *irq_name;int err; +/* Clear stale mailbox interrupt state before installing the handler. */ +otx2_write64 (pf,RVU_PF_INT ,BIT_ULL (0 )); +/* Register mailbox interrupt handler */ irq_name = &hw->irq_name[RVU_PF_INT_VEC_AFPF_MBOX *NAME_SIZE ];snprintf (irq_name,NAME_SIZE ,"RVUPFAF Mbox" );
@@ -1038,10 +1041,7 @@ static int otx2_register_mbox_intr(strucreturn err; } -/* Enable mailbox interrupt for msgs coming from AF. -* First clear to avoid spurious interrupts, if any. -*/ -otx2_write64 (pf,RVU_PF_INT ,BIT_ULL (0 )); +/* Enable mailbox interrupt for msgs coming from AF. */ otx2_write64 (pf,RVU_PF_INT_ENA_W1S ,BIT_ULL (0 ));if (!probe_af)
Greg Kroah-Hartman
maintainerPATCH 395/484 octeontx2-vf: clear stale mailbox IRQ state before request_irq()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Runyu Xiao <[email protected]>
[ Upstream commit 0b352f04b9be2c83c0240aa6dae7257fefa90464 ]
otx2vf_register_mbox_intr() currently installs the VF mailbox IRQ handler before clearing stale mailbox interrupt state. The code then says that local interrupt bits should be cleared first to avoid spurious interrupts, but that clear still happens only after request_irq() has already made the handler reachable.
A running system can reach this during VF mailbox interrupt registration while stale or latched RVU_VF_INT state is still present. If delivery happens in the request_irq()-to-clear window, otx2vf_vfaf_mbox_intr_handler() can run before local quiesce and touch the same vf->mbox and vf->mbox_wq carrier that probe and teardown later reuse or destroy.
Move the stale mailbox interrupt clear ahead of request_irq(), but keep interrupt enabling after the handler is installed. This closes the pre-clear early-IRQ window without creating a new enable-before-handler window.
--- drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-)
drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c +8 -5
@@ -245,9 +245,15 @@ static int otx2vf_register_mbox_intr(str {struct otx2_hw *hw = &vf->hw;struct msg_req *req; +u64 mbox_int_mask;char *irq_name;int err; + mbox_int_mask =BIT_ULL (0 ); + +/* Clear stale mailbox interrupt state before installing the handler. */ +otx2_write64 (vf,RVU_VF_INT , mbox_int_mask); +/* Register mailbox interrupt handler */ irq_name = &hw->irq_name[RVU_VF_INT_VEC_MBOX *NAME_SIZE ];snprintf (irq_name,NAME_SIZE ,"RVUVFAF Mbox" );
@@ -259,11 +265,8 @@ static int otx2vf_register_mbox_intr(strreturn err; } -/* Enable mailbox interrupt for msgs coming from PF. -* First clear to avoid spurious interrupts, if any. -*/ -otx2_write64 (vf,RVU_VF_INT ,BIT_ULL (0 )); -otx2_write64 (vf,RVU_VF_INT_ENA_W1S ,BIT_ULL (0 )); +/* Enable mailbox interrupt for msgs coming from PF. */ +otx2_write64 (vf,RVU_VF_INT_ENA_W1S , mbox_int_mask);if (!probe_pf)return 0 ;
Greg Kroah-Hartman
maintainerPATCH 396/484 fbdev/efifb: Replace references to global screen_info by local pointer
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thomas Zimmermann <[email protected]>
[ Upstream commit 8505e70821ade706f98f7114a2cbcd51326f7b82 ]
Get the global screen_info's address once and access the data via this pointer. Limits the use of global state.
v3:
* use const screen_info in several places (Sui)
* fix build for deferred takeover (kernel test robot)
Stable-dep-of: 9b6eaf101656 ("fbdev: efifb: fix memory leak in efifb_probe()")
--- drivers/video/fbdev/efifb.c | 124 ++++++++++++++++++++++---------------------- 1 file changed, 64 insertions(+), 60 deletions(-)
drivers/video/fbdev/efifb.c +64 -60
@@ -108,7 +108,7 @@ static int efifb_setcolreg(unsigned regn*/ #if definedCONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER && \ definedCONFIG_ACPI_BGRT -static void efifb_copy_bmp (u8 *src,u32 *dst,int width,struct screen_info *si) +static void efifb_copy_bmp (u8 *src,u32 *dst,int width,const struct screen_info *si) {u8 r, g, b;
@@ -130,7 +130,7 @@ static void efifb_copy_bmp(u8 *src, u32* resolution still fits, it will be displayed very close to the right edge of * the display looking quite bad. This function checks for this. */ -static bool efifb_bgrt_sanity_check (struct screen_info *si,u32 bmp_width) +static bool efifb_bgrt_sanity_check (const struct screen_info *si,u32 bmp_width) {/* * All x86 firmwares horizontally center the image (the yoffset
@@ -141,16 +141,15 @@ static bool efifb_bgrt_sanity_check(stru return bgrt_tab.image_offset_x == expected_xoffset; }#else -static bool efifb_bgrt_sanity_check (struct screen_info *si,u32 bmp_width) +static bool efifb_bgrt_sanity_check (const struct screen_info *si,u32 bmp_width) {return true; }#endif -static void efifb_show_boot_graphics (struct fb_info *info) +static void efifb_show_boot_graphics (struct fb_info *info,const struct screen_info *si) {u32 bmp_width, bmp_height, bmp_pitch, dst_x, y, src_y; -struct screen_info *si = &screen_info;struct bmp_file_header *file_header;struct bmp_dib_header *dib_header;void *bgrt_image =NULL ;
@@ -247,7 +246,8 @@ error: pr_warn("efifb: Ignoring BGRT: unexpected or invalid BMP data\n"); }#else -static inline void efifb_show_boot_graphics (struct fb_info *info) {} +static inline void efifb_show_boot_graphics (struct fb_info *info,const struct screen_info *si) +{ }#endif /*
@@ -282,7 +282,7 @@ static const struct fb_ops efifb_ops = { .fb_setcolreg = efifb_setcolreg, }; -static int efifb_setup (char *options) +static int efifb_setup (struct screen_info *si,char *options) {char *this_opt;
@@ -290,16 +290,16 @@ static int efifb_setup(char *options)while ((this_opt =strsep (&options,"," )) !=NULL ) {if (!*this_opt)continue ; -efifb_setup_from_dmi (&screen_info, this_opt); +efifb_setup_from_dmi (si, this_opt);if (!strncmp (this_opt,"base:" ,5 )) - screen_info.lfb_base =simple_strtoul (this_opt+5 ,NULL ,0 ); + si->lfb_base =simple_strtoul (this_opt+5 ,NULL ,0 );else if (!strncmp (this_opt,"stride:" ,7 )) - screen_info.lfb_linelength =simple_strtoul (this_opt+7 ,NULL ,0 ) *4 ; + si->lfb_linelength =simple_strtoul (this_opt+7 ,NULL ,0 ) *4 ;else if (!strncmp (this_opt,"height:" ,7 )) - screen_info.lfb_height =simple_strtoul (this_opt+7 ,NULL ,0 ); + si->lfb_height =simple_strtoul (this_opt+7 ,NULL ,0 );else if (!strncmp (this_opt,"width:" ,6 )) - screen_info.lfb_width =simple_strtoul (this_opt+6 ,NULL ,0 ); + si->lfb_width =simple_strtoul (this_opt+6 ,NULL ,0 );else if (!strcmp (this_opt,"nowc" )) mem_flags &= ~EFI_MEMORY_WC ;else if (!strcmp (this_opt,"nobgrt" ))
@@ -310,15 +310,15 @@ static int efifb_setup(char *options)return 0 ; } -static inline bool fb_base_is_valid (void ) +static inline bool fb_base_is_valid (struct screen_info *si) { -if (screen_info.lfb_base) +if (si->lfb_base)return true; -if (!(screen_info.capabilities &VIDEO_CAPABILITY_64BIT_BASE )) +if (!(si->capabilities &VIDEO_CAPABILITY_64BIT_BASE ))return false; -if (screen_info.ext_lfb_base) +if (si->ext_lfb_base)return true;return false;
@@ -329,7 +329,10 @@ static ssize_t name##_show(struct devicestruct device_attribute *attr, \ char *buf) \ { \ - return sprintf(buf, fmt "\n", (screen_info.lfb_##name)); \ + struct screen_info *si = dev_get_platdata(dev); \ +if (!si) \ +return -ENODEV ; \ + return sprintf(buf, fmt "\n", (si->lfb_##name)); \ } \static DEVICE_ATTR_RO (name)
@@ -356,6 +359,7 @@ static u64 bar_offset;static int efifb_probe (struct platform_device *dev) { +struct screen_info *si = &screen_info;struct fb_info *info;struct efifb_par *par;int err, orientation;
@@ -365,48 +369,48 @@ static int efifb_probe(struct platform_dchar *option =NULL ;efi_memory_desc_t md; -if (screen_info.orig_video_isVGA !=VIDEO_TYPE_EFI || pci_dev_disabled) +if (si->orig_video_isVGA !=VIDEO_TYPE_EFI || pci_dev_disabled)return -ENODEV ;if (fb_get_options ("efifb" , &option))return -ENODEV ; -efifb_setup (option); +efifb_setup (si, option);/* We don't get linelength from UGA Draw Protocol, only from * EFI Graphics Protocol. So if it's not in DMI, and it's not * passed in from the user, we really can't use the framebuffer. */ -if (!screen_info.lfb_linelength) +if (!si->lfb_linelength)return -ENODEV ; -if (!screen_info.lfb_depth) - screen_info.lfb_depth =32 ; -if (!screen_info.pages) - screen_info.pages =1 ; -if (!fb_base_is_valid ()) { +if (!si->lfb_depth) + si->lfb_depth =32 ; +if (!si->pages) + si->pages =1 ; +if (!fb_base_is_valid (si)) {printk (KERN_DEBUG "efifb: invalid framebuffer address\n" );return -ENODEV ; }printk (KERN_INFO "efifb: probing for efifb\n" );/* just assume they're all unset if any are */ -if (!screen_info.blue_size) { - screen_info.blue_size =8 ; - screen_info.blue_pos =0 ; - screen_info.green_size =8 ; - screen_info.green_pos =8 ; - screen_info.red_size =8 ; - screen_info.red_pos =16 ; - screen_info.rsvd_size =8 ; - screen_info.rsvd_pos =24 ; +if (!si->blue_size) { + si->blue_size =8 ; + si->blue_pos =0 ; + si->green_size =8 ; + si->green_pos =8 ; + si->red_size =8 ; + si->red_pos =16 ; + si->rsvd_size =8 ; + si->rsvd_pos =24 ; } - efifb_fix.smem_start = screen_info.lfb_base; + efifb_fix.smem_start = si->lfb_base; -if (screen_info.capabilities &VIDEO_CAPABILITY_64BIT_BASE ) { +if (si->capabilities &VIDEO_CAPABILITY_64BIT_BASE ) {u64 ext_lfb_base; - ext_lfb_base = (u64 )(unsigned long )screen_info.ext_lfb_base <<32 ; + ext_lfb_base = (u64 )(unsigned long )si->ext_lfb_base <<32 ; efifb_fix.smem_start |= ext_lfb_base; }
@@ -417,10 +421,10 @@ static int efifb_probe(struct platform_d efifb_fix.smem_start = bar_resource->start + bar_offset; } - efifb_defined.bits_per_pixel = screen_info.lfb_depth; - efifb_defined.xres = screen_info.lfb_width; - efifb_defined.yres = screen_info.lfb_height; - efifb_fix.line_length = screen_info.lfb_linelength; + efifb_defined.bits_per_pixel = si->lfb_depth; + efifb_defined.xres = si->lfb_width; + efifb_defined.yres = si->lfb_height; + efifb_fix.line_length = si->lfb_linelength;/* size_vmode -- that is the amount of memory needed for the * used video mode, i.e. the minimum amount of
@@ -430,7 +434,7 @@ static int efifb_probe(struct platform_d/* size_total -- all video memory we have. Used for * entries, ressource allocation and bounds * checking. */ - size_total = screen_info.lfb_size; + size_total = si->lfb_size;if (size_total < size_vmode) size_total = size_vmode;
@@ -505,14 +509,14 @@ static int efifb_probe(struct platform_d goto err_release_fb; } -efifb_show_boot_graphics (info); +efifb_show_boot_graphics (info, si);pr_info ("efifb: framebuffer at 0x%lx, using %dk, total %dk\n" , efifb_fix.smem_start, size_remap/1024 , size_total/1024 );pr_info ("efifb: mode is %dx%dx%d, linelength=%d, pages=%d\n" , efifb_defined.xres, efifb_defined.yres, efifb_defined.bits_per_pixel, efifb_fix.line_length, - screen_info.pages); + si->pages); efifb_defined.xres_virtual = efifb_defined.xres; efifb_defined.yres_virtual = efifb_fix.smem_len /
@@ -526,26 +530,26 @@ static int efifb_probe(struct platform_d efifb_defined.left_margin = (efifb_defined.xres /8 ) &0xf8 ; efifb_defined.hsync_len = (efifb_defined.xres /8 ) &0xf8 ; - efifb_defined.red.offset = screen_info.red_pos; - efifb_defined.red.length = screen_info.red_size; - efifb_defined.green.offset = screen_info.green_pos; - efifb_defined.green.length = screen_info.green_size; - efifb_defined.blue.offset = screen_info.blue_pos; - efifb_defined.blue.length = screen_info.blue_size; - efifb_defined.transp.offset = screen_info.rsvd_pos; - efifb_defined.transp.length = screen_info.rsvd_size; + efifb_defined.red.offset = si->red_pos; + efifb_defined.red.length = si->red_size; + efifb_defined.green.offset = si->green_pos; + efifb_defined.green.length = si->green_size; + efifb_defined.blue.offset = si->blue_pos; + efifb_defined.blue.length = si->blue_size; + efifb_defined.transp.offset = si->rsvd_pos; + efifb_defined.transp.length = si->rsvd_size;pr_info ("efifb: %s: " "size=%d:%d:%d:%d, shift=%d:%d:%d:%d\n" ,"Truecolor" , - screen_info.rsvd_size, - screen_info.red_size, - screen_info.green_size, - screen_info.blue_size, - screen_info.rsvd_pos, - screen_info.red_pos, - screen_info.green_pos, - screen_info.blue_pos); + si->rsvd_size, + si->red_size, + si->green_size, + si->blue_size, + si->rsvd_pos, + si->red_pos, + si->green_pos, + si->blue_pos); efifb_fix.ypanstep =0 ; efifb_fix.ywrapstep =0 ;
Greg Kroah-Hartman
maintainerPATCH 397/484 fbdev: efifb: fix memory leak in efifb_probe()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abdun Nihaal <[email protected]>
[ Upstream commit 9b6eaf101656958397a6012bf43f6e2e42c9e5cb ]
Since commit 73ce73c30ba9 ("fbdev: Transfer video= option strings to caller; clarify ownership") the string returned from fb_get_options() is expected to be freed by the caller, but the string is not freed in efifb_probe(). Fix that by freeing the option string after setup.
--- drivers/video/fbdev/efifb.c | 1 + 1 file changed, 1 insertion(+)
drivers/video/fbdev/efifb.c +1 -0
@@ -375,6 +375,7 @@ static int efifb_probe(struct platform_dif (fb_get_options ("efifb" , &option))return -ENODEV ;efifb_setup (si, option); +kfree (option);/* We don't get linelength from UGA Draw Protocol, only from * EFI Graphics Protocol. So if it's not in DMI, and it's not
Greg Kroah-Hartman
maintainerPATCH 398/484 ASoC: mediatek: mt8195: Remove afe-dai component and rework codec link
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: AngeloGioacchino Del Regno <[email protected]>
[ Upstream commit e70b8dd26711704b1ff1f1b4eb3d048ba69e29da ]
Remove the extra 'mt8195-afe-pcm-dai' component, register the DAI drivers to the main AFE component, and rework the DAI linking between the headset codec (RT5682/RT5682S) and the TDM interface in the probe function to stop assigning name, relying on the of_node of the codec. Also replace the COMP_DUMMY codec entry with a COMP_EMPTY for the ETDM2_IN and remove it entirely from ETDM1_OUT to fix the registration flow for this sound card.
While at it, since we also need to swap the codec init function from ETDM2_IN to ETDM1_OUT, remove the static assignment of both ops and init for both, as we now assign these dynamically during probe.
Stable-dep-of: e24d5dde56a5 ("ASoC: mediatek: mt8192: Check runtime resume during probe")
--- sound/soc/mediatek/mt8195/mt8195-afe-pcm.c | 33 ----------------------- sound/soc/mediatek/mt8195/mt8195-mt6359.c | 41 +++++++++++++++++++---------- 2 files changed, 29 insertions(+), 45 deletions(-)
sound/soc/mediatek/mt8195/mt8195-afe-pcm.c +1 -32
@@ -1795,10 +1795,6 @@ static const struct snd_kcontrol_new mt8 MT8195_AFE_IRQ_28), }; -static const struct snd_soc_component_driver mt8195_afe_pcm_dai_component = { - .name ="mt8195-afe-pcm-dai" , -}; -static const struct mtk_base_memif_data memif_data[MT8195_AFE_MEMIF_NUM ] = { [MT8195_AFE_MEMIF_DL2 ] = { .name ="DL2" ,
@@ -3037,7 +3033,6 @@ static int mt8195_afe_pcm_dev_probe(strustruct device *dev = &pdev->dev;struct reset_control *rstc;int i, irq_id, ret; -struct snd_soc_component *component; ret =of_reserved_mem_device_init (dev);if (ret)
@@ -3170,36 +3165,12 @@ static int mt8195_afe_pcm_dev_probe(stru/* register component */ ret =devm_snd_soc_register_component (dev, &mt8195_afe_component, -NULL ,0 ); + afe->dai_drivers, afe->num_dai_drivers);if (ret) {dev_warn (dev,"err_platform\n" ); goto err_pm_put; } - component =devm_kzalloc (dev,sizeof (*component),GFP_KERNEL ); -if (!component) { - ret = -ENOMEM ; - goto err_pm_put; - } - - ret =snd_soc_component_initialize (component, - &mt8195_afe_pcm_dai_component, - dev); -if (ret) - goto err_pm_put; - -#ifdef CONFIG_DEBUG_FS - component->debugfs_prefix ="pcm" ; -#endif - - ret =snd_soc_add_component (component, - afe->dai_drivers, - afe->num_dai_drivers); -if (ret) { -dev_warn (dev,"err_dai_component\n" ); - goto err_pm_put; - } - ret =regmap_multi_reg_write (afe->regmap, mt8195_afe_reg_defaults,ARRAY_SIZE (mt8195_afe_reg_defaults));if (ret)
@@ -3224,8 +3195,6 @@ err_pm_put:static void mt8195_afe_pcm_dev_remove (struct platform_device *pdev) { -snd_soc_unregister_component (&pdev->dev); -pm_runtime_disable (&pdev->dev);if (!pm_runtime_status_suspended (&pdev->dev))mt8195_afe_runtime_suspend (&pdev->dev);
sound/soc/mediatek/mt8195/mt8195-mt6359.c +28 -13
82 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 399/484 ASoC: mediatek: mt8192-afe-pcm: Convert to devm_pm_runtime_enable()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: AngeloGioacchino Del Regno <[email protected]>
[ Upstream commit 7aaaa22de56ce0dae15fd9f42a69a1d1a7a6e078 ]
Switch from pm_runtime_enable() to devm_pm_runtime_enable(), allowing to remove all gotos from the probe function.
Stable-dep-of: e24d5dde56a5 ("ASoC: mediatek: mt8192: Check runtime resume during probe")
--- sound/soc/mediatek/mt8192/mt8192-afe-pcm.c | 46 ++++++++++------------------- 1 file changed, 17 insertions(+), 29 deletions(-)
sound/soc/mediatek/mt8192/mt8192-afe-pcm.c +17 -29
@@ -2233,21 +2233,20 @@ static int mt8192_afe_pcm_dev_probe(strureturn ret; } -pm_runtime_enable (&pdev->dev); -if (!pm_runtime_enabled (&pdev->dev)) - goto err_pm_disable; + ret =devm_pm_runtime_enable (&pdev->dev); +if (ret) +return ret;/* regmap init */ afe->regmap =syscon_node_to_regmap (dev->parent->of_node);if (IS_ERR (afe->regmap)) {dev_err (dev,"could not get regmap from parent\n" ); - ret =PTR_ERR (afe->regmap); - goto err_pm_disable; +return PTR_ERR (afe->regmap); } ret =regmap_attach_dev (dev, afe->regmap, &mt8192_afe_regmap_config);if (ret) {dev_warn (dev,"regmap_attach_dev fail, ret %d\n" , ret); - goto err_pm_disable; +return ret; }/* enable clock for regcache get default value from hw */
@@ -2257,7 +2256,7 @@ static int mt8192_afe_pcm_dev_probe(stru ret =regmap_reinit_cache (afe->regmap, &mt8192_afe_regmap_config);if (ret) {dev_err (dev,"regmap_reinit_cache fail, ret %d\n" , ret); - goto err_pm_disable; +return ret; }pm_runtime_put_sync (&pdev->dev);
@@ -2270,10 +2269,8 @@ static int mt8192_afe_pcm_dev_probe(stru afe->memif_size =MT8192_MEMIF_NUM ; afe->memif =devm_kcalloc (dev, afe->memif_size,sizeof (*afe->memif),GFP_KERNEL ); -if (!afe->memif) { - ret = -ENOMEM ; - goto err_pm_disable; - } +if (!afe->memif) +return -ENOMEM ;for (i =0 ; i < afe->memif_size; i++) { afe->memif[i].data = &memif_data[i];
@@ -2287,26 +2284,22 @@ static int mt8192_afe_pcm_dev_probe(stru afe->irqs_size =MT8192_IRQ_NUM ; afe->irqs =devm_kcalloc (dev, afe->irqs_size,sizeof (*afe->irqs),GFP_KERNEL ); -if (!afe->irqs) { - ret = -ENOMEM ; - goto err_pm_disable; - } +if (!afe->irqs) +return -ENOMEM ;for (i =0 ; i < afe->irqs_size; i++) afe->irqs[i].irq_data = &irq_data[i];/* request irq */ irq_id =platform_get_irq (pdev,0 ); -if (irq_id <0 ) { - ret = irq_id; - goto err_pm_disable; - } +if (irq_id <0 ) +return irq_id; ret =devm_request_irq (dev, irq_id, mt8192_afe_irq_handler,IRQF_TRIGGER_NONE ,"asys-isr" , (void *)afe);if (ret) {dev_err (dev,"could not request_irq for Afe_ISR_Handle\n" ); - goto err_pm_disable; +return ret; }/* init sub_dais */
@@ -2317,7 +2310,7 @@ static int mt8192_afe_pcm_dev_probe(struif (ret) {dev_warn (afe->dev,"dai register i %d fail, ret %d\n" , i, ret); - goto err_pm_disable; +return ret; } }
@@ -2326,7 +2319,7 @@ static int mt8192_afe_pcm_dev_probe(struif (ret) {dev_warn (afe->dev,"mtk_afe_combine_sub_dai fail, ret %d\n" , ret); - goto err_pm_disable; +return ret; }/* others */
@@ -2345,7 +2338,7 @@ static int mt8192_afe_pcm_dev_probe(stru &mt8192_afe_component, NULL, 0);if (ret) {dev_warn (dev,"err_platform\n" ); - goto err_pm_disable; +return ret; } ret = devm_snd_soc_register_component(&pdev->dev,
@@ -2354,15 +2347,10 @@ static int mt8192_afe_pcm_dev_probe(stru afe->num_dai_drivers);if (ret) {dev_warn (dev,"err_dai_component\n" ); - goto err_pm_disable; +return ret; }return 0 ; - -err_pm_disable: -pm_runtime_disable (&pdev->dev); - -return ret; }static void mt8192_afe_pcm_dev_remove (struct platform_device *pdev)
Greg Kroah-Hartman
maintainerPATCH 400/484 ASoC: mediatek: mt8192-afe-pcm: Simplify with dev_err_probe()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: AngeloGioacchino Del Regno <[email protected]>
[ Upstream commit 324c603a4efca7d1045e0bf3477ca54970eac72c ]
Simplify the probe function by switching error prints to return dev_err_probe(), lowering the lines count; while at it, also beautify some messages and change some others' level from warn to error.
Stable-dep-of: e24d5dde56a5 ("ASoC: mediatek: mt8192: Check runtime resume during probe")
--- sound/soc/mediatek/mt8192/mt8192-afe-pcm.c | 65 +++++++++-------------------- 1 file changed, 22 insertions(+), 43 deletions(-)
sound/soc/mediatek/mt8192/mt8192-afe-pcm.c +22 -43
@@ -2221,17 +2221,12 @@ static int mt8192_afe_pcm_dev_probe(stru/* reset controller to reset audio regs before regmap cache */ rstc =devm_reset_control_get_exclusive (dev,"audiosys" ); -if (IS_ERR (rstc)) { - ret =PTR_ERR (rstc); -dev_err (dev,"could not get audiosys reset:%d\n" , ret); -return ret; - } +if (IS_ERR (rstc)) +return dev_err_probe (dev,PTR_ERR (rstc),"could not get audiosys reset\n" ); ret =reset_control_reset (rstc); -if (ret) { -dev_err (dev,"failed to trigger audio reset:%d\n" , ret); -return ret; - } +if (ret) +return dev_err_probe (dev, ret,"failed to trigger audio reset\n" ); ret =devm_pm_runtime_enable (&pdev->dev);if (ret)
@@ -2239,25 +2234,21 @@ static int mt8192_afe_pcm_dev_probe(stru/* regmap init */ afe->regmap =syscon_node_to_regmap (dev->parent->of_node); -if (IS_ERR (afe->regmap)) { -dev_err (dev,"could not get regmap from parent\n" ); -return PTR_ERR (afe->regmap); - } +if (IS_ERR (afe->regmap)) +return dev_err_probe (dev,PTR_ERR (afe->regmap), +"could not get regmap from parent" ); + ret =regmap_attach_dev (dev, afe->regmap, &mt8192_afe_regmap_config); -if (ret) { -dev_warn (dev,"regmap_attach_dev fail, ret %d\n" , ret); -return ret; - } +if (ret) +return dev_err_probe (dev, ret,"regmap_attach_dev fail\n" );/* enable clock for regcache get default value from hw */ afe_priv->pm_runtime_bypass_reg_ctl = true;pm_runtime_get_sync (&pdev->dev); ret =regmap_reinit_cache (afe->regmap, &mt8192_afe_regmap_config); -if (ret) { -dev_err (dev,"regmap_reinit_cache fail, ret %d\n" , ret); -return ret; - } +if (ret) +return dev_err_probe (dev, ret,"regmap_reinit_cache fail\n" );pm_runtime_put_sync (&pdev->dev); afe_priv->pm_runtime_bypass_reg_ctl = false;
@@ -2297,30 +2288,22 @@ static int mt8192_afe_pcm_dev_probe(stru ret =devm_request_irq (dev, irq_id, mt8192_afe_irq_handler,IRQF_TRIGGER_NONE ,"asys-isr" , (void *)afe); -if (ret) { -dev_err (dev,"could not request_irq for Afe_ISR_Handle\n" ); -return ret; - } +if (ret) +return dev_err_probe (dev, ret,"could not request_irq for Afe_ISR_Handle\n" );/* init sub_dais */ INIT_LIST_HEAD (&afe->sub_dais);for (i =0 ; i <ARRAY_SIZE (dai_register_cbs); i++) { ret = dai_register_cbs[i](afe); -if (ret) { -dev_warn (afe->dev,"dai register i %d fail, ret %d\n" , - i, ret); -return ret; - } +if (ret) +return dev_err_probe (afe->dev, ret,"dai %d register fail" , i); }/* init dai_driver and component_driver */ ret =mtk_afe_combine_sub_dai (afe); -if (ret) { -dev_warn (afe->dev,"mtk_afe_combine_sub_dai fail, ret %d\n" , - ret); -return ret; - } +if (ret) +return dev_err_probe (afe->dev, ret,"mtk_afe_combine_sub_dai fail\n" );/* others */ afe->mtk_afe_hardware = &mt8192_afe_hardware;
@@ -2336,19 +2319,15 @@ static int mt8192_afe_pcm_dev_probe(stru/* register platform */ ret =devm_snd_soc_register_component (&pdev->dev, &mt8192_afe_component,NULL ,0 ); -if (ret) { -dev_warn (dev,"err_platform\n" ); -return ret; - } +if (ret) +return dev_err_probe (dev, ret,"Couldn't register AFE component\n" ); ret =devm_snd_soc_register_component (&pdev->dev, &mt8192_afe_pcm_component, afe->dai_drivers, afe->num_dai_drivers); -if (ret) { -dev_warn (dev,"err_dai_component\n" ); -return ret; - } +if (ret) +return dev_err_probe (dev, ret,"Couldn't register AFE-PCM component\n" );return 0 ; }