Skip to contentSkip to conversation

6.6.153-rc1 review

PATCH In review Greg Kroah-Hartman 12 participants 181 messages Aug 20 – Aug 21 Page 1 of 2

Subsystems (2)
lore
Share & tools
lore

Greg Kroah-Hartman

author maintainer
#0 lore raw

cover 6.6.153-rc1 review

This is the start of the stable review cycle for the 6.6.153 release. There are 166 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.

Responses should be made by Sat, 22 Aug 2026 14:51:11 +0000. Anything received after that time might be too late.

The whole patch series can be found in one patch at:

	https://www.kernel.org/pub/linux/kernel/v6.x/stable-review/patch-6.6.153-rc1.gz

or in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.git linux-6.6.y
and the diffstat can be found below.

thanks,

greg k-h

-------------
Pseudo-Shortlog of commits:

Greg Kroah-Hartman <[email protected]>

    Linux 6.6.153-rc1

Paolo Abeni <[email protected]>
net: harmonize tstats and dstats

Christian Brauner <[email protected]>
binfmt_misc: use exe_file_deny_write_access() for the interpreter clone

Baul Lee <[email protected]>
net/x25: fix use-after-free of the socket by its timers

Gao Xiang <[email protected]>
erofs: fix EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS on some UP platforms

Jamal Hadi Salim <[email protected]>
net/sched: cls_bpf: reject dev-bound programs bound to a different device

Siddharth Vadapalli <[email protected]>
net: ethernet: ti: am65-cpsw-nuss: Fix port_id extraction from SRC TAG

Uwe Kleine-König <[email protected]>
m68k: Define NR_CPUS to 1

Christoph Hellwig <[email protected]>
m68k: use the coherent DMA code for coldfire without data cache

Christoph Hellwig <[email protected]>
dma-direct: add a CONFIG_ARCH_HAS_DMA_ALLOC symbol

Zhang Changzhong <[email protected]>
net/sched: cls_u32: skip hash tables in u32_bind_class()

Jamal Hadi Salim <[email protected]>

    net/sched: act_api: fix TOCTOU NULL deref on a->goto_chain

Victor Nogueira <[email protected]>
net: sched: Add initial TC error skb drop reasons

Victor Nogueira <[email protected]>
net: sched: Move drop_reason to struct tc_skb_cb

Yan Zhai <[email protected]>
packet: add a generic drop reason for receive

Jamal Hadi Salim <[email protected]>
net, sched: Fix SKB_NOT_DROPPED_YET splat under debug config

Daniel Borkmann <[email protected]>
net, sched: Add tcf_set_drop_reason for {__,}tcf_classify

Daniel Borkmann <[email protected]>
net, sched: Make tc-related drop reason more flexible

Eric Dumazet <[email protected]>
af_packet: Don't send zero-byte data in tpacket_snd().

Rosen Penev <[email protected]>
ASoC: xilinx: formatter_pcm: pass aud_drv_data to irq handlers

Chuck Lever <[email protected]>
net/tls: Fail tls_sw_splice_read() after a failed async decrypt

Wei Fang <[email protected]>
net: packet: fix wrong transport_header when sending VLAN-tagged frame

Eric Dumazet <[email protected]>
macvlan: inherit needed_headroom and needed_tailroom from lowerdev

Eric Dumazet <[email protected]>
ipvlan: inherit needed_headroom and needed_tailroom from phy_dev

Florian Westphal <[email protected]>
netfilter: ipset: let destroy callbacks adjust ext mem size

Florian Westphal <[email protected]>
netfilter: ipset: fix list type element drift bug

Jérémy Jean <[email protected]>
netfilter: flowtable: publish GC-visible tuple last

Alexey Velichayshiy <[email protected]>
netfilter: nf_tables_offload: suppress WARN_ON_ONCE for ENOMEM in abort path

Xiang Mei (Microsoft) <[email protected]>
netfilter: ipset: fix refcount race between list:set GC and swap

Herbert Xu <[email protected]>
crypto: ccm - Set rfc4309 maxauthsize from child

Jon Hunter <[email protected]>
arm64: tegra: Add EL2 virtual timer interrupt for Tegra194

Lorenzo Stoakes (ARM) <[email protected]>
mm/huge_memory: fix huge_zero_pfn race

Jérémy Jean <[email protected]>
tls: rx: restore msg_iter before TLS 1.3 optimistic retry

Sabrina Dubroca <[email protected]>
selftests: tls: add rekey tests

Paolo Bonzini <[email protected]>
KVM: SVM: Serialize accesses to the owner and mirror list with separate lock

Lorenzo Stoakes (ARM) <[email protected]>
mm/ptdump: always stabilise against page table freeing using init_mm

Lorenzo Stoakes (ARM) <[email protected]>
mm/vmalloc: acquire init_mm lock on huge vmap to avoid ptdump UAF

Lorenzo Stoakes <[email protected]>
mm/pagewalk: split walk_page_range_novma() into kernel/user parts

Pierre-Eric Pelloux-Prayer <[email protected]>
drm/amdgpu: move debug_vm handling to amdgpu_cs_parser_fini

Yang Wang <[email protected]>
drm/amd/pm: fix pptable use-after-free

Yang Wang <[email protected]>
drm/amd/pm: fix torn gpu metrics reads

Tu Nguyen <[email protected]>
can: rcar_canfd: change the initializing flow for clocks and resets

Jonas Gorski <[email protected]>
i2c: iproc: reset bus after timeout if START_BUSY is stuck

Wolfram Sang <[email protected]>
i2c: bcm-iproc: remove printout on handled timeouts

Eric Dumazet <[email protected]>
vxlan: use pskb_network_may_pull() for transmit path header pulls

Guillaume Nault <[email protected]>
vxlan: Handle stats using NETDEV_PCPU_STAT_DSTATS.

Guillaume Nault <[email protected]>
vrf: Make pcpu_dstats update functions available to other modules.

Jeremy Kerr <[email protected]>
net: core,vrf: Change pcpu_dstat fields to u64_stats_t

Breno Leitao <[email protected]>
vxlan: Do not alloc tstats manually

Dawid Osuchowski <[email protected]>
ice: fix VF interrupts cleanup

Christian Brauner <[email protected]>
binfmt_misc: restore write access when removing an entry

Amir Goldstein <[email protected]>
fs: don't block write during exec on pre-content watched files

Matt Fleming <[email protected]>
veth: convert frag_list skbs before running XDP

Chengfeng Ye <[email protected]>
net: pktgen: fix proc entry use-after-free

Peter Seiderer <[email protected]>
net: pktgen: fix code style (WARNING: Block comments)

Namjae Jeon <[email protected]>
ksmbd: reject repeated SMB2 NEGOTIATE requests

Namjae Jeon <[email protected]>
ksmbd: conn lock to serialize smb2 negotiate

David Carlier <[email protected]>
igc: remove napi_synchronize() in igc_down()

Mark Brown <[email protected]>
ASoC: tas2562: Validate values for volume writes

Chengfeng Ye <[email protected]>
Bluetooth: hci_sync: Fix advertising data UAFs

Luiz Augusto von Dentz <[email protected]>
Bluetooth: eir: Fix possible crashes on eir_create_adv_data

Luiz Augusto von Dentz <[email protected]>

    Bluetooth: hci_core: Fix not handling hdev->le_num_of_adv_sets=1

Gustavo A. R. Silva <[email protected]>
Bluetooth: hci_conn, hci_sync: Use __counted_by() to avoid -Wfamnae warnings

Kees Cook <[email protected]>
overflow: Change DEFINE_FLEX to take __counted_by member

Maciej Fijalkowski <[email protected]>
ice: make ice_vsi_cfg_rxq() static

Przemek Kitszel <[email protected]>
ice: make use of DEFINE_FLEX() in ice_switch.c

Kefeng Wang <[email protected]>
mm: migrate_device: fix pte_pfn/pte_dirty called on non-present PTE

Guanghui Yang <[email protected]>
btrfs: zoned: fix missing chunk metadata reservation

Filipe Manana <[email protected]>
btrfs: remove fs_info argument from btrfs_zoned_activate_one_bg()

Naohiro Aota <[email protected]>
btrfs: add space_info argument to btrfs_chunk_alloc()

David Sterba <[email protected]>
btrfs: add debug build only WARN

Yuxiang Yang <[email protected]>
tcp: challenge ACK for non-exact RST in SYN-RECEIVED

Ilpo Järvinen <[email protected]>
tcp: fast path functions later

Ilpo Järvinen <[email protected]>
tcp: Pass flags to __tcp_send_ack

Namjae Jeon <[email protected]>
ksmbd: validate minimum PDU size for transform requests

ChenXiaoSong <[email protected]>
smb/server: fix minimum SMB2 PDU size

ChenXiaoSong <[email protected]>
smb/server: fix minimum SMB1 PDU size

Namjae Jeon <[email protected]>
ksmbd: rename smb2_get_msg to smb_get_msg

ChenXiaoSong <[email protected]>
smb/server: rename include guard in smb_common.h

ZhangGuoDong <[email protected]>
smb: move get_rfc1002_len() to common/smbglob.h

ZhangGuoDong <[email protected]>
smb: move smb_version_values to common/smbglob.h

Christian Brauner <[email protected]>
super: fix emergency thaw deadlock on frozen block devices

Aldo Ariel Panzardo <[email protected]>
net/sched: serialize qdisc_rtab_list against concurrent get/put

Shiming Cheng <[email protected]>
net: gro: fix double aggregation of flush-marked skbs

Felix Fietkau <[email protected]>
net: move skb_gro_receive_list from udp to core

Pavitra Jha <[email protected]>
libceph: fix two unsafe bare decodes in decode_lockers()

Mike Rapoport (Microsoft) <[email protected]>
userfaultfd: prevent registration of special VMAs

Mike Rapoport (Microsoft) <[email protected]>
userfaultfd: move vma_can_userfault out of line

Chunyan Zhang <[email protected]>
mm: userfaultfd: add pgtable_supports_uffd_wp()

Zhang Yi <[email protected]>
iomap: fix out-of-bounds bitmap_set() with zero-length range

Matthew Wilcox (Oracle) <[email protected]>
iomap: hold state_lock over call to ifs_set_range_uptodate()

Chancel Liu <[email protected]>
ASoC: fsl_sai: Fix spurious BCLK on resume by clearing BYP

Xiang Mei <[email protected]>
libceph: bound pg_{temp,upmap,upmap_items} length to CEPH_PG_MAX_SIZE

Andy Shevchenko <[email protected]>
libceph: Amend checking to fix make W=1 build breakage

Matthew Wilcox (Oracle) <[email protected]>
ceph: Use a folio in ceph_page_mkwrite()

Max Kellermann <[email protected]>
ceph: fix hanging __ceph_get_caps() with stale mds_wanted

Matthew Wilcox (Oracle) <[email protected]>
ceph: Remove ceph_writepage()

Max Kellermann <[email protected]>

    ceph: avoid fs reclaim while using current->journal_info

Sabrina Dubroca <[email protected]>
selftests: tls: add test with a partially invalid iov

Palmer Dabbelt <[email protected]>
RISC-V: Provide pgtable_l5_enabled on rv32

Christoph Hellwig <[email protected]>
xfs: check v5 superblock features early

Long Li <[email protected]>
xfs: don't swallow dquot recovery verification errors

Long Li <[email protected]>
xfs: fix ilock leak on error in xfs_dq_get_next_id

Darrick J. Wong <[email protected]>
xfs: only check mergeability of bnobt records

Junrui Luo <[email protected]>
drm/amdgpu: disallow multiple FENCE chunks in one submit

David Rosca <[email protected]>
drm/amdgpu: Fix UVD decode image min size calculation

David Rosca <[email protected]>
drm/amdgpu: Fix UVD dpb min size calculation for H264

David Rosca <[email protected]>
drm/amdgpu: Fix UVD min buffer sizes

David Rosca <[email protected]>
drm/amdgpu: Implement insert_end for VCE 3

David Rosca <[email protected]>
drm/amdgpu: Reject UVD message with dimensions above 4096

Candice Li <[email protected]>
drm/amdgpu: validate GEM_CREATE domain combinations

David Rosca <[email protected]>
drm/amdgpu: Reject UVD message with invalid number of h265 refs

Eric Farman <[email protected]>
s390/vfio_ccw: Selectively expand io_mutex

Eric Farman <[email protected]>
s390/vfio_ccw: Move cp cleanup out of not operational

Eric Farman <[email protected]>
s390/vfio_ccw: Fix out of bounds check on CCW array

Eric Farman <[email protected]>
s390/vfio_ccw: Ensure index for read/write regions are within range

Eric Farman <[email protected]>
s390/vfio_ccw: Cancel existing workqueues

Eric Farman <[email protected]>
s390/vfio_ccw: Limit the number of channel program segments

Eric Farman <[email protected]>
s390/vfio_ccw: Free all memory if cp_init() fails

Guangshuo Li <[email protected]>
drm/radeon: fix autosuspend cleanup during teardown

Pei Xiao <[email protected]>
mmc: atmel-mci: Fix use-after-free in atmci_remove due to race condition

Haibo Chen <[email protected]>
mmc: sdhci: make tuning_err a signed int

Myeonghun Pak <[email protected]>
mmc: sdhci: unmap the bounce buffer before device release

Zhan Xusheng <[email protected]>
mmc: omap_hsmmc: fix busy_timeout overflow in ns conversion on 32-bit

Kefu Chai <[email protected]>
libceph: tolerate addrvecs with multiple entries of the same type

Yiming Zhu <[email protected]>
ceph: fix MDS random selection readiness predicate

Raphael Zimmer <[email protected]>
libceph: Avoid using invalid osd indices from primary_temp

Dmitry Torokhov <[email protected]>
Input: sur40 - fix V4L error path cleanup

Dmitry Torokhov <[email protected]>
Input: sur40 - fix input device registration ordering

Ali Ahmet Memis <[email protected]>
openrisc: signal: do not restore privileged SR bits on sigreturn

Josh Poimboeuf <[email protected]>
ftrace: Fix off-by-one fentry site disable in ftrace_free_mem()

Leon Hwang <[email protected]>
ftrace: Protect direct_functions in ftrace_find_rec_direct

Pavitra Jha <[email protected]>
libceph: fix multiple unsafe decodes in decode_locker()

Thorsten Blum <[email protected]>
crypto: qce - fix error path in devm_qce_register_algs

Jianing Li <[email protected]>
Input: hynitron_cstxxx - validate touch count and finger IDs

Dmitry Torokhov <[email protected]>
Input: synaptics-rmi4 - propagate F54 worker errors to V4L2 queue

Dmitry Torokhov <[email protected]>
Input: synaptics-rmi4 - block s_input when F54 queue is busy

Bryam Vargas <[email protected]>
Input: synaptics-rmi4 - bound the F54 report size to the allocated buffer

Dmitry Torokhov <[email protected]>
Input: synaptics-rmi4 - zero report size on F54 work error

George Wilson <[email protected]>

    powerpc/pseries: lparcfg - fix kbuf[] underflow

Pengpeng Hou <[email protected]>
Input: iforce - validate input packet lengths

Zhefu Zhang <[email protected]>
Input: atkbd - skip deactivate for Xiaomi Book Pro 14's internal keyboard

Linmao Li <[email protected]>
Input: psxpad-spi - set driver data before use

Richard Davies <[email protected]>
Input: focaltech - fix array out-of-bounds in focaltech_process_rel_packet

Dmitry Torokhov <[email protected]>
Input: synaptics-rmi4 - fix F55 transmitter electrode count typo

George Wilson <[email protected]>
powerpc/pseries: pci - logic bug

Kyohei Kadota <[email protected]>
Input: xpad - add support for ZENAIM LEVERLESS

Asad Kamal <[email protected]>
drm/amdgpu: fix aperture iounmap skipped on device removal

Mingyu Wang <[email protected]>
fbdev: core: Fix pointer desynchronization in fb_io_read()

Dawid Wróbel <[email protected]>
ASoC: codecs: lpass-wsa-macro: Fix enum kcontrol accesses

Peter Ujfalusi <[email protected]>
ASoC: cs35l41: sort the register default table

Peter Ujfalusi <[email protected]>
ASoC: cs35l45: sort the register default table

Peter Ujfalusi <[email protected]>
ASoC: cs4265: sort the register default table

Peter Ujfalusi <[email protected]>
ASoC: SOF: sof-audio: Fix error path in sof_widget_setup_unlocked()

Hidayath Khan <[email protected]>
s390/qeth: validate user buffer length in SNMP and ARP query ioctls

Wyatt Feng <[email protected]>
mptcp: fastopen: only mark MPTFO subflows with SYN data

Matthieu Baerts (NGI0) <[email protected]>
mptcp: options: reset DSS fields in case of unexpected size

Matthieu Baerts (NGI0) <[email protected]>
mptcp: avoid combining some incoming suboptions

Gang Yan <[email protected]>
selftests: mptcp: join: mark tests with data corruption as failed

Bryam Vargas <[email protected]>
selinux: reject an unclaimed class value in security_get_classes()

Bryam Vargas <[email protected]>
selinux: do not cancel a policy conversion that never started

Bryam Vargas <[email protected]>
selinux: reject a class permission count below its inherited common

Bryam Vargas <[email protected]>
selinux: require every boolean value to be defined

Erni Sri Satya Vennela <[email protected]>
net: mana: Fix EQ leak in mana_remove on NULL port

Yizhou Zhao <[email protected]>
ipvs: separate destination availability state

Chao Yu <[email protected]>
f2fs: fix UAF issue in f2fs_merge_page_bio()

Chao Shi <[email protected]>
block: stop the timeout timer when releasing a never added disk

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

Diffstat:

 Makefile                                           |   4 +-
 arch/arm/Kconfig                                   |   1 +
 arch/arm64/boot/dts/nvidia/tegra194.dtsi           |   2 +
 arch/m68k/Kconfig                                  |   7 +-
 arch/m68k/Kconfig.cpu                              |  16 +
 arch/m68k/kernel/Makefile                          |   2 +-
 arch/m68k/kernel/dma.c                             |   2 +-
 arch/openrisc/include/asm/processor.h              |   2 +
 arch/openrisc/kernel/dma.c                         |   4 +-
 arch/openrisc/kernel/signal.c                      |   5 +-
 arch/parisc/Kconfig                                |   1 +
 arch/powerpc/platforms/pseries/lparcfg.c           |   2 +-
 arch/powerpc/platforms/pseries/pci.c               |   2 +-
 arch/riscv/include/asm/pgtable-32.h                |   3 +
 arch/riscv/include/asm/pgtable.h                   |   1 -
 arch/riscv/mm/init.c                               |   2 +
 arch/riscv/mm/pageattr.c                           |   8 +-
 arch/x86/kvm/svm/sev.c                             |  34 +-
 arch/x86/kvm/svm/svm.h                             |   1 +
 block/genhd.c                                      |  12 +-
 crypto/ccm.c                                       |   2 +-
 drivers/crypto/qce/core.c                          |   2 +-
 drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c             |  34 +-
 drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c            |  21 +
 drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c            |  10 +-
 drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c            |  85 +---
 drivers/gpu/drm/amd/amdgpu/vce_v3_0.c              |  26 +-
 drivers/gpu/drm/amd/pm/amdgpu_dpm.c                |  27 +-
 drivers/gpu/drm/amd/pm/amdgpu_pm.c                 |  16 +-
 drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h            |   6 +-
 drivers/gpu/drm/radeon/radeon_kms.c                |   1 +
 drivers/i2c/busses/i2c-bcm-iproc.c                 |  11 +-
 drivers/input/joystick/iforce/iforce-packets.c     |  11 +-
 drivers/input/joystick/iforce/iforce-usb.c         |   3 +
 drivers/input/joystick/psxpad-spi.c                |   1 +
 drivers/input/joystick/xpad.c                      |   2 +
 drivers/input/keyboard/atkbd.c                     |   8 +
 drivers/input/mouse/focaltech.c                    |   2 +-
 drivers/input/rmi4/rmi_f54.c                       |  43 +-
 drivers/input/rmi4/rmi_f55.c                       |   4 +-
 drivers/input/touchscreen/hynitron_cstxxx.c        |  13 +-
 drivers/input/touchscreen/sur40.c                  |  23 +-
 drivers/mmc/host/atmel-mci.c                       |   2 +
 drivers/mmc/host/omap_hsmmc.c                      |   2 +-
 drivers/mmc/host/sdhci.c                           |  16 +
 drivers/mmc/host/sdhci.h                           |   2 +-
 drivers/net/can/rcar/rcar_canfd.c                  |  28 +-
 drivers/net/ethernet/intel/ice/ice_base.c          |  58 ++-
 drivers/net/ethernet/intel/ice/ice_base.h          |   3 +-
 drivers/net/ethernet/intel/ice/ice_lib.c           |  56 ---
 drivers/net/ethernet/intel/ice/ice_lib.h           |   4 -
 drivers/net/ethernet/intel/ice/ice_switch.c        |  63 +--
 drivers/net/ethernet/intel/ice/ice_vf_lib.c        |  27 +
 .../net/ethernet/intel/ice/ice_vf_lib_private.h    |   1 +
 drivers/net/ethernet/intel/ice/ice_virtchnl.c      |  21 +
 drivers/net/ethernet/intel/ice/ice_xsk.c           |   2 +-
 drivers/net/ethernet/intel/igc/igc_main.c          |   1 -
 drivers/net/ethernet/microsoft/mana/mana_en.c      |   4 +-
 drivers/net/ethernet/ti/am65-cpsw-nuss.c           |   2 +
 drivers/net/ipvlan/ipvlan_main.c                   |   4 +
 drivers/net/macvlan.c                              |   4 +
 drivers/net/veth.c                                 |   4 +-
 drivers/net/vrf.c                                  |  49 +-
 drivers/net/vxlan/vxlan_core.c                     |  45 +-
 drivers/net/vxlan/vxlan_mdb.c                      |   4 +-
 drivers/s390/cio/vfio_ccw_async.c                  |  16 +
 drivers/s390/cio/vfio_ccw_chp.c                    |  17 +-
 drivers/s390/cio/vfio_ccw_cp.c                     |  53 +-
 drivers/s390/cio/vfio_ccw_cp.h                     |   8 +
 drivers/s390/cio/vfio_ccw_drv.c                    |  15 +-
 drivers/s390/cio/vfio_ccw_fsm.c                    |   8 +-
 drivers/s390/cio/vfio_ccw_ops.c                    |  35 +-
 drivers/s390/cio/vfio_ccw_private.h                |   6 +-
 drivers/s390/net/qeth_core_main.c                  |   3 +
 drivers/s390/net/qeth_l3_main.c                    |   5 +
 drivers/video/fbdev/core/fb_io_fops.c              |   8 +
 fs/binfmt_elf.c                                    |   4 +-
 fs/binfmt_elf_fdpic.c                              |   4 +-
 fs/binfmt_misc.c                                   |  14 +-
 fs/btrfs/block-group.c                             |  61 ++-
 fs/btrfs/block-group.h                             |   3 +-
 fs/btrfs/extent-tree.c                             |   6 +-
 fs/btrfs/messages.h                                |   7 +
 fs/btrfs/space-info.c                              |   2 +-
 fs/btrfs/transaction.c                             |   5 +-
 fs/btrfs/zoned.c                                   |   5 +-
 fs/btrfs/zoned.h                                   |   6 +-
 fs/ceph/addr.c                                     |  54 +-
 fs/ceph/caps.c                                     |  17 +-
 fs/ceph/file.c                                     |   9 +-
 fs/ceph/mds_client.c                               |  11 +
 fs/ceph/mds_client.h                               |   2 +
 fs/ceph/mdsmap.c                                   |   2 +-
 fs/erofs/Kconfig                                   |   3 +-
 fs/exec.c                                          |   6 +-
 fs/f2fs/data.c                                     |   2 +-
 fs/iomap/buffered-io.c                             |  42 +-
 fs/smb/client/cifsglob.h                           |  27 -
 fs/smb/client/cifssmb.c                            |   6 +-
 fs/smb/client/connect.c                            |   2 +-
 fs/smb/client/transport.c                          |   8 +-
 fs/smb/common/cifsglob.h                           |  36 ++
 fs/smb/server/auth.c                               |   4 +-
 fs/smb/server/connection.c                         |  23 +-
 fs/smb/server/connection.h                         |   5 +
 fs/smb/server/oplock.c                             |   8 +-
 fs/smb/server/smb2misc.c                           |   2 +-
 fs/smb/server/smb2ops.c                            |  32 +-
 fs/smb/server/smb2pdu.c                            |  88 ++--
 fs/smb/server/smb2pdu.h                            |   9 -
 fs/smb/server/smb_common.c                         |  43 +-
 fs/smb/server/smb_common.h                         |  43 +-
 fs/super.c                                         |  22 +-
 fs/userfaultfd.c                                   |  20 +-
 fs/xfs/libxfs/xfs_sb.c                             |   4 +-
 fs/xfs/scrub/alloc.c                               |   5 +-
 fs/xfs/xfs_dquot.c                                 |   3 +-
 fs/xfs/xfs_dquot_item_recover.c                    |   2 +-
 include/asm-generic/pgtable_uffd.h                 |  17 +
 include/linux/fs.h                                 |  12 +
 include/linux/mm_inline.h                          |   7 +-
 include/linux/mmap_lock.h                          |   4 +
 include/linux/netdevice.h                          |  52 +-
 include/linux/overflow.h                           |  52 ++
 include/linux/pagewalk.h                           |   7 +-
 include/linux/userfaultfd_k.h                      |  19 +-
 include/net/bluetooth/hci.h                        |   8 +-
 include/net/bluetooth/hci_core.h                   |   1 +
 include/net/dropreason-core.h                      |  21 +
 include/net/gro.h                                  |   1 +
 include/net/ip_vs.h                                |   7 +
 include/net/pkt_cls.h                              |  16 +
 include/net/pkt_sched.h                            |   3 +-
 include/net/sch_generic.h                          |   2 -
 include/net/tcp.h                                  |  58 +--
 include/uapi/linux/ip_vs.h                         |   6 -
 kernel/dma/Kconfig                                 |   9 +
 kernel/dma/direct.c                                |  12 +-
 kernel/fork.c                                      |  12 +-
 kernel/trace/ftrace.c                              |   9 +-
 lib/overflow_kunit.c                               |  19 +
 mm/huge_memory.c                                   |  49 +-
 mm/internal.h                                      |   7 +
 mm/migrate_device.c                                |   5 +-
 mm/pagewalk.c                                      |  82 ++-
 mm/ptdump.c                                        |  10 +-
 mm/userfaultfd.c                                   |  26 +
 mm/vmalloc.c                                       |  49 +-
 net/bluetooth/eir.c                                |   7 +-
 net/bluetooth/eir.h                                |   2 +-
 net/bluetooth/hci_core.c                           |   9 +
 net/bluetooth/hci_event.c                          |   3 +-
 net/bluetooth/hci_sync.c                           | 237 +++++----
 net/ceph/cls_lock_client.c                         |  21 +-
 net/ceph/decode.c                                  |  18 +-
 net/ceph/osdmap.c                                  |   9 +-
 net/core/dev.c                                     |  29 +-
 net/core/gro.c                                     |  31 ++
 net/core/pktgen.c                                  |  43 +-
 net/ipv4/bpf_tcp_ca.c                              |   2 +-
 net/ipv4/tcp_dctcp.h                               |   2 +-
 net/ipv4/tcp_input.c                               |  56 ++-
 net/ipv4/tcp_minisocks.c                           |  12 +-
 net/ipv4/tcp_output.c                              |   6 +-
 net/ipv4/udp_offload.c                             |  29 --
 net/mptcp/fastopen.c                               |   7 +-
 net/mptcp/options.c                                |  67 ++-
 net/mptcp/protocol.h                               |   1 +
 net/netfilter/ipset/ip_set_bitmap_gen.h            |   2 +-
 net/netfilter/ipset/ip_set_core.c                  |  19 +-
 net/netfilter/ipset/ip_set_list_set.c              |  10 +-
 net/netfilter/ipvs/ip_vs_conn.c                    |   4 +-
 net/netfilter/ipvs/ip_vs_core.c                    |   6 +-
 net/netfilter/ipvs/ip_vs_ctl.c                     |   4 +-
 net/netfilter/ipvs/ip_vs_dh.c                      |   4 +-
 net/netfilter/ipvs/ip_vs_lblc.c                    |   2 +-
 net/netfilter/ipvs/ip_vs_lblcr.c                   |   8 +-
 net/netfilter/nf_flow_table_core.c                 |   7 +-
 net/netfilter/nf_tables_offload.c                  |   2 +-
 net/packet/af_packet.c                             |  27 +-
 net/sched/act_api.c                                |  13 +-
 net/sched/cls_api.c                                |  32 +-
 net/sched/cls_bpf.c                                |  18 +-
 net/sched/cls_u32.c                                |   3 +
 net/sched/sch_api.c                                |  24 +-
 net/tls/tls_sw.c                                   |   7 +
 net/x25/af_x25.c                                   |   4 +-
 net/x25/x25_timer.c                                |  25 +-
 security/selinux/ss/policydb.c                     |  31 ++
 security/selinux/ss/services.c                     |  28 +-
 sound/soc/codecs/cs35l41-lib.c                     |   2 +-
 sound/soc/codecs/cs35l45-tables.c                  |  32 +-
 sound/soc/codecs/cs4265.c                          |   4 +-
 sound/soc/codecs/lpass-wsa-macro.c                 |   8 +-
 sound/soc/codecs/tas2562.c                         |   9 +-
 sound/soc/fsl/fsl_sai.c                            |  29 +-
 sound/soc/sof/sof-audio.c                          |   9 +-
 sound/soc/xilinx/xlnx_formatter_pcm.c              |  10 +-
 tools/testing/selftests/net/mptcp/mptcp_join.sh    |   4 +-
 tools/testing/selftests/net/tls.c                  | 549 +++++++++++++++++++++
 200 files changed, 2659 insertions(+), 1145 deletions(-)
author maintainer
#1 lore raw

PATCH 1/166 block: stop the timeout timer when releasing a never added disk

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

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

From: Chao Shi <[email protected]>

[ Upstream commit 26cb8ebbfaf713c82e142d08828d4d765057633b ]

disk_release() undoes blk_mq_init_allocated_queue() for a disk whose probe failed before add_disk(), but it only calls blk_mq_exit_queue(). Nothing there stops q->timeout, and that timer rolls forward: it stays pending until it next expires, not until the last request completes. So if the driver issued any I/O before adding the disk, the request_queue is freed while still linked into a timer wheel bucket.

Commit 6f8191fdf41d ("block: simplify disk shutdown") dropped the blk_cleanup_queue() call that used to stop it. __del_gendisk() and blk_mq_destroy_queue() still do; only the probe failure path lost it.

nvme gets there because nvme_update_ns_info() submits Report Zones or FDP io-mgmt-recv on ns->queue before the disk is added, so a later failure - a concurrent reset setting NVME_CTRL_FROZEN, or device_add_disk() failing - lands in put_disk() with the timer armed:

  BUG: KASAN: slab-use-after-free in detach_if_pending+0x30c/0x340
  Write of size 8 at addr ffff888004d71310 by task kworker/u8:2/37
   __timer_delete_sync+0x156/0x240 kernel/time/timer.c:1621
   blk_sync_queue+0x22/0x40 block/blk-core.c:222
   nvme_sync_queues+0x100/0x150 drivers/nvme/host/core.c:5362
   nvme_reset_work+0x138/0x930 drivers/nvme/host/pci.c:3264

  Allocated by task 34:
   __blk_mq_alloc_disk+0x33/0x100 block/blk-mq.c:4462
   nvme_alloc_ns+0x290/0x3870 drivers/nvme/host/core.c:4146

  Freed by task 0:
   blk_free_queue_rcu+0x3a/0x50 block/blk-core.c:254
   rcu_core+0xc10/0x1730 kernel/rcu/tree.c:2857

The queue being synced there is ctrl->admin_q, only a victim sharing a timer wheel bucket with the freed queue's dangling entry; other runs tripped in enqueue_timer(), __run_timers() or blk_mq_timeout_work(). Failing nvme_alloc_ns() with a debug patch makes it deterministic: one leaked timer trips KASAN within seconds, while 1987 patched releases produced no splat.

Stop the timer and the queue work items before blk_mq_exit_queue(), like blk_mq_destroy_queue() does.

Found by FuzzNvme.

---
 block/genhd.c | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)
block/genhd.c +8 -4
index 6d01b0e3e1ea..3e88fe53f269 100644
@@ -1174,14 +1174,18 @@ static void disk_release(struct device *dev)
 	/*
 	 * To undo the all initialization from blk_mq_init_allocated_queue in
 	 * case of a probe failure where add_disk is never called we have to
-	 * call blk_mq_exit_queue here. We can't do this for the more common
-	 * teardown case (yet) as the tagset can be gone by the time the disk
-	 * is released once it was added.
+	 * call blk_mq_exit_queue here, after stopping the timer and work items
+	 * that I/O issued before add_disk may have left pending.  We can't do
+	 * this for the more common teardown case (yet) as the tagset can be
+	 * gone by the time the disk is released once it was added.
 	 */
 	if (queue_is_mq(disk->queue) &&
 	    test_bit(GD_OWNS_QUEUE, &disk->state) &&
-	    !test_bit(GD_ADDED, &disk->state))
+	    !test_bit(GD_ADDED, &disk->state)) {
+		blk_sync_queue(disk->queue);
+		blk_mq_cancel_work_sync(disk->queue);
 		blk_mq_exit_queue(disk->queue);
+	}
 
 	blkcg_exit_disk(disk);
 
Signature · 1 line
2.53.0
author maintainer
#2 lore raw

PATCH 2/166 f2fs: fix UAF issue in f2fs_merge_page_bio()

In reply to Greg Kroah-Hartman

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

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

From: Chao Yu <[email protected]>

commit edf7e9040fc52c922db947f9c6c36f07377c52ea upstream.

As JY reported in bugzilla [1],

Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000
pc : [0xffffffe51d249484] f2fs_is_cp_guaranteed+0x70/0x98
lr : [0xffffffe51d24adbc] f2fs_merge_page_bio+0x520/0x6d4
CPU: 3 UID: 0 PID: 6790 Comm: kworker/u16:3 Tainted: P    B   W  OE      6.12.30-android16-5-maybe-dirty-4k #1 5f7701c9cbf727d1eebe77c89bbbeb3371e895e5
Tainted: [P]=PROPRIETARY_MODULE, [B]=BAD_PAGE, [W]=WARN, [O]=OOT_MODULE, [E]=UNSIGNED_MODULE
Workqueue: writeback wb_workfn (flush-254:49)
Call trace:
 f2fs_is_cp_guaranteed+0x70/0x98
 f2fs_inplace_write_data+0x174/0x2f4
 f2fs_do_write_data_page+0x214/0x81c
 f2fs_write_single_data_page+0x28c/0x764
 f2fs_write_data_pages+0x78c/0xce4
 do_writepages+0xe8/0x2fc
 __writeback_single_inode+0x4c/0x4b4
 writeback_sb_inodes+0x314/0x540
 __writeback_inodes_wb+0xa4/0xf4
 wb_writeback+0x160/0x448
 wb_workfn+0x2f0/0x5dc
 process_scheduled_works+0x1c8/0x458
 worker_thread+0x334/0x3f0
 kthread+0x118/0x1ac
 ret_from_fork+0x10/0x20

[1] https://bugzilla.kernel.org/show_bug.cgi?id=220575

The panic was caused by UAF issue w/ below race condition:

kworker
- writepages
- f2fs_write_cache_pages
- f2fs_write_single_data_page
- f2fs_do_write_data_page
- f2fs_inplace_write_data
- f2fs_merge_page_bio
- add_inu_page
: cache page #1 into bio & cache bio in
io->bio_list
- f2fs_write_single_data_page
- f2fs_do_write_data_page
- f2fs_inplace_write_data
- f2fs_merge_page_bio
- add_inu_page
: cache page #2 into bio which is linked
in io->bio_list
write
- f2fs_write_begin
: write page #1
- f2fs_folio_wait_writeback
- f2fs_submit_merged_ipu_write
- f2fs_submit_write_bio
: submit bio which inclues page #1 and #2

software IRQ
- f2fs_write_end_io
- fscrypt_free_bounce_page
: freed bounced page which belongs to page #2
- inc_page_count( , WB_DATA_TYPE(data_folio), false)
: data_folio points to fio->encrypted_page
the bounced page can be freed before
accessing it in f2fs_is_cp_guarantee()

It can reproduce w/ below testcase:
Run below script in shell #1:
for ((i=1;i>0;i++)) do xfs_io -f /mnt/f2fs/enc/file \
-c "pwrite 0 32k" -c "fdatasync"

Run below script in shell #2:
for ((i=1;i>0;i++)) do xfs_io -f /mnt/f2fs/enc/file \
-c "pwrite 0 32k" -c "fdatasync"

So, in f2fs_merge_page_bio(), let's avoid using fio->encrypted_page after commit page into internal ipu cache.

Fixes 0b20fcec8651 ("f2fs: cache global IPU bio")
[Jiucheng Xu: backport to 6.6.y]
---
 fs/f2fs/data.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/f2fs/data.c +1 -1
index af1438277575..d068111436d7 100644
@@ -948,7 +948,7 @@ int f2fs_merge_page_bio(struct f2fs_io_info *fio)
 	if (fio->io_wbc)
 		wbc_account_cgroup_owner(fio->io_wbc, fio->page, PAGE_SIZE);
 
-	inc_page_count(fio->sbi, WB_DATA_TYPE(page, false));
+	inc_page_count(fio->sbi, WB_DATA_TYPE(fio->page, false));
 
 	*fio->last_block = fio->new_blkaddr;
 	*fio->bio = bio;
Signature · 1 line
2.53.0
author maintainer
#3 lore raw

PATCH 3/166 ipvs: separate destination availability state

In reply to Greg Kroah-Hartman

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

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

From: Yizhou Zhao <[email protected]>

commit cdcc4e46180df8161f4d2f3c6fd6beaf6990133d upstream.

IPVS configuration paths update destination availability while connection accounting updates destination overload state. The two independent states share dest->flags, so their read-modify-write updates can race and lose one another.

Keep OVERLOAD in flags, where the preceding patch serializes its updates with dst_lock, and move AVAILABLE to cflags. This keeps configuration- controlled availability out of the scheduler hot cacheline until a scheduler needs to check it. It also prevents availability updates from clobbering overload state.

The destination status bits are not exposed through the IPVS sockopt or netlink interfaces, so keep their definitions in the internal IPVS header.

Readers can still observe stale destination state; this does not provide a cross-field snapshot.

Fixes 1da177e4c3f4 ("Linux-2.6.12-rc2")
Assisted-by Claude-Code:GLM-5.2
(cherry picked from commit cdcc4e46180df8161f4d2f3c6fd6beaf6990133d)
[ Julian: Backport by removing the hunks from ip_vs_xmit.c ]
---
 include/net/ip_vs.h              | 7 +++++++
 include/uapi/linux/ip_vs.h       | 6 ------
 net/netfilter/ipvs/ip_vs_conn.c  | 4 ++--
 net/netfilter/ipvs/ip_vs_core.c  | 6 +++---
 net/netfilter/ipvs/ip_vs_ctl.c   | 4 ++--
 net/netfilter/ipvs/ip_vs_dh.c    | 4 ++--
 net/netfilter/ipvs/ip_vs_lblc.c  | 2 +-
 net/netfilter/ipvs/ip_vs_lblcr.c | 8 ++++----
 8 files changed, 21 insertions(+), 20 deletions(-)
include/net/ip_vs.h +7 -0
index e0077fd28ad1c..35f012f620150 100644
@@ -34,6 +34,12 @@
 #define IP_VS_HDR_INVERSE	1
 #define IP_VS_HDR_ICMP		2
 
+/* Destination Server Flags */
+#define IP_VS_DEST_F_OVERLOAD	0x0002		/* server is overloaded */
+
+/* Destination Server Config Flags */
+#define IP_VS_DEST_CF_AVAILABLE	0x0001		/* server is available */
+
 /* Generic access of ipvs struct */
 static inline struct netns_ipvs *net_ipvs(struct net* net)
 {
@@ -725,6 +731,7 @@ struct ip_vs_dest {
 	volatile unsigned int	flags;		/* dest status flags */
 	atomic_t		conn_flags;	/* flags to copy to conn */
 	atomic_t		weight;		/* server weight */
+	unsigned long		cflags;		/* config flags */
 	atomic_t		last_weight;	/* server latest weight */
 	__u16			tun_type;	/* tunnel type */
 	__be16			tun_port;	/* tunnel port */
include/uapi/linux/ip_vs.h +0 -6
index 1ed234e7f2513..2c37c6ac7525a 100644
@@ -28,12 +28,6 @@
 #define IP_VS_SVC_F_SCHED_SH_FALLBACK	IP_VS_SVC_F_SCHED1 /* SH fallback */
 #define IP_VS_SVC_F_SCHED_SH_PORT	IP_VS_SVC_F_SCHED2 /* SH use port */
 
-/*
- *      Destination Server Flags
- */
-#define IP_VS_DEST_F_AVAILABLE	0x0001		/* server is available */
-#define IP_VS_DEST_F_OVERLOAD	0x0002		/* server is overloaded */
-
 /*
  *      IPVS sync daemon states
  */
net/netfilter/ipvs/ip_vs_conn.c +2 -2
index b2b74ce674909..f0a00e902450b 100644
@@ -758,7 +758,7 @@ int ip_vs_check_template(struct ip_vs_conn *ct, struct ip_vs_dest *cdest)
 	 * Checking the dest server status.
 	 */
 	if ((dest == NULL) ||
-	    !(dest->flags & IP_VS_DEST_F_AVAILABLE) ||
+	    !(dest->cflags & IP_VS_DEST_CF_AVAILABLE) ||
 	    expire_quiescent_template(ipvs, dest) ||
 	    (cdest && (dest != cdest))) {
 		IP_VS_DBG_BUF(9, "check_template: dest not available for "
@@ -1401,7 +1401,7 @@ void ip_vs_expire_nodest_conn_flush(struct netns_ipvs *ipvs)
 				continue;
 
 			dest = cp->dest;
-			if (!dest || (dest->flags & IP_VS_DEST_F_AVAILABLE))
+			if (!dest || (dest->cflags & IP_VS_DEST_CF_AVAILABLE))
 				continue;
 
 			if (atomic_read(&cp->n_control))
net/netfilter/ipvs/ip_vs_core.c +3 -3
index a722bacce4613..d15661e5a7f16 100644
@@ -124,7 +124,7 @@ ip_vs_in_stats(struct ip_vs_conn *cp, struct sk_buff *skb)
 	struct ip_vs_dest *dest = cp->dest;
 	struct netns_ipvs *ipvs = cp->ipvs;
 
-	if (dest && (dest->flags & IP_VS_DEST_F_AVAILABLE)) {
+	if (dest && (dest->cflags & IP_VS_DEST_CF_AVAILABLE)) {
 		struct ip_vs_cpu_stats *s;
 		struct ip_vs_service *svc;
 
@@ -160,7 +160,7 @@ ip_vs_out_stats(struct ip_vs_conn *cp, struct sk_buff *skb)
 	struct ip_vs_dest *dest = cp->dest;
 	struct netns_ipvs *ipvs = cp->ipvs;
 
-	if (dest && (dest->flags & IP_VS_DEST_F_AVAILABLE)) {
+	if (dest && (dest->cflags & IP_VS_DEST_CF_AVAILABLE)) {
 		struct ip_vs_cpu_stats *s;
 		struct ip_vs_service *svc;
 
@@ -2026,7 +2026,7 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state
 	}
 
 	/* Check the server status */
-	if (cp && cp->dest && !(cp->dest->flags & IP_VS_DEST_F_AVAILABLE)) {
+	if (cp && cp->dest && !(cp->dest->cflags & IP_VS_DEST_CF_AVAILABLE)) {
 		/* the destination server is not available */
 		if (sysctl_expire_nodest_conn(ipvs)) {
 			bool old_ct = ip_vs_conn_uses_old_conntrack(cp, skb);
net/netfilter/ipvs/ip_vs_ctl.c +2 -2
index c98bdcea8b2f5..97a7de8498d54 100644
@@ -1058,7 +1058,7 @@ __ip_vs_update_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest,
 	}
 
 	/* set the dest status flags */
-	dest->flags |= IP_VS_DEST_F_AVAILABLE;
+	dest->cflags |= IP_VS_DEST_CF_AVAILABLE;
 
 	if (READ_ONCE(dest->u_threshold) != udest->u_threshold ||
 	    READ_ONCE(dest->l_threshold) != udest->l_threshold) {
@@ -1325,7 +1325,7 @@ static void __ip_vs_unlink_dest(struct ip_vs_service *svc,
 				struct ip_vs_dest *dest,
 				int svcupd)
 {
-	dest->flags &= ~IP_VS_DEST_F_AVAILABLE;
+	dest->cflags &= ~IP_VS_DEST_CF_AVAILABLE;
 
 	/*
 	 *  Remove it from the d-linked destination list.
net/netfilter/ipvs/ip_vs_dh.c +2 -2
index 5e6ec32aff2b1..6d88bb4bf4ae8 100644
@@ -220,8 +220,8 @@ ip_vs_dh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
 
 	s = (struct ip_vs_dh_state *) svc->sched_data;
 	dest = ip_vs_dh_get(svc->af, s, &iph->daddr);
-	if (!dest
-	    || !(dest->flags & IP_VS_DEST_F_AVAILABLE)
+	if (!dest ||
+	    !(dest->cflags & IP_VS_DEST_CF_AVAILABLE)
 	    || atomic_read(&dest->weight) <= 0
 	    || is_overloaded(dest)) {
 		ip_vs_scheduler_err(svc, "no destination available");
net/netfilter/ipvs/ip_vs_lblc.c +1 -1
index cf78ba4ce5ffd..70014c89955d6 100644
@@ -503,7 +503,7 @@ ip_vs_lblc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
 		 */
 
 		dest = en->dest;
-		if ((dest->flags & IP_VS_DEST_F_AVAILABLE) &&
+		if ((dest->cflags & IP_VS_DEST_CF_AVAILABLE) &&
 		    atomic_read(&dest->weight) > 0 && !is_overloaded(dest, svc))
 			goto out;
 	}
net/netfilter/ipvs/ip_vs_lblcr.c +4 -4
index 9eddf118b40ec..6b582be087785 100644
@@ -170,8 +170,8 @@ static inline struct ip_vs_dest *ip_vs_dest_set_min(struct ip_vs_dest_set *set)
 		if (least->flags & IP_VS_DEST_F_OVERLOAD)
 			continue;
 
-		if ((atomic_read(&least->weight) > 0)
-		    && (least->flags & IP_VS_DEST_F_AVAILABLE)) {
+		if ((atomic_read(&least->weight) > 0) &&
+		    (least->cflags & IP_VS_DEST_CF_AVAILABLE)) {
 			loh = ip_vs_dest_conn_overhead(least);
 			goto nextstage;
 		}
@@ -187,8 +187,8 @@ static inline struct ip_vs_dest *ip_vs_dest_set_min(struct ip_vs_dest_set *set)
 
 		doh = ip_vs_dest_conn_overhead(dest);
 		if (((__s64)loh * atomic_read(&dest->weight) >
-		     (__s64)doh * atomic_read(&least->weight))
-		    && (dest->flags & IP_VS_DEST_F_AVAILABLE)) {
+		     (__s64)doh * atomic_read(&least->weight)) &&
+		    (dest->cflags & IP_VS_DEST_CF_AVAILABLE)) {
 			least = dest;
 			loh = doh;
 		}
Signature · 1 line
2.53.0
author maintainer
#4 lore raw

PATCH 4/166 net: mana: Fix EQ leak in mana_remove on NULL port

In reply to Greg Kroah-Hartman

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

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

From: Erni Sri Satya Vennela <[email protected]>

[ Upstream commit 65267c9c4f28199985505977bc2c628c82fc50ef ]

In mana_remove(), when a NULL port is encountered in the port iteration loop, 'goto out' skips the mana_destroy_eq(ac) call, leaking the event queues allocated earlier by mana_create_eq().

This can happen when mana_probe_port() fails for port 0, leaving ac->ports[0] as NULL. On driver unload or error cleanup, mana_remove() hits the NULL entry and jumps past mana_destroy_eq().

Change 'goto out' to 'break' so the for-loop exits normally and mana_destroy_eq() is always reached. Remove the now-unreferenced out:
label.

[ hmahfooz: resolve conflict in mana_remove() due to missing commit
3b194343c250 ("net: mana: Implement ndo_tx_timeout and serialize queue resets per port.") ]

---
 drivers/net/ethernet/microsoft/mana/mana_en.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/ethernet/microsoft/mana/mana_en.c +2 -2
index ec5d38164d41b..2808527315fe5 100644
@@ -2931,7 +2931,7 @@ void mana_remove(struct gdma_dev *gd, bool suspending)
 		if (!ndev) {
 			if (i == 0)
 				dev_err(dev, "No net device to remove\n");
-			goto out;
+			break;
 		}
 
 		/* All cleanup actions should stay after rtnl_lock(), otherwise
@@ -2958,7 +2958,7 @@ void mana_remove(struct gdma_dev *gd, bool suspending)
 	}
 
 	mana_destroy_eq(ac);
-out:
+
 	mana_gd_deregister_device(gd);
 
 	if (suspending)
Signature · 1 line
2.53.0
author maintainer
#5 lore raw

PATCH 5/166 selinux: require every boolean value to be defined

In reply to Greg Kroah-Hartman

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

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

From: Bryam Vargas <[email protected]>

commit a93d37a09b863810653f93d371fb197457d59deb upstream.

p_bools.nprim comes from the policy image independently of how many booleans follow it, and cond_index_bool() fills bool_val_to_struct[] at value - 1, so a count larger than the values present leaves NULL entries. Every user of that array then walks it by index and dereferences each
entry: cond_evaluate_expr() on the access-vector path, security_get_bools() and security_get_bool_value() behind selinuxfs, and security_set_bools(). A sparse class value is absorbed by policydb_class_isvalid() and its siblings; booleans have no such predicate, and no consumer that could use one.

Reject a boolean value that no boolean defines, once, where the array is built. Conforming policies define every boolean they declare and are unaffected.

---
 security/selinux/ss/policydb.c |   19 +++++++++++++++++++
 1 file changed, 19 insertions(+)
security/selinux/ss/policydb.c +19 -0
@@ -715,6 +715,7 @@ static inline void symtab_hash_eval(stru
 static int policydb_index(struct policydb *p)
 {
 	int i, rc;
+	u32 v;
 
 	if (p->mls_enabled)
 		pr_debug("SELinux:  %d users, %d roles, %d types, %d bools, %d sens, %d cats\n",
@@ -770,6 +771,24 @@ static int policydb_index(struct policyd
 		if (rc)
 			goto out;
 	}
+
+	/*
+	 * A sparse class value is absorbed by policydb_class_isvalid() and
+	 * its siblings, but no such predicate exists for booleans: every
+	 * user of bool_val_to_struct[] walks it by index and dereferences
+	 * each entry -- cond_evaluate_expr(), the two getters and
+	 * security_set_bools() -- so an unclaimed one has no consumer that
+	 * can tolerate it.
+	 */
+	for (v = 0; v < p->p_bools.nprim; v++) {
+		if (!p->bool_val_to_struct[v]) {
+			pr_err("SELinux:  boolean %u is declared but not defined\n",
+			       v + 1);
+			rc = -EINVAL;
+			goto out;
+		}
+	}
+
 	rc = 0;
 out:
 	return rc;
author maintainer
#6 lore raw

PATCH 6/166 selinux: reject a class permission count below its inherited common

In reply to Greg Kroah-Hartman

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

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

From: Bryam Vargas <[email protected]>

commit 9a82dcd98b6e6e11cfd162410967951f12152528 upstream.

security_get_permissions() maps an inherited common's permissions into an array sized by the class's own permissions.nprim, but class_read() takes that nprim verbatim from the policy image and never checks that it covers the common. A class that inherits a common of N permissions while declaring a smaller nprim is accepted, and on load the common's permissions are written past the class-sized array -- an out-of-bounds heap write.

Reject a class whose permission count is below its inherited common's. Well-formed policies, where the class count already includes the inherited permissions, are unaffected.

Fixes 55fcf09b3fe4 ("selinux: add support for querying object classes and permissions from the running policy")
---
 security/selinux/ss/policydb.c |   12 ++++++++++++
 1 file changed, 12 insertions(+)
security/selinux/ss/policydb.c +12 -0
@@ -1351,6 +1351,18 @@ static int class_read(struct policydb *p
 			       cladatum->comkey);
 			goto bad;
 		}
+
+		/*
+		 * security_get_permissions() maps the common's permissions
+		 * into an array sized by this class's nprim, so a class must
+		 * declare at least as many as the common it inherits.
+		 */
+		if (cladatum->permissions.nprim <
+		    cladatum->comdatum->permissions.nprim) {
+			pr_err("SELinux:  class %s has fewer permissions than common %s\n",
+			       key, cladatum->comkey);
+			goto bad;
+		}
 	}
 	for (i = 0; i < nel; i++) {
 		rc = perm_read(p, &cladatum->permissions, fp);
author maintainer
#7 lore raw

PATCH 7/166 selinux: do not cancel a policy conversion that never started

In reply to Greg Kroah-Hartman

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

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

From: Bryam Vargas <[email protected]>

commit e5c0235a3c4e9eb047a16cd02323fe4ecf2f570e upstream.

sel_write_load() calls selinux_policy_cancel() when sel_make_policy_nodes() fails, and that helper dereferences the outgoing policy to cancel its sidtab conversion. On the first policy load there is no outgoing policy:
security_load_policy() returns early for that case, before it converts anything, and state->policy is still NULL. A first load that fails while building the selinuxfs tree therefore takes a NULL dereference in selinux_policy_cancel(), reached from a write(2) to /sys/fs/selinux/load.

Skip the cancel when there is no old policy, mirroring the check security_load_policy() already makes before it converts.

Fixes 02a52c5c8c3b ("selinux: move policy commit after updating selinuxfs")
---
 security/selinux/ss/services.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
security/selinux/ss/services.c +3 -1
@@ -2157,7 +2157,9 @@ void selinux_policy_cancel(struct selinu
 	oldpolicy = rcu_dereference_protected(state->policy,
 					lockdep_is_held(&state->policy_mutex));
 
-	sidtab_cancel_convert(oldpolicy->sidtab);
+	/* a first load has no outgoing policy and converted nothing */
+	if (oldpolicy)
+		sidtab_cancel_convert(oldpolicy->sidtab);
 	selinux_policy_free(load_state->policy);
 	kfree(load_state->convert_data);
 }
author maintainer
#8 lore raw

PATCH 8/166 selinux: reject an unclaimed class value in security_get_classes()

In reply to Greg Kroah-Hartman

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

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

From: Bryam Vargas <[email protected]>

commit 22b05fec62c0fe9864cfceb52f7d0f3a34d9b1dd upstream.

security_get_classes() sizes an array by p_classes.nprim and fills it at value - 1, so a class value the policy never defines leaves a NULL. sel_make_classes() passes every entry to sel_make_dir(), reaching the same d_alloc_name() dereference as the permission array. The class symbol table is allowed to be sparse (policydb_class_isvalid() exists to absorb that), but this getter builds its own array straight from the hash table and has no such predicate.

Fail the lookup when a value went unclaimed instead of handing out the NULL. Conforming policies define every class they declare and are unaffected.

Fixes 55fcf09b3fe4 ("selinux: add support for querying object classes and permissions from the running policy")
---
 security/selinux/ss/services.c |   24 +++++++++++++++++++-----
 1 file changed, 19 insertions(+), 5 deletions(-)
security/selinux/ss/services.c +19 -5
@@ -3349,6 +3349,7 @@ int security_get_classes(struct selinux_
 			 char ***classes, u32 *nclasses)
 {
 	struct policydb *policydb;
+	u32 i;
 	int rc;
 
 	policydb = &policy->policydb;
@@ -3361,16 +3362,29 @@ int security_get_classes(struct selinux_
 
 	rc = hashtab_map(&policydb->p_classes.table, get_classes_callback,
 			 *classes);
-	if (rc) {
-		u32 i;
+	if (rc)
+		goto err;
 
-		for (i = 0; i < *nclasses; i++)
-			kfree((*classes)[i]);
-		kfree(*classes);
+	/*
+	 * The class symtab may be sparse, which policydb_class_isvalid() exists
+	 * to absorb; the callback fills this array by value, so an unclaimed
+	 * one leaves a NULL that sel_make_classes() hands to sel_make_dir().
+	 */
+	for (i = 0; i < *nclasses; i++) {
+		if (!(*classes)[i]) {
+			rc = -EINVAL;
+			goto err;
+		}
 	}
 
 out:
 	return rc;
+
+err:
+	for (i = 0; i < *nclasses; i++)
+		kfree((*classes)[i]);
+	kfree(*classes);
+	return rc;
 }
 
 static int get_permissions_callback(void *k, void *d, void *args)
author maintainer
#9 lore raw

PATCH 9/166 selftests: mptcp: join: mark tests with data corruption as failed

In reply to Greg Kroah-Hartman

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

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

From: Gang Yan <[email protected]>

commit ca318e7bbb7723f57bcd9e69a2873b5884435552 upstream.

check_transfer() compares the input and output files byte-by-byte using cmp -l "$in" "$out" | while read .... Because the while-loop body runs in a subshell (the script sets neither lastpipe nor pipefail), the fail_test call inside it -- which sets the global ret/last_test_failed -- and the return 1 both act on the subshell, not on check_transfer().

check_transfer() thus always falls through to return 0, and any data corruption affecting only the payload (leaving the subflow/PM counters untouched) is silently reported as PASS.

---
 tools/testing/selftests/net/mptcp/mptcp_join.sh |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
tools/testing/selftests/net/mptcp/mptcp_join.sh +2 -2
@@ -573,7 +573,7 @@ check_transfer()
 		mv "$tmpfile" "$out"
 		tmpfile=""
 	fi
-	cmp -l "$in" "$out" | while read -r i a b; do
+	while read -r i a b; do
 		local sum=$((0${a} + 0${b}))
 		if [ $check_invert -eq 0 ] || [ $sum -ne $((0xff)) ]; then
 			fail_test "$what does not match (in, out):"
@@ -584,7 +584,7 @@ check_transfer()
 		else
 			print_info "$what has inverted byte at ${i}"
 		fi
-	done
+	done < <(cmp -l "$in" "$out")
 
 	return 0
 }
author maintainer
#10 lore raw

PATCH 10/166 mptcp: avoid combining some incoming suboptions

In reply to Greg Kroah-Hartman

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

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

From: Matthieu Baerts (NGI0) <[email protected]>

commit b6ee361524641f57b2e2363f7737f20e17f67827 upstream.

Some MPTCP suboptions are mutually exclusive according to the RFC8684, but also because in different places, the code doesn't expect some combinations to be present. That's specially true for suboptions that would be present twice, but with different attributes.

The new restrictions are the same as the ones applied on the output side, with mptcp_write_options. The same rules can be reused with a small fix: an MP_FASTCLOSE can be used with a DSS when the sender picks this option [1], which is not the case on Linux. Here are the rules:

Which options can be used together?

X: mutually exclusive
O: often used together
C: can be used together in some cases
P: could be used together but we prefer not to (optimisations)

  | Opt: | MPC  | MPJ  | DSS  | ADD  |  RM  | PRIO | FAIL |  FC  |
  |------|------|------|------|------|------|------|------|------|
  | MPC  |------|------|------|------|------|------|------|------|
  | MPJ  |  X   |------|------|------|------|------|------|------|
  | DSS  |  X   |  X   |------|------|------|------|------|------|
  | ADD  |  X   |  X   |  P   |------|------|------|------|------|
  | RM   |  C   |  C   |  C   |  P   |------|------|------|------|
  | PRIO |  X   |  C   |  C   |  C   |  C   |------|------|------|
  | FAIL |  X   |  X   |  C   |  X   |  X   |  X   |------|------|
  | FC   |  X   |  X   |  P   |  X   |  X   |  X   |  X   |------|
  | RST  |  X   |  X   |  X   |  X   |  X   |  X   |  O   |  O   |
  |------|------|------|------|------|------|------|------|------|

The only difference is with the 'P': another stack could send and ADD_ADDR with other suboptions (DSS, RM_ADDR), and this should be allowed.

A few points of attention:

- In theory, an MP_CAPABLE could be used with a RM_ADDR, but there is no reason to add it with a SYN. Note that even with a 4th ACK, it doesn't seem to be useful, except when IDs are known in advance via another channel. Better not to break that.

- Now, combining both an MP_CAPABLE and an MP_JOIN will no longer result to a reject of the two options, but only the second suboption is ignored. That seems OK to do that for this unexpected error. At least now all inconsistent combinations are handled the same way. This could change later in next. This also means the explicit checks for having both MPC + MPJ in subflow.c will now be unreachable. That's fine, they will be removed in a follow-up patch.

- In case of conflicting combinations, the extra suboption(s) is/are
ignored: having such combinations either means the remote peer is buggy, or is evil. The simplest action is then taken in this case:
stop processing the current suboption.

- In mp_opt->suboptions, there is also a bit reserved to the checksum, which can be used in an MP_CAPABLE and a DSS. Each time a DSS option can be used in parallel with another option, the checksum can be set, so the verification is combined into a new OPTIONS_MPTCP_DSS macro.

- An MP_CAPABLE ACK can carry a Data-Level Length, and an optional
Checksum: they are the same as the ones found in a DSS, because a DSS cannot be used in parallel to an MP_CAPABLE. Similarly, even if there is room, a DSS cannot be used with an MP_JOIN.

---
 net/mptcp/options.c  |   59 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 net/mptcp/protocol.h |    1 
 2 files changed, 59 insertions(+), 1 deletion(-)
net/mptcp/options.c +58 -1
@@ -50,6 +50,14 @@ static void mptcp_parse_option(const str
 			}
 		}
 
+		/* Only the MPC + ACK can be used with a RM_ADDR */
+		if (subopt == OPTION_MPTCP_MPC_ACK) {
+			if ((mp_opt->suboptions & ~OPTION_MPTCP_RM_ADDR) != 0)
+				break;
+		} else if (mp_opt->suboptions != 0) {
+			break;
+		}
+
 		/* Cfr RFC 8684 Section 3.3.0:
 		 * If a checksum is present but its use had
 		 * not been negotiated in the MP_CAPABLE handshake, the receiver MUST
@@ -122,6 +130,11 @@ static void mptcp_parse_option(const str
 		break;
 
 	case MPTCPOPT_MP_JOIN:
+		/* Can be used with a restricted number of other options */
+		if ((mp_opt->suboptions & ~(OPTION_MPTCP_RM_ADDR |
+					    OPTION_MPTCP_PRIO)) != 0)
+			break;
+
 		if (opsize == TCPOLEN_MPTCP_MPJ_SYN) {
 			mp_opt->suboptions |= OPTION_MPTCP_MPJ_SYN;
 			mp_opt->backup = *ptr++ & MPTCPOPT_BACKUP;
@@ -153,6 +166,14 @@ static void mptcp_parse_option(const str
 		break;
 
 	case MPTCPOPT_DSS:
+		/* Can be used with a restricted number of other options */
+		if ((mp_opt->suboptions & ~(OPTION_MPTCP_ADD_ADDR |
+					    OPTION_MPTCP_RM_ADDR |
+					    OPTION_MPTCP_PRIO |
+					    OPTION_MPTCP_FASTCLOSE |
+					    OPTION_MPTCP_FAIL)) != 0)
+			break;
+
 		pr_debug("DSS\n");
 		ptr++;
 
@@ -234,6 +255,12 @@ static void mptcp_parse_option(const str
 		break;
 
 	case MPTCPOPT_ADD_ADDR:
+		/* Can be used with a restricted number of other options */
+		if ((mp_opt->suboptions & ~(OPTIONS_MPTCP_DSS |
+					    OPTION_MPTCP_RM_ADDR |
+					    OPTION_MPTCP_PRIO)) != 0)
+			break;
+
 		mp_opt->echo = (*ptr++) & MPTCP_ADDR_ECHO;
 		if (!mp_opt->echo) {
 			if (opsize == TCPOLEN_MPTCP_ADD_ADDR ||
@@ -293,6 +320,14 @@ static void mptcp_parse_option(const str
 		break;
 
 	case MPTCPOPT_RM_ADDR:
+		/* Can be used with a restricted number of other options */
+		if ((mp_opt->suboptions & ~(OPTION_MPTCP_MPC_ACK |
+					    OPTIONS_MPTCP_MPJ |
+					    OPTIONS_MPTCP_DSS |
+					    OPTION_MPTCP_ADD_ADDR |
+					    OPTION_MPTCP_PRIO)) != 0)
+			break;
+
 		if (opsize < TCPOLEN_MPTCP_RM_ADDR_BASE + 1 ||
 		    opsize > TCPOLEN_MPTCP_RM_ADDR_BASE + MPTCP_RM_IDS_MAX)
 			break;
@@ -307,6 +342,13 @@ static void mptcp_parse_option(const str
 		break;
 
 	case MPTCPOPT_MP_PRIO:
+		/* Can be used with a restricted number of other options */
+		if ((mp_opt->suboptions & ~(OPTIONS_MPTCP_MPJ |
+					    OPTIONS_MPTCP_DSS |
+					    OPTION_MPTCP_ADD_ADDR |
+					    OPTION_MPTCP_RM_ADDR)) != 0)
+			break;
+
 		if (opsize != TCPOLEN_MPTCP_PRIO)
 			break;
 
@@ -316,6 +358,11 @@ static void mptcp_parse_option(const str
 		break;
 
 	case MPTCPOPT_MP_FASTCLOSE:
+		/* Can be used with a restricted number of other options */
+		if ((mp_opt->suboptions & ~(OPTIONS_MPTCP_DSS |
+					    OPTION_MPTCP_RST)) != 0)
+			break;
+
 		if (opsize != TCPOLEN_MPTCP_FASTCLOSE)
 			break;
 
@@ -327,6 +374,11 @@ static void mptcp_parse_option(const str
 		break;
 
 	case MPTCPOPT_RST:
+		/* Can be used with a restricted number of other options */
+		if ((mp_opt->suboptions & ~(OPTION_MPTCP_FAIL |
+					    OPTION_MPTCP_FASTCLOSE)) != 0)
+			break;
+
 		if (opsize != TCPOLEN_MPTCP_RST)
 			break;
 
@@ -342,6 +394,11 @@ static void mptcp_parse_option(const str
 		break;
 
 	case MPTCPOPT_MP_FAIL:
+		/* Can be used with a restricted number of other options */
+		if ((mp_opt->suboptions & ~(OPTIONS_MPTCP_DSS |
+					    OPTION_MPTCP_RST)) != 0)
+			break;
+
 		if (opsize != TCPOLEN_MPTCP_FAIL)
 			break;
 
@@ -1415,7 +1472,7 @@ void mptcp_write_options(struct tcphdr *
 	 *  RM   |  C   |  C   |  C   |  P   |------|------|------|------|
 	 *  PRIO |  X   |  C   |  C   |  C   |  C   |------|------|------|
 	 *  FAIL |  X   |  X   |  C   |  X   |  X   |  X   |------|------|
-	 *  FC   |  X   |  X   |  X   |  X   |  X   |  X   |  X   |------|
+	 *  FC   |  X   |  X   |  P   |  X   |  X   |  X   |  X   |------|
 	 *  RST  |  X   |  X   |  X   |  X   |  X   |  X   |  O   |  O   |
 	 * ------|------|------|------|------|------|------|------|------|
 	 *
net/mptcp/protocol.h +1 -0
@@ -36,6 +36,7 @@
 				 OPTION_MPTCP_MPC_ACK)
 #define OPTIONS_MPTCP_MPJ	(OPTION_MPTCP_MPJ_SYN | OPTION_MPTCP_MPJ_SYNACK | \
 				 OPTION_MPTCP_MPJ_ACK)
+#define OPTIONS_MPTCP_DSS	(OPTION_MPTCP_DSS | OPTION_MPTCP_CSUMREQD)
 
 /* MPTCP option subtypes */
 #define MPTCPOPT_MP_CAPABLE	0
author maintainer
#11 lore raw

PATCH 11/166 mptcp: options: reset DSS fields in case of unexpected size

In reply to Greg Kroah-Hartman

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

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

From: Matthieu Baerts (NGI0) <[email protected]>

commit 35772b4981f38ba8059372cde8753e8e477e98ec upstream.

A remote peer could send a malformed DSS with a wrong size, followed by another DSS or MPC + Data. In this case, the first suboption will be ignored, but leaving some fields written, which could lead to inconsistency or access uninitialized data.

Explicitly reset the fields that could have been modified in case of unexpected size.

---
 net/mptcp/options.c |    8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)
net/mptcp/options.c +7 -1
@@ -209,8 +209,14 @@ static void mptcp_parse_option(const str
 		 * RFC 8684 Section 3.3.0 checks later in subflow_data_ready
 		 */
 		if (opsize != expected_opsize &&
-		    opsize != expected_opsize + TCPOLEN_MPTCP_DSS_CHECKSUM)
+		    opsize != expected_opsize + TCPOLEN_MPTCP_DSS_CHECKSUM) {
+			mp_opt->dsn64 = 0;
+			mp_opt->use_map = 0;
+			mp_opt->ack64 = 0;
+			mp_opt->use_ack = 0;
+			mp_opt->data_fin = 0;
 			break;
+		}
 
 		mp_opt->suboptions |= OPTION_MPTCP_DSS;
 		if (mp_opt->use_ack) {
author maintainer
#12 lore raw

PATCH 12/166 mptcp: fastopen: only mark MPTFO subflows with SYN data

In reply to Greg Kroah-Hartman

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

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

From: Wyatt Feng <[email protected]>

commit e00b63056fb4f261455b3e5df5268a1f8ce47a87 upstream.

Passive TCP Fast Open accepts a valid-cookie SYN even when it carries no data. In that case the child socket's receive queue is intentionally left empty.

mptcp_fastopen_subflow_synack_set_params() set is_mptfo before checking for queued SYN data. That made data-less TFO SYNs hit a WARN and, if the warning was non-fatal, left stale MPTFO state behind. The stale flag could later trigger a state-confusion bug in check_fully_established().

Only mark the subflow as MPTFO after confirming that an SKB was queued. Return quietly when the receive queue is empty.

Note that mptcp_subflow_context's is_mptfo field is now not just about subflows where the TFO was present, but about MPTFO subflow that consumed SYN data. Only having a valid cookie but not carrying data is not really "doing TFO".

---
 net/mptcp/fastopen.c |    7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
net/mptcp/fastopen.c +4 -3
@@ -24,12 +24,13 @@ void mptcp_fastopen_subflow_synack_set_p
 	sk = subflow->conn;
 	tp = tcp_sk(ssk);
 
-	subflow->is_mptfo = 1;
-
+	/* A valid TFO cookie does not guarantee SYN data. */
 	skb = skb_peek(&ssk->sk_receive_queue);
-	if (WARN_ON_ONCE(!skb))
+	if (!skb)
 		return;
 
+	subflow->is_mptfo = 1;
+
 	/* dequeue the skb from sk receive queue */
 	__skb_unlink(skb, &ssk->sk_receive_queue);
 	skb_ext_reset(skb);
author maintainer
#13 lore raw

PATCH 13/166 s390/qeth: validate user buffer length in SNMP and ARP query ioctls

In reply to Greg Kroah-Hartman

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

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

From: Hidayath Khan <[email protected]>

commit d141f087b1af656f055d7c5793a3e87817ba0bbe upstream.

qeth_snmp_command() and qeth_l3_arp_query() allocate a buffer sized by a user-supplied length (udata_len) without checking a lower bound, then set udata_offset to a fixed non-zero value and pass both to a reply callback. The callback bounds-checks the copy with

if ((udata_len - udata_offset) < len)

Both fields are u32, so a udata_len smaller than udata_offset makes the subtraction wrap and the check pass, and the following memcpy() writes past the allocation. A udata_len of 0 also yields ZERO_SIZE_PTR from kzalloc(), which the existing NULL check does not catch.

Reject buffers smaller than udata_offset before allocating, so the callback subtraction can no longer underflow.

---
 drivers/s390/net/qeth_core_main.c |    3 +++
 drivers/s390/net/qeth_l3_main.c   |    5 +++++
 2 files changed, 8 insertions(+)
drivers/s390/net/qeth_core_main.c +3 -0
@@ -4710,6 +4710,9 @@ static int qeth_snmp_command(struct qeth
 	if (req_len > QETH_BUFSIZE)
 		return -EINVAL;
 
+	if (qinfo.udata_len < sizeof(struct qeth_snmp_ureq_hdr))
+		return -EINVAL;
+
 	iob = qeth_get_adapter_cmd(card, IPA_SETADP_SET_SNMP_CONTROL, req_len);
 	if (!iob)
 		return -ENOMEM;
drivers/s390/net/qeth_l3_main.c +5 -0
@@ -1415,6 +1415,11 @@ static int qeth_l3_arp_query(struct qeth
 		rc = -EFAULT;
 		goto out;
 	}
+
+	if (qinfo.udata_len < QETH_QARP_ENTRIES_OFFSET) {
+		rc = -EINVAL;
+		goto out;
+	}
 	qinfo.udata = kzalloc(qinfo.udata_len, GFP_KERNEL);
 	if (!qinfo.udata) {
 		rc = -ENOMEM;
author maintainer
#14 lore raw

PATCH 14/166 ASoC: SOF: sof-audio: Fix error path in sof_widget_setup_unlocked()

In reply to Greg Kroah-Hartman

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

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

From: Peter Ujfalusi <[email protected]>

commit e780e4917d43683224812400fe3dc4816fceba75 upstream.

If either tplg_ops->dai_config or widget_kcontrol_setup fail during widget setup we would double decrement the use_count of the widget because the sof_widget_free_unlocked() would be called twice, similarly the core_put would be invoked twice as well.

Since the use_count and core_put() is handled within the widget_free function we need to return without falling through the pipe_widget_free label.

The fixes tag is picked to the last change around this part of the code which is adequately old enough for backporting purposes.

---
 sound/soc/sof/sof-audio.c |    9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)
sound/soc/sof/sof-audio.c +4 -5
@@ -139,7 +139,6 @@ static int sof_widget_setup_unlocked(str
 {
 	const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
 	struct snd_sof_pipeline *spipe = swidget->spipe;
-	bool use_count_decremented = false;
 	int ret;
 	int i;
 
@@ -218,9 +217,10 @@ static int sof_widget_setup_unlocked(str
 	return 0;
 
 widget_free:
-	/* widget use_count will be decremented by sof_widget_free() */
+	/* widget use_count and core_put handled by sof_widget_free() */
 	sof_widget_free_unlocked(sdev, swidget);
-	use_count_decremented = true;
+	return ret;
+
 pipe_widget_free:
 	if (swidget->id != snd_soc_dapm_scheduler) {
 		sof_widget_free_unlocked(sdev, swidget->spipe->pipe_widget);
@@ -235,8 +235,7 @@ pipe_widget_free:
 		}
 	}
 use_count_dec:
-	if (!use_count_decremented)
-		swidget->use_count--;
+	swidget->use_count--;
 
 	return ret;
 }
author maintainer
#15 lore raw

PATCH 15/166 ASoC: cs4265: sort the register default table

In reply to Greg Kroah-Hartman

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

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

From: Peter Ujfalusi <[email protected]>

commit e4fe3e046524e5de3c04c6eef3743780cbdc231c upstream.

reg_defaults must be sorted by ascending register address, as regcache_lookup_reg() locates entries in it with bsearch(). See commit fd80df352ba1 ("regcache: Add support for sorting defaults arrays").

cs4265_reg_defaults[] lists CS4265_INT_MASK (0x0e), CS4265_STATUS_MODE_MSB (0x0f) and CS4265_STATUS_MODE_LSB (0x10) after CS4265_SPDIF_CTL1 (0x11) and CS4265_SPDIF_CTL2 (0x12), so the binary search does not find those three entries. regcache_reg_needs_sync() then cannot compare them against their default and reports that a sync is needed, so they are written to the device on every regcache_sync() even when they were never touched.

Sort the table by register address.

---
 sound/soc/codecs/cs4265.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
sound/soc/codecs/cs4265.c +2 -2
@@ -46,11 +46,11 @@ static const struct reg_default cs4265_r
 	{ CS4265_DAC_CHA_VOL, 0x00 },
 	{ CS4265_DAC_CHB_VOL, 0x00 },
 	{ CS4265_DAC_CTL2, 0xC0 },
-	{ CS4265_SPDIF_CTL1, 0x00 },
-	{ CS4265_SPDIF_CTL2, 0x00 },
 	{ CS4265_INT_MASK, 0x00 },
 	{ CS4265_STATUS_MODE_MSB, 0x00 },
 	{ CS4265_STATUS_MODE_LSB, 0x00 },
+	{ CS4265_SPDIF_CTL1, 0x00 },
+	{ CS4265_SPDIF_CTL2, 0x00 },
 };
 
 static bool cs4265_readable_register(struct device *dev, unsigned int reg)
author maintainer
#16 lore raw

PATCH 16/166 ASoC: cs35l45: sort the register default table

In reply to Greg Kroah-Hartman

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

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

From: Peter Ujfalusi <[email protected]>

commit f39a68ed08bb6eef0ae711b41d645ee5e9448c09 upstream.

reg_defaults must be sorted by ascending register address, as regcache_lookup_reg() locates entries in it with bsearch(). See commit fd80df352ba1 ("regcache: Add support for sorting defaults arrays").

cs35l45_defaults[] lists the DSP1_RX*_RATE and DSP1_TX*_RATE registers (0x02b80080 - 0x02b802b8) in the middle of the table, ahead of entries with much lower addresses, so the binary search does not find 36 of its 73 entries. regcache_reg_needs_sync() then cannot compare those against their default and reports that a sync is needed, so they are written to the device on every regcache_sync() even when they were never touched.

Sort the table by register address.

---
 sound/soc/codecs/cs35l45-tables.c |   32 ++++++++++++++++----------------
 1 file changed, 16 insertions(+), 16 deletions(-)
sound/soc/codecs/cs35l45-tables.c +16 -16
@@ -66,22 +66,6 @@ static const struct reg_default cs35l45_
 	{ CS35L45_ASPTX3_INPUT,			0x00000020 },
 	{ CS35L45_ASPTX4_INPUT,			0x00000028 },
 	{ CS35L45_ASPTX5_INPUT,			0x00000048 },
-	{ CS35L45_DSP1_RX1_RATE,		0x00000001 },
-	{ CS35L45_DSP1_RX2_RATE,		0x00000001 },
-	{ CS35L45_DSP1_RX3_RATE,		0x00000001 },
-	{ CS35L45_DSP1_RX4_RATE,		0x00000001 },
-	{ CS35L45_DSP1_RX5_RATE,		0x00000001 },
-	{ CS35L45_DSP1_RX6_RATE,		0x00000001 },
-	{ CS35L45_DSP1_RX7_RATE,		0x00000001 },
-	{ CS35L45_DSP1_RX8_RATE,		0x00000001 },
-	{ CS35L45_DSP1_TX1_RATE,		0x00000001 },
-	{ CS35L45_DSP1_TX2_RATE,		0x00000001 },
-	{ CS35L45_DSP1_TX3_RATE,		0x00000001 },
-	{ CS35L45_DSP1_TX4_RATE,		0x00000001 },
-	{ CS35L45_DSP1_TX5_RATE,		0x00000001 },
-	{ CS35L45_DSP1_TX6_RATE,		0x00000001 },
-	{ CS35L45_DSP1_TX7_RATE,		0x00000001 },
-	{ CS35L45_DSP1_TX8_RATE,		0x00000001 },
 	{ CS35L45_DSP1RX1_INPUT,		0x00000008 },
 	{ CS35L45_DSP1RX2_INPUT,		0x00000009 },
 	{ CS35L45_DSP1RX3_INPUT,		0x00000018 },
@@ -113,6 +97,22 @@ static const struct reg_default cs35l45_
 	{ CS35L45_GPIO1_CTRL1,			0x81000001 },
 	{ CS35L45_GPIO2_CTRL1,			0x81000001 },
 	{ CS35L45_GPIO3_CTRL1,			0x81000001 },
+	{ CS35L45_DSP1_RX1_RATE,		0x00000001 },
+	{ CS35L45_DSP1_RX2_RATE,		0x00000001 },
+	{ CS35L45_DSP1_RX3_RATE,		0x00000001 },
+	{ CS35L45_DSP1_RX4_RATE,		0x00000001 },
+	{ CS35L45_DSP1_RX5_RATE,		0x00000001 },
+	{ CS35L45_DSP1_RX6_RATE,		0x00000001 },
+	{ CS35L45_DSP1_RX7_RATE,		0x00000001 },
+	{ CS35L45_DSP1_RX8_RATE,		0x00000001 },
+	{ CS35L45_DSP1_TX1_RATE,		0x00000001 },
+	{ CS35L45_DSP1_TX2_RATE,		0x00000001 },
+	{ CS35L45_DSP1_TX3_RATE,		0x00000001 },
+	{ CS35L45_DSP1_TX4_RATE,		0x00000001 },
+	{ CS35L45_DSP1_TX5_RATE,		0x00000001 },
+	{ CS35L45_DSP1_TX6_RATE,		0x00000001 },
+	{ CS35L45_DSP1_TX7_RATE,		0x00000001 },
+	{ CS35L45_DSP1_TX8_RATE,		0x00000001 },
 };
 
 static bool cs35l45_readable_reg(struct device *dev, unsigned int reg)
author maintainer
#17 lore raw

PATCH 17/166 ASoC: cs35l41: sort the register default table

In reply to Greg Kroah-Hartman

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

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

From: Peter Ujfalusi <[email protected]>

commit d74aac116cfb2058b15df53996d23232b310f7ff upstream.

reg_defaults must be sorted by ascending register address, as regcache_lookup_reg() locates entries in it with bsearch(). See commit fd80df352ba1 ("regcache: Add support for sorting defaults arrays").

cs35l41_reg[] lists CS35L41_BSTCVRT_PEAK_CUR (0x3808) after CS35L41_BSTCVRT_COEFF (0x3810) and CS35L41_BSTCVRT_SLOPE_LBST (0x3814), so the binary search does not find those two entries. regcache_reg_needs_sync() then cannot compare them against their default and reports that a sync is needed, so they are written to the device on every regcache_sync() even when they were never touched.

Sort the table by register address.

---
 sound/soc/codecs/cs35l41-lib.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
sound/soc/codecs/cs35l41-lib.c +1 -1
@@ -23,9 +23,9 @@ static const struct reg_default cs35l41_
 	{ CS35L41_GPIO_PAD_CONTROL,		0x00000000 },
 	{ CS35L41_GLOBAL_CLK_CTRL,		0x00000003 },
 	{ CS35L41_TST_FS_MON0,			0x00020016 },
+	{ CS35L41_BSTCVRT_PEAK_CUR,		0x0000004A },
 	{ CS35L41_BSTCVRT_COEFF,		0x00002424 },
 	{ CS35L41_BSTCVRT_SLOPE_LBST,		0x00007500 },
-	{ CS35L41_BSTCVRT_PEAK_CUR,		0x0000004A },
 	{ CS35L41_SP_ENABLES,			0x00000000 },
 	{ CS35L41_SP_RATE_CTRL,			0x00000028 },
 	{ CS35L41_SP_FORMAT,			0x18180200 },
author maintainer
#18 lore raw

PATCH 18/166 ASoC: codecs: lpass-wsa-macro: Fix enum kcontrol accesses

In reply to Greg Kroah-Hartman

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

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

From: Dawid Wróbel <[email protected]>

commit 56f24311fd5607588a47e44675195a9efb200f29 upstream.

EAR SPKR PA Gain" and the four "WSA RX* Mux" controls are enumerated, but their get and put callbacks access the value through ucontrol->value.integer.value[0] (a long) instead of ucontrol->value.enumerated.item[0] (an unsigned int).

This same pattern was fixed in the sibling drivers by commit bcfe5f76cc40 ("ASoC: codecs: rx-macro: fix accessing array out of bounds for enum type") and
commit 0ea5eff7c606 ("ASoC: codecs: va-macro: fix accessing array out of bounds for enum type"), but wsa-macro was missed.

On 64-bit kernels with CONFIG_SND_CTL_DEBUG this trips the elem value sanity check and every read of these controls fails with -EINVAL.

Fixes 809bcbcecebf ("ASoC: codecs: lpass-wsa-macro: Add support to WSA Macro")
Fixes 2c4066e5d428 ("ASoC: codecs: lpass-wsa-macro: add dapm widgets and route")
Assisted-by Claude:claude-fable-5
---
 sound/soc/codecs/lpass-wsa-macro.c |    8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
sound/soc/codecs/lpass-wsa-macro.c +4 -4
@@ -1859,7 +1859,7 @@ static int wsa_macro_ear_spkr_pa_gain_ge
 	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
 	struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
 
-	ucontrol->value.integer.value[0] = wsa->ear_spkr_gain;
+	ucontrol->value.enumerated.item[0] = wsa->ear_spkr_gain;
 
 	return 0;
 }
@@ -1870,7 +1870,7 @@ static int wsa_macro_ear_spkr_pa_gain_pu
 	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
 	struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
 
-	wsa->ear_spkr_gain =  ucontrol->value.integer.value[0];
+	wsa->ear_spkr_gain =  ucontrol->value.enumerated.item[0];
 
 	return 0;
 }
@@ -1884,7 +1884,7 @@ static int wsa_macro_rx_mux_get(struct s
 				snd_soc_dapm_to_component(widget->dapm);
 	struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
 
-	ucontrol->value.integer.value[0] =
+	ucontrol->value.enumerated.item[0] =
 			wsa->rx_port_value[widget->shift];
 	return 0;
 }
@@ -1898,7 +1898,7 @@ static int wsa_macro_rx_mux_put(struct s
 				snd_soc_dapm_to_component(widget->dapm);
 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
 	struct snd_soc_dapm_update *update = NULL;
-	u32 rx_port_value = ucontrol->value.integer.value[0];
+	u32 rx_port_value = ucontrol->value.enumerated.item[0];
 	u32 bit_input;
 	u32 aif_rst;
 	struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
author maintainer
#19 lore raw

PATCH 19/166 fbdev: core: Fix pointer desynchronization in fb_io_read()

In reply to Greg Kroah-Hartman

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

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

From: Mingyu Wang <[email protected]>

commit 81cc73be40c6f028f1ee3f438ace46afe666dbae upstream.

In fb_io_read(), if copy_to_user() performs a partial copy (e.g., due to a faulty user buffer), the loop adjusts the chunk size 'c' and updates the remaining 'count'. However, the hardware 'src' pointer has already been eagerly advanced by the original chunk size.

If the loop is allowed to continue, the read will resume from an incorrect, over-advanced offset. Since the remaining 'count' was only decremented by the successful bytes, this desynchronization causes the next iterations to execute more hardware reads than originally bounded, eventually leading to out-of-bounds I/O reads.

Fix this by breaking out of the loop immediately upon a partial copy_to_user(). A partial copy indicates a faulty user buffer, making subsequent read attempts futile. Breaking out ensures we return the number of successfully read bytes without risking out-of-bounds hardware accesses in subsequent mismatched iterations.

Fixes 6121cd9ef911 ("fbdev: Move I/O read and write code into helper functions")
---
 drivers/video/fbdev/core/fb_io_fops.c |    8 ++++++++
 1 file changed, 8 insertions(+)
drivers/video/fbdev/core/fb_io_fops.c +8 -0
@@ -57,6 +57,14 @@ ssize_t fb_io_read(struct fb_info *info,
 		buf += c;
 		cnt += c;
 		count -= c;
+
+		/*
+		 * If there was a partial copy, the user buffer is faulty.
+		 * Break out to avoid over-advancing the src pointer and
+		 * reading out of bounds in the next iteration.
+		 */
+		if (trailing)
+			break;
 	}
 
 	kfree(buffer);
author maintainer
#20 lore raw

PATCH 20/166 drm/amdgpu: fix aperture iounmap skipped on device removal

In reply to Greg Kroah-Hartman

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

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

From: Asad Kamal <[email protected]>

commit f9e5f51549000e2665e3b5e02ff876b9e09cfe95 upstream.

amdgpu_pci_remove() calls drm_dev_unplug() before invoking the fini routines. After drm_dev_unplug() the drm_dev_enter() guard in amdgpu_ttm_fini() always returns false, so iounmap() for aper_base_kaddr is silently skipped. On connected_to_cpu hardware ioremap_cache() maps the aperture as WB; when iounmap() is skipped the stale WB PAT entry persists. On reload IP discovery's memremap(MEMREMAP_WC) on the same aperture range hits a WB/WC conflict, producing an ioremap error and failing re-probe.

Remove the drm_dev_enter() guard and call iounmap() unconditionally. The aperture mapping is plain MMIO and does not require device-presence protection. Surprise-removal cleanup of aper_base_kaddr is already handled unconditionally by amdgpu_device_unmap_mmio().

Fixes 62d5f9f7110a ("drm/amdgpu: Unmap MMIO mappings when device is not unplugged")
(cherry picked from commit fb3f68af9f6fce9343a2bd13b4d68a1c02d283df)
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c |   10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +2 -8
@@ -2044,8 +2044,6 @@ int amdgpu_ttm_init(struct amdgpu_device
  */
 void amdgpu_ttm_fini(struct amdgpu_device *adev)
 {
-	int idx;
-
 	if (!adev->mman.initialized)
 		return;
 
@@ -2068,13 +2066,9 @@ void amdgpu_ttm_fini(struct amdgpu_devic
 	amdgpu_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);
+	if (adev->mman.aper_base_kaddr) {
+		iounmap(adev->mman.aper_base_kaddr);
 		adev->mman.aper_base_kaddr = NULL;
-
-		drm_dev_exit(idx);
 	}
 
 	amdgpu_vram_mgr_fini(adev);
author maintainer
#21 lore raw

PATCH 21/166 Input: xpad - add support for ZENAIM LEVERLESS

In reply to Greg Kroah-Hartman

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

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

From: Kyohei Kadota <[email protected]>

commit 7d971337ebfad0b173cb46097c709db174ac3557 upstream.

Add the VID/PID for the ZENAIM LEVERLESS controller to xpad_device and the VID to xpad_table.

---
 drivers/input/joystick/xpad.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/input/joystick/xpad.c +2 -0
@@ -417,6 +417,7 @@ static const struct xpad_device {
 	{ 0x3285, 0x0646, "Nacon Pro Compact", 0, XTYPE_XBOXONE },
 	{ 0x3285, 0x0662, "Nacon Revolution5 Pro", 0, XTYPE_XBOX360 },
 	{ 0x3285, 0x0663, "Nacon Evol-X", 0, XTYPE_XBOXONE },
+	{ 0x3507, 0x000b, "ZENAIM LEVERLESS", 0, XTYPE_XBOX360 },
 	{ 0x3537, 0x1004, "GameSir T4 Kaleid", 0, XTYPE_XBOX360 },
 	{ 0x3537, 0x100f, "GameSir Nova 2 Lite", 0, XTYPE_XBOX360 },
 	{ 0x3537, 0x1010, "GameSir G7 SE", 0, XTYPE_XBOXONE },
@@ -575,6 +576,7 @@ static const struct usb_device_id xpad_t
 	XPAD_XBOX360_VENDOR(0x31e3),		/* Wooting Keyboards */
 	XPAD_XBOX360_VENDOR(0x3285),		/* Nacon GC-100 */
 	XPAD_XBOXONE_VENDOR(0x3285),		/* Nacon Evol-X */
+	XPAD_XBOX360_VENDOR(0x3507),		/* ZENAIM Controllers */
 	XPAD_XBOX360_VENDOR(0x3537),		/* GameSir Controllers */
 	XPAD_XBOXONE_VENDOR(0x3537),		/* GameSir Controllers */
 	XPAD_XBOX360_VENDOR(0x413d),		/* Black Shark Green Ghost Controller */
author maintainer
#22 lore raw

PATCH 22/166 powerpc/pseries: pci - logic bug

In reply to Greg Kroah-Hartman

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

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

From: George Wilson <[email protected]>

commit 649c10bff5cb7a514bf299094833ec8c9190aac3 upstream.

The checks on num_vfs in pseries_pci_sriov_enable() are ANDed where OR was apparently intended. Change it to OR.

Fixes 9a7f6b438664 ("powerpc/pseries/pci: Associate PEs to VFs in configure SR-IOV")
---
 arch/powerpc/platforms/pseries/pci.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
arch/powerpc/platforms/pseries/pci.c +1 -1
@@ -159,7 +159,7 @@ static int pseries_pci_sriov_enable(stru
 
 	/* First integer stores max config */
 	max_config_vfs = of_read_number(&max_vfs[0], 1);
-	if (max_config_vfs < num_vfs && num_vfs > MAX_VFS_FOR_MAP_PE) {
+	if (max_config_vfs < num_vfs || num_vfs > MAX_VFS_FOR_MAP_PE) {
 		dev_err(&pdev->dev,
 			"Num VFs %x > %x Configurable VFs\n",
 			num_vfs, (num_vfs > MAX_VFS_FOR_MAP_PE) ?
author maintainer
#23 lore raw

PATCH 23/166 Input: synaptics-rmi4 - fix F55 transmitter electrode count typo

In reply to Greg Kroah-Hartman

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

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

From: Dmitry Torokhov <[email protected]>

commit 6058f0fea10f3caf63a435677358d1b8e9325114 upstream.

During F55 sensor detection, the transmitter (TX) electrode count was incorrectly assigned the value of the receiver (RX) electrode count due to copy-paste typos.

This incorrect value was then propagated to the driver data and used by F54 to determine the diagnostics report size. On devices with more RX than TX electrodes, this inflated the perceived TX count, leading to incorrect report size calculations and potential out-of-bounds buffer accesses.

Fix the typos by correctly assigning the TX electrode counts.

Fixes 6adba43fd222 ("Input: synaptics-rmi4 - add support for F55 sensor tuning")
Fixes c762cc68b6a1 ("Input: synaptics-rmi4 - propagate correct number of rx and tx electrodes to F54")
Reported-by [email protected]
Assisted-by Antigravity:gemini-3.5-flash
---
 drivers/input/rmi4/rmi_f55.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/input/rmi4/rmi_f55.c +2 -2
@@ -54,10 +54,10 @@ static int rmi_f55_detect(struct rmi_fun
 	f55->num_tx_electrodes = f55->qry[F55_NUM_TX_OFFSET];
 
 	f55->cfg_num_rx_electrodes = f55->num_rx_electrodes;
-	f55->cfg_num_tx_electrodes = f55->num_rx_electrodes;
+	f55->cfg_num_tx_electrodes = f55->num_tx_electrodes;
 
 	drv_data->num_rx_electrodes = f55->cfg_num_rx_electrodes;
-	drv_data->num_tx_electrodes = f55->cfg_num_rx_electrodes;
+	drv_data->num_tx_electrodes = f55->cfg_num_tx_electrodes;
 
 	if (f55->qry[F55_PHYS_CHAR_OFFSET] & F55_CAP_SENSOR_ASSIGN) {
 		int i, total;
author maintainer
#24 lore raw

PATCH 24/166 Input: focaltech - fix array out-of-bounds in focaltech_process_rel_packet

In reply to Greg Kroah-Hartman

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

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

From: Richard Davies <[email protected]>

commit 296736076b3fd078742651c719555a488624023a upstream.

Make finger2 (and also finger1) unsigned, so that if the finger index in the packet is 0 then subtracting 1 creates an array index which overflows above the existing check for FOC_MAX_FINGERS, as the existing comment says it should, instead of writing to state->fingers[-1].

Fixes 05be1d079ec0 ("Input: psmouse - support for the FocalTech PS/2 protocol extensions")
---
 drivers/input/mouse/focaltech.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/input/mouse/focaltech.c +1 -1
@@ -197,7 +197,7 @@ static void focaltech_process_rel_packet
 {
 	struct focaltech_data *priv = psmouse->private;
 	struct focaltech_hw_state *state = &priv->state;
-	int finger1, finger2;
+	unsigned int finger1, finger2;
 
 	state->pressed = packet[0] >> 7;
 	finger1 = ((packet[0] >> 4) & 0x7) - 1;
author maintainer
#25 lore raw

PATCH 25/166 Input: psxpad-spi - set driver data before use

In reply to Greg Kroah-Hartman

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

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

From: Linmao Li <[email protected]>

commit 732f38c36059e68ba3b4b89c56911d777fd3185c upstream.

psxpad_spi_suspend() retrieves the controller state with spi_get_drvdata(), but probe never stores it, so suspend dereferences a NULL pointer. Store it during probe.

Fixes 8be193c7b1f4 ("Input: add support for PlayStation 1/2 joypads connected via SPI")
---
 drivers/input/joystick/psxpad-spi.c |    1 +
 1 file changed, 1 insertion(+)
drivers/input/joystick/psxpad-spi.c +1 -0
@@ -369,6 +369,7 @@ static int psxpad_spi_probe(struct spi_d
 		return err;
 	}
 
+	spi_set_drvdata(spi, pad);
 	pm_runtime_enable(&spi->dev);
 
 	return 0;
author maintainer
#26 lore raw

PATCH 26/166 Input: atkbd - skip deactivate for Xiaomi Book Pro 14s internal keyboard

In reply to Greg Kroah-Hartman

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

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

From: Zhefu Zhang <[email protected]>

commit 3a046db33bb9f28b43a951a7a090db771dc0f8b3 upstream.

The internal keyboard of the Xiaomi Book Pro 14 does not work unless atkbd skips deactivating it at the end of atkbd_probe().

Using 'i8042.dumbkbd=1' also makes the keyboard work, but then the driver never writes to the keyboard at all, so the Caps Lock LED is lost. The atkbd_deactivate_fixup quirk fixes both without a boot parameter.

DMI: XIAOMI Xiaomi Book Pro 14/TM2424, BIOS XMAPT4B0P0909 05/06/2026

---
 drivers/input/keyboard/atkbd.c |    8 ++++++++
 1 file changed, 8 insertions(+)
drivers/input/keyboard/atkbd.c +8 -0
@@ -1959,6 +1959,14 @@ static const struct dmi_system_id atkbd_
 		},
 		.callback = atkbd_deactivate_fixup,
 	},
+	{
+		/* Xiaomi Book Pro 14 (TM2424) */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "XIAOMI"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Xiaomi Book Pro 14"),
+		},
+		.callback = atkbd_deactivate_fixup,
+	},
 	{ }
 };
 
author maintainer
#27 lore raw

PATCH 27/166 Input: iforce - validate input packet lengths

In reply to Greg Kroah-Hartman

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

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

From: Pengpeng Hou <[email protected]>

commit 5751c781d3c97ab6ce0e2a966156ed882152c415 upstream.

iforce_process_packet() reads fixed fields from joystick, wheel and status packets without first checking their lengths. In particular, the shared hats-and-buttons helper unconditionally reads data[6]. The status tail is a sequence of 16-bit effect addresses, but an incomplete final address is also consumed. A successful zero-length USB URB additionally reads the packet ID before the common parser is called.

Reject the zero-length USB transfer, require the seven-byte joystick and wheel prefixes and the two-byte status prefix, and consume only complete status-tail addresses.

---
 drivers/input/joystick/iforce/iforce-packets.c |   11 ++++++++++-
 drivers/input/joystick/iforce/iforce-usb.c     |    3 +++
 2 files changed, 13 insertions(+), 1 deletion(-)
drivers/input/joystick/iforce/iforce-packets.c +10 -1
@@ -161,6 +161,9 @@ void iforce_process_packet(struct iforce
 	switch (packet_id) {
 
 	case 0x01:	/* joystick position data */
+		if (len < 7)
+			break;
+
 		input_report_abs(dev, ABS_X,
 				 (__s16) get_unaligned_le16(data));
 		input_report_abs(dev, ABS_Y,
@@ -176,6 +179,9 @@ void iforce_process_packet(struct iforce
 		break;
 
 	case 0x03:	/* wheel position data */
+		if (len < 7)
+			break;
+
 		input_report_abs(dev, ABS_WHEEL,
 				 (__s16) get_unaligned_le16(data));
 		input_report_abs(dev, ABS_GAS,   255 - data[2]);
@@ -187,6 +193,9 @@ void iforce_process_packet(struct iforce
 		break;
 
 	case 0x02:	/* status report */
+		if (len < 2)
+			break;
+
 		input_report_key(dev, BTN_DEAD, data[0] & 0x02);
 		input_sync(dev);
 
@@ -206,7 +215,7 @@ void iforce_process_packet(struct iforce
 			}
 		}
 
-		for (j = 3; j < len; j += 2)
+		for (j = 3; j + sizeof(u16) <= len; j += sizeof(u16))
 			mark_core_as_ready(iforce, get_unaligned_le16(data + j));
 
 		break;
drivers/input/joystick/iforce/iforce-usb.c +3 -0
@@ -159,6 +159,9 @@ static void iforce_usb_irq(struct urb *u
 		goto exit;
 	}
 
+	if (!urb->actual_length)
+		goto exit;
+
 	iforce_process_packet(iforce, iforce_usb->data_in[0],
 			      iforce_usb->data_in + 1, urb->actual_length - 1);
 
author maintainer
#28 lore raw

PATCH 28/166 powerpc/pseries: lparcfg - fix kbuf[] underflow

In reply to Greg Kroah-Hartman

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

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

From: George Wilson <[email protected]>

commit fb442a6673ff1046bf67754957d95880fdb394b5 upstream.

In lparcfg_write(), a count of 0 results in kbuf[] being indexed at -1. Check for count == 0 in the existing check for count > sizeof(kbuf) and return -EINVAL if true.

Fixes 74422e2b1939 ("powerpc/pseries: Remove VLA from lparcfg_write()")
---
 arch/powerpc/platforms/pseries/lparcfg.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
arch/powerpc/platforms/pseries/lparcfg.c +1 -1
@@ -673,7 +673,7 @@ static ssize_t lparcfg_write(struct file
 	if (!firmware_has_feature(FW_FEATURE_SPLPAR))
 		return -EINVAL;
 
-	if (count > sizeof(kbuf))
+	if (count == 0 || count > sizeof(kbuf))
 		return -EINVAL;
 
 	if (copy_from_user(kbuf, buf, count))
author maintainer
#29 lore raw

PATCH 29/166 Input: synaptics-rmi4 - zero report size on F54 work error

In reply to Greg Kroah-Hartman

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

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

From: Dmitry Torokhov <[email protected]>

commit dc76c3c8e8ad09362b8c1561f3928288c15cba2e upstream.

In rmi_f54_work(), if an error occurs during report request or command verification, the code jumped directly to the 'error' label, bypassing the 'abort' label where f54->report_size was normally zeroed out.

This left f54->report_size containing its previous successful payload size. If a user then altered the V4L2 format to a smaller size, and a subsequent run failed, rmi_f54_buffer_queue() would copy the stale, larger payload size into the shrunken V4L2 buffer, causing a heap buffer overflow.

Fix this by merging the 'abort' and 'error' labels into a single 'out' exit path, and ensuring that f54->report_size is always set to 0 on failure by checking for error and zeroing the local report_size first.

Fixes 3a762dbd5347 ("[media] Input: synaptics-rmi4 - add support for F54 diagnostics")
Reported-by [email protected]
Assisted-by Antigravity:gemini-3.5-flash
---
 drivers/input/rmi4/rmi_f54.c |   16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)
drivers/input/rmi4/rmi_f54.c +8 -8
@@ -547,7 +547,7 @@ static void rmi_f54_work(struct work_str
 		dev_err(&fn->dev, "Bad report size, report type=%d\n",
 				f54->report_type);
 		error = -EINVAL;
-		goto error;     /* retry won't help */
+		goto out;     /* retry won't help */
 	}
 
 	/*
@@ -558,7 +558,7 @@ static void rmi_f54_work(struct work_str
 			 &command);
 	if (error) {
 		dev_err(&fn->dev, "Failed to read back command\n");
-		goto error;
+		goto out;
 	}
 	if (command & F54_GET_REPORT) {
 		if (time_after(jiffies, f54->timeout)) {
@@ -566,7 +566,7 @@ static void rmi_f54_work(struct work_str
 			error = -ETIMEDOUT;
 		}
 		report_size = 0;
-		goto error;
+		goto out;
 	}
 
 	rmi_dbg(RMI_DEBUG_FN, &fn->dev, "Get report command completed, reading data\n");
@@ -581,7 +581,7 @@ static void rmi_f54_work(struct work_str
 					fifo, sizeof(fifo));
 		if (error) {
 			dev_err(&fn->dev, "Failed to set fifo start offset\n");
-			goto abort;
+			goto out;
 		}
 
 		error = rmi_read_block(fn->rmi_dev, fn->fd.data_base_addr +
@@ -590,16 +590,16 @@ static void rmi_f54_work(struct work_str
 		if (error) {
 			dev_err(&fn->dev, "%s: read [%d bytes] returned %d\n",
 				__func__, size, error);
-			goto abort;
+			goto out;
 		}
 	}
 
-abort:
-	f54->report_size = error ? 0 : report_size;
-error:
+out:
 	if (error)
 		report_size = 0;
 
+	f54->report_size = report_size;
+
 	if (report_size == 0 && !error) {
 		queue_delayed_work(f54->workqueue, &f54->work,
 				   msecs_to_jiffies(1));
author maintainer
#30 lore raw

PATCH 30/166 Input: synaptics-rmi4 - bound the F54 report size to the allocated buffer

In reply to Greg Kroah-Hartman

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

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

From: Bryam Vargas <[email protected]>

commit 49c5adc2b7d6e43c5cf033e1c86fdb9c16ababb1 upstream.

rmi_f54_work() reads a diagnostics report from the device into f54->report_data, sizing the transfer with rmi_f54_get_report_size():

	report_size = rmi_f54_get_report_size(f54);
	...
	for (i = 0; i < report_size; i += F54_REPORT_DATA_SIZE) {
		int size = min(F54_REPORT_DATA_SIZE, report_size - i);
		...
		rmi_read_block(.., f54->report_data + i, size);
	}

report_data is allocated once at probe from F54's own electrode counts (array3_size(f54->num_tx_electrodes, f54->num_rx_electrodes, sizeof(u16))), but rmi_f54_get_report_size() computes the size from drv_data->num_*_electrodes when those are set, i.e. from the F55 function's electrode counts. Both counts come straight from device queries (F54 and F55 each report up to 255 electrodes) and nothing constrains the F55 counts to the F54 ones.

A malicious or malfunctioning RMI4 device that reports larger F55 electrode counts than its F54 counts makes report_size exceed the allocation, so the read loop writes past report_data (and the V4L2 dequeue memcpy() then reads past it). On conforming hardware the F55 configured electrodes are a subset of the F54 physical electrodes, so report_size never exceeds the buffer and well-behaved devices are unaffected.

Record the allocation size and reject a report that does not fit, mirroring the existing zero-size check.

Fixes c762cc68b6a1 ("Input: synaptics-rmi4 - propagate correct number of rx and tx electrodes to F54")
Assisted-by Antigravity:gemini-3.5-flash
---
 drivers/input/rmi4/rmi_f54.c |   12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)
drivers/input/rmi4/rmi_f54.c +10 -2
@@ -104,6 +104,7 @@ struct f54_data {
 
 	enum rmi_f54_report_type report_type;
 	u8 *report_data;
+	size_t max_report_size;
 	int report_size;
 
 	bool is_busy;
@@ -550,6 +551,13 @@ static void rmi_f54_work(struct work_str
 		goto out;     /* retry won't help */
 	}
 
+	if (report_size > f54->max_report_size) {
+		dev_err(&fn->dev, "Report size %d exceeds buffer size %zu\n",
+			report_size, f54->max_report_size);
+		error = -EINVAL;
+		goto out;
+	}
+
 	/*
 	 * Need to check if command has completed.
 	 * If not try again later.
@@ -680,8 +688,8 @@ static int rmi_f54_probe(struct rmi_func
 
 	rx = f54->num_rx_electrodes;
 	tx = f54->num_tx_electrodes;
-	f54->report_data = devm_kzalloc(&fn->dev,
-					array3_size(tx, rx, sizeof(u16)),
+	f54->max_report_size = array3_size(tx, rx, sizeof(u16));
+	f54->report_data = devm_kzalloc(&fn->dev, f54->max_report_size,
 					GFP_KERNEL);
 	if (f54->report_data == NULL)
 		return -ENOMEM;
author maintainer
#31 lore raw

PATCH 31/166 Input: synaptics-rmi4 - block s_input when F54 queue is busy

In reply to Greg Kroah-Hartman

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

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

From: Dmitry Torokhov <[email protected]>

commit fbfd76746adc16d64be29ff113f673b70bc3f5c2 upstream.

Changing the input (diagnostic report type) mid-stream changes the report size. Since V4L2 buffers are allocated based on the size at stream start, changing the input while streaming could lead to a heap buffer overflow if the new size is larger than the allocated buffers.

Prevent this by blocking VIDIOC_S_INPUT with -EBUSY if the V4L2 queue is busy (streaming).

Fixes 3a762dbd5347 ("[media] Input: synaptics-rmi4 - add support for F54 diagnostics")
Assisted-by Antigravity:gemini-3.5-flash
---
 drivers/input/rmi4/rmi_f54.c |    7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)
drivers/input/rmi4/rmi_f54.c +6 -1
@@ -447,7 +447,12 @@ static int rmi_f54_set_input(struct f54_
 
 static int rmi_f54_vidioc_s_input(struct file *file, void *priv, unsigned int i)
 {
-	return rmi_f54_set_input(video_drvdata(file), i);
+	struct f54_data *f54 = video_drvdata(file);
+
+	if (vb2_is_busy(&f54->queue))
+		return -EBUSY;
+
+	return rmi_f54_set_input(f54, i);
 }
 
 static int rmi_f54_vidioc_g_input(struct file *file, void *priv,
author maintainer
#32 lore raw

PATCH 32/166 Input: synaptics-rmi4 - propagate F54 worker errors to V4L2 queue

In reply to Greg Kroah-Hartman

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

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

From: Dmitry Torokhov <[email protected]>

commit 8786d74bf50e6797b6f655eb381ef6b25451161f upstream.

Previously, rmi_f54_buffer_queue() waited for the worker thread to finish but ignored whether it succeeded. If the worker failed (e.g., due to a timeout or register read failure), the queue thread would silently return success, delivering stale or uninitialized memory to userspace.

Add a 'report_error' field to struct f54_data to store the worker's exit status. Check this field in rmi_f54_buffer_queue() after the worker finishes, and mark the buffer as VB2_BUF_STATE_ERROR if an error occurred.

Fixes 3a762dbd5347 ("[media] Input: synaptics-rmi4 - add support for F54 diagnostics")
Reported-by [email protected]
Assisted-by Antigravity:gemini-3.5-flash
---
 drivers/input/rmi4/rmi_f54.c |    8 ++++++++
 1 file changed, 8 insertions(+)
drivers/input/rmi4/rmi_f54.c +8 -0
@@ -106,6 +106,7 @@ struct f54_data {
 	u8 *report_data;
 	size_t max_report_size;
 	int report_size;
+	int report_error;
 
 	bool is_busy;
 	struct mutex status_mutex;
@@ -340,6 +341,12 @@ static void rmi_f54_buffer_queue(struct
 		mutex_lock(&f54->data_mutex);
 	}
 
+	if (f54->report_error) {
+		dev_err(&f54->fn->dev, "Error acquiring report: %d\n", f54->report_error);
+		state = VB2_BUF_STATE_ERROR;
+		goto data_done;
+	}
+
 	ptr = vb2_plane_vaddr(vb, 0);
 	if (!ptr) {
 		dev_err(&f54->fn->dev, "Error acquiring frame ptr\n");
@@ -612,6 +619,7 @@ out:
 		report_size = 0;
 
 	f54->report_size = report_size;
+	f54->report_error = error;
 
 	if (report_size == 0 && !error) {
 		queue_delayed_work(f54->workqueue, &f54->work,
author maintainer
#33 lore raw

PATCH 33/166 Input: hynitron_cstxxx - validate touch count and finger IDs

In reply to Greg Kroah-Hartman

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

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

From: Jianing Li <[email protected]>

commit 27f380ef0e1d3de3cde114e02d33f9320ce3a5a6 upstream.

The driver allocates max_touch_num input slots, which are indexed from zero through max_touch_num - 1. The current check allows a finger ID equal to max_touch_num to reach cst3xx_report_contact(). While the input core ignores out-of-range slot indices, reporting touch data without a valid slot change corrupts the touch state of the previously active slot.

The touch count is read from the controller's report and is used to index the fixed-size report buffer without first checking its range. Reject counts larger than the supported number of touch slots before checking the trailing byte or parsing touch data.

Reject finger IDs equal to or greater than max_touch_num, and return immediately when an invalid finger ID is encountered so that corrupt touch frames are discarded instead of reporting partial contact state.

The V821 Avaota F1 board configures the vendor driver with one touch slot, so finger ID 1 is already invalid on that device.

Fixes 66603243f528 ("Input: add driver for Hynitron cstxxx touchscreens")
Assisted-by Antigravity:gemini-3.6-flash
---
 drivers/input/touchscreen/hynitron_cstxxx.c |   13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)
drivers/input/touchscreen/hynitron_cstxxx.c +10 -3
@@ -313,6 +313,12 @@ static void cst3xx_touch_report(struct i
 		return;
 
 	touch_cnt = buf[5] & CST3XX_TOUCH_COUNT_MASK;
+	if (touch_cnt > ts_data->chip->max_touch_num) {
+		dev_err(&client->dev, "cst3xx invalid touch count (%d vs %d max)\n",
+			touch_cnt, ts_data->chip->max_touch_num);
+		return;
+	}
+
 	/*
 	 * Check the check bit of the last touch slot. The check bit is
 	 * always present after touch point 1 for valid data, and then
@@ -335,9 +341,10 @@ static void cst3xx_touch_report(struct i
 		finger_id = (buf[idx] >> 4) & 0x0f;
 
 		/* Sanity check we don't have more fingers than we expect */
-		if (ts_data->chip->max_touch_num < finger_id) {
-			dev_err(&client->dev, "cst3xx touch read failure\n");
-			break;
+		if (finger_id >= ts_data->chip->max_touch_num) {
+			dev_err(&client->dev,
+				"cst3xx invalid finger id %d\n", finger_id);
+			return;
 		}
 
 		/* sw value of 0 means no touch, 0x03 means touch */
author maintainer
#34 lore raw

PATCH 34/166 crypto: qce - fix error path in devm_qce_register_algs

In reply to Greg Kroah-Hartman

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

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

From: Thorsten Blum <[email protected]>

commit 9c75402286409f5e1a75e4a445555c84066f89db upstream.

If ops->register_algs() fails, the error path repeatedly calls the same ops->unregister_algs() from the failed registration. Use the loop index to unregister the previously registered algorithms instead.

Fixes e80cf84b6087 ("crypto: qce - unregister previously registered algos in error path")
---
 drivers/crypto/qce/core.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/crypto/qce/core.c +1 -1
@@ -58,7 +58,7 @@ static int qce_register_algs(struct qce_
 		ret = ops->register_algs(qce);
 		if (ret) {
 			for (j = i - 1; j >= 0; j--)
-				ops->unregister_algs(qce);
+				qce_ops[j]->unregister_algs(qce);
 			return ret;
 		}
 	}
author maintainer
#35 lore raw

PATCH 35/166 libceph: fix multiple unsafe decodes in decode_locker()

In reply to Greg Kroah-Hartman

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

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

From: Pavitra Jha <[email protected]>

commit 437b6551cfcc235eea1d735a874f9d421f555e17 upstream.

decode_locker() in cls_lock_client.c contains three unsafe decode operations that allow a malicious or compromised OSD to trigger slab-out-of-bounds reads:

1. ceph_decode_copy() at the locker_id_t name field has no preceding bounds check. With p == end after ceph_start_decoding() accepts struct_len=0, this reads sizeof(ceph_entity_name) = 9 bytes past the validated buffer boundary.

2. *p += sizeof(struct ceph_timespec) after the locker_info_t header is an unchecked pointer advance. A malicious OSD can position p past end, causing all subsequent _safe checks to pass against a bogus boundary.

3. len = ceph_decode_32(p) has no preceding bounds check, and the immediately following *p += len is uncapped. A malicious OSD can send len=0xffffffff, advancing p gigabytes past end and escaping the decode window entirely.

Fix all three by replacing bare operations with their safe variants:

  ceph_decode_copy   -> ceph_decode_copy_safe
  *p += sizeof(...)  -> ceph_decode_skip_n
  ceph_decode_32(p)  -> ceph_decode_32_safe
  *p += len          -> ceph_decode_skip_n

A new label is added to return -EINVAL on any bounds violation. -EINVAL is appropriate here: the data received from the OSD is structurally malformed, which is an invalid argument to the decode contract regardless of whether the caller or the wire is at fault.

Attacker model: a malicious or compromised OSD in a multi-tenant Ceph deployment can trigger this against any kernel client that issues the lock.get_info class method (e.g. during RBD exclusive lock acquisition) without any further privileges beyond OSD session establishment.

[ idryomov: use ceph_decode_skip_string() to skip description, trim
changelog ]

Fixes d4ed4a530562 ("libceph: support for lock.lock_info")
---
 net/ceph/cls_lock_client.c |   13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)
net/ceph/cls_lock_client.c +9 -4
@@ -259,7 +259,8 @@ static int decode_locker(void **p, void
 	if (ret)
 		return ret;
 
-	ceph_decode_copy(p, &locker->id.name, sizeof(locker->id.name));
+	ceph_decode_copy_safe(p, end, &locker->id.name,
+			      sizeof(locker->id.name), bad);
 	s = ceph_extract_encoded_string(p, end, NULL, GFP_NOIO);
 	if (IS_ERR(s))
 		return PTR_ERR(s);
@@ -270,19 +271,23 @@ static int decode_locker(void **p, void
 	if (ret)
 		return ret;
 
-	*p += sizeof(struct ceph_timespec); /* skip expiration */
+	/* skip expiration */
+	ceph_decode_skip_n(p, end, sizeof(struct ceph_timespec), bad);
 
 	ret = ceph_decode_entity_addr(p, end, &locker->info.addr);
 	if (ret)
 		return ret;
 
-	len = ceph_decode_32(p);
-	*p += len; /* skip description */
+	/* skip description */
+	ceph_decode_skip_string(p, end, bad);
 
 	dout("%s %s%llu cookie %s addr %s\n", __func__,
 	     ENTITY_NAME(locker->id.name), locker->id.cookie,
 	     ceph_pr_addr(&locker->info.addr));
 	return 0;
+
+bad:
+	return -EINVAL;
 }
 
 static int decode_lockers(void **p, void *end, u8 *type, char **tag,
author maintainer
#36 lore raw

PATCH 36/166 ftrace: Protect direct_functions in ftrace_find_rec_direct

In reply to Greg Kroah-Hartman

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

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

From: Leon Hwang <[email protected]>

commit 63444b7617c09aeed36282e061c3f80818f2b600 upstream.

Fix accessing the __rcu pointer direct_functions with RCU protection.

---
 kernel/trace/ftrace.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
kernel/trace/ftrace.c +2 -1
@@ -2562,7 +2562,8 @@ unsigned long ftrace_find_rec_direct(uns
 {
 	struct ftrace_func_entry *entry;
 
-	entry = __ftrace_lookup_ip(direct_functions, ip);
+	guard(preempt_notrace)();
+	entry = __ftrace_lookup_ip(rcu_dereference_sched(direct_functions), ip);
 	if (!entry)
 		return 0;
 
author maintainer
#37 lore raw

PATCH 37/166 ftrace: Fix off-by-one fentry site disable in ftrace_free_mem()

In reply to Greg Kroah-Hartman

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

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

From: Josh Poimboeuf <[email protected]>

commit 8b8292d6487c81bd57c2605a9b404b1cf8f1edfb upstream.

When a module's init text is freed, do_init_module() calls ftrace_free_mem() with a half-open [start, end) range. However the ftrace_cmp_recs() comparator treats the upper bound as inclusive, as all its other users do, passing 'ip + size - 1'. So ftrace_free_mem() can delete a record sitting exactly at 'end', which is outside the freed range.

For a kernel without CFI or IBT, the first record of a function is at the function start, which for the first function in a module is also the base of its text allocation. As the module allocator packs its regions, that address is often the 'end' passed by a neighboring module's do_init_module(), causing the first function's ftrace location to get disabled, preventing an attempt to livepatch it:

livepatch: failed to find location for function 'pcspkr_probe'

Convert the exclusive end to the inclusive 'end - 1' the comparator expects, and return early for an empty range to avoid the subtraction from underflowing when the init text size is zero.

---
 kernel/trace/ftrace.c |    6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
kernel/trace/ftrace.c +5 -1
@@ -7250,7 +7250,8 @@ static void add_to_clear_hash_list(struc
 void ftrace_free_mem(struct module *mod, void *start_ptr, void *end_ptr)
 {
 	unsigned long start = (unsigned long)(start_ptr);
-	unsigned long end = (unsigned long)(end_ptr);
+	/* end is inclusive and end_ptr is exclusive */
+	unsigned long end = (unsigned long)(end_ptr) - 1;
 	struct ftrace_page **last_pg = &ftrace_pages_start;
 	struct ftrace_page *tmp_page = NULL;
 	struct ftrace_page *pg;
@@ -7260,6 +7261,9 @@ void ftrace_free_mem(struct module *mod,
 	struct ftrace_init_func *func, *func_next;
 	LIST_HEAD(clear_hash);
 
+	if (start_ptr >= end_ptr)
+		return;
+
 	key.ip = start;
 	key.flags = end;	/* overload flags, as it is unsigned long */
 
author maintainer
#38 lore raw

PATCH 38/166 openrisc: signal: do not restore privileged SR bits on sigreturn

In reply to Greg Kroah-Hartman

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

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

From: Ali Ahmet Memis <[email protected]>

commit 32ef1b30ad736519f7a207bcc2986f3d4129d972 upstream.

restore_sigcontext() copies the whole supervision register (SR) from the signal frame and only clears SPR_SR_SM before the value is reloaded into the hardware SR (through ESR and l.rfe) on the return to user space. All other SR bits are left under user control.

An unprivileged task can thus return from a signal handler through a crafted sigframe that clears SPR_SR_DME. With the data MMU disabled the CPU performs no translation or protection on data accesses, so the task gains read and write access to arbitrary physical memory, a local privilege escalation. SPR_SR_IME, SPR_SR_SUMRA, SPR_SR_LEE, SPR_SR_EPH and the cache-enable bits are exposed the same way. The ptrace GPR regset already refuses any change to SR for exactly this reason.

Restore only the arithmetic flag bits (F, CY, OV) from the signal frame and take every privileged control bit from the SR the kernel saved on signal entry.

Verified with qemu-system-or1k -M or1k-sim: before this change an unprivileged PoC clears SPR_SR_DME in rt_sigreturn and writes a marker to physical address 0x03000000 (beyond the kernel's mem=32M); afterwards the same PoC receives SIGSEGV and physical memory is unchanged.

Fixes ac689eb7f9d4 ("OpenRISC: Signal handling")
---
 arch/openrisc/include/asm/processor.h |    2 ++
 arch/openrisc/kernel/signal.c         |    5 +++--
 2 files changed, 5 insertions(+), 2 deletions(-)
arch/openrisc/include/asm/processor.h +2 -0
@@ -26,6 +26,8 @@
 		   | SPR_SR_DCE | SPR_SR_SM)
 #define USER_SR   (SPR_SR_DME | SPR_SR_IME | SPR_SR_ICE \
 		   | SPR_SR_DCE | SPR_SR_IEE | SPR_SR_TEE)
+/* SR bits user space may change via sigreturn, the rest stay kernel owned */
+#define SPR_SR_USER_MASK  (SPR_SR_F | SPR_SR_CY | SPR_SR_OV)
 
 /*
  * User space process size. This is hardcoded into a few places,
arch/openrisc/kernel/signal.c +3 -2
@@ -42,6 +42,7 @@ asmlinkage int do_work_pending(struct pt
 static int restore_sigcontext(struct pt_regs *regs,
 			      struct sigcontext __user *sc)
 {
+	unsigned long old_sr = regs->sr;
 	int err = 0;
 
 	/* Always make any pending restarted system calls return -EINTR */
@@ -57,8 +58,8 @@ static int restore_sigcontext(struct pt_
 	err |= __copy_from_user(&regs->sr, &sc->regs.sr, sizeof(unsigned long));
 	err |= __copy_from_user(&regs->fpcsr, &sc->fpcsr, sizeof(unsigned long));
 
-	/* make sure the SM-bit is cleared so user-mode cannot fool us */
-	regs->sr &= ~SPR_SR_SM;
+	/* keep the privileged SR bits kernel owned, restore only user flags */
+	regs->sr = (old_sr & ~SPR_SR_USER_MASK) | (regs->sr & SPR_SR_USER_MASK);
 
 	regs->orig_gpr11 = -1;	/* Avoid syscall restart checks */
 
author maintainer
#39 lore raw

PATCH 39/166 Input: sur40 - fix input device registration ordering

In reply to Greg Kroah-Hartman

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

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

From: Dmitry Torokhov <[email protected]>

commit 9da976eb649c9e2f588a4499410e4d8af687925f upstream.

In sur40_probe(), input_register_device() was previously called early before the V4L2 video device and vb2_queue components were fully initialized. If userspace opened the input device immediately upon registration, sur40_open() would trigger and start the sur40_poll() worker thread. This worker thread invokes sur40_process_video() and accesses the uninitialized vb2_queue structure, leading to a data race and potential system crash.

Furthermore, if V4L2 or video registration failed after input_register_device() succeeded, the error path fell through to calling input_free_device() on a successfully registered device instead of input_unregister_device(), corrupting input core state.

Move input_register_device() to the very end of sur40_probe(). This ensures the V4L2 and video queue structures are fully initialized before polling can start, and naturally resolves the error path bug since input_free_device() is now only called when input registration has not yet occurred.

To maintain strict LIFO (Last-In, First-Out) teardown ordering, also move input_unregister_device() to the very beginning of sur40_disconnect(). This guarantees that the input polling worker thread is stopped before V4L2 video components or control handlers are unregistered.

---
 drivers/input/touchscreen/sur40.c |   21 +++++++++++----------
 1 file changed, 11 insertions(+), 10 deletions(-)
drivers/input/touchscreen/sur40.c +11 -10
@@ -725,21 +725,13 @@ static int sur40_probe(struct usb_interf
 		goto err_free_input;
 	}
 
-	/* register the polled input device */
-	error = input_register_device(input);
-	if (error) {
-		dev_err(&interface->dev,
-			"Unable to register polled input device.");
-		goto err_free_buffer;
-	}
-
 	/* register the video master device */
 	snprintf(sur40->v4l2.name, sizeof(sur40->v4l2.name), "%s", DRIVER_LONG);
 	error = v4l2_device_register(sur40->dev, &sur40->v4l2);
 	if (error) {
 		dev_err(&interface->dev,
 			"Unable to register video master device.");
-		goto err_unreg_v4l2;
+		goto err_free_buffer;
 	}
 
 	/* initialize the lock and subdevice */
@@ -798,6 +790,14 @@ static int sur40_probe(struct usb_interf
 		goto err_unreg_video;
 	}
 
+	/* register the polled input device */
+	error = input_register_device(input);
+	if (error) {
+		dev_err(&interface->dev,
+			"Unable to register polled input device.");
+		goto err_unreg_video;
+	}
+
 	/* we can register the device now, as it is ready */
 	usb_set_intfdata(interface, sur40);
 	dev_dbg(&interface->dev, "%s is now attached\n", DRIVER_DESC);
@@ -823,11 +823,12 @@ static void sur40_disconnect(struct usb_
 {
 	struct sur40_state *sur40 = usb_get_intfdata(interface);
 
+	input_unregister_device(sur40->input);
+
 	v4l2_ctrl_handler_free(&sur40->hdl);
 	video_unregister_device(&sur40->vdev);
 	v4l2_device_unregister(&sur40->v4l2);
 
-	input_unregister_device(sur40->input);
 	kfree(sur40->bulk_in_buffer);
 	kfree(sur40);
 
author maintainer
#40 lore raw

PATCH 40/166 Input: sur40 - fix V4L error path cleanup

In reply to Greg Kroah-Hartman

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

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

From: Dmitry Torokhov <[email protected]>

commit 062dc4693e2c10d77de06f61e6f3faf37c0a8383 upstream.

In sur40_probe(), if video_register_device() fails, the error path jumps to err_unreg_video. This incorrectly attempts to unregister a video device that was never successfully registered, and fails to free the V4L2 control handler (v4l2_ctrl_handler_free) that was initialized immediately prior.

Fix this by introducing an err_free_ctrl label to properly free the V4L2 control handler and bypass video_unregister_device() when video device registration fails.

---
 drivers/input/touchscreen/sur40.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/input/touchscreen/sur40.c +3 -1
@@ -787,7 +787,7 @@ static int sur40_probe(struct usb_interf
 	if (error) {
 		dev_err(&interface->dev,
 			"Unable to register video subdevice.");
-		goto err_unreg_video;
+		goto err_free_ctrl;
 	}
 
 	/* register the polled input device */
@@ -806,6 +806,8 @@ static int sur40_probe(struct usb_interf
 
 err_unreg_video:
 	video_unregister_device(&sur40->vdev);
+err_free_ctrl:
+	v4l2_ctrl_handler_free(&sur40->hdl);
 err_unreg_v4l2:
 	v4l2_device_unregister(&sur40->v4l2);
 err_free_buffer:
author maintainer
#41 lore raw

PATCH 41/166 libceph: Avoid using invalid osd indices from primary_temp

In reply to Greg Kroah-Hartman

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

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

From: Raphael Zimmer <[email protected]>

commit 3660b98d1204b419f6a77e9a295f148dcf38d042 upstream.

A corrupted osdmap received from a Ceph monitor or OSD may contain osd indices in its pg_temp, primary_temp, pg_upmap, and pg_upmap_items parts that don't exist, i.e., that are greater than max_osd or smaller than CEPH_HOMELESS_OSD (-1). These indices are used to create the up and acting set in ceph_pg_to_up_acting_osds(), called from calc_target(). While most of these osd indices are checked, the one from primary_temp is not. Subsequently, this may lead to calc_target() returning this (potentially invalid) index as target osd for a (linger) request. Because the osd_state, osd_weight, and osd_addr arrays only contain max_osd entries (with indices 0 to max_osd -1), this leads to out-of-bounds accesses when trying to read values from these arrays.

This patch fixes the issue by adding a check to get_temp_osds(), so that only valid osd indices from primary_temp are used, and it falls back to using the primary from pg_temp or the up set if it is invalid.

[ idryomov: changelog ]

Fixes 5e8d4d36bf23 ("libceph: add support for primary_temp mappings")
---
 net/ceph/osdmap.c |    5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
net/ceph/osdmap.c +3 -2
@@ -2811,9 +2811,10 @@ static void get_temp_osds(struct ceph_os
 		}
 	}
 
-	/* primary_temp? */
+	/* primary_temp? (shouldn't ever be a nonexistent or down OSD) */
 	pg = lookup_pg_mapping(&osdmap->primary_temp, pgid);
-	if (pg)
+	if (pg && !WARN_ON_ONCE(ceph_osd_is_down(osdmap,
+						 pg->primary_temp.osd)))
 		temp->primary = pg->primary_temp.osd;
 }
 
author maintainer
#42 lore raw

PATCH 42/166 ceph: fix MDS random selection readiness predicate

In reply to Greg Kroah-Hartman

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

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

From: Yiming Zhu <[email protected]>

commit 2c11c4bfdb7bd2808b3b3ac228e1f2d9bcf25457 upstream.

CEPH_MDS_IS_READY() is parsed so that the ternary expression can return true for an MDS entry with state 0 when it is not laggy. This allows the random selector to choose a down/DNE rank.

Group the ternary expression under the state check so zero-state ranks are not treated as ready.

Fixes b38c9eb4757d ("ceph: add possible_max_rank and make the code more readable")
---
 fs/ceph/mdsmap.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/ceph/mdsmap.c +1 -1
@@ -15,7 +15,7 @@
 #include "super.h"
 
 #define CEPH_MDS_IS_READY(i, ignore_laggy) \
-	(m->m_info[i].state > 0 && ignore_laggy ? true : !m->m_info[i].laggy)
+	(m->m_info[i].state > 0 && (ignore_laggy ? true : !m->m_info[i].laggy))
 
 static int __mdsmap_get_random_mds(struct ceph_mdsmap *m, bool ignore_laggy)
 {
author maintainer
#43 lore raw

PATCH 43/166 libceph: tolerate addrvecs with multiple entries of the same type

In reply to Greg Kroah-Hartman

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

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

From: Kefu Chai <[email protected]>

commit 5a87925539acecfe88229bad76ab81bd75a7e3f5 upstream.

ceph_decode_entity_addrvec() rejects any addrvec containing more than one entry that matches the requested msgr type (LEGACY or MSGR2), logging "another match of type N in addrvec" and returning -EINVAL.

Some admin tooling (e.g. pveceph mon create from Proxmox VE) generates addrvecs with multiple same-type entries when public_network lists more than one CIDR: it picks one local IP per subnet and emits both a v2 and a v1 entry for each IP. Monmaps shaped this way cause:

libceph: mon0 (1)10.10.10.15:6789 session established
libceph: another match of type 1 in addrvec
libceph: problem decoding monmap, -22

No Ceph code uses the extra entries: since Nautilus, the userspace messenger (AsyncMessenger) unconditionally picks the first address of the requested type and ignores any subsequent matches.

Match that behavior: use the first matching entry and silently skip any subsequent ones. This is a compatibility fix for existing deployments and does not enable dual-stack or multi-subnet address selection.

[ idryomov: tweak ceph_decode_entity_addrvec() comment ]

Fixes a5cbd5fc22d5 ("libceph, ceph: get and handle cluster maps with addrvecs")
---
 net/ceph/decode.c |   18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)
net/ceph/decode.c +9 -9
@@ -87,8 +87,9 @@ bad:
 EXPORT_SYMBOL(ceph_decode_entity_addr);
 
 /*
- * Return addr of desired type (MSGR2 or LEGACY) or error.
- * Make sure there is only one match.
+ * Return addr of desired type (MSGR2 or LEGACY) or error.  In case of
+ * multiple matches, use the first one for compatibility with userspace
+ * messenger.
  *
  * Assume encoding with MSG_ADDR2.
  */
@@ -121,14 +122,13 @@ int ceph_decode_entity_addrvec(void **p,
 
 		dout("%s i %d addr %s\n", __func__, i, ceph_pr_addr(&tmp_addr));
 		if (tmp_addr.type == my_type) {
-			if (found) {
-				pr_err("another match of type %d in addrvec\n",
-				       le32_to_cpu(my_type));
-				return -EINVAL;
+			if (!found) {
+				memcpy(addr, &tmp_addr, sizeof(*addr));
+				found = true;
+			} else {
+				dout("%s skipping extra match of type %d in addrvec\n",
+				     __func__, le32_to_cpu(my_type));
 			}
-
-			memcpy(addr, &tmp_addr, sizeof(*addr));
-			found = true;
 		}
 	}
 
author maintainer
#44 lore raw

PATCH 44/166 mmc: omap_hsmmc: fix busy_timeout overflow in ns conversion on 32-bit

In reply to Greg Kroah-Hartman

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

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

From: Zhan Xusheng <[email protected]>

commit f64ea900e4bda3055ef24a2c906f8d049cf1c3bd upstream.

omap_hsmmc_prepare_data() converts the command busy timeout to nanoseconds with:

	timeout = req->cmd->busy_timeout * NSEC_PER_MSEC;

busy_timeout is an unsigned int (milliseconds) and timeout is a u64, but NSEC_PER_MSEC is 1000000L. On 32-bit builds the multiplication is performed in 32-bit arithmetic and wraps for busy_timeout values above ~4294 ms, before the result is assigned to the u64.

The driver does not set mmc->max_busy_timeout, so the core does not cap the busy timeout, and commands such as erase or SANITIZE (MMC_SANITIZE_TIMEOUT_MS is 240000 ms) can pass a busy_timeout far larger than 4294 ms. The wrapped, much smaller ns value is then programmed via set_data_timeout(), so the data timeout is set too short and the operation can time out prematurely.

Cast busy_timeout to u64 before the multiplication so the conversion is done in 64-bit arithmetic.

Fixes 8cc9a3e73de1 ("mmc: host: omap_hsmmc: use generic_cmd6_time to program timeout value for CMD6")
---
 drivers/mmc/host/omap_hsmmc.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/mmc/host/omap_hsmmc.c +1 -1
@@ -1358,7 +1358,7 @@ omap_hsmmc_prepare_data(struct omap_hsmm
 	if (req->data == NULL) {
 		OMAP_HSMMC_WRITE(host->base, BLK, 0);
 		if (req->cmd->flags & MMC_RSP_BUSY) {
-			timeout = req->cmd->busy_timeout * NSEC_PER_MSEC;
+			timeout = (u64)req->cmd->busy_timeout * NSEC_PER_MSEC;
 
 			/*
 			 * Set an arbitrary 100ms data timeout for commands with
author maintainer
#45 lore raw

PATCH 45/166 mmc: sdhci: unmap the bounce buffer before device release

In reply to Greg Kroah-Hartman

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

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

From: Myeonghun Pak <[email protected]>

commit 9e9f561269dff35e6f84ed21776ec37fd6360b03 upstream.

sdhci_allocate_bounce_buffer() allocates its buffer with devm_kmalloc() but maps it with dma_map_single(). The buffer is therefore released by devres without the streaming DMA mapping being unmapped.

Register a managed action after dma_map_single() succeeds so the mapping is removed before devres releases the buffer. The action is registered only for buffers allocated and mapped by the SDHCI core, leaving buffers provided by host drivers under their existing ownership.

Fixes bd9b902798ab ("mmc: sdhci: Implement an SDHCI-specific bounce buffer")
---
 drivers/mmc/host/sdhci.c |   16 ++++++++++++++++
 1 file changed, 16 insertions(+)
drivers/mmc/host/sdhci.c +16 -0
@@ -4152,6 +4152,14 @@ void __sdhci_read_caps(struct sdhci_host
 }
 EXPORT_SYMBOL_GPL(__sdhci_read_caps);
 
+static void sdhci_unmap_bounce_buffer(void *data)
+{
+	struct sdhci_host *host = data;
+
+	dma_unmap_single(mmc_dev(host->mmc), host->bounce_addr,
+			 host->bounce_buffer_size, DMA_BIDIRECTIONAL);
+}
+
 static void sdhci_allocate_bounce_buffer(struct sdhci_host *host)
 {
 	struct mmc_host *mmc = host->mmc;
@@ -4206,6 +4214,14 @@ static void sdhci_allocate_bounce_buffer
 	}
 
 	host->bounce_buffer_size = bounce_size;
+	ret = devm_add_action_or_reset(mmc_dev(mmc),
+				       sdhci_unmap_bounce_buffer, host);
+	if (ret) {
+		devm_kfree(mmc_dev(mmc), host->bounce_buffer);
+		host->bounce_buffer = NULL;
+		host->bounce_buffer_size = 0;
+		return;
+	}
 
 	/* Lie about this since we're bouncing */
 	mmc->max_segs = max_blocks;
author maintainer
#46 lore raw

PATCH 46/166 mmc: sdhci: make tuning_err a signed int

In reply to Greg Kroah-Hartman

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

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

From: Haibo Chen <[email protected]>

commit ae31bcc92bb42502bb7c9029e6dc7a824cf6cd14 upstream.

Coverity report INTEGER_OVERFLOW for host->tuning_err.
The tuning_err field in struct sdhci_host is used to store an error code for re-tuning, but it was declared as unsigned int. Several call sites store negative error codes into it and later compare against negative values:

- sdhci.c, sdhci-of-dwcmshc.c and sdhci-pci-gli.c assign it the

    return value of __sdhci_execute_tuning()/__sdhci_execute_tuning_9750(),
    both of which return a signed int (possibly a negative errno);
  - sdhci-of-esdhc.c assigns host->tuning_err = -EAGAIN and later does
    "ret = host->tuning_err; if (ret == -EAGAIN ...)";

- sdhci-of-dwcmshc.c prints it with the %d (signed) conversion.

Storing a negative errno in an unsigned int and reading it back as a signed int only happens to work because of two's-complement, same-width integer conversions. It is misleading and triggers sign-conversion warnings. All users treat the value either as a signed error code or as a boolean (zero / non-zero), so changing the type to a signed int is safe and makes the intent explicit.

Fixes 7d8bb1f46e13 ("mmc: sdhci: add tuning error codes")
Assisted-by Cline:claude-sonnet [read_file, search_files, git]
---
 drivers/mmc/host/sdhci.h |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/mmc/host/sdhci.h +1 -1
@@ -606,7 +606,7 @@ struct sdhci_host {
 
 	unsigned int		tuning_count;	/* Timer count for re-tuning */
 	unsigned int		tuning_mode;	/* Re-tuning mode supported by host */
-	unsigned int		tuning_err;	/* Error code for re-tuning */
+	int			tuning_err;	/* Error code for re-tuning */
 #define SDHCI_TUNING_MODE_1	0
 #define SDHCI_TUNING_MODE_2	1
 #define SDHCI_TUNING_MODE_3	2
author maintainer
#47 lore raw

PATCH 47/166 mmc: atmel-mci: Fix use-after-free in atmci_remove due to race condition

In reply to Greg Kroah-Hartman

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

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

From: Pei Xiao <[email protected]>

commit c125ee35a49a0518521b52b27631eef061b8719a upstream.

In atmci_probe, &host->bh_work is bound with atmci_work_func, and atmci_interrupt, atmci_timeout_timer and atmci_dma_complete can all queue this work on system_bh_wq.

If we remove the module, atmci_remove makes cleanup and the memory allocated for host with devm_kzalloc() is released after the remove callback returns, while the work mentioned above may still be pending or running. The sequence of operations that may lead to a UAF bug is as follows:

CPU0 CPU1

                                          | atmci_interrupt
                                          | queue_work(system_bh_wq,
                                          |            &host->bh_work)
atmci_remove                              |
atmci_cleanup_slot(...)                   |
atmci_writel(host, ATMCI_IDR, ~0UL)       |
timer_delete_sync(&host->timer)           |
dma_release_channel(host->dma.chan)       |
free_irq(platform_get_irq(pdev, 0), host) |
                                          | atmci_work_func
                                          | // use host
// devm resources released after          |
// remove returns, host is freed          |
                                          | // use host (use-after-free)

Fix it by canceling the work after all the sources that can schedule it (IRQ handler, timeout timer and DMA completion callback) have been stopped, and before proceeding with the remaining cleanup in atmci_remove.

Fixes 7d2be0749a59 ("atmel-mci: Driver for Atmel on-chip MMC controllers")
Assisted-by Codex:deepseek-v4-flash
---
 drivers/mmc/host/atmel-mci.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/mmc/host/atmel-mci.c +2 -0
@@ -2658,6 +2658,8 @@ static void atmci_remove(struct platform
 
 	free_irq(platform_get_irq(pdev, 0), host);
 
+	cancel_work_sync(&host->bh_work);
+
 	clk_disable_unprepare(host->mck);
 
 	pm_runtime_disable(&pdev->dev);
author maintainer
#48 lore raw

PATCH 48/166 drm/radeon: fix autosuspend cleanup during teardown

In reply to Greg Kroah-Hartman

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

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

From: Guangshuo Li <[email protected]>

commit 587be7a17358ef8c0106775fcedae5a7bef50735 upstream.

radeon_driver_load_kms() calls pm_runtime_use_autosuspend() for PX devices, but radeon_driver_unload_kms() does not call the matching pm_runtime_dont_use_autosuspend() during teardown.

If the autosuspend delay is set to a negative value while autosuspend is enabled, the runtime PM core increments usage_count to prevent runtime suspend. Without calling pm_runtime_dont_use_autosuspend() during teardown, this reference is not dropped.

The documentation for pm_runtime_use_autosuspend() also notes that it is important to undo it with pm_runtime_dont_use_autosuspend() at driver exit time, unless runtime PM was initially enabled with devm_pm_runtime_enable().

Add the missing pm_runtime_dont_use_autosuspend() call to the driver unload path.

This issue was found by manual code inspection.

Fixes 10ebc0bc0934 ("drm/radeon: add runtime PM support (v2)")
(cherry picked from commit 0fdc1ff82ea14844c22795e9e0813c3ca03235e1)
---
 drivers/gpu/drm/radeon/radeon_kms.c |    1 +
 1 file changed, 1 insertion(+)
drivers/gpu/drm/radeon/radeon_kms.c +1 -0
@@ -71,6 +71,7 @@ void radeon_driver_unload_kms(struct drm
 	if (radeon_is_px(dev)) {
 		pm_runtime_get_sync(dev->dev);
 		pm_runtime_forbid(dev->dev);
+		pm_runtime_dont_use_autosuspend(dev->dev);
 	}
 
 	radeon_acpi_fini(rdev);
author maintainer
#49 lore raw

PATCH 49/166 s390/vfio_ccw: Free all memory if cp_init() fails

In reply to Greg Kroah-Hartman

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

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

From: Eric Farman <[email protected]>

commit 74186c2968f8f756ac3226b545b598457c910c75 upstream.

The routine cp_free() is called to unpin/free any memory once an I/O is completed successfully, or if cp_prefetch() fails. But if cp_init() fails, and cp->initialized is not enabled, the same routine cannot be used to free all the memory.

An attempt to address this exists in ccwchain_handle_ccw(), where a single call to ccwchain_free() is made for the currently-processed CCW segment. But this will leak other segments (created as a result of a Transfer in Channel) that had been allocated as part of the same channel program.

Address this by performing the cleanup outside of the recursive ccwchain_handle_ccw()/ccwchain_loop_tic() logic.

Fixes 8b515be512a2 ("vfio-ccw: Fix memory leak and don't call cp_free in cp_init")
---
 drivers/s390/cio/vfio_ccw_cp.c |   22 ++++++++++++++++++----
 1 file changed, 18 insertions(+), 4 deletions(-)
drivers/s390/cio/vfio_ccw_cp.c +18 -4
@@ -452,9 +452,6 @@ static int ccwchain_handle_ccw(u32 cda,
 	/* Loop for tics on this new chain. */
 	ret = ccwchain_loop_tic(chain, cp);
 
-	if (ret)
-		ccwchain_free(chain);
-
 	return ret;
 }
 
@@ -483,6 +480,23 @@ static int ccwchain_loop_tic(struct ccwc
 	return 0;
 }
 
+static int ccwchain_build_ccws(dma32_t cda, struct channel_program *cp)
+{
+	struct ccwchain *chain, *temp;
+	int ret;
+
+	ret = ccwchain_handle_ccw(cda, cp);
+
+	if (ret) {
+		/* Cleanup if an error occurred */
+		list_for_each_entry_safe(chain, temp, &cp->ccwchain_list, next) {
+			ccwchain_free(chain);
+		}
+	}
+
+	return ret;
+}
+
 static int ccwchain_fetch_tic(struct ccw1 *ccw,
 			      struct channel_program *cp)
 {
@@ -729,7 +743,7 @@ int cp_init(struct channel_program *cp,
 	memcpy(&cp->orb, orb, sizeof(*orb));
 
 	/* Build a ccwchain for the first CCW segment */
-	ret = ccwchain_handle_ccw(orb->cmd.cpa, cp);
+	ret = ccwchain_build_ccws(orb->cmd.cpa, cp);
 
 	if (!ret)
 		cp->initialized = true;
author maintainer
#50 lore raw

PATCH 50/166 s390/vfio_ccw: Limit the number of channel program segments

In reply to Greg Kroah-Hartman

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

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

From: Eric Farman <[email protected]>

commit 5405c90d6a47b3014e74ee0618a162449abbbc93 upstream.

The processing of channel programs, and the CCWs within them, is done recursively. As such, there is an arbitrary (but not architectural) limit to the number of CCWs that can exist in a single channel program.

The vfio-ccw logic breaks these channel programs into segments whenever it encounters a Transfer-In-Channel (TIC) CCW, and the combined number of segments count towards the global limit. Impose an equivalent limit to the number of segments until such logic can be made non-recursive.

Fixes 0a19e61e6d4c ("vfio: ccw: introduce channel program interfaces")
---
 drivers/s390/cio/vfio_ccw_cp.c |    6 ++++++
 drivers/s390/cio/vfio_ccw_cp.h |    8 ++++++++
 2 files changed, 14 insertions(+)
drivers/s390/cio/vfio_ccw_cp.c +6 -0
@@ -331,6 +331,7 @@ static struct ccwchain *ccwchain_alloc(s
 		goto out_err;
 
 	list_add_tail(&chain->next, &cp->ccwchain_list);
+	cp->ccwchain_count++;
 
 	return chain;
 
@@ -438,6 +439,10 @@ static int ccwchain_handle_ccw(u32 cda,
 	if (len < 0)
 		return len;
 
+	/* Limit number of chains in a single channel program */
+	if (cp->ccwchain_count >= CCWCHAIN_COUNT_MAX)
+		return -EINVAL;
+
 	/* Need alloc a new chain for this one. */
 	chain = ccwchain_alloc(cp, len);
 	if (!chain)
@@ -739,6 +744,7 @@ int cp_init(struct channel_program *cp,
 			vdev->dev,
 			"Prefetching channel program even though prefetch not specified in ORB");
 
+	cp->ccwchain_count = 0;
 	INIT_LIST_HEAD(&cp->ccwchain_list);
 	memcpy(&cp->orb, orb, sizeof(*orb));
 
drivers/s390/cio/vfio_ccw_cp.h +8 -0
@@ -23,11 +23,18 @@
  */
 #define CCWCHAIN_LEN_MAX	256
 
+/*
+ * Maximum number of chains
+ */
+#define CCWCHAIN_COUNT_MAX	16
+
 /**
  * struct channel_program - manage information for channel program
  * @ccwchain_list: list head of ccwchains
  * @orb: orb for the currently processed ssch request
  * @initialized: whether this instance is actually initialized
+ * @guest_cp: copy of guest channel program
+ * @ccwchain_count: number of channel program segments (linked by TIC)
  *
  * @ccwchain_list is the head of a ccwchain list, that contents the
  * translated result of the guest channel program that pointed out by
@@ -38,6 +45,7 @@ struct channel_program {
 	union orb orb;
 	bool initialized;
 	struct ccw1 *guest_cp;
+	unsigned int ccwchain_count;
 };
 
 int cp_init(struct channel_program *cp, union orb *orb);
author maintainer
#51 lore raw

PATCH 51/166 s390/vfio_ccw: Cancel existing workqueues

In reply to Greg Kroah-Hartman

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

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

From: Eric Farman <[email protected]>

commit 79c60b2c61105368dcc8444eb45847e21734f7c4 upstream.

The initialization of the io_work and crw_work workqueues begs the question of whether they should be un-initialized. Add the corresponding cleanup tags in _release_dev to ensure work isn't dispatched after the private struct is free'd.

Fixes e5f84dbaea59 ("vfio: ccw: return I/O results asynchronously")
Fixes 3f02cb2fd9d2 ("vfio-ccw: Wire up the CRW irq and CRW region")
---
 drivers/s390/cio/vfio_ccw_ops.c |   15 +++++++++++++++
 1 file changed, 15 insertions(+)
drivers/s390/cio/vfio_ccw_ops.c +15 -0
@@ -132,6 +132,13 @@ static void vfio_ccw_mdev_release_dev(st
 		container_of(vdev, struct vfio_ccw_private, vdev);
 	struct vfio_ccw_crw *crw, *temp;
 
+	/*
+	 * Ensure these work items are fully drained, so none can
+	 * fire after being released.
+	 */
+	cancel_work_sync(&private->io_work);
+	cancel_work_sync(&private->crw_work);
+
 	list_for_each_entry_safe(crw, temp, &private->crw, next) {
 		list_del(&crw->next);
 		kfree(crw);
@@ -203,6 +210,14 @@ static void vfio_ccw_mdev_close_device(s
 		container_of(vdev, struct vfio_ccw_private, vdev);
 
 	vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_CLOSE);
+
+	/*
+	 * Ensure these work items are drained, in the event the
+	 * device is re-opened instead of released.
+	 */
+	cancel_work_sync(&private->io_work);
+	cancel_work_sync(&private->crw_work);
+
 	vfio_ccw_unregister_dev_regions(private);
 }
 
author maintainer
#52 lore raw

PATCH 52/166 s390/vfio_ccw: Ensure index for read/write regions are within range

In reply to Greg Kroah-Hartman

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

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

From: Eric Farman <[email protected]>

commit 9f5f9a78fedc45bc29d6a0a64e3a3472361afae5 upstream.

The introduction of the capability chain rightly clamped the region indexes to the range of the capabilities itself, but neglected to do so for the existing read/write regions which should also be enforced.

Fixes db8e5d17ac03 ("vfio-ccw: add capabilities chain")
Cc Cornelia Huck <[email protected]>
---
 drivers/s390/cio/vfio_ccw_async.c |   16 ++++++++++++++++
 drivers/s390/cio/vfio_ccw_chp.c   |   15 +++++++++++++++
 drivers/s390/cio/vfio_ccw_ops.c   |    7 +++----
 3 files changed, 34 insertions(+), 4 deletions(-)
drivers/s390/cio/vfio_ccw_async.c +16 -0
@@ -8,6 +8,7 @@
  */
 
 #include <linux/vfio.h>
+#include <linux/nospec.h>
 
 #include "vfio_ccw_private.h"
 
@@ -24,11 +25,20 @@ static ssize_t vfio_ccw_async_region_rea
 		return -EINVAL;
 
 	mutex_lock(&private->io_mutex);
+
+	if (i >= private->num_regions) {
+		ret = -EINVAL;
+		goto out_unlock;
+	}
+
+	i = array_index_nospec(i, private->num_regions);
 	region = private->region[i].data;
 	if (copy_to_user(buf, (void *)region + pos, count))
 		ret = -EFAULT;
 	else
 		ret = count;
+
+out_unlock:
 	mutex_unlock(&private->io_mutex);
 	return ret;
 }
@@ -48,6 +58,12 @@ static ssize_t vfio_ccw_async_region_wri
 	if (!mutex_trylock(&private->io_mutex))
 		return -EAGAIN;
 
+	if (i >= private->num_regions) {
+		ret = -EINVAL;
+		goto out_unlock;
+	}
+
+	i = array_index_nospec(i, private->num_regions);
 	region = private->region[i].data;
 	if (copy_from_user((void *)region + pos, buf, count)) {
 		ret = -EFAULT;
drivers/s390/cio/vfio_ccw_chp.c +15 -0
@@ -9,6 +9,7 @@
  */
 
 #include <linux/slab.h>
+#include <linux/nospec.h>
 #include <linux/vfio.h>
 #include "vfio_ccw_private.h"
 
@@ -26,6 +27,13 @@ static ssize_t vfio_ccw_schib_region_rea
 		return -EINVAL;
 
 	mutex_lock(&private->io_mutex);
+
+	if (i >= private->num_regions) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	i = array_index_nospec(i, private->num_regions);
 	region = private->region[i].data;
 
 	if (cio_update_schib(sch)) {
@@ -97,6 +105,12 @@ static ssize_t vfio_ccw_crw_region_read(
 		list_del(&crw->next);
 
 	mutex_lock(&private->io_mutex);
+	if (i >= private->num_regions) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	i = array_index_nospec(i, private->num_regions);
 	region = private->region[i].data;
 
 	if (crw)
@@ -109,6 +123,7 @@ static ssize_t vfio_ccw_crw_region_read(
 
 	region->crw = 0;
 
+out:
 	mutex_unlock(&private->io_mutex);
 
 	kfree(crw);
drivers/s390/cio/vfio_ccw_ops.c +3 -4
@@ -259,6 +259,7 @@ static ssize_t vfio_ccw_mdev_read(struct
 		return vfio_ccw_mdev_read_io_region(private, buf, count, ppos);
 	default:
 		index -= VFIO_CCW_NUM_REGIONS;
+		index = array_index_nospec(index, private->num_regions);
 		return private->region[index].ops->read(private, buf, count,
 							ppos);
 	}
@@ -311,6 +312,7 @@ static ssize_t vfio_ccw_mdev_write(struc
 		return vfio_ccw_mdev_write_io_region(private, buf, count, ppos);
 	default:
 		index -= VFIO_CCW_NUM_REGIONS;
+		index = array_index_nospec(index, private->num_regions);
 		return private->region[index].ops->write(private, buf, count,
 							 ppos);
 	}
@@ -353,11 +355,8 @@ static int vfio_ccw_mdev_get_region_info
 		    VFIO_CCW_NUM_REGIONS + private->num_regions)
 			return -EINVAL;
 
-		info->index = array_index_nospec(info->index,
-						 VFIO_CCW_NUM_REGIONS +
-						 private->num_regions);
-
 		i = info->index - VFIO_CCW_NUM_REGIONS;
+		i = array_index_nospec(i, private->num_regions);
 
 		info->offset = VFIO_CCW_INDEX_TO_OFFSET(info->index);
 		info->size = private->region[i].size;
author maintainer
#53 lore raw

PATCH 53/166 s390/vfio_ccw: Fix out of bounds check on CCW array

In reply to Greg Kroah-Hartman

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

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

From: Eric Farman <[email protected]>

commit a005b7f1a491ffda61bff0fd0f6548f8986fb977 upstream.

The routine ccwchain_calc_length() counts the number of channel command words (CCWs) that are chained together in a single channel program, and rejects anything larger than CCWCHAIN_LEN_MAX (256) CCWs.

The loop itself is "do..while (count < 257)", and while the logic in is_cpa_within_range() correctly adjusts between the 0-index array of CCWs and the count of CCWs starting at 1, this means it would look at a possible 257th CCW before ending the loop and (correctly) returning an error.

Fix this by restructuring the loop to break as soon as 256 CCWs (thus indexes 0-255) are examined, without looking at memory outside the range.

Fixes 0a19e61e6d4c ("vfio: ccw: introduce channel program interfaces")
---
 drivers/s390/cio/vfio_ccw_cp.c |   17 +++++------------
 1 file changed, 5 insertions(+), 12 deletions(-)
drivers/s390/cio/vfio_ccw_cp.c +5 -12
@@ -376,11 +376,9 @@ static void ccwchain_cda_free(struct ccw
 static int ccwchain_calc_length(u64 iova, struct channel_program *cp)
 {
 	struct ccw1 *ccw = cp->guest_cp;
-	int cnt = 0;
-
-	do {
-		cnt++;
+	int cnt;
 
+	for (cnt = 1; cnt <= CCWCHAIN_LEN_MAX; cnt++, ccw++) {
 		/*
 		 * We want to keep counting if the current CCW has the
 		 * command-chaining flag enabled, or if it is a TIC CCW
@@ -390,15 +388,10 @@ static int ccwchain_calc_length(u64 iova
 		 * after the TIC, depending on the results of its operation.
 		 */
 		if (!ccw_is_chain(ccw) && !is_tic_within_range(ccw, iova, cnt))
-			break;
-
-		ccw++;
-	} while (cnt < CCWCHAIN_LEN_MAX + 1);
-
-	if (cnt == CCWCHAIN_LEN_MAX + 1)
-		cnt = -EINVAL;
+			return cnt;
+	}
 
-	return cnt;
+	return -EINVAL;
 }
 
 static int tic_target_chain_exists(struct ccw1 *tic, struct channel_program *cp)
author maintainer
#54 lore raw

PATCH 54/166 s390/vfio_ccw: Move cp cleanup out of not operational

In reply to Greg Kroah-Hartman

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

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

From: Eric Farman <[email protected]>

commit 0c11f61a876ed6fcca53d442ed3f33ea8362a0f9 upstream.

The fsm_notoper() routine is called when the device has been lost, and is (by definition) no longer operational. Since this can happen asynchronously from the normal behavior of the driver, the cleanup may happen when holding other locks in the calling sequence (notably, the cio subchannel lock).

Push the cleanup of the private->cp resources to a workqueue, where it can be done out from under that lock sequence and a future patch can safely manage the locking requirements.

Fixes 204b394a23ad ("vfio/ccw: Move FSM open/close to MDEV open/close")
---
 drivers/s390/cio/vfio_ccw_drv.c     |    9 +++++++++
 drivers/s390/cio/vfio_ccw_fsm.c     |    3 +--
 drivers/s390/cio/vfio_ccw_ops.c     |   13 +++++++++++++
 drivers/s390/cio/vfio_ccw_private.h |    3 +++
 4 files changed, 26 insertions(+), 2 deletions(-)
drivers/s390/cio/vfio_ccw_drv.c +9 -0
@@ -125,6 +125,15 @@ void vfio_ccw_crw_todo(struct work_struc
 		eventfd_signal(private->crw_trigger, 1);
 }
 
+void vfio_ccw_notoper_todo(struct work_struct *work)
+{
+	struct vfio_ccw_private *private;
+
+	private = container_of(work, struct vfio_ccw_private, notoper_work);
+
+	cp_free(&private->cp);
+}
+
 /*
  * Css driver callbacks
  */
drivers/s390/cio/vfio_ccw_fsm.c +1 -2
@@ -170,8 +170,7 @@ static void fsm_notoper(struct vfio_ccw_
 	css_sched_sch_todo(sch, SCH_TODO_UNREG);
 	private->state = VFIO_CCW_STATE_NOT_OPER;
 
-	/* This is usually handled during CLOSE event */
-	cp_free(&private->cp);
+	queue_work(vfio_ccw_work_q, &private->notoper_work);
 }
 
 /*
drivers/s390/cio/vfio_ccw_ops.c +13 -0
@@ -54,6 +54,7 @@ static int vfio_ccw_mdev_init_dev(struct
 	INIT_LIST_HEAD(&private->crw);
 	INIT_WORK(&private->io_work, vfio_ccw_sch_io_todo);
 	INIT_WORK(&private->crw_work, vfio_ccw_crw_todo);
+	INIT_WORK(&private->notoper_work, vfio_ccw_notoper_todo);
 
 	private->cp.guest_cp = kcalloc(CCWCHAIN_LEN_MAX, sizeof(struct ccw1),
 				       GFP_KERNEL);
@@ -135,9 +136,16 @@ static void vfio_ccw_mdev_release_dev(st
 	/*
 	 * Ensure these work items are fully drained, so none can
 	 * fire after being released.
+	 *
+	 * notoper_work should have nothing to do here, because only
+	 * open devices could have channel_program resources in use
+	 * and those would be released during close. Nevertheless,
+	 * call flush here as well to be certain anything that was
+	 * allocated is freed.
 	 */
 	cancel_work_sync(&private->io_work);
 	cancel_work_sync(&private->crw_work);
+	flush_work(&private->notoper_work);
 
 	list_for_each_entry_safe(crw, temp, &private->crw, next) {
 		list_del(&crw->next);
@@ -214,9 +222,14 @@ static void vfio_ccw_mdev_close_device(s
 	/*
 	 * Ensure these work items are drained, in the event the
 	 * device is re-opened instead of released.
+	 *
+	 * notoper_work needs to be given a chance to run if it
+	 * is queued, so any memory associated with the channel
+	 * program can be returned.
 	 */
 	cancel_work_sync(&private->io_work);
 	cancel_work_sync(&private->crw_work);
+	flush_work(&private->notoper_work);
 
 	vfio_ccw_unregister_dev_regions(private);
 }
drivers/s390/cio/vfio_ccw_private.h +3 -0
@@ -102,6 +102,7 @@ struct vfio_ccw_parent {
  * @req_trigger: eventfd ctx for signaling userspace to return device
  * @io_work: work for deferral process of I/O handling
  * @crw_work: work for deferral process of CRW handling
+ * @notoper_work: work for deferred processing in not-operational state
  */
 struct vfio_ccw_private {
 	struct vfio_device vdev;
@@ -125,11 +126,13 @@ struct vfio_ccw_private {
 	struct eventfd_ctx	*req_trigger;
 	struct work_struct	io_work;
 	struct work_struct	crw_work;
+	struct work_struct	notoper_work;
 } __aligned(8);
 
 int vfio_ccw_sch_quiesce(struct subchannel *sch);
 void vfio_ccw_sch_io_todo(struct work_struct *work);
 void vfio_ccw_crw_todo(struct work_struct *work);
+void vfio_ccw_notoper_todo(struct work_struct *work);
 
 extern struct mdev_driver vfio_ccw_mdev_driver;
 
author maintainer
#55 lore raw

PATCH 55/166 s390/vfio_ccw: Selectively expand io_mutex

In reply to Greg Kroah-Hartman

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

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

From: Eric Farman <[email protected]>

commit 34f4feff3e90bd09308fad0974e97113b23b812a upstream.

The io_mutex was defined to serialize the io_regions, but then has also sort of been associated with the I/O themselves because of the close relationship they share.

With the handful of races that are possible, the choices are either to:
A) expand the scope of io_mutex to close these remaining windows, or
B) reduce the scope of io_mutex to just io_region, and introduce a new lock mechanism for the remaining I/O resources

This patch implements A, since B brings with it a lot more interactions that would need to be tracked and kept in a correct hierarchy. It also takes advantage of the workqueue element for cp_free() that now gets called out of fsm_notoper(), which could be invoked out of an interrupt context and thus cannot acquire a mutex itself.

Fixes 4f76617378ee ("vfio-ccw: protect the I/O region")
---
 drivers/s390/cio/vfio_ccw_chp.c     |    2 +-
 drivers/s390/cio/vfio_ccw_cp.c      |    8 +++++++-
 drivers/s390/cio/vfio_ccw_drv.c     |    6 ++++--
 drivers/s390/cio/vfio_ccw_fsm.c     |    5 +++++
 drivers/s390/cio/vfio_ccw_private.h |    3 ++-
 5 files changed, 19 insertions(+), 5 deletions(-)
drivers/s390/cio/vfio_ccw_chp.c +1 -1
@@ -98,13 +98,13 @@ static ssize_t vfio_ccw_crw_region_read(
 	if (pos + count > sizeof(*region))
 		return -EINVAL;
 
+	mutex_lock(&private->io_mutex);
 	crw = list_first_entry_or_null(&private->crw,
 				       struct vfio_ccw_crw, next);
 
 	if (crw)
 		list_del(&crw->next);
 
-	mutex_lock(&private->io_mutex);
 	if (i >= private->num_regions) {
 		ret = -EINVAL;
 		goto out;
drivers/s390/cio/vfio_ccw_cp.c +7 -1
@@ -953,17 +953,23 @@ void cp_update_scsw(struct channel_progr
  */
 bool cp_iova_pinned(struct channel_program *cp, u64 iova, u64 length)
 {
+	struct vfio_ccw_private *private =
+		container_of(cp, struct vfio_ccw_private, cp);
 	struct ccwchain *chain;
 	int i;
 
 	if (!cp->initialized)
 		return false;
 
+	mutex_lock(&private->io_mutex);
 	list_for_each_entry(chain, &cp->ccwchain_list, next) {
 		for (i = 0; i < chain->ch_len; i++)
-			if (page_array_iova_pinned(&chain->ch_pa[i], iova, length))
+			if (page_array_iova_pinned(&chain->ch_pa[i], iova, length)) {
+				mutex_unlock(&private->io_mutex);
 				return true;
+			}
 	}
+	mutex_unlock(&private->io_mutex);
 
 	return false;
 }
drivers/s390/cio/vfio_ccw_drv.c +4 -2
@@ -91,6 +91,7 @@ void vfio_ccw_sch_io_todo(struct work_st
 
 	is_final = !(scsw_actl(&irb->scsw) &
 		     (SCSW_ACTL_DEVACT | SCSW_ACTL_SCHACT));
+	mutex_lock(&private->io_mutex);
 	if (scsw_is_solicited(&irb->scsw)) {
 		cp_update_scsw(&private->cp, &irb->scsw);
 		if (is_final && private->state == VFIO_CCW_STATE_CP_PENDING) {
@@ -98,9 +99,7 @@ void vfio_ccw_sch_io_todo(struct work_st
 			cp_is_finished = true;
 		}
 	}
-	mutex_lock(&private->io_mutex);
 	memcpy(private->io_region->irb_area, irb, sizeof(*irb));
-	mutex_unlock(&private->io_mutex);
 
 	/*
 	 * Reset to IDLE only if processing of a channel program
@@ -110,6 +109,7 @@ void vfio_ccw_sch_io_todo(struct work_st
 	 */
 	if (cp_is_finished)
 		private->state = VFIO_CCW_STATE_IDLE;
+	mutex_unlock(&private->io_mutex);
 
 	if (private->io_trigger)
 		eventfd_signal(private->io_trigger, 1);
@@ -131,7 +131,9 @@ void vfio_ccw_notoper_todo(struct work_s
 
 	private = container_of(work, struct vfio_ccw_private, notoper_work);
 
+	mutex_lock(&private->io_mutex);
 	cp_free(&private->cp);
+	mutex_unlock(&private->io_mutex);
 }
 
 /*
drivers/s390/cio/vfio_ccw_fsm.c +5 -0
@@ -170,6 +170,7 @@ static void fsm_notoper(struct vfio_ccw_
 	css_sched_sch_todo(sch, SCH_TODO_UNREG);
 	private->state = VFIO_CCW_STATE_NOT_OPER;
 
+	/* This routine could be called from IRQ context, so defer */
 	queue_work(vfio_ccw_work_q, &private->notoper_work);
 }
 
@@ -409,7 +410,11 @@ static void fsm_close(struct vfio_ccw_pr
 
 	private->state = VFIO_CCW_STATE_STANDBY;
 	spin_unlock_irq(&sch->lock);
+
+	mutex_lock(&private->io_mutex);
 	cp_free(&private->cp);
+	mutex_unlock(&private->io_mutex);
+
 	return;
 
 err_unlock:
drivers/s390/cio/vfio_ccw_private.h +2 -1
@@ -88,7 +88,8 @@ struct vfio_ccw_parent {
  * @state: internal state of the device
  * @completion: synchronization helper of the I/O completion
  * @io_region: MMIO region to input/output I/O arguments/results
- * @io_mutex: protect against concurrent update of I/O regions
+ * @io_mutex: protect against concurrent update of I/O resources
+ *            and @cp lifecycle
  * @region: additional regions for other subchannel operations
  * @cmd_region: MMIO region for asynchronous I/O commands other than START
  * @schib_region: MMIO region for SCHIB information
author maintainer
#56 lore raw

PATCH 56/166 drm/amdgpu: Reject UVD message with invalid number of h265 refs

In reply to Greg Kroah-Hartman

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

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

From: David Rosca <[email protected]>

commit 9fca434208f1f9ab977feac62df8ebb1cc7ce893 upstream.

Same change as for h264, avoids overflow later when calculating min dpb size.

(cherry picked from commit a4b0720e4f1601f97f59a2be9c1b4b94fa6527d5)
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c |    3 +++
 1 file changed, 3 insertions(+)
drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +3 -0
@@ -795,6 +795,9 @@ static int amdgpu_uvd_cs_msg_decode(stru
 		image_size = ALIGN(image_size, 256);
 
 		num_dpb_buffer = (le32_to_cpu(msg[59]) & 0xff) + 2;
+		if (num_dpb_buffer > 17)
+			return -EINVAL;
+
 		min_dpb_size = image_size * num_dpb_buffer;
 		min_ctx_size = ((width + 255) / 16) * ((height + 255) / 16)
 					   * 16 * num_dpb_buffer + 52 * 1024;
author maintainer
#57 lore raw

PATCH 57/166 drm/amdgpu: validate GEM_CREATE domain combinations

In reply to Greg Kroah-Hartman

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

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

From: Candice Li <[email protected]>

commit 5e9d136ad74df4edec67e502ce267597064d8f86 upstream.

AMDGPU_GEM_CREATE checked domain bits against AMDGPU_GEM_DOMAIN_MASK, but did not validate domain combinations. Userspace could combine CPU|GTT|VRAM with DOORBELL, GDS, GWS, or OA, making amdgpu_bo_placement_from_domain() exceed AMDGPU_BO_MAX_PLACEMENTS and hit BUG_ON().

Allow combinations only within CPU/GTT/VRAM, and require non-CPU/GTT/ VRAM domains to be specified one at a time. Return -EINVAL for invalid combinations in amdgpu_gem_create_ioctl().

v2: Rename helper from amdgpu_gem_domain_valid() to
amdgpu_gem_are_domains_valid() (Christian)

(cherry picked from commit db39852d0c39843cb02048dfb47e4b8c703e9080)
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c |   21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)
drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +21 -0
@@ -273,6 +273,25 @@ static const struct drm_gem_object_funcs
 	.vm_ops = &amdgpu_gem_vm_ops,
 };
 
+static bool amdgpu_gem_are_domains_valid(u32 domains)
+{
+	u32 normal = AMDGPU_GEM_DOMAIN_CPU |
+		     AMDGPU_GEM_DOMAIN_GTT |
+		     AMDGPU_GEM_DOMAIN_VRAM;
+	/* Treat all non CPU/GTT/VRAM domains as special domains. */
+	u32 special = AMDGPU_GEM_DOMAIN_MASK & ~normal;
+	u32 normal_mask = domains & normal;
+	u32 special_mask = domains & special;
+
+	if (!special_mask)
+		return true;
+
+	if (normal_mask)
+		return false;
+
+	return !(special_mask & (special_mask - 1));
+}
+
 /*
  * GEM ioctls.
  */
@@ -308,6 +327,8 @@ int amdgpu_gem_create_ioctl(struct drm_d
 	/* reject invalid gem domains */
 	if (args->in.domains & ~AMDGPU_GEM_DOMAIN_MASK)
 		return -EINVAL;
+	if (!amdgpu_gem_are_domains_valid(args->in.domains))
+		return -EINVAL;
 
 	if (!amdgpu_is_tmz(adev) && (flags & AMDGPU_GEM_CREATE_ENCRYPTED)) {
 		DRM_NOTE_ONCE("Cannot allocate secure buffer since TMZ is disabled\n");
author maintainer
#58 lore raw

PATCH 58/166 drm/amdgpu: Reject UVD message with dimensions above 4096

In reply to Greg Kroah-Hartman

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

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

From: David Rosca <[email protected]>

commit 8c9aebcdd9f46f7a14b98d6ab18574b7a48fbb08 upstream.

Fixes potential overflow in DPB size calculations.

(cherry picked from commit 05e1387d151f71569fbe122d2c89f9db0c21dc10)
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +2 -2
@@ -647,8 +647,8 @@ static int amdgpu_uvd_cs_msg_decode(stru
 	unsigned 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) {
+	/* Reject invalid dimensions */
+	if (width < 16 || height < 16 || width > 4096 || height > 4096) {
 		dev_WARN_ONCE(adev->dev, 1,
 			      "Invalid UVD decoding dimensions (%dx%d)!\n",
 			      width, height);
author maintainer
#59 lore raw

PATCH 59/166 drm/amdgpu: Implement insert_end for VCE 3

In reply to Greg Kroah-Hartman

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

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

From: David Rosca <[email protected]>

commit d5ab4c6a64efef2d143a96df5357f59703cd703d upstream.

After a recent change VCE now hangs when VCE_CMD_END is emitted after a pipeline sync without VM flush.
Implement insert_end to correctly insert only one VCE_CMD_END per job.

Fixes bc639a9eadc7 ("drm/amdgpu: always emit the job vm fence")
(cherry picked from commit 8897ea8c761b856f02061848a7908040a1fe5e68)
---
 drivers/gpu/drm/amd/amdgpu/vce_v3_0.c |   26 ++++++++++++++++++++++----
 1 file changed, 22 insertions(+), 4 deletions(-)
drivers/gpu/drm/amd/amdgpu/vce_v3_0.c +22 -4
@@ -873,6 +873,23 @@ static void vce_v3_0_ring_emit_ib(struct
 	amdgpu_ring_write(ring, ib->length_dw);
 }
 
+static void vce_v3_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr,
+			u64 seq, unsigned flags)
+{
+	WARN_ON(flags & AMDGPU_FENCE_FLAG_64BIT);
+
+	amdgpu_ring_write(ring, VCE_CMD_FENCE);
+	amdgpu_ring_write(ring, addr);
+	amdgpu_ring_write(ring, upper_32_bits(addr));
+	amdgpu_ring_write(ring, seq);
+	amdgpu_ring_write(ring, VCE_CMD_TRAP);
+}
+
+static void vce_v3_0_ring_insert_end(struct amdgpu_ring *ring)
+{
+	amdgpu_ring_write(ring, VCE_CMD_END);
+}
+
 static void vce_v3_0_emit_vm_flush(struct amdgpu_ring *ring,
 				   unsigned int vmid, uint64_t pd_addr)
 {
@@ -882,7 +899,6 @@ static void vce_v3_0_emit_vm_flush(struc
 
 	amdgpu_ring_write(ring, VCE_CMD_FLUSH_TLB);
 	amdgpu_ring_write(ring, vmid);
-	amdgpu_ring_write(ring, VCE_CMD_END);
 }
 
 static void vce_v3_0_emit_pipeline_sync(struct amdgpu_ring *ring)
@@ -952,17 +968,19 @@ static const struct amdgpu_ring_funcs vc
 	.set_wptr = vce_v3_0_ring_set_wptr,
 	.parse_cs = amdgpu_vce_ring_parse_cs_vm,
 	.emit_frame_size =
-		6 + /* vce_v3_0_emit_vm_flush */
+		5 + /* vce_v3_0_emit_vm_flush */
 		4 + /* vce_v3_0_emit_pipeline_sync */
-		6 + 6, /* amdgpu_vce_ring_emit_fence x2 vm fence */
+		5 + 5 + /* vce_v3_0_ring_emit_fence x2 vm fence */
+		1, /* vce_v3_0_ring_insert_end */
 	.emit_ib_size = 5, /* vce_v3_0_ring_emit_ib */
 	.emit_ib = vce_v3_0_ring_emit_ib,
 	.emit_vm_flush = vce_v3_0_emit_vm_flush,
 	.emit_pipeline_sync = vce_v3_0_emit_pipeline_sync,
-	.emit_fence = amdgpu_vce_ring_emit_fence,
+	.emit_fence = vce_v3_0_ring_emit_fence,
 	.test_ring = amdgpu_vce_ring_test_ring,
 	.test_ib = amdgpu_vce_ring_test_ib,
 	.insert_nop = amdgpu_ring_insert_nop,
+	.insert_end = vce_v3_0_ring_insert_end,
 	.pad_ib = amdgpu_ring_generic_pad_ib,
 	.begin_use = amdgpu_vce_ring_begin_use,
 	.end_use = amdgpu_vce_ring_end_use,
author maintainer
#60 lore raw

PATCH 60/166 drm/amdgpu: Fix UVD min buffer sizes

In reply to Greg Kroah-Hartman

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

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

From: David Rosca <[email protected]>

commit 18727670b44753865b81c56a9338c0d7bd102c54 upstream.

Use correct size for message buffer = sizeof(struct ruvd_msg). Add ITSCALING_TABLE_BUFFER size.

(cherry picked from commit 37519d007e4261febbcf35b3045f8344f3145497)
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c |   12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +7 -5
@@ -967,15 +967,16 @@ static int amdgpu_uvd_cs_pass2(struct am
 				  ctx->buf_sizes[cmd]);
 			return -EINVAL;
 		}
+	} else if (cmd == 0x204 || cmd == 0x206) {
+		unsigned int min_size = ctx->buf_sizes[cmd == 0x204 ? 5 : 4];
 
-	} else if (cmd == 0x206) {
-		if ((end - start) < ctx->buf_sizes[4]) {
+		if ((end - start) < min_size) {
 			DRM_ERROR("buffer (%d) to small (%d / %d)!\n", cmd,
 					  (unsigned int)(end - start),
-					  ctx->buf_sizes[4]);
+					  min_size);
 			return -EINVAL;
 		}
-	} else if ((cmd != 0x100) && (cmd != 0x204)) {
+	} else if ((cmd != 0x100)) {
 		DRM_ERROR("invalid UVD command %X!\n", cmd);
 		return -EINVAL;
 	}
@@ -1105,11 +1106,12 @@ int amdgpu_uvd_ring_parse_cs(struct amdg
 {
 	struct amdgpu_uvd_cs_ctx ctx = {};
 	unsigned int buf_sizes[] = {
-		[0x00000000]	=	2048,
+		[0x00000000]	=	3556,
 		[0x00000001]	=	0xFFFFFFFF,
 		[0x00000002]	=	0xFFFFFFFF,
 		[0x00000003]	=	2048,
 		[0x00000004]	=	0xFFFFFFFF,
+		[0x00000005]	=	992,
 	};
 	int r;
 
author maintainer
#61 lore raw

PATCH 61/166 drm/amdgpu: Fix UVD dpb min size calculation for H264

In reply to Greg Kroah-Hartman

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

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

From: David Rosca <[email protected]>

commit 21a8084cd76223a13493237e04d45f5226d7cee6 upstream.

This should use actual number of references from the decode message, instead of maximum derived from level.

(cherry picked from commit 64b525edb7e7bdfcdc77883c5e413804e2396856)
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c |   62 ++------------------------------
 1 file changed, 4 insertions(+), 58 deletions(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +4 -58
@@ -638,11 +638,9 @@ static int amdgpu_uvd_cs_msg_decode(stru
 	unsigned int height = msg[7];
 	unsigned int dpb_size = msg[9];
 	unsigned int pitch = msg[28];
-	unsigned int level = msg[57];
 
 	unsigned int width_in_mb = width / 16;
 	unsigned int height_in_mb = ALIGN(height / 16, 2);
-	unsigned int fs_in_mb = width_in_mb * height_in_mb;
 
 	unsigned int image_size, tmp, min_dpb_size, num_dpb_buffer;
 	unsigned int min_ctx_size = ~0;
@@ -661,35 +659,9 @@ static int amdgpu_uvd_cs_msg_decode(stru
 
 	switch (stream_type) {
 	case 0: /* H264 */
-		switch (level) {
-		case 30:
-			num_dpb_buffer = 8100 / fs_in_mb;
-			break;
-		case 31:
-			num_dpb_buffer = 18000 / fs_in_mb;
-			break;
-		case 32:
-			num_dpb_buffer = 20480 / fs_in_mb;
-			break;
-		case 41:
-			num_dpb_buffer = 32768 / fs_in_mb;
-			break;
-		case 42:
-			num_dpb_buffer = 34816 / fs_in_mb;
-			break;
-		case 50:
-			num_dpb_buffer = 110400 / fs_in_mb;
-			break;
-		case 51:
-			num_dpb_buffer = 184320 / fs_in_mb;
-			break;
-		default:
-			num_dpb_buffer = 184320 / fs_in_mb;
-			break;
-		}
-		num_dpb_buffer++;
+		num_dpb_buffer = ((msg[61] >> 16) & 0xff) + 1;
 		if (num_dpb_buffer > 17)
-			num_dpb_buffer = 17;
+			return -EINVAL;
 
 		/* reference picture buffer */
 		min_dpb_size = image_size * num_dpb_buffer;
@@ -739,35 +711,9 @@ static int amdgpu_uvd_cs_msg_decode(stru
 		break;
 
 	case 7: /* H264 Perf */
-		switch (level) {
-		case 30:
-			num_dpb_buffer = 8100 / fs_in_mb;
-			break;
-		case 31:
-			num_dpb_buffer = 18000 / fs_in_mb;
-			break;
-		case 32:
-			num_dpb_buffer = 20480 / fs_in_mb;
-			break;
-		case 41:
-			num_dpb_buffer = 32768 / fs_in_mb;
-			break;
-		case 42:
-			num_dpb_buffer = 34816 / fs_in_mb;
-			break;
-		case 50:
-			num_dpb_buffer = 110400 / fs_in_mb;
-			break;
-		case 51:
-			num_dpb_buffer = 184320 / fs_in_mb;
-			break;
-		default:
-			num_dpb_buffer = 184320 / fs_in_mb;
-			break;
-		}
-		num_dpb_buffer++;
+		num_dpb_buffer = ((msg[61] >> 16) & 0xff) + 1;
 		if (num_dpb_buffer > 17)
-			num_dpb_buffer = 17;
+			return -EINVAL;
 
 		/* reference picture buffer */
 		min_dpb_size = image_size * num_dpb_buffer;
author maintainer
#62 lore raw

PATCH 62/166 drm/amdgpu: Fix UVD decode image min size calculation

In reply to Greg Kroah-Hartman

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

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

From: David Rosca <[email protected]>

commit b8bb9ba3f101a1b0011f785a577a4a0a38371174 upstream.

This needs to use pitch instead of width. Also reject pitch over 4096 to avoid overflow.

(cherry picked from commit b41c8cb12e202b220353332ab87dc01a11f69304)
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +2 -2
@@ -754,7 +754,7 @@ static int amdgpu_uvd_cs_msg_decode(stru
 		return -EINVAL;
 	}
 
-	if (width > pitch) {
+	if (width > pitch || pitch > 4096) {
 		DRM_ERROR("Invalid UVD decoding target pitch!\n");
 		return -EINVAL;
 	}
@@ -766,7 +766,7 @@ static int amdgpu_uvd_cs_msg_decode(stru
 	}
 
 	buf_sizes[0x1] = dpb_size;
-	buf_sizes[0x2] = image_size;
+	buf_sizes[0x2] = (pitch * height) * 3 / 2;
 	buf_sizes[0x4] = min_ctx_size;
 	/* store image width to adjust nb memory pstate */
 	adev->uvd.decode_image_width = width;
author maintainer
#63 lore raw

PATCH 63/166 drm/amdgpu: disallow multiple FENCE chunks in one submit

In reply to Greg Kroah-Hartman

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

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

From: Junrui Luo <[email protected]>

commit 931cd1d1baeae68e8eb2c23bc1f3d8934dca6241 upstream.

amdgpu_cs_pass1() dispatches on chunk_id once per chunk without rejecting repeated ids. p->uf_bo is a single-slot field, so a submission carrying two AMDGPU_CHUNK_ID_FENCE chunks runs amdgpu_cs_p1_user_fence() twice, and the second run overwrites p->uf_bo with a freshly referenced BO without dropping the reference taken by the first.

amdgpu_cs_parser_fini() only unrefs the final p->uf_bo, so every FENCE chunk but the last leaks a BO reference. The leaked BO outlives handle close and process exit.

Reject duplicate FENCE chunks the same way commit fec5f8e8c6bc ("drm/amdgpu: disallow multiple BO_HANDLES chunks in one submit") did for p->bo_list.

Fixes d38ceaf99ed0 ("drm/amdgpu: add core driver (v4)")
Assisted-by Claude:claude-opus-5
(cherry picked from commit 665b1fc2a1845206408f9a2c6da67101789edb82)
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c |    4 ++++
 1 file changed, 4 insertions(+)
drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +4 -0
@@ -253,6 +253,10 @@ static int amdgpu_cs_pass1(struct amdgpu
 			if (size < sizeof(struct drm_amdgpu_cs_chunk_fence))
 				goto free_partial_kdata;
 
+			/* Only a single user fence is allowed to simplify handling. */
+			if (p->uf_bo)
+				goto free_partial_kdata;
+
 			ret = amdgpu_cs_p1_user_fence(p, p->chunks[i].kdata,
 						      &uf_offset);
 			if (ret)
author maintainer
#64 lore raw

PATCH 64/166 xfs: only check mergeability of bnobt records

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit 97efed1091a50e4dbf31307015138b43a972d2e4 upstream.

In the cntbt (free space by block count) btree, records are not supposed to be in startblock order. Hence the mergeability check is pointless. Remove it, since it does nothing, as LOLLM points out.

Fixes d5784ae82778d9 ("xfs: flag free space btree records that could be merged")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/scrub/alloc.c |    5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
fs/xfs/scrub/alloc.c +3 -2
@@ -124,7 +124,7 @@ xchk_allocbt_rec(
 	const union xfs_btree_rec	*rec)
 {
 	struct xfs_alloc_rec_incore	irec;
-	struct xchk_alloc	*ca = bs->private;
+	struct xchk_alloc		*ca = bs->private;
 
 	xfs_alloc_btrec_to_irec(rec, &irec);
 	if (xfs_alloc_check_irec(bs->cur, &irec) != NULL) {
@@ -132,7 +132,8 @@ xchk_allocbt_rec(
 		return 0;
 	}
 
-	xchk_allocbt_mergeable(bs, ca, &irec);
+	if (bs->sc->sm->sm_type == XFS_SCRUB_TYPE_BNOBT)
+		xchk_allocbt_mergeable(bs, ca, &irec);
 	xchk_allocbt_xref(bs->sc, &irec);
 
 	return 0;
author maintainer
#65 lore raw

PATCH 65/166 xfs: fix ilock leak on error in xfs_dq_get_next_id

In reply to Greg Kroah-Hartman

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

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

From: Long Li <[email protected]>

commit 63320a0f70f66f311f4bccff3af0719c2119f46c upstream.

xfs_dq_get_next_id() takes the quota inode ILOCK before calling xfs_iread_extents(). If xfs_iread_extents() fails, the function returns immediately without releasing the lock, leaking the quota inode ILOCK. This can leave the quota inode locked and cause subsequent quota operations to hang.

Fix this by jumping to a common unlock path on error instead of returning directly.

Fixes bda250dbaf39f ("xfs: rewrite xfs_dq_get_next_id using xfs_iext_lookup_extent")
---
 fs/xfs/xfs_dquot.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
fs/xfs/xfs_dquot.c +2 -1
@@ -723,7 +723,7 @@ xfs_dq_get_next_id(
 	lock_flags = xfs_ilock_data_map_shared(quotip);
 	error = xfs_iread_extents(NULL, quotip, XFS_DATA_FORK);
 	if (error)
-		return error;
+		goto out_unlock;
 
 	if (xfs_iext_lookup_extent(quotip, &quotip->i_df, start, &cur, &got)) {
 		/* contiguous chunk, bump startoff for the id calculation */
@@ -734,6 +734,7 @@ xfs_dq_get_next_id(
 		error = -ENOENT;
 	}
 
+out_unlock:
 	xfs_iunlock(quotip, lock_flags);
 
 	return error;
author maintainer
#66 lore raw

PATCH 66/166 xfs: dont swallow dquot recovery verification errors

In reply to Greg Kroah-Hartman

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

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

From: Long Li <[email protected]>

commit e2b4a856085e9bd939bde2dee0d08b1d41babde9 upstream.

xlog_recover_dquot_commit_pass2() validates the recovered dquot with xfs_dqblk_verify() and, on failure, sets error = -EFSCORRUPTED and jumps to out_release. But out_release unconditionally returns 0, so the corruption error is discarded: the caller xlog_recover_items_pass2() sees success, log recovery proceeds as if the dquot were valid, and the corrupt quota buffer can be written back to disk.

Fixes 9c235dfc3d3f ("xfs: dquot recovery does not validate the recovered dquot")
---
 fs/xfs/xfs_dquot_item_recover.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/xfs/xfs_dquot_item_recover.c +1 -1
@@ -173,7 +173,7 @@ xlog_recover_dquot_commit_pass2(
 
 out_release:
 	xfs_buf_relse(bp);
-	return 0;
+	return error;
 }
 
 const struct xlog_recover_item_ops xlog_dquot_item_ops = {
author maintainer
#67 lore raw

PATCH 67/166 xfs: check v5 superblock features early

In reply to Greg Kroah-Hartman

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

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

From: Christoph Hellwig <[email protected]>

commit eb6b2cc1fc8ad566d746d128a559989ff0bba5cc upstream.

When working on a new features that reuses the existing pad in the superblock, I noticed that mounting such a file system on an old kernel logs a rather confusing warning:

XFS (vdc): Metadir superblock padding fields must be zero.

This is because we only validate the various feature fields in v5 superblocks after the common superblock validation helper is called.

Fix this by calling the feature validation first.

Fixes eca383fcd63b ("xfs: refactor superblock verifiers")
Cc <[email protected]> # v4.19
---
 fs/xfs/libxfs/xfs_sb.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
fs/xfs/libxfs/xfs_sb.c +2 -2
@@ -865,10 +865,10 @@ xfs_sb_read_verify(
 	 * because _verify_common checks the on-disk values.
 	 */
 	__xfs_sb_from_disk(&sb, dsb, false);
-	error = xfs_validate_sb_common(mp, bp, &sb);
+	error = xfs_validate_sb_read(mp, &sb);
 	if (error)
 		goto out_error;
-	error = xfs_validate_sb_read(mp, &sb);
+	error = xfs_validate_sb_common(mp, bp, &sb);
 
 out_error:
 	if (error == -EFSCORRUPTED || error == -EFSBADCRC)
author maintainer
#68 lore raw

PATCH 68/166 RISC-V: Provide pgtable_l5_enabled on rv32

In reply to Greg Kroah-Hartman

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

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

From: Palmer Dabbelt <[email protected]>

commit 10128f8b1663a8bce27df051c750d116bb8cd737 upstream.

A few of the other page table level helpers are defined on rv32, but not pgtable_l5_enabled. This adds the definition as a constant and converts pgtable_l4_enabled to a constant as well.

[ Ben: Applies unchanged; needed by both 6.1.y and 6.6.y. Both trees took
commit e59e5e2754bf ("riscv: correct pt_level name via pgtable_l5/4_enabled") -- 6.1.y as of v6.1.64 -- without this prerequisite from the same series, so arch/riscv/mm/ptdump.c fails to build on rv32 whenever CONFIG_PTDUMP_CORE is enabled:

arch/riscv/mm/ptdump.c: In function 'ptdump_init':
arch/riscv/mm/ptdump.c:387:28: error: 'pgtable_l5_enabled' undeclared (first use in this function); did you mean 'pgtable_l4_enabled'?

Only pgtable_l5_enabled fails because pgtable_l4_enabled still has an unconditional extern in asm/pgtable.h; this patch removes that and provides both as constants for rv32. The only assignments to them, in disable_pgtable_l4/l5(), are already inside
#if defined(CONFIG_64BIT) && !defined(CONFIG_XIP_KERNEL), so making them const on rv32 is safe. v6.12 and later are unaffected. ]

---
 arch/riscv/include/asm/pgtable-32.h |    3 +++
 arch/riscv/include/asm/pgtable.h    |    1 -
 arch/riscv/mm/init.c                |    2 ++
 3 files changed, 5 insertions(+), 1 deletion(-)
arch/riscv/include/asm/pgtable-32.h +3 -0
@@ -33,4 +33,7 @@
 					  _PAGE_WRITE | _PAGE_EXEC |	\
 					  _PAGE_USER | _PAGE_GLOBAL))
 
+static const __maybe_unused int pgtable_l4_enabled;
+static const __maybe_unused int pgtable_l5_enabled;
+
 #endif /* _ASM_RISCV_PGTABLE_32_H */
arch/riscv/include/asm/pgtable.h +0 -1
@@ -905,7 +905,6 @@ extern uintptr_t _dtb_early_pa;
 #define dtb_early_pa	_dtb_early_pa
 #endif /* CONFIG_XIP_KERNEL */
 extern u64 satp_mode;
-extern bool pgtable_l4_enabled;
 
 void paging_init(void);
 void misc_mem_init(void);
arch/riscv/mm/init.c +2 -0
@@ -50,10 +50,12 @@ u64 satp_mode __ro_after_init = SATP_MOD
 #endif
 EXPORT_SYMBOL(satp_mode);
 
+#ifdef CONFIG_64BIT
 bool pgtable_l4_enabled = IS_ENABLED(CONFIG_64BIT) && !IS_ENABLED(CONFIG_XIP_KERNEL);
 bool pgtable_l5_enabled = IS_ENABLED(CONFIG_64BIT) && !IS_ENABLED(CONFIG_XIP_KERNEL);
 EXPORT_SYMBOL(pgtable_l4_enabled);
 EXPORT_SYMBOL(pgtable_l5_enabled);
+#endif
 
 phys_addr_t phys_ram_base __ro_after_init;
 EXPORT_SYMBOL(phys_ram_base);
author maintainer
#69 lore raw

PATCH 69/166 selftests: tls: add test with a partially invalid iov

In reply to Greg Kroah-Hartman

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

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

From: Sabrina Dubroca <[email protected]>

[ Upstream commit dc54b813df63020e946ccdef35b64d4fa99fd622 ]

Make sure that we don't return more bytes than we actually received if the userspace buffer was bogus. We expect to receive at least the rest of rec1, and possibly some of rec2 (currently, we don't, but that would be ok).

Stable-dep-of: 1c8629651cb5 ("tls: rx: restore msg_iter before TLS 1.3 optimistic retry")

---
 tools/testing/selftests/net/tls.c |   34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)
tools/testing/selftests/net/tls.c +34 -0
@@ -1570,6 +1570,40 @@ TEST_F(tls, getsockopt)
 	EXPECT_EQ(errno, EINVAL);
 }
 
+TEST_F(tls, recv_efault)
+{
+	char *rec1 = "1111111111";
+	char *rec2 = "2222222222";
+	struct msghdr hdr = {};
+	struct iovec iov[2];
+	char recv_mem[12];
+	int ret;
+
+	if (self->notls)
+		SKIP(return, "no TLS support");
+
+	EXPECT_EQ(send(self->fd, rec1, 10, 0), 10);
+	EXPECT_EQ(send(self->fd, rec2, 10, 0), 10);
+
+	iov[0].iov_base = recv_mem;
+	iov[0].iov_len = sizeof(recv_mem);
+	iov[1].iov_base = NULL; /* broken iov to make process_rx_list fail */
+	iov[1].iov_len = 1;
+
+	hdr.msg_iovlen = 2;
+	hdr.msg_iov = iov;
+
+	EXPECT_EQ(recv(self->cfd, recv_mem, 1, 0), 1);
+	EXPECT_EQ(recv_mem[0], rec1[0]);
+
+	ret = recvmsg(self->cfd, &hdr, 0);
+	EXPECT_LE(ret, sizeof(recv_mem));
+	EXPECT_GE(ret, 9);
+	EXPECT_EQ(memcmp(rec1, recv_mem, 9), 0);
+	if (ret > 9)
+		EXPECT_EQ(memcmp(rec2, recv_mem + 9, ret - 9), 0);
+}
+
 FIXTURE(tls_err)
 {
 	int fd, cfd;
author maintainer
#70 lore raw

PATCH 70/166 ceph: avoid fs reclaim while using current->journal_info

In reply to Greg Kroah-Hartman

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

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

From: Max Kellermann <[email protected]>

[ Upstream commit 5b602344a49e039e792ce5a8923bcc61412ee134 ]

handle_reply() stores a ceph_mds_request pointer in current->journal_info while filling the inode and dentry cache from an MDS reply.

An allocation in this section can enter direct reclaim and prune dentries from another filesystem. If this dirties an ext4 inode, ext4 starts a JBD2 transaction. JBD2 interprets the Ceph request in current->journal_info as a journal handle and dereferences the request's r_tid as h_transaction, causing a kernel crash, e.g.:

 Unable to handle kernel paging request at virtual address 00000000077b4818
 [...]
 Internal error: Oops: 0000000096000004 [#1]  SMP
 Modules linked in:
 CPU: 6 UID: 0 PID: 2699135 Comm: kworker/6:3 Tainted: G        W           6.18.38-i3 #1113 NONE
 [...]
 Workqueue: ceph-msgr ceph_con_workfn
 pstate: 80400009 (Nzcv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
 pc : jbd2__journal_start+0x2c/0x208
 lr : __ext4_journal_start_sb+0x100/0x178
 [...]
 Call trace:
  jbd2__journal_start+0x2c/0x208 (P)
  __ext4_journal_start_sb+0x100/0x178
  ext4_dirty_inode+0x3c/0x90
  __mark_inode_dirty+0x58/0x400
  iput.part.0+0x2b0/0x370
  iput+0x18/0x30
  dentry_unlink_inode+0xc0/0x158
  __dentry_kill+0x80/0x250
  shrink_dentry_list+0x90/0x130
  prune_dcache_sb+0x60/0x98
  super_cache_scan+0xe8/0x190
  do_shrink_slab+0x174/0x388
  shrink_slab+0xd8/0x4c0
  shrink_node+0x31c/0x908
  do_try_to_free_pages+0xd0/0x508
  try_to_free_pages+0x11c/0x238
  __alloc_frozen_pages_noprof+0x4d0/0xdd0
  __folio_alloc_noprof+0x18/0x70
  __filemap_get_folio+0x248/0x440
  ceph_readdir_prepopulate+0x570/0x9e8
  mds_dispatch+0x1424/0x1ba0
  ceph_con_process_message+0x74/0xa0
  ceph_con_v1_try_read+0x3a0/0x1510
  ceph_con_workfn+0x260/0x460

Enter a scoped NOFS allocation context and leave it after clearing journal_info. This prevents filesystem reclaim from recursing into another filesystem while the field contains Ceph-private data.

---
 fs/ceph/mds_client.c |   11 +++++++++++
 1 file changed, 11 insertions(+)
fs/ceph/mds_client.c +11 -0
@@ -6,6 +6,7 @@
 #include <linux/slab.h>
 #include <linux/gfp.h>
 #include <linux/sched.h>
+#include <linux/sched/mm.h>
 #include <linux/debugfs.h>
 #include <linux/seq_file.h>
 #include <linux/ratelimit.h>
@@ -3675,6 +3676,7 @@ static void handle_reply(struct ceph_mds
 	struct ceph_mds_reply_head *head = msg->front.iov_base;
 	struct ceph_mds_reply_info_parsed *rinfo;  /* parsed reply info */
 	struct ceph_snap_realm *realm;
+	unsigned int nofs_flags;
 	u64 tid;
 	int err, result;
 	int mds = session->s_mds;
@@ -3817,6 +3819,14 @@ static void handle_reply(struct ceph_mds
 
 	/* insert trace into our cache */
 	mutex_lock(&req->r_fill_mutex);
+
+	/* disable fs reclaim while we are using current->journal_info
+	 * for our own purposes, or else shrinkers of other
+	 * filesystems might dereference this pointer as a different
+	 * type
+	 */
+	nofs_flags = memalloc_nofs_save();
+
 	current->journal_info = req;
 	err = ceph_fill_trace(mdsc->fsc->sb, req);
 	if (err == 0) {
@@ -3825,6 +3835,7 @@ static void handle_reply(struct ceph_mds
 			err = ceph_readdir_prepopulate(req, req->r_session);
 	}
 	current->journal_info = NULL;
+	memalloc_nofs_restore(nofs_flags);
 	mutex_unlock(&req->r_fill_mutex);
 
 	up_read(&mdsc->snap_rwsem);
author maintainer
#71 lore raw

PATCH 71/166 ceph: Remove ceph_writepage()

In reply to Greg Kroah-Hartman

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

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

From: "Matthew Wilcox (Oracle)" <[email protected]>

[ Upstream commit 19a288110435408fcf2cb274823f2a17d03caf9b ]

Ceph already has a writepages operation which is preferred over writepage in all situations except for page migration. By adding a migrate_folio operation, there will be no situations in which ->writepage should be called. filemap_migrate_folio() is an appropriate operation to use because the ceph data stored in folio->private does not contain any reference to the memory address of the folio.

Stable-dep-of: cbf59617cd71 ("ceph: fix writeback_count leak in write_folio_nounlock()")

---
 fs/ceph/addr.c |   28 +---------------------------
 1 file changed, 1 insertion(+), 27 deletions(-)
fs/ceph/addr.c +1 -27
@@ -810,32 +810,6 @@ static int writepage_nounlock(struct pag
 	return err;
 }
 
-static int ceph_writepage(struct page *page, struct writeback_control *wbc)
-{
-	int err;
-	struct inode *inode = page->mapping->host;
-	BUG_ON(!inode);
-	ihold(inode);
-
-	if (wbc->sync_mode == WB_SYNC_NONE &&
-	    ceph_inode_to_fs_client(inode)->write_congested) {
-		redirty_page_for_writepage(wbc, page);
-		return AOP_WRITEPAGE_ACTIVATE;
-	}
-
-	wait_on_page_fscache(page);
-
-	err = writepage_nounlock(page, wbc);
-	if (err == -ERESTARTSYS) {
-		/* direct memory reclaimer was killed by SIGKILL. return 0
-		 * to prevent caller from setting mapping/page error */
-		err = 0;
-	}
-	unlock_page(page);
-	iput(inode);
-	return err;
-}
-
 /*
  * async writeback completion handler.
  *
@@ -1586,7 +1560,6 @@ out:
 const struct address_space_operations ceph_aops = {
 	.read_folio = netfs_read_folio,
 	.readahead = netfs_readahead,
-	.writepage = ceph_writepage,
 	.writepages = ceph_writepages_start,
 	.write_begin = ceph_write_begin,
 	.write_end = ceph_write_end,
@@ -1594,6 +1567,7 @@ const struct address_space_operations ce
 	.invalidate_folio = ceph_invalidate_folio,
 	.release_folio = ceph_release_folio,
 	.direct_IO = noop_direct_IO,
+	.migrate_folio = filemap_migrate_folio,
 };
 
 static void ceph_block_sigs(sigset_t *oldset)
author maintainer
#72 lore raw

PATCH 72/166 ceph: fix hanging __ceph_get_caps() with stale mds_wanted

In reply to Greg Kroah-Hartman

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

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

From: Max Kellermann <[email protected]>

[ Upstream commit 50958bb928bad3bdba9e5d1b7ff4bbadcf6951e6 ]

A reader can hang forever in __ceph_get_caps() when the client no longer holds FILE_RD, but local cap state still says that the capability is already wanted (via mds_wanted).

One way to trigger this is through MDS cap revocation. If another client performs a conflicting operation, the MDS can revoke FILE_RD from the reader; the next read then has to reacquire FILE_RD. If the cap update that should request FILE_RD never reaches the MDS after cap->mds_wanted was raised, the reader is left holding only non-file caps while local mds_wanted still includes the file read caps.

In that state, try_get_cap_refs() sees need <= mds_wanted and returns 0, so __ceph_get_caps() just waits on i_cap_wq. If the cap update that was supposed to request FILE_RD never reaches the MDS after cap->mds_wanted was` raised, no further request is sent and the waiter can sleep indefinitely until unrelated cap traffic happens to wake it up.

The ordering issue is that cap->mds_wanted is updated in __prep_cap() before the CEPH_MSG_CLIENT_CAPS message is actually queued for send. That makes one field serve two different meanings at
once: what this client wants, and what the client believes the MDS already knows it wants.

A proper fix would be to split those states and track whether a cap update is actually in flight or has been observed by the MDS. However, simply moving the cap->mds_wanted assignment later would not be sufficient: queueing the message in the messenger does not guarantee that the MDS processed that specific wanted set, and reconnect or message loss can still invalidate that assumption. Fixing that properly would require a larger rework of the cap state machine.

To allow simpler backports to stable kernels, this patch implements a simpler workaround:

- stop waiting forever in __ceph_get_caps(); after a bounded wait, fall back to the renew path

- make ceph_renew_caps() issue a synchronous OPEN request whenever the inode still does not actually hold the wanted caps, instead of only calling ceph_check_caps()

The extra issued-vs-wanted check in ceph_renew_caps() is necessary because the previous test only checked whether the inode still had any real caps at all. That is not enough after revocation: the client can still hold something like pLs and yet be missing FILE_RD completely. In that case, falling back to ceph_check_caps() is not sufficient, because it still trusts cap->mds_wanted and may resend nothing. By requiring (issued & wanted) == wanted before taking the asynchronous path, the code only uses ceph_check_caps() when the wanted caps are already actually issued. Otherwise, it sends the synchronous OPEN renew.

This preserves the existing asynchronous fast path when the wanted caps are already issued, avoids changing cap-state semantics, and fixes the hang by guaranteeing that a stalled waiter eventually retries through a path that does not rely on the stale mds_wanted state.

[ idryomov: move CEPH_GET_CAPS_WAIT_TIMEOUT from libceph.h to
mds_client.h, formatting ]

Fixes 0a454bdd501a ("ceph: reorganize __send_cap for less spinlock abuse")
---
 fs/ceph/caps.c       |   17 +++++++++++++++--
 fs/ceph/file.c       |    9 +++++----
 fs/ceph/mds_client.h |    2 ++
 3 files changed, 22 insertions(+), 6 deletions(-)
fs/ceph/caps.c +15 -2
@@ -3091,7 +3091,19 @@ int __ceph_get_caps(struct inode *inode,
 					ret = -ERESTARTSYS;
 					break;
 				}
-				wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
+
+				/*
+				 * If a cap update is lost after
+				 * mds_wanted was raised, waiting
+				 * forever will never make progress.
+				 * Retry the renew path periodically
+				 * so we can resend synchronously.
+				 */
+				if (!wait_woken(&wait, TASK_INTERRUPTIBLE,
+						CEPH_GET_CAPS_WAIT_TIMEOUT)) {
+					ret = -EUCLEAN;
+					break;
+				}
 			}
 
 			remove_wait_queue(&ci->i_cap_wq, &wait);
@@ -3123,7 +3135,8 @@ int __ceph_get_caps(struct inode *inode,
 				continue;
 			}
 			if (ret == -EUCLEAN) {
-				/* session was killed, try renew caps */
+				/* session was killed or a waited cap
+				 * request needs a retry */
 				ret = ceph_renew_caps(inode, flags);
 				if (ret == 0)
 					continue;
fs/ceph/file.c +5 -4
@@ -294,7 +294,7 @@ static int ceph_init_file(struct inode *
 }
 
 /*
- * try renew caps after session gets killed.
+ * Retry cap acquisition after a stale session or a lost cap update.
  */
 int ceph_renew_caps(struct inode *inode, int fmode)
 {
@@ -302,14 +302,15 @@ int ceph_renew_caps(struct inode *inode,
 	struct ceph_client *cl = mdsc->fsc->client;
 	struct ceph_inode_info *ci = ceph_inode(inode);
 	struct ceph_mds_request *req;
-	int err, flags, wanted;
+	int err, flags, wanted, issued;
 
 	spin_lock(&ci->i_ceph_lock);
 	__ceph_touch_fmode(ci, mdsc, fmode);
 	wanted = __ceph_caps_file_wanted(ci);
+	issued = __ceph_caps_issued(ci, NULL);
 	if (__ceph_is_any_real_caps(ci) &&
-	    (!(wanted & CEPH_CAP_ANY_WR) || ci->i_auth_cap)) {
-		int issued = __ceph_caps_issued(ci, NULL);
+	    (!(wanted & CEPH_CAP_ANY_WR) || ci->i_auth_cap) &&
+	    (issued & wanted) == wanted) {
 		spin_unlock(&ci->i_ceph_lock);
 		doutc(cl, "%p %llx.%llx want %s issued %s updating mds_wanted\n",
 		      inode, ceph_vinop(inode), ceph_cap_string(wanted),
fs/ceph/mds_client.h +2 -0
@@ -68,6 +68,8 @@ enum ceph_feature_type {
 struct ceph_fs_client;
 struct ceph_cap;
 
+#define CEPH_GET_CAPS_WAIT_TIMEOUT (5 * HZ)
+
 /*
  * parsed info about a single inode.  pointers are into the encoded
  * on-wire structures within the mds reply message payload.
author maintainer
#73 lore raw

PATCH 73/166 ceph: Use a folio in ceph_page_mkwrite()

In reply to Greg Kroah-Hartman

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

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

From: "Matthew Wilcox (Oracle)" <[email protected]>

[ Upstream commit 88a59bda3f3786107694a3f5fd7f9df421752c21 ]

Convert the passed page to a folio and use it
throughout ceph_page_mkwrite(). Removes the last call to page_mkwrite_check_truncate(), the last call to offset_in_thp() and one of the last calls to thp_size(). Saves a few calls to compound_head().

Stable-dep-of: cbf59617cd71 ("ceph: fix writeback_count leak in write_folio_nounlock()")

---
 fs/ceph/addr.c |   26 +++++++++++++-------------
 1 file changed, 13 insertions(+), 13 deletions(-)
fs/ceph/addr.c +13 -13
@@ -1684,8 +1684,8 @@ static vm_fault_t ceph_page_mkwrite(stru
 	struct ceph_inode_info *ci = ceph_inode(inode);
 	struct ceph_file_info *fi = vma->vm_file->private_data;
 	struct ceph_cap_flush *prealloc_cf;
-	struct page *page = vmf->page;
-	loff_t off = page_offset(page);
+	struct folio *folio = page_folio(vmf->page);
+	loff_t off = folio_pos(folio);
 	loff_t size = i_size_read(inode);
 	size_t len;
 	int want, got, err;
@@ -1702,10 +1702,10 @@ static vm_fault_t ceph_page_mkwrite(stru
 	sb_start_pagefault(inode->i_sb);
 	ceph_block_sigs(&oldset);
 
-	if (off + thp_size(page) <= size)
-		len = thp_size(page);
+	if (off + folio_size(folio) <= size)
+		len = folio_size(folio);
 	else
-		len = offset_in_thp(page, size);
+		len = offset_in_folio(folio, size);
 
 	doutc(cl, "%llx.%llx %llu~%zd getting caps i_size %llu\n",
 	      ceph_vinop(inode), off, len, size);
@@ -1722,30 +1722,30 @@ static vm_fault_t ceph_page_mkwrite(stru
 	doutc(cl, "%llx.%llx %llu~%zd got cap refs on %s\n", ceph_vinop(inode),
 	      off, len, ceph_cap_string(got));
 
-	/* Update time before taking page lock */
+	/* Update time before taking folio lock */
 	file_update_time(vma->vm_file);
 	inode_inc_iversion_raw(inode);
 
 	do {
 		struct ceph_snap_context *snapc;
 
-		lock_page(page);
+		folio_lock(folio);
 
-		if (page_mkwrite_check_truncate(page, inode) < 0) {
-			unlock_page(page);
+		if (folio_mkwrite_check_truncate(folio, inode) < 0) {
+			folio_unlock(folio);
 			ret = VM_FAULT_NOPAGE;
 			break;
 		}
 
-		snapc = ceph_find_incompatible(page);
+		snapc = ceph_find_incompatible(&folio->page);
 		if (!snapc) {
-			/* success.  we'll keep the page locked. */
-			set_page_dirty(page);
+			/* success.  we'll keep the folio locked. */
+			folio_mark_dirty(folio);
 			ret = VM_FAULT_LOCKED;
 			break;
 		}
 
-		unlock_page(page);
+		folio_unlock(folio);
 
 		if (IS_ERR(snapc)) {
 			ret = VM_FAULT_SIGBUS;
author maintainer
#74 lore raw

PATCH 74/166 libceph: Amend checking to fix `make W=1` build breakage

In reply to Greg Kroah-Hartman

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

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

From: Andy Shevchenko <[email protected]>

[ Upstream commit 04d8712b079327409b09dee628378f9583e2e035 ]

In a few cases the code compares 32-bit value to a SIZE_MAX derived constant which is much higher than that value on 64-bit platforms, Clang, in particular, is not happy about this

net/ceph/osdmap.c:1441:10: error: result of comparison of constant 4611686018427387891 with expression of type 'u32' (aka 'unsigned int') is always false [-Werror,-Wtautological-constant-out-of-range-compare]
1441 | if (len > (SIZE_MAX - sizeof(*pg)) / sizeof(u32))

      |             ~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
net/ceph/osdmap.c:1624:10: error: result of comparison of constant 2305843009213693945 with expression of type 'u32' (aka 'unsigned int') is always false [-Werror,-Wtautological-constant-out-of-range-compare]
 1624 |         if (len > (SIZE_MAX - sizeof(*pg)) / (2 * sizeof(u32)))
      |             ~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Fix this by casting to size_t. Note, that possible replacement of SIZE_MAX by U32_MAX may lead to the behaviour changes on the corner cases.

Stable-dep-of: 9f00f9cf2be2 ("libceph: bound pg_{temp,upmap,upmap_items} length to CEPH_PG_MAX_SIZE")

---
 net/ceph/osdmap.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
net/ceph/osdmap.c +2 -2
@@ -1440,7 +1440,7 @@ static struct ceph_pg_mapping *__decode_
 	ceph_decode_32_safe(p, end, len, e_inval);
 	if (len == 0 && incremental)
 		return NULL;	/* new_pg_temp: [] to remove */
-	if (len > (SIZE_MAX - sizeof(*pg)) / sizeof(u32))
+	if ((size_t)len > (SIZE_MAX - sizeof(*pg)) / sizeof(u32))
 		return ERR_PTR(-EINVAL);
 
 	ceph_decode_need(p, end, len * sizeof(u32), e_inval);
@@ -1621,7 +1621,7 @@ static struct ceph_pg_mapping *__decode_
 	u32 len, i;
 
 	ceph_decode_32_safe(p, end, len, e_inval);
-	if (len > (SIZE_MAX - sizeof(*pg)) / (2 * sizeof(u32)))
+	if ((size_t)len > (SIZE_MAX - sizeof(*pg)) / (2 * sizeof(u32)))
 		return ERR_PTR(-EINVAL);
 
 	ceph_decode_need(p, end, 2 * len * sizeof(u32), e_inval);
author maintainer
#75 lore raw

PATCH 75/166 libceph: bound pg_{temp,upmap,upmap_items} length to CEPH_PG_MAX_SIZE

In reply to Greg Kroah-Hartman

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

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

From: Xiang Mei <[email protected]>

[ Upstream commit 9f00f9cf2be293efe899db67dc5272e3a9c62717 ]

__decode_pg_temp() decodes an user-controlled length but only rejects values large enough to overflow the allocation; it does not bound it to CEPH_PG_MAX_SIZE. The helper backs both pg_temp and pg_upmap decoding, and apply_upmap()/get_temp_osds() later copy the decoded list into the fixed-size on-stack array struct ceph_osds.osds[CEPH_PG_MAX_SIZE]. A monitor that sends an OSDMap with a pg_temp/pg_upmap entry longer than 32 thus causes a stack out-of-bounds write.

An OSD set for a single PG can never exceed CEPH_PG_MAX_SIZE, so reject longer entries at decode time. The bound is well below the old overflow threshold, so it also covers the allocation-size overflow the previous check guarded against.

  BUG: KASAN: stack-out-of-bounds in ceph_pg_to_up_acting_osds
  Write of size 4 ... by task exploit
   kasan_report (mm/kasan/report.c:595)
   ceph_pg_to_up_acting_osds (net/ceph/osdmap.c:2617 net/ceph/osdmap.c:2833)
   calc_target (net/ceph/osd_client.c:1638)
   __submit_request (net/ceph/osd_client.c:2394)
   ceph_osdc_start_request (net/ceph/osd_client.c:2490)
   ceph_osdc_call (net/ceph/osd_client.c:5164)
   rbd_dev_image_probe (drivers/block/rbd.c:6899)
   do_rbd_add (drivers/block/rbd.c:7138)
   ...
  kernel BUG at net/ceph/osdmap.c:2670!

[ idryomov: do the same in __decode_pg_upmap_items() ]

Fixes a303bb0e5834 ("libceph: introduce and switch to decode_pg_mapping()")
Assisted-by Claude:claude-opus-4-8
---
 net/ceph/osdmap.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
net/ceph/osdmap.c +2 -2
@@ -1440,7 +1440,7 @@ static struct ceph_pg_mapping *__decode_
 	ceph_decode_32_safe(p, end, len, e_inval);
 	if (len == 0 && incremental)
 		return NULL;	/* new_pg_temp: [] to remove */
-	if ((size_t)len > (SIZE_MAX - sizeof(*pg)) / sizeof(u32))
+	if (len > CEPH_PG_MAX_SIZE)
 		return ERR_PTR(-EINVAL);
 
 	ceph_decode_need(p, end, len * sizeof(u32), e_inval);
@@ -1621,7 +1621,7 @@ static struct ceph_pg_mapping *__decode_
 	u32 len, i;
 
 	ceph_decode_32_safe(p, end, len, e_inval);
-	if ((size_t)len > (SIZE_MAX - sizeof(*pg)) / (2 * sizeof(u32)))
+	if (len > CEPH_PG_MAX_SIZE)
 		return ERR_PTR(-EINVAL);
 
 	ceph_decode_need(p, end, 2 * len * sizeof(u32), e_inval);
author maintainer
#76 lore raw

PATCH 76/166 ASoC: fsl_sai: Fix spurious BCLK on resume by clearing BYP

In reply to Greg Kroah-Hartman

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

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

From: Chancel Liu <[email protected]>

[ Upstream commit d091132889c1378dd0944a72f86eae3e4da1e4fa ]

When the BCLK divider ratio is 1:1, fsl_sai_set_bclk() enables bypass mode by setting BYP, but never clears the bit. The BYP=1 value remains in the regcache, and is restored by regcache_sync() on the next runtime resume.

Since BYP=1 combined with BCD=1 immediately outputs the ungated MCLK as BCLK without waiting for BCE/TE/RE to be enabled, the clock is driven prematurely before the stream is fully configured, causing noise on some codecs.

Fix this by clearing BYP and BCI in fsl_sai_hw_free() taking into account sync mode and the opposite stream's state, so that the regcache holds BYP=0 before runtime suspend and regcache_sync() on resume will not restore bypass mode prematurely.

---
 sound/soc/fsl/fsl_sai.c |   29 +++++++++++++++++++++++++----
 1 file changed, 25 insertions(+), 4 deletions(-)
sound/soc/fsl/fsl_sai.c +25 -4
@@ -716,6 +716,8 @@ static int fsl_sai_hw_free(struct snd_pc
 	struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
 	bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
 	unsigned int ofs = sai->soc_data->reg_offset;
+	int adir = tx ? RX : TX;
+	int dir  = tx ? TX : RX;
 
 	/* Clear xMR to avoid channel swap with mclk_with_tere enabled case */
 	regmap_write(sai->regmap, FSL_SAI_xMR(tx), 0);
@@ -723,10 +725,29 @@ static int fsl_sai_hw_free(struct snd_pc
 	regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx, ofs),
 			   FSL_SAI_CR3_TRCE_MASK, 0);
 
-	if (!sai->is_consumer_mode &&
-			sai->mclk_streams & BIT(substream->stream)) {
-		clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[tx]]);
-		sai->mclk_streams &= ~BIT(substream->stream);
+	if (!sai->is_consumer_mode) {
+		bool adir_active = !!(sai->mclk_streams & BIT(!substream->stream));
+		/*
+		 * If opposite stream provides clocks for synchronous mode and
+		 * it is inactive, Clear BYP and BCI
+		 */
+		if (fsl_sai_dir_is_synced(sai, adir) && !adir_active)
+			regmap_update_bits(sai->regmap, FSL_SAI_xCR2(!tx, ofs),
+					   FSL_SAI_CR2_BCI | FSL_SAI_CR2_BYP, 0);
+		/*
+		 * Clear BYP and BCI of current stream if either of:
+		 * 1. current stream doesn't provide clocks for synchronous mode
+		 * 2. current stream provides clocks for synchronous mode but no
+		 *    more stream is active.
+		 */
+		if (!fsl_sai_dir_is_synced(sai, dir) || !adir_active)
+			regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs),
+					   FSL_SAI_CR2_BCI | FSL_SAI_CR2_BYP, 0);
+
+		if (sai->mclk_streams & BIT(substream->stream)) {
+			clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[tx]]);
+			sai->mclk_streams &= ~BIT(substream->stream);
+		}
 	}
 
 	return 0;
author maintainer
#77 lore raw

PATCH 77/166 iomap: hold state_lock over call to ifs_set_range_uptodate()

In reply to Greg Kroah-Hartman

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

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

From: "Matthew Wilcox (Oracle)" <[email protected]>

[ Upstream commit 279d5fc3227f04ef2c6125e5c440e7952173a89a ]

Patch series "Add folio_end_read", v2.

The core of this patchset is the new folio_end_read() call which filesystems can use when finishing a page cache read instead of separate calls to mark the folio uptodate and unlock it. As an illustration of its use, I converted ext4, iomap & mpage; more can be converted.

I think that's useful by itself, but the interesting optimisation is that we can implement that with a single XOR instruction that sets the uptodate bit, clears the lock bit, tests the waiter bit and provides a write memory barrier. That removes one memory barrier and one atomic instruction from each page read, which seems worth doing. That's in patch 15.

The last two patches could be a separate series, but basically we can do the same thing with the writeback flag that we do with the unlock flag; clear it and test the waiters bit at the same time.

This patch (of 17):

This is really preparation for the next patch, but it lets us call folio_mark_uptodate() in just one place instead of two.

Cc Matthew Wilcox (Oracle) <[email protected]>
Cc Nicholas Piggin <[email protected]>
Cc "Theodore Ts'o" <[email protected]>
Cc Andreas Dilger <[email protected]>
Cc Richard Henderson <[email protected]>
Cc Ivan Kokshaysky <[email protected]>
Cc Matt Turner <[email protected]>
Cc Thomas Bogendoerfer <[email protected]>
Cc Michael Ellerman <[email protected]>
Cc Christophe Leroy <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Palmer Dabbelt <[email protected]>
Cc Albert Ou <[email protected]>
Cc Heiko Carstens <[email protected]>
Cc Vasily Gorbik <[email protected]>
Cc Alexander Gordeev <[email protected]>
Cc Christian Borntraeger <[email protected]>
Cc Sven Schnelle <[email protected]>
Cc Geert Uytterhoeven <[email protected]>

Stable-dep-of: 9c7d8f7c8994 ("iomap: fix out-of-bounds bitmap_set() with zero-length range")

---
 fs/iomap/buffered-io.c |   20 +++++++++++---------
 1 file changed, 11 insertions(+), 9 deletions(-)
fs/iomap/buffered-io.c +11 -9
@@ -57,30 +57,32 @@ static inline bool ifs_block_is_uptodate
 	return test_bit(block, ifs->state);
 }
 
-static void ifs_set_range_uptodate(struct folio *folio,
+static bool ifs_set_range_uptodate(struct folio *folio,
 		struct iomap_folio_state *ifs, size_t off, size_t len)
 {
 	struct inode *inode = folio->mapping->host;
 	unsigned int first_blk = off >> inode->i_blkbits;
 	unsigned int last_blk = (off + len - 1) >> inode->i_blkbits;
 	unsigned int nr_blks = last_blk - first_blk + 1;
-	unsigned long flags;
 
-	spin_lock_irqsave(&ifs->state_lock, flags);
 	bitmap_set(ifs->state, first_blk, nr_blks);
-	if (ifs_is_fully_uptodate(folio, ifs))
-		folio_mark_uptodate(folio);
-	spin_unlock_irqrestore(&ifs->state_lock, flags);
+	return ifs_is_fully_uptodate(folio, ifs);
 }
 
 static void iomap_set_range_uptodate(struct folio *folio, size_t off,
 		size_t len)
 {
 	struct iomap_folio_state *ifs = folio->private;
+	unsigned long flags;
+	bool uptodate = true;
+
+	if (ifs) {
+		spin_lock_irqsave(&ifs->state_lock, flags);
+		uptodate = ifs_set_range_uptodate(folio, ifs, off, len);
+		spin_unlock_irqrestore(&ifs->state_lock, flags);
+	}
 
-	if (ifs)
-		ifs_set_range_uptodate(folio, ifs, off, len);
-	else
+	if (uptodate)
 		folio_mark_uptodate(folio);
 }
 
author maintainer
#78 lore raw

PATCH 78/166 iomap: fix out-of-bounds bitmap_set() with zero-length range

In reply to Greg Kroah-Hartman

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

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

From: Zhang Yi <[email protected]>

[ Upstream commit 9c7d8f7c8994c790fca501dc45ce66e7356cbe05 ]

ifs_set_range_dirty() and ifs_set_range_uptodate() compute last_blk as (off + len - 1) >> i_blkbits. When off is 0 and len is 0, the unsigned subtraction underflows to SIZE_MAX, producing a huge last_blk and nr_blks value that causes bitmap_set() to write far beyond the ifs->state allocation.

Regarding ifs_set_range_uptodate(), it is temporarily safe because len cannot be passed in as 0. However, for ifs_set_range_dirty() this is reachable from __iomap_write_end(): when copy_folio_from_iter_atomic() returns 0 (e.g. user buffer fault) and the folio is already uptodate, the guard at the top of __iomap_write_end() does not trigger because !folio_test_uptodate() is false, and iomap_set_range_dirty() is called with copied == 0.

Add a !len guard to both functions before the computation, so that a zero-length range is a no-op.

---
 fs/iomap/buffered-io.c |   22 ++++++++++++++--------
 1 file changed, 14 insertions(+), 8 deletions(-)
fs/iomap/buffered-io.c +14 -8
@@ -61,11 +61,13 @@ static bool ifs_set_range_uptodate(struc
 		struct iomap_folio_state *ifs, size_t off, size_t len)
 {
 	struct inode *inode = folio->mapping->host;
-	unsigned int first_blk = off >> inode->i_blkbits;
-	unsigned int last_blk = (off + len - 1) >> inode->i_blkbits;
-	unsigned int nr_blks = last_blk - first_blk + 1;
+	unsigned int first_blk, last_blk;
 
-	bitmap_set(ifs->state, first_blk, nr_blks);
+	if (len) {
+		first_blk = off >> inode->i_blkbits;
+		last_blk = (off + len - 1) >> inode->i_blkbits;
+		bitmap_set(ifs->state, first_blk, last_blk - first_blk + 1);
+	}
 	return ifs_is_fully_uptodate(folio, ifs);
 }
 
@@ -127,13 +129,17 @@ static void ifs_set_range_dirty(struct f
 {
 	struct inode *inode = folio->mapping->host;
 	unsigned int blks_per_folio = i_blocks_per_folio(inode, folio);
-	unsigned int first_blk = (off >> inode->i_blkbits);
-	unsigned int last_blk = (off + len - 1) >> inode->i_blkbits;
-	unsigned int nr_blks = last_blk - first_blk + 1;
+	unsigned int first_blk, last_blk;
 	unsigned long flags;
 
+	if (!len)
+		return;
+
+	first_blk = off >> inode->i_blkbits;
+	last_blk = (off + len - 1) >> inode->i_blkbits;
 	spin_lock_irqsave(&ifs->state_lock, flags);
-	bitmap_set(ifs->state, first_blk + blks_per_folio, nr_blks);
+	bitmap_set(ifs->state, first_blk + blks_per_folio,
+		   last_blk - first_blk + 1);
 	spin_unlock_irqrestore(&ifs->state_lock, flags);
 }
 
author maintainer
#79 lore raw

PATCH 79/166 mm: userfaultfd: add pgtable_supports_uffd_wp()

In reply to Greg Kroah-Hartman

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

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

From: Chunyan Zhang <[email protected]>

[ Upstream commit f59c0924d61aa2a2bb85936a593140f327112787 ]

Some platforms can customize the PTE/PMD entry uffd-wp bit making it unavailable even if the architecture provides the resource. This patch adds a macro API pgtable_supports_uffd_wp() that allows architectures to define their specific implementations to check if the uffd-wp bit is available on which device the kernel is running.

Also this patch is removing "ifdef CONFIG_HAVE_ARCH_USERFAULTFD_WP" and "ifdef CONFIG_PTE_MARKER_UFFD_WP" in favor of pgtable_supports_uffd_wp() and uffd_supports_wp_marker() checks respectively that default to IS_ENABLED(CONFIG_HAVE_ARCH_USERFAULTFD_WP) and "IS_ENABLED(CONFIG_HAVE_ARCH_USERFAULTFD_WP) && IS_ENABLED(CONFIG_PTE_MARKER_UFFD_WP)" if not overridden by the architecture, no change in behavior is expected.

Cc Albert Ou <[email protected]>
Cc Alexandre Ghiti <[email protected]>
Cc Alexandre Ghiti <[email protected]>
Cc Al Viro <[email protected]>
Cc Andrew Jones <[email protected]>
Cc Arnd Bergmann <[email protected]>
Cc Axel Rasmussen <[email protected]>
Cc Christian Brauner <[email protected]>
Cc Conor Dooley <[email protected]>
Cc Conor Dooley <[email protected]>
Cc Deepak Gupta <[email protected]>
Cc Jan Kara <[email protected]>
Cc Liam Howlett <[email protected]>
Cc Lorenzo Stoakes <[email protected]>
Cc Michal Hocko <[email protected]>
Cc Mike Rapoport <[email protected]>
Cc Palmer Dabbelt <[email protected]>
Cc Paul Walmsley <[email protected]>
Cc Peter Xu <[email protected]>
Cc Rob Herring <[email protected]>
Cc Suren Baghdasaryan <[email protected]>
Cc Vlastimil Babka <[email protected]>
Cc Yuanchu Xie <[email protected]>

Stable-dep-of: 3c58f641e813 ("userfaultfd: prevent registration of special VMAs")

---
 fs/userfaultfd.c                   |   20 ++++++++++----------
 include/asm-generic/pgtable_uffd.h |   17 +++++++++++++++++
 include/linux/mm_inline.h          |    7 ++++---
 include/linux/userfaultfd_k.h      |    8 +++++---
 4 files changed, 36 insertions(+), 16 deletions(-)
fs/userfaultfd.c +10 -10
@@ -1354,9 +1354,9 @@ static int userfaultfd_register(struct u
 	if (uffdio_register.mode & UFFDIO_REGISTER_MODE_MISSING)
 		vm_flags |= VM_UFFD_MISSING;
 	if (uffdio_register.mode & UFFDIO_REGISTER_MODE_WP) {
-#ifndef CONFIG_HAVE_ARCH_USERFAULTFD_WP
-		goto out;
-#endif
+		if (!pgtable_supports_uffd_wp())
+			goto out;
+
 		vm_flags |= VM_UFFD_WP;
 	}
 	if (uffdio_register.mode & UFFDIO_REGISTER_MODE_MINOR) {
@@ -2068,13 +2068,13 @@ static int userfaultfd_api(struct userfa
 	uffdio_api.features &=
 		~(UFFD_FEATURE_MINOR_HUGETLBFS | UFFD_FEATURE_MINOR_SHMEM);
 #endif
-#ifndef CONFIG_HAVE_ARCH_USERFAULTFD_WP
-	uffdio_api.features &= ~UFFD_FEATURE_PAGEFAULT_FLAG_WP;
-#endif
-#ifndef CONFIG_PTE_MARKER_UFFD_WP
-	uffdio_api.features &= ~UFFD_FEATURE_WP_HUGETLBFS_SHMEM;
-	uffdio_api.features &= ~UFFD_FEATURE_WP_UNPOPULATED;
-#endif
+	if (!pgtable_supports_uffd_wp())
+		uffdio_api.features &= ~UFFD_FEATURE_PAGEFAULT_FLAG_WP;
+
+	if (!uffd_supports_wp_marker()) {
+		uffdio_api.features &= ~UFFD_FEATURE_WP_HUGETLBFS_SHMEM;
+		uffdio_api.features &= ~UFFD_FEATURE_WP_UNPOPULATED;
+	}
 
 	ret = -EINVAL;
 	if (features & ~uffdio_api.features)
include/asm-generic/pgtable_uffd.h +17 -0
@@ -1,6 +1,23 @@
 #ifndef _ASM_GENERIC_PGTABLE_UFFD_H
 #define _ASM_GENERIC_PGTABLE_UFFD_H
 
+/*
+ * Some platforms can customize the uffd-wp bit, making it unavailable
+ * even if the architecture provides the resource.
+ * Adding this API allows architectures to add their own checks for the
+ * devices on which the kernel is running.
+ * Note: When overriding it, please make sure the
+ * CONFIG_HAVE_ARCH_USERFAULTFD_WP is part of this macro.
+ */
+#ifndef pgtable_supports_uffd_wp
+#define pgtable_supports_uffd_wp()	IS_ENABLED(CONFIG_HAVE_ARCH_USERFAULTFD_WP)
+#endif
+
+static inline bool uffd_supports_wp_marker(void)
+{
+	return pgtable_supports_uffd_wp() && IS_ENABLED(CONFIG_PTE_MARKER_UFFD_WP);
+}
+
 #ifndef CONFIG_HAVE_ARCH_USERFAULTFD_WP
 static __always_inline int pte_uffd_wp(pte_t pte)
 {
include/linux/mm_inline.h +4 -3
@@ -547,7 +547,6 @@ static inline pte_marker copy_pte_marker
 
 	return dstm;
 }
-#endif
 
 /*
  * If this pte is wr-protected by uffd-wp in any form, arm the special pte to
@@ -565,9 +564,11 @@ static inline void
 pte_install_uffd_wp_if_needed(struct vm_area_struct *vma, unsigned long addr,
 			      pte_t *pte, pte_t pteval)
 {
-#ifdef CONFIG_PTE_MARKER_UFFD_WP
 	bool arm_uffd_pte = false;
 
+	if (!uffd_supports_wp_marker())
+		return;
+
 	/* The current status of the pte should be "cleared" before calling */
 	WARN_ON_ONCE(!pte_none(ptep_get(pte)));
 
@@ -594,7 +595,6 @@ pte_install_uffd_wp_if_needed(struct vm_
 	if (unlikely(arm_uffd_pte))
 		set_pte_at(vma->vm_mm, addr, pte,
 			   make_pte_marker(PTE_MARKER_UFFD_WP));
-#endif
 }
 
 static inline bool vma_has_recency(struct vm_area_struct *vma)
@@ -607,5 +607,6 @@ static inline bool vma_has_recency(struc
 
 	return true;
 }
+#endif
 
 #endif
include/linux/userfaultfd_k.h +5 -3
@@ -166,15 +166,17 @@ static inline bool vma_can_userfault(str
 	if ((vm_flags & VM_UFFD_MINOR) &&
 	    (!is_vm_hugetlb_page(vma) && !vma_is_shmem(vma)))
 		return false;
-#ifndef CONFIG_PTE_MARKER_UFFD_WP
+
 	/*
 	 * If user requested uffd-wp but not enabled pte markers for
 	 * uffd-wp, then shmem & hugetlbfs are not supported but only
 	 * anonymous.
 	 */
-	if ((vm_flags & VM_UFFD_WP) && !vma_is_anonymous(vma))
+	if (!uffd_supports_wp_marker() && (vm_flags & VM_UFFD_WP) &&
+	    !vma_is_anonymous(vma))
 		return false;
-#endif
+
+	/* By default, allow any of anon|shmem|hugetlb */
 	return vma_is_anonymous(vma) || is_vm_hugetlb_page(vma) ||
 	    vma_is_shmem(vma);
 }
author maintainer
#80 lore raw

PATCH 80/166 userfaultfd: move vma_can_userfault out of line

In reply to Greg Kroah-Hartman

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

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

From: "Mike Rapoport (Microsoft)" <[email protected]>

[ Upstream commit a5bb8669872b6b8463b8777a7a259a8305060016 ]

vma_can_userfault() has grown pretty big and it's not called on performance critical path.

Move it out of line.

No functional changes.

Cc Andrea Arcangeli <[email protected]>
Cc Andrei Vagin <[email protected]>
Cc Axel Rasmussen <[email protected]>
Cc Baolin Wang <[email protected]>
Cc Harry Yoo <[email protected]>
Cc Harry Yoo (Oracle) <[email protected]>
Cc Hugh Dickins <[email protected]>
Cc James Houghton <[email protected]>
Cc Lorenzo Stoakes (Oracle) <[email protected]>
Cc Matthew Wilcox (Oracle) <[email protected]>
Cc Michal Hocko <[email protected]>
Cc Muchun Song <[email protected]>
Cc Nikita Kalyazin <[email protected]>
Cc Oscar Salvador <[email protected]>
Cc Paolo Bonzini <[email protected]>
Cc Peter Xu <[email protected]>
Cc Sean Christopherson <[email protected]>
Cc Shuah Khan <[email protected]>
Cc Suren Baghdasaryan <[email protected]>
Cc Vlastimil Babka <[email protected]>
Cc David Carlier <[email protected]>

Stable-dep-of: 3c58f641e813 ("userfaultfd: prevent registration of special VMAs")

---
 include/linux/userfaultfd_k.h |   21 +--------------------
 mm/userfaultfd.c              |   20 ++++++++++++++++++++
 2 files changed, 21 insertions(+), 20 deletions(-)
include/linux/userfaultfd_k.h +1 -20
@@ -160,26 +160,7 @@ static inline bool userfaultfd_armed(str
 	return vma->vm_flags & __VM_UFFD_FLAGS;
 }
 
-static inline bool vma_can_userfault(struct vm_area_struct *vma,
-				     unsigned long vm_flags)
-{
-	if ((vm_flags & VM_UFFD_MINOR) &&
-	    (!is_vm_hugetlb_page(vma) && !vma_is_shmem(vma)))
-		return false;
-
-	/*
-	 * If user requested uffd-wp but not enabled pte markers for
-	 * uffd-wp, then shmem & hugetlbfs are not supported but only
-	 * anonymous.
-	 */
-	if (!uffd_supports_wp_marker() && (vm_flags & VM_UFFD_WP) &&
-	    !vma_is_anonymous(vma))
-		return false;
-
-	/* By default, allow any of anon|shmem|hugetlb */
-	return vma_is_anonymous(vma) || is_vm_hugetlb_page(vma) ||
-	    vma_is_shmem(vma);
-}
+bool vma_can_userfault(struct vm_area_struct *vma, unsigned long vm_flags);
 
 extern int dup_userfaultfd(struct vm_area_struct *, struct list_head *);
 extern void dup_userfaultfd_complete(struct list_head *);
mm/userfaultfd.c +20 -0
@@ -891,3 +891,23 @@ out_unlock:
 	mmap_read_unlock(dst_mm);
 	return err;
 }
+
+bool vma_can_userfault(struct vm_area_struct *vma, unsigned long vm_flags)
+{
+	if ((vm_flags & VM_UFFD_MINOR) &&
+	    (!is_vm_hugetlb_page(vma) && !vma_is_shmem(vma)))
+		return false;
+
+	/*
+	 * If user requested uffd-wp but not enabled pte markers for
+	 * uffd-wp, then shmem & hugetlbfs are not supported but only
+	 * anonymous.
+	 */
+	if (!uffd_supports_wp_marker() && (vm_flags & VM_UFFD_WP) &&
+	    !vma_is_anonymous(vma))
+		return false;
+
+	/* By default, allow any of anon|shmem|hugetlb */
+	return vma_is_anonymous(vma) || is_vm_hugetlb_page(vma) ||
+	    vma_is_shmem(vma);
+}
author maintainer
#81 lore raw

PATCH 81/166 userfaultfd: prevent registration of special VMAs

In reply to Greg Kroah-Hartman

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

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

From: "Mike Rapoport (Microsoft)" <[email protected]>

[ Upstream commit 3c58f641e813c3c71039f8fd4d4e2a3aab713288 ]

Vova Tokarev says:

userfaultfd allows registration on shadow stack VMAs. With userfaultfd access, you can register on the shadow stack, discard a page ... and inject a page with chosen return addresses via UFFDIO_COPY.

Update vma_can_userfault() to reject VM_SHADOW_STACK.

While on it, also reject VM_SPECIAL so that if a driver would implement vm_uffd_ops, it wouldn't be possible to register special VMAs with userfaultfd.

Since VM_SPECIAL includes VM_DONTEXPAND which is set but hugetlb, exclude hugetlb VMAs from the check for VM_SPECIAL.

---
 mm/userfaultfd.c |    6 ++++++
 1 file changed, 6 insertions(+)
mm/userfaultfd.c +6 -0
@@ -894,6 +894,12 @@ out_unlock:
 
 bool vma_can_userfault(struct vm_area_struct *vma, unsigned long vm_flags)
 {
+	if (vma->vm_flags & VM_SHADOW_STACK)
+		return false;
+
+	if (!is_vm_hugetlb_page(vma) && (vma->vm_flags & VM_SPECIAL))
+		return false;
+
 	if ((vm_flags & VM_UFFD_MINOR) &&
 	    (!is_vm_hugetlb_page(vma) && !vma_is_shmem(vma)))
 		return false;
author maintainer
#82 lore raw

PATCH 82/166 libceph: fix two unsafe bare decodes in decode_lockers()

In reply to Greg Kroah-Hartman

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

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

From: Pavitra Jha <[email protected]>

[ Upstream commit a109a556115271ca7896dcda7b4b7e45e156c227 ]

decode_lockers() in cls_lock_client.c contains two bare decode operations that allow a malicious or compromised OSD to trigger slab-out-of-bounds reads:

1. ceph_decode_32(p) at the num_lockers field has no preceding bounds check. ceph_start_decoding() accepts struct_len=0 as valid -- the internal ceph_decode_need(p, end, 0, bad) always passes -- so when an OSD sends struct_len=0, ceph_start_decoding() returns success with p == end. The immediately following bare ceph_decode_32(p) then reads 4 bytes past the validated buffer boundary. The garbage value is passed directly to kzalloc_objs() as the locker count.

The sibling function decode_watchers() in osd_client.c already uses ceph_decode_32_safe() after its own ceph_start_decoding() call. decode_lockers() was the only site using the bare variant.

2. ceph_decode_8(p) after the decode_locker() loop has no preceding bounds check. If an OSD crafts num_lockers such that the loop advances p exactly to end, the subsequent bare ceph_decode_8(p) reads one byte past the validated buffer boundary. The result is passed directly into *type, which is used as a lock type discriminator by callers, giving an OSD-controlled one-byte OOB read with direct influence over the lock type field.

Fix both by replacing bare operations with their safe variants:
ceph_decode_32(p) -> ceph_decode_32_safe(p, end, *num_lockers,
err_inval)
ceph_decode_8(p) -> ceph_decode_8_safe(p, end, *type,
err_free_lockers)

The goto targets differ intentionally:
err_inval: is a new label returning -EINVAL directly. It is used for the pre-allocation failure path where *lockers is not yet allocated and must not be passed to ceph_free_lockers().

err_free_lockers: is the existing label. It is used for the post-allocation failure path where *lockers is allocated and must be freed.

ret is set to -EINVAL before ceph_decode_8_safe() so that err_free_lockers returns the correct error code on bounds violation. Without this, err_free_lockers would return a stale ret value (0 from the successful decode_locker() loop), silently swallowing the error.

-EINVAL is correct for both failure paths. The data received from the OSD is structurally malformed. -ENOMEM would misrepresent the failure class to callers and to stable@ backporters triaging error paths.

Attacker model: a malicious or compromised OSD in a multi-tenant Ceph deployment can trigger this against any kernel client that issues the lock.get_info class method (e.g. during RBD exclusive lock acquisition).

[ idryomov: trim changelog, formatting ]

Fixes d4ed4a530562 ("libceph: support for lock.lock_info")
---
 net/ceph/cls_lock_client.c |    8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)
net/ceph/cls_lock_client.c +6 -2
@@ -304,7 +304,7 @@ static int decode_lockers(void **p, void
 	if (ret)
 		return ret;
 
-	*num_lockers = ceph_decode_32(p);
+	ceph_decode_32_safe(p, end, *num_lockers, err_inval);
 	*lockers = kcalloc(*num_lockers, sizeof(**lockers), GFP_NOIO);
 	if (!*lockers)
 		return -ENOMEM;
@@ -315,7 +315,8 @@ static int decode_lockers(void **p, void
 			goto err_free_lockers;
 	}
 
-	*type = ceph_decode_8(p);
+	ret = -EINVAL;
+	ceph_decode_8_safe(p, end, *type, err_free_lockers);
 	s = ceph_extract_encoded_string(p, end, NULL, GFP_NOIO);
 	if (IS_ERR(s)) {
 		ret = PTR_ERR(s);
@@ -325,6 +326,9 @@ static int decode_lockers(void **p, void
 	*tag = s;
 	return 0;
 
+err_inval:
+	return -EINVAL;
+
 err_free_lockers:
 	ceph_free_lockers(*lockers, *num_lockers);
 	return ret;
author maintainer
#83 lore raw

PATCH 83/166 net: move skb_gro_receive_list from udp to core

In reply to Greg Kroah-Hartman

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

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

From: Felix Fietkau <[email protected]>

[ Upstream commit 8928756d53d5b99dcd18073dc7738b8ebdbe7d96 ]

This helper function will be used for TCP fraglist GRO support

Stable-dep-of: e751256486d0 ("net: gro: fix double aggregation of flush-marked skbs")

---
 include/net/gro.h      |    1 +
 net/core/gro.c         |   29 +++++++++++++++++++++++++++++
 net/ipv4/udp_offload.c |   29 -----------------------------
 3 files changed, 30 insertions(+), 29 deletions(-)
include/net/gro.h +1 -0
@@ -442,6 +442,7 @@ static inline __wsum ip6_gro_compute_pse
 }
 
 int skb_gro_receive(struct sk_buff *p, struct sk_buff *skb);
+int skb_gro_receive_list(struct sk_buff *p, struct sk_buff *skb);
 
 /* Pass the currently batched GRO_NORMAL SKBs up to the stack. */
 static inline void gro_normal_list(struct napi_struct *napi)
net/core/gro.c +29 -0
@@ -230,6 +230,35 @@ done:
 	return 0;
 }
 
+int skb_gro_receive_list(struct sk_buff *p, struct sk_buff *skb)
+{
+	if (unlikely(p->len + skb->len >= 65536))
+		return -E2BIG;
+
+	if (NAPI_GRO_CB(p)->last == p)
+		skb_shinfo(p)->frag_list = skb;
+	else
+		NAPI_GRO_CB(p)->last->next = skb;
+
+	skb_pull(skb, skb_gro_offset(skb));
+
+	NAPI_GRO_CB(p)->last = skb;
+	NAPI_GRO_CB(p)->count++;
+	p->data_len += skb->len;
+
+	/* sk ownership - if any - completely transferred to the aggregated packet */
+	skb->destructor = NULL;
+	skb->sk = NULL;
+	p->truesize += skb->truesize;
+	p->len += skb->len;
+
+	skb_shinfo(p)->flags |= skb_shinfo(skb)->flags & SKBFL_SHARED_FRAG;
+
+	NAPI_GRO_CB(skb)->same_flow = 1;
+
+	return 0;
+}
+
 
 static void napi_gro_complete(struct napi_struct *napi, struct sk_buff *skb)
 {
net/ipv4/udp_offload.c +0 -29
@@ -527,35 +527,6 @@ out:
 	return segs;
 }
 
-static int skb_gro_receive_list(struct sk_buff *p, struct sk_buff *skb)
-{
-	if (unlikely(p->len + skb->len >= 65536))
-		return -E2BIG;
-
-	if (NAPI_GRO_CB(p)->last == p)
-		skb_shinfo(p)->frag_list = skb;
-	else
-		NAPI_GRO_CB(p)->last->next = skb;
-
-	skb_pull(skb, skb_gro_offset(skb));
-
-	NAPI_GRO_CB(p)->last = skb;
-	NAPI_GRO_CB(p)->count++;
-	p->data_len += skb->len;
-
-	/* sk ownership - if any - completely transferred to the aggregated packet */
-	skb->destructor = NULL;
-	skb->sk = NULL;
-	p->truesize += skb->truesize;
-	p->len += skb->len;
-
-	skb_shinfo(p)->flags |= skb_shinfo(skb)->flags & SKBFL_SHARED_FRAG;
-
-	NAPI_GRO_CB(skb)->same_flow = 1;
-
-	return 0;
-}
-
 
 #define UDP_GRO_CNT_MAX 64
 static struct sk_buff *udp_gro_receive_segment(struct list_head *head,
author maintainer
#84 lore raw

PATCH 84/166 net: gro: fix double aggregation of flush-marked skbs

In reply to Greg Kroah-Hartman

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

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

From: Shiming Cheng <[email protected]>

[ Upstream commit e751256486d0ded20f5a9f9863467f1dce65142f ]

Commit 0ab03f353d36 ("net-gro: Fix GRO flush when receiving a GSO packet.") added a flush check to skb_gro_receive(), but skb_gro_receive_list() lacks the same validation.

As a result, packets marked with NAPI_GRO_CB(skb)->flush may still be re-aggregated.

This allows already-GRO'd packets with existing frag_list to be re-aggregated into a new GRO session, corrupting the frag_list chain structure. When skb_segment() attempts to unpack these malformed packets, it encounters invalid state and triggers a kernel panic.

Scenario (Tethering/Device forwarding):
1. Driver: Generated aggregated packet P1 via LRO with frag_list
2. Dev A: Receives aggregated fraglist packet and flush flag set
3. Dev A: Re-enters GRO, skb_gro_receive_list() is called
4. Missing flush check allows re-aggregation despite flush flag
5. Frag_list chain becomes corrupted (loops or dangling refs)
6. Dev B: TX path calls skb_segment(), crashes on corrupted frag_list

Root cause in skb_segment():
The check at line ~4891:

    if (hsize <= 0 && i >= nfrags && skb_headlen(list_skb) &&
        (skb_headlen(list_skb) == len || sg)) {

When frag_list is corrupted by double aggregation, when list_skb is a NULL pointer from skb->next, skb_headlen(list_skb) dereference NULL/corrupted pointers occurs.

Call Trace:
 skb_headlen(NULL skb)
 skb_segment
 tcp_gso_segment
 tcp4_gso_segment
 inet_gso_segment
 skb_mac_gso_segment
 __skb_gso_segment
 skb_gso_segment
 validate_xmit_skb
 validate_xmit_skb_list
 sch_direct_xmit
 qdisc_restart
 __qdisc_run
 qdisc_run
 net_tx_action

Fix: Add NAPI_GRO_CB(skb)->flush validation to the early-return check in skb_gro_receive_list(), matching the defensive programming pattern of skb_gro_receive().

---
 net/core/gro.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
net/core/gro.c +3 -1
@@ -232,7 +232,9 @@ done:
 
 int skb_gro_receive_list(struct sk_buff *p, struct sk_buff *skb)
 {
-	if (unlikely(p->len + skb->len >= 65536))
+	/* make sure to check flush flag and to not merge */
+	if (unlikely(p->len + skb->len >= 65536 ||
+		     NAPI_GRO_CB(skb)->flush))
 		return -E2BIG;
 
 	if (NAPI_GRO_CB(p)->last == p)
author maintainer
#85 lore raw

PATCH 85/166 net/sched: serialize qdisc_rtab_list against concurrent get/put

In reply to Greg Kroah-Hartman

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

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

From: Aldo Ariel Panzardo <[email protected]>

[ Upstream commit f43ee0c0730d6191629b5ee1ceae27b1ebfdc047 ]

qdisc_get_rtab() and qdisc_put_rtab() mutate the process-global singly linked list qdisc_rtab_list and a plain non-atomic 'int refcnt' with no lock. This was only safe because every caller historically held the RTNL mutex, which serialized all rate-table lookups, inserts and frees.

That invariant no longer holds. cls_flower sets TCF_PROTO_OPS_DOIT_UNLOCKED, so tc_new_tfilter() keeps rtnl_held == false for it and sets TCA_ACT_FLAGS_NO_RTNL. That flag propagates through tcf_exts_validate_ex() -> tcf_action_init() -> tcf_action_init_1() -> tcf_police_init(), which calls qdisc_get_rtab()/qdisc_put_rtab() with the RTNL mutex NOT held. Two RTM_NEWTFILTER requests on different CPUs, each adding a flower filter with a police action carrying the same rate, then race on qdisc_rtab_list and on the non-atomic refcnt, leading to a use-after-free / double-free of the kmalloc-2k struct qdisc_rate_table. qdisc_rtab_list is a single global (not per-netns), so the corrupted object is shared system-wide.

  BUG: KASAN: slab-use-after-free in qdisc_put_rtab+0x12f/0x160
   qdisc_put_rtab+0x12f/0x160
   tcf_police_init+0xda9/0x1590
   tcf_action_init_1+0x460/0x6b0
   tcf_action_init+0x439/0xa40
   tcf_exts_validate_ex+0x42d/0x550
   fl_change+0xddd/0x7da0
   tc_new_tfilter+0xaa7/0x2420
   rtnetlink_rcv_msg+0x95e/0xe90
  which belongs to the cache kmalloc-2k of size 2048

Protect qdisc_rtab_list and the refcount with a dedicated spinlock. The (sleeping, GFP_KERNEL) allocation in qdisc_get_rtab() is performed before taking the lock; if a concurrent inserter added an identical table in the meantime the freshly allocated one is freed under the lock, so no duplicate is leaked. qdisc_put_rtab() now decrements the refcount and unlinks under the same lock.

---
 net/sched/sch_api.c |   24 +++++++++++++++++++-----
 1 file changed, 19 insertions(+), 5 deletions(-)
net/sched/sch_api.c +19 -5
@@ -413,12 +413,13 @@ static __u8 __detect_linklayer(struct tc
 }
 
 static struct qdisc_rate_table *qdisc_rtab_list;
+static DEFINE_SPINLOCK(qdisc_rtab_lock);
 
 struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r,
 					struct nlattr *tab,
 					struct netlink_ext_ack *extack)
 {
-	struct qdisc_rate_table *rtab;
+	struct qdisc_rate_table *rtab, *new_rtab;
 
 	if (tab == NULL || r->rate == 0 ||
 	    r->cell_log == 0 || r->cell_log >= 32 ||
@@ -427,15 +428,20 @@ struct qdisc_rate_table *qdisc_get_rtab(
 		return NULL;
 	}
 
+	new_rtab = kmalloc(sizeof(*new_rtab), GFP_KERNEL);
+
+	spin_lock(&qdisc_rtab_lock);
 	for (rtab = qdisc_rtab_list; rtab; rtab = rtab->next) {
 		if (!memcmp(&rtab->rate, r, sizeof(struct tc_ratespec)) &&
 		    !memcmp(&rtab->data, nla_data(tab), 1024)) {
 			rtab->refcnt++;
+			spin_unlock(&qdisc_rtab_lock);
+			kfree(new_rtab);
 			return rtab;
 		}
 	}
 
-	rtab = kmalloc(sizeof(*rtab), GFP_KERNEL);
+	rtab = new_rtab;
 	if (rtab) {
 		rtab->rate = *r;
 		rtab->refcnt = 1;
@@ -447,6 +453,7 @@ struct qdisc_rate_table *qdisc_get_rtab(
 	} else {
 		NL_SET_ERR_MSG(extack, "Failed to allocate new qdisc rate table");
 	}
+	spin_unlock(&qdisc_rtab_lock);
 	return rtab;
 }
 EXPORT_SYMBOL(qdisc_get_rtab);
@@ -455,18 +462,25 @@ void qdisc_put_rtab(struct qdisc_rate_ta
 {
 	struct qdisc_rate_table *rtab, **rtabp;
 
-	if (!tab || --tab->refcnt)
+	if (!tab)
+		return;
+
+	spin_lock(&qdisc_rtab_lock);
+	if (--tab->refcnt) {
+		spin_unlock(&qdisc_rtab_lock);
 		return;
+	}
 
 	for (rtabp = &qdisc_rtab_list;
 	     (rtab = *rtabp) != NULL;
 	     rtabp = &rtab->next) {
 		if (rtab == tab) {
 			*rtabp = rtab->next;
-			kfree(rtab);
-			return;
+			break;
 		}
 	}
+	spin_unlock(&qdisc_rtab_lock);
+	kfree(tab);
 }
 EXPORT_SYMBOL(qdisc_put_rtab);
 
author maintainer
#86 lore raw

PATCH 86/166 super: fix emergency thaw deadlock on frozen block devices

In reply to Greg Kroah-Hartman

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

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

From: Christian Brauner <[email protected]>

[ Upstream commit 749d7aa0377aae32af8c0a4ad43371e7bf830ab5 ]

do_thaw_all_callback() calls bdev_thaw() while holding sb->s_umount exclusively. If the block device was frozen via bdev_freeze() dropping the last block layer freeze reference calls fs_bdev_thaw() which reacquires s_umount:

do_thaw_all_callback(sb)

    super_lock_excl(sb)                     # holds sb->s_umount
    bdev_thaw(sb->s_bdev)
      mutex_lock(&bdev->bd_fsfreeze_mutex)
      # bd_fsfreeze_count drops 1 -> 0
      bd_holder_ops->thaw == fs_bdev_thaw
        get_bdev_super(bdev)
          bdev_super_lock(bdev, true)
            super_lock(sb, true)
              down_write(&sb->s_umount)     # same task: deadlock

The emergency thaw worker deadlocks against itself holding both s_umount and bd_fsfreeze_mutex. That fscks any subsequent unmount, freeze, or thaw of that filesystem and block device.

  [   81.878470] sysrq: Show Blocked State
  [   81.880140] task:kworker/0:1     state:D stack:0     pid:11    tgid:11    ppid:2      task_flags:0x4208060 flags:0x00080000
  [   81.884876] Workqueue: events do_thaw_all
  [   81.886656] Call Trace:
  [   81.887759]  <TASK>
  [   81.888763]  __schedule+0x579/0x1420
  [   81.890372]  schedule+0x3a/0x100
  [   81.891794]  schedule_preempt_disabled+0x15/0x30
  [   81.893848]  rwsem_down_write_slowpath+0x1ea/0x900
  [   81.895191]  ? __pfx_do_thaw_all_callback+0x10/0x10
  [   81.896528]  down_write+0xbd/0xc0
  [   81.897505]  super_lock+0x91/0x180
  [   81.898457]  ? __mutex_lock+0xa99/0x1140
  [   81.900748]  ? __mutex_unlock_slowpath+0x1f/0x400
  [   81.902069]  bdev_super_lock+0x5b/0x150
  [   81.903132]  get_bdev_super+0x10/0x60
  [   81.904042]  fs_bdev_thaw+0x23/0xf0
  [   81.904755]  bdev_thaw+0x82/0x100
  [   81.905484]  do_thaw_all_callback+0x2c/0x50
  [   81.906298]  __iterate_supers+0x5d/0x130
  [   81.907067]  do_thaw_all+0x20/0x40
  [   81.907739]  process_one_work+0x206/0x5e0
  [   81.908545]  worker_thread+0x1e2/0x3c0
  [   81.909339]  ? __pfx_worker_thread+0x10/0x10
  [   81.910171]  kthread+0xf4/0x130
  [   81.910799]  ? __pfx_kthread+0x10/0x10
  [   81.911528]  ret_from_fork+0x2e2/0x3b0
  [   81.912259]  ? __pfx_kthread+0x10/0x10
  [   81.913010]  ret_from_fork_asm+0x1a/0x30
  [   81.913806]  </TASK>

bdev_super_lock() even documents the violated requirement with lockdep_assert_not_held(&sb->s_umount).

Acquiring bd_fsfreeze_mutex under s_umount also inverts the bd_fsfreeze_mutex vs. s_umount ordering established by bdev_{freeze,thaw}() and can thus ABBA against a concurrent block-layer freeze even when the recursive path isn't hit.

Fix this by not holding s_umount around the bdev_thaw() loop at all. Pin the superblock with an active reference instead as filesystems_freeze_callback() does. The active reference keeps the superblock from being shut down and so ->s_bdev stays valid without holding s_umount. The block-layer-held freeze is dropped by fs_bdev_thaw() with FREEZE_MAY_NEST | FREEZE_HOLDER_USERSPACE exactly as a regular unfreeze would and thaw_super_locked() handles filesystem-level freezes as before.

The emergency thaw path has deadlocked like this in one form or another for a long long time but the current exclusively-held shape dates back to commit [1] where thaw_bdev() already ended in thaw_super() with s_umount held by do_thaw_all_callback().

---
 fs/super.c |   22 +++++++++++++++-------
 1 file changed, 15 insertions(+), 7 deletions(-)
fs/super.c +15 -7
@@ -1217,16 +1217,24 @@ void emergency_remount(void)
 
 static void do_thaw_all_callback(struct super_block *sb)
 {
-	bool born = super_lock_excl(sb);
+	bool active = false;
 
-	if (born && sb->s_root) {
-		if (IS_ENABLED(CONFIG_BLOCK))
-			while (sb->s_bdev && !thaw_bdev(sb->s_bdev))
-				pr_warn("Emergency Thaw on %pg\n", sb->s_bdev);
+	if (super_lock_excl(sb))
+		active = atomic_inc_not_zero(&sb->s_active);
+	super_unlock_excl(sb);
+	if (!active)
+		return;
+
+	/* thaw_bdev() acquires s_umount so it must not be held here */
+	if (IS_ENABLED(CONFIG_BLOCK))
+		while (sb->s_bdev && !thaw_bdev(sb->s_bdev))
+			pr_warn("Emergency Thaw on %pg\n", sb->s_bdev);
+
+	if (super_lock_excl(sb))
 		thaw_super_locked(sb, FREEZE_HOLDER_USERSPACE);
-	} else {
+	else
 		super_unlock_excl(sb);
-	}
+	deactivate_super(sb);
 }
 
 static void do_thaw_all(struct work_struct *work)
author maintainer
#87 lore raw

PATCH 87/166 smb: move smb_version_values to common/smbglob.h

In reply to Greg Kroah-Hartman

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

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

From: ZhangGuoDong <[email protected]>

[ Upstream commit 34cf191bb6a349dc88ec2c4f6355fe006ac669e0 ]

Merge the struct members of the server and the client:

- req_capabilities: from client
- header_preamble_size: from client
- cap_unicode: from client
- capabilities: from server, rename to req_capabilities
- max_read_size: from server
- max_write_size: from server
- max_trans_size: from server
- max_credits: from server
- create_durable_size: from server
- create_durable_v2_size: from server
- create_mxac_size: from server
- create_disk_id_size: from server
- create_posix_size: from server

Then move duplicate definitions to common header file.

Stable-dep-of: cfc0b8e5080a ("ksmbd: validate minimum PDU size for transform requests")

---
 fs/smb/client/cifsglob.h   |   21 ---------------------
 fs/smb/common/cifsglob.h   |   31 +++++++++++++++++++++++++++++++
 fs/smb/server/smb2misc.c   |    2 +-
 fs/smb/server/smb2ops.c    |   32 ++++++++++++++++----------------
 fs/smb/server/smb2pdu.c    |   10 +++++-----
 fs/smb/server/smb_common.h |   29 -----------------------------
 6 files changed, 53 insertions(+), 72 deletions(-)
fs/smb/client/cifsglob.h +0 -21
@@ -606,27 +606,6 @@ struct smb_version_operations {
 				      const char *symname);
 };
 
-struct smb_version_values {
-	char		*version_string;
-	__u16		protocol_id;
-	__u32		req_capabilities;
-	__u32		large_lock_type;
-	__u32		exclusive_lock_type;
-	__u32		shared_lock_type;
-	__u32		unlock_lock_type;
-	size_t		header_preamble_size;
-	size_t		header_size;
-	size_t		max_header_size;
-	size_t		read_rsp_size;
-	__le16		lock_cmd;
-	unsigned int	cap_unix;
-	unsigned int	cap_nt_find;
-	unsigned int	cap_large_files;
-	__u16		signing_enabled;
-	__u16		signing_required;
-	size_t		create_lease_size;
-};
-
 #define HEADER_SIZE(server) (server->vals->header_size)
 #define MAX_HEADER_SIZE(server) (server->vals->max_header_size)
 #define HEADER_PREAMBLE_SIZE(server) (server->vals->header_preamble_size)
fs/smb/common/cifsglob.h +31 -0
@@ -9,6 +9,37 @@
 #ifndef _COMMON_CIFS_GLOB_H
 #define _COMMON_CIFS_GLOB_H
 
+struct smb_version_values {
+	char		*version_string;
+	__u16		protocol_id;
+	__le16		lock_cmd;
+	__u32		req_capabilities;
+	__u32		max_read_size;
+	__u32		max_write_size;
+	__u32		max_trans_size;
+	__u32		max_credits;
+	__u32		large_lock_type;
+	__u32		exclusive_lock_type;
+	__u32		shared_lock_type;
+	__u32		unlock_lock_type;
+	size_t		header_preamble_size;
+	size_t		header_size;
+	size_t		max_header_size;
+	size_t		read_rsp_size;
+	unsigned int	cap_unix;
+	unsigned int	cap_nt_find;
+	unsigned int	cap_large_files;
+	unsigned int	cap_unicode;
+	__u16		signing_enabled;
+	__u16		signing_required;
+	size_t		create_lease_size;
+	size_t		create_durable_size;
+	size_t		create_durable_v2_size;
+	size_t		create_mxac_size;
+	size_t		create_disk_id_size;
+	size_t		create_posix_size;
+};
+
 static inline void inc_rfc1001_len(void *buf, int count)
 {
 	be32_add_cpu((__be32 *)buf, count);
fs/smb/server/smb2misc.c +1 -1
@@ -465,7 +465,7 @@ int ksmbd_smb2_check_message(struct ksmb
 	}
 
 validate_credit:
-	if ((work->conn->vals->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU) &&
+	if ((work->conn->vals->req_capabilities & SMB2_GLOBAL_CAP_LARGE_MTU) &&
 	    smb2_validate_credit_charge(work->conn, hdr))
 		return 1;
 
fs/smb/server/smb2ops.c +16 -16
@@ -15,7 +15,7 @@
 static struct smb_version_values smb21_server_values = {
 	.version_string = SMB21_VERSION_STRING,
 	.protocol_id = SMB21_PROT_ID,
-	.capabilities = SMB2_GLOBAL_CAP_LARGE_MTU,
+	.req_capabilities = SMB2_GLOBAL_CAP_LARGE_MTU,
 	.max_read_size = SMB21_DEFAULT_IOSIZE,
 	.max_write_size = SMB21_DEFAULT_IOSIZE,
 	.max_trans_size = SMB21_DEFAULT_IOSIZE,
@@ -41,7 +41,7 @@ static struct smb_version_values smb21_s
 static struct smb_version_values smb30_server_values = {
 	.version_string = SMB30_VERSION_STRING,
 	.protocol_id = SMB30_PROT_ID,
-	.capabilities = SMB2_GLOBAL_CAP_LARGE_MTU,
+	.req_capabilities = SMB2_GLOBAL_CAP_LARGE_MTU,
 	.max_read_size = SMB3_DEFAULT_IOSIZE,
 	.max_write_size = SMB3_DEFAULT_IOSIZE,
 	.max_trans_size = SMB3_DEFAULT_TRANS_SIZE,
@@ -68,7 +68,7 @@ static struct smb_version_values smb30_s
 static struct smb_version_values smb302_server_values = {
 	.version_string = SMB302_VERSION_STRING,
 	.protocol_id = SMB302_PROT_ID,
-	.capabilities = SMB2_GLOBAL_CAP_LARGE_MTU,
+	.req_capabilities = SMB2_GLOBAL_CAP_LARGE_MTU,
 	.max_read_size = SMB3_DEFAULT_IOSIZE,
 	.max_write_size = SMB3_DEFAULT_IOSIZE,
 	.max_trans_size = SMB3_DEFAULT_TRANS_SIZE,
@@ -95,7 +95,7 @@ static struct smb_version_values smb302_
 static struct smb_version_values smb311_server_values = {
 	.version_string = SMB311_VERSION_STRING,
 	.protocol_id = SMB311_PROT_ID,
-	.capabilities = SMB2_GLOBAL_CAP_LARGE_MTU,
+	.req_capabilities = SMB2_GLOBAL_CAP_LARGE_MTU,
 	.max_read_size = SMB3_DEFAULT_IOSIZE,
 	.max_write_size = SMB3_DEFAULT_IOSIZE,
 	.max_trans_size = SMB3_DEFAULT_TRANS_SIZE,
@@ -204,7 +204,7 @@ void init_smb2_1_server(struct ksmbd_con
 	conn->signing_algorithm = SIGNING_ALG_HMAC_SHA256_LE;
 
 	if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES)
-		conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING;
+		conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_LEASING;
 }
 
 /**
@@ -221,20 +221,20 @@ void init_smb3_0_server(struct ksmbd_con
 	conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE;
 
 	if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES)
-		conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING |
+		conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_LEASING |
 			SMB2_GLOBAL_CAP_DIRECTORY_LEASING;
 
 	if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION &&
 	    conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION)
-		conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION;
+		conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION;
 
 	if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION ||
 	    (!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF) &&
 	     conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION))
-		conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION;
+		conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION;
 
 	if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL)
-		conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
+		conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
 }
 
 /**
@@ -251,19 +251,19 @@ void init_smb3_02_server(struct ksmbd_co
 	conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE;
 
 	if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES)
-		conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING |
+		conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_LEASING |
 			SMB2_GLOBAL_CAP_DIRECTORY_LEASING;
 
 	if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION ||
 	    (!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF) &&
 	     conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION))
-		conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION;
+		conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION;
 
 	if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL)
-		conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
+		conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
 
 	if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE)
-		conn->vals->capabilities |= SMB2_GLOBAL_CAP_PERSISTENT_HANDLES;
+		conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_PERSISTENT_HANDLES;
 }
 
 /**
@@ -280,14 +280,14 @@ int init_smb3_11_server(struct ksmbd_con
 	conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE;
 
 	if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES)
-		conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING |
+		conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_LEASING |
 			SMB2_GLOBAL_CAP_DIRECTORY_LEASING;
 
 	if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL)
-		conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
+		conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
 
 	if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE)
-		conn->vals->capabilities |= SMB2_GLOBAL_CAP_PERSISTENT_HANDLES;
+		conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_PERSISTENT_HANDLES;
 
 	INIT_LIST_HEAD(&conn->preauth_sess_table);
 	return 0;
fs/smb/server/smb2pdu.c +5 -5
@@ -284,7 +284,7 @@ int init_smb2_neg_rsp(struct ksmbd_work
 	/* Not setting conn guid rsp->ServerGUID, as it
 	 * not used by client for identifying connection
 	 */
-	rsp->Capabilities = cpu_to_le32(conn->vals->capabilities);
+	rsp->Capabilities = cpu_to_le32(conn->vals->req_capabilities);
 	/* Default Max Message Size till SMB2.0, 64K*/
 	rsp->MaxTransactSize = cpu_to_le32(conn->vals->max_trans_size);
 	rsp->MaxReadSize = cpu_to_le32(conn->vals->max_read_size);
@@ -963,7 +963,7 @@ bool smb3_encryption_negotiated(struct k
 	 * SMB 3.0 and 3.0.2 dialects use the SMB2_GLOBAL_CAP_ENCRYPTION flag.
 	 * SMB 3.1.1 uses the cipher_type field.
 	 */
-	return (conn->vals->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION) ||
+	return (conn->vals->req_capabilities & SMB2_GLOBAL_CAP_ENCRYPTION) ||
 	    conn->cipher_type;
 }
 
@@ -1216,7 +1216,7 @@ int smb2_handle_negotiate(struct ksmbd_w
 		rc = -EINVAL;
 		goto err_out;
 	}
-	rsp->Capabilities = cpu_to_le32(conn->vals->capabilities);
+	rsp->Capabilities = cpu_to_le32(conn->vals->req_capabilities);
 
 	/* For stats */
 	conn->connection_type = conn->dialect;
@@ -3480,7 +3480,7 @@ int smb2_open(struct ksmbd_work *work)
 	share_ret = ksmbd_smb_check_shared_mode(fp->filp, fp);
 	if (!test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_OPLOCKS) ||
 	    (req_op_level == SMB2_OPLOCK_LEVEL_LEASE &&
-	     !(conn->vals->capabilities & SMB2_GLOBAL_CAP_LEASING))) {
+	     !(conn->vals->req_capabilities & SMB2_GLOBAL_CAP_LEASING))) {
 		if (share_ret < 0 && !S_ISDIR(file_inode(fp->filp)->i_mode)) {
 			rc = share_ret;
 			goto err_out1;
@@ -8081,7 +8081,7 @@ static int fsctl_validate_negotiate_info
 		goto err_out;
 	}
 
-	neg_rsp->Capabilities = cpu_to_le32(conn->vals->capabilities);
+	neg_rsp->Capabilities = cpu_to_le32(conn->vals->req_capabilities);
 	memset(neg_rsp->Guid, 0, SMB2_CLIENT_GUID_SIZE);
 	neg_rsp->SecurityMode = cpu_to_le16(conn->srv_sec_mode);
 	neg_rsp->Dialect = cpu_to_le16(conn->dialect);
fs/smb/server/smb_common.h +0 -29
@@ -336,35 +336,6 @@ struct file_id_full_dir_info {
 	char FileName[];
 } __packed; /* level 0x105 FF rsp data */
 
-struct smb_version_values {
-	char		*version_string;
-	__u16		protocol_id;
-	__le16		lock_cmd;
-	__u32		capabilities;
-	__u32		max_read_size;
-	__u32		max_write_size;
-	__u32		max_trans_size;
-	__u32		max_credits;
-	__u32		large_lock_type;
-	__u32		exclusive_lock_type;
-	__u32		shared_lock_type;
-	__u32		unlock_lock_type;
-	size_t		header_size;
-	size_t		max_header_size;
-	size_t		read_rsp_size;
-	unsigned int	cap_unix;
-	unsigned int	cap_nt_find;
-	unsigned int	cap_large_files;
-	__u16		signing_enabled;
-	__u16		signing_required;
-	size_t		create_lease_size;
-	size_t		create_durable_size;
-	size_t		create_durable_v2_size;
-	size_t		create_mxac_size;
-	size_t		create_disk_id_size;
-	size_t		create_posix_size;
-};
-
 struct filesystem_posix_info {
 	/* For undefined recommended transfer size return -1 in that field */
 	__le32 OptimalTransferSize;  /* bsize on some os, iosize on other os */
author maintainer
#88 lore raw

PATCH 88/166 smb: move get_rfc1002_len() to common/smbglob.h

In reply to Greg Kroah-Hartman

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

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

From: ZhangGuoDong <[email protected]>

[ Upstream commit 36c31540cf5279262bfd148d8537cd04866499f2 ]

Rename get_rfc1002_length() to get_rfc1002_len(), then move duplicate definitions to common header file.

Stable-dep-of: cfc0b8e5080a ("ksmbd: validate minimum PDU size for transform requests")

---
 fs/smb/client/cifsglob.h   |    6 ------
 fs/smb/client/cifssmb.c    |    6 +++---
 fs/smb/client/connect.c    |    2 +-
 fs/smb/client/transport.c  |    8 ++++----
 fs/smb/common/cifsglob.h   |    5 +++++
 fs/smb/server/smb_common.h |    5 -----
 6 files changed, 13 insertions(+), 19 deletions(-)
fs/smb/client/cifsglob.h +0 -6
@@ -643,12 +643,6 @@ struct cifs_mnt_data {
 	int flags;
 };
 
-static inline unsigned int
-get_rfc1002_length(void *buf)
-{
-	return be32_to_cpu(*((__be32 *)buf)) & 0xffffff;
-}
-
 struct TCP_Server_Info {
 	struct list_head tcp_ses_list;
 	struct list_head smb_ses_list;
fs/smb/client/cifssmb.c +3 -3
@@ -595,7 +595,7 @@ CIFSSMBEcho(struct TCP_Server_Info *serv
 
 	iov[0].iov_len = 4;
 	iov[0].iov_base = smb;
-	iov[1].iov_len = get_rfc1002_length(smb);
+	iov[1].iov_len = get_rfc1002_len(smb);
 	iov[1].iov_base = (char *)smb + 4;
 
 	rc = cifs_call_async(server, &rqst, NULL, cifs_echo_callback, NULL,
@@ -1363,7 +1363,7 @@ cifs_async_readv(struct cifs_readdata *r
 	rdata->iov[0].iov_base = smb;
 	rdata->iov[0].iov_len = 4;
 	rdata->iov[1].iov_base = (char *)smb + 4;
-	rdata->iov[1].iov_len = get_rfc1002_length(smb);
+	rdata->iov[1].iov_len = get_rfc1002_len(smb);
 
 	kref_get(&rdata->refcount);
 	rc = cifs_call_async(tcon->ses->server, &rqst, cifs_readv_receive,
@@ -1707,7 +1707,7 @@ cifs_async_writev(struct cifs_writedata
 	/* 4 for RFC1001 length + 1 for BCC */
 	iov[0].iov_len = 4;
 	iov[0].iov_base = smb;
-	iov[1].iov_len = get_rfc1002_length(smb) + 1;
+	iov[1].iov_len = get_rfc1002_len(smb) + 1;
 	iov[1].iov_base = (char *)smb + 4;
 
 	rqst.rq_iov = iov;
fs/smb/client/connect.c +1 -1
@@ -1230,7 +1230,7 @@ cifs_demultiplex_thread(void *p)
 		 * The right amount was read from socket - 4 bytes,
 		 * so we can now interpret the length field.
 		 */
-		pdu_length = get_rfc1002_length(buf);
+		pdu_length = get_rfc1002_len(buf);
 
 		cifs_dbg(FYI, "RFC1002 header 0x%x\n", pdu_length);
 		if (!is_smb_response(server, buf[0]))
fs/smb/client/transport.c +4 -4
@@ -882,7 +882,7 @@ SendReceiveNoRsp(const unsigned int xid,
 	int resp_buf_type;
 
 	iov[0].iov_base = in_buf;
-	iov[0].iov_len = get_rfc1002_length(in_buf) + 4;
+	iov[0].iov_len = get_rfc1002_len(in_buf) + 4;
 	flags |= CIFS_NO_RSP_BUF;
 	rc = SendReceive2(xid, ses, iov, 1, &resp_buf_type, flags, &rsp_iov);
 	cifs_dbg(NOISY, "SendRcvNoRsp flags %d rc %d\n", flags, rc);
@@ -942,7 +942,7 @@ int
 cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
 		   bool log_error)
 {
-	unsigned int len = get_rfc1002_length(mid->resp_buf) + 4;
+	unsigned int len = get_rfc1002_len(mid->resp_buf) + 4;
 
 	dump_smb(mid->resp_buf, min_t(u32, 92, len));
 
@@ -1462,7 +1462,7 @@ SendReceive(const unsigned int xid, stru
 		goto out;
 	}
 
-	*pbytes_returned = get_rfc1002_length(midQ->resp_buf);
+	*pbytes_returned = get_rfc1002_len(midQ->resp_buf);
 	memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4);
 	rc = cifs_check_receive(midQ, server, 0);
 out:
@@ -1647,7 +1647,7 @@ SendReceiveBlockingLock(const unsigned i
 		goto out;
 	}
 
-	*pbytes_returned = get_rfc1002_length(midQ->resp_buf);
+	*pbytes_returned = get_rfc1002_len(midQ->resp_buf);
 	memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4);
 	rc = cifs_check_receive(midQ, server, 0);
 out:
fs/smb/common/cifsglob.h +5 -0
@@ -40,6 +40,11 @@ struct smb_version_values {
 	size_t		create_posix_size;
 };
 
+static inline unsigned int get_rfc1002_len(void *buf)
+{
+	return be32_to_cpu(*((__be32 *)buf)) & 0xffffff;
+}
+
 static inline void inc_rfc1001_len(void *buf, int count)
 {
 	be32_add_cpu((__be32 *)buf, count);
fs/smb/server/smb_common.h +0 -5
@@ -421,9 +421,4 @@ unsigned int ksmbd_server_side_copy_max_
 unsigned int ksmbd_server_side_copy_max_total_size(void);
 bool is_asterisk(char *p);
 __le32 smb_map_generic_desired_access(__le32 daccess);
-
-static inline unsigned int get_rfc1002_len(void *buf)
-{
-	return be32_to_cpu(*((__be32 *)buf)) & 0xffffff;
-}
 #endif /* __SMB_COMMON_H__ */
author maintainer
#89 lore raw

PATCH 89/166 smb/server: rename include guard in smb_common.h

In reply to Greg Kroah-Hartman

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

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

From: ChenXiaoSong <[email protected]>

[ Upstream commit 01ab0d1640e379f0a0d6602250b33ff2b45e9560 ]

Make the include guard more descriptive to avoid conflicts with include guards that may be used in the future.

Stable-dep-of: cfc0b8e5080a ("ksmbd: validate minimum PDU size for transform requests")

---
 fs/smb/server/smb_common.h |    6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
fs/smb/server/smb_common.h +3 -3
@@ -3,8 +3,8 @@
  *   Copyright (C) 2018 Samsung Electronics Co., Ltd.
  */
 
-#ifndef __SMB_COMMON_H__
-#define __SMB_COMMON_H__
+#ifndef __SMB_SERVER_COMMON_H__
+#define __SMB_SERVER_COMMON_H__
 
 #include <linux/kernel.h>
 
@@ -421,4 +421,4 @@ unsigned int ksmbd_server_side_copy_max_
 unsigned int ksmbd_server_side_copy_max_total_size(void);
 bool is_asterisk(char *p);
 __le32 smb_map_generic_desired_access(__le32 daccess);
-#endif /* __SMB_COMMON_H__ */
+#endif /* __SMB_SERVER_COMMON_H__ */
author maintainer
#90 lore raw

PATCH 90/166 ksmbd: rename smb2_get_msg to smb_get_msg

In reply to Greg Kroah-Hartman

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

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

From: Namjae Jeon <[email protected]>

[ Upstream commit 0b444cfd8b74ebce421ccd96eac9c495e536c92e ]

With the removal of the RFC1002 length field from the SMB header, smb2_get_msg is now used to get the smb1 request from the request buffer. Since this function is no longer exclusive to smb2 and now supports smb1 as well, This patch rename it to smb_get_msg to better reflect its usage.

Stable-dep-of: cfc0b8e5080a ("ksmbd: validate minimum PDU size for transform requests")

---
 fs/smb/server/auth.c       |    4 +-
 fs/smb/server/connection.c |    2 -
 fs/smb/server/oplock.c     |    8 ++---
 fs/smb/server/smb2pdu.c    |   70 ++++++++++++++++++++++-----------------------
 fs/smb/server/smb2pdu.h    |    9 -----
 fs/smb/server/smb_common.c |    6 +--
 fs/smb/server/smb_common.h |    9 +++++
 7 files changed, 54 insertions(+), 54 deletions(-)
fs/smb/server/auth.c +2 -2
@@ -920,7 +920,7 @@ int ksmbd_gen_preauth_integrity_hash(str
 				     __u8 *pi_hash)
 {
 	int rc;
-	struct smb2_hdr *rcv_hdr = smb2_get_msg(buf);
+	struct smb2_hdr *rcv_hdr = smb_get_msg(buf);
 	char *all_bytes_msg = (char *)&rcv_hdr->ProtocolId;
 	int msg_size = get_rfc1002_len(buf);
 	struct ksmbd_crypto_ctx *ctx = NULL;
@@ -1107,7 +1107,7 @@ int ksmbd_crypt_message(struct ksmbd_wor
 			unsigned int nvec, int enc)
 {
 	struct ksmbd_conn *conn = work->conn;
-	struct smb2_transform_hdr *tr_hdr = smb2_get_msg(iov[0].iov_base);
+	struct smb2_transform_hdr *tr_hdr = smb_get_msg(iov[0].iov_base);
 	unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20;
 	int rc;
 	DECLARE_CRYPTO_WAIT(wait);
fs/smb/server/connection.c +1 -1
@@ -475,7 +475,7 @@ recheck:
 		if (!ksmbd_smb_request(conn))
 			break;
 
-		if (((struct smb2_hdr *)smb2_get_msg(conn->request_buf))->ProtocolId ==
+		if (((struct smb2_hdr *)smb_get_msg(conn->request_buf))->ProtocolId ==
 		    SMB2_PROTO_NUMBER) {
 			if (pdu_size < SMB2_MIN_SUPPORTED_HEADER_SIZE)
 				break;
fs/smb/server/oplock.c +4 -4
@@ -663,7 +663,7 @@ static void __smb2_oplock_break_noti(str
 		goto out;
 	}
 
-	rsp_hdr = smb2_get_msg(work->response_buf);
+	rsp_hdr = smb_get_msg(work->response_buf);
 	memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2);
 	rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER;
 	rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE;
@@ -677,7 +677,7 @@ static void __smb2_oplock_break_noti(str
 	rsp_hdr->SessionId = 0;
 	memset(rsp_hdr->Signature, 0, 16);
 
-	rsp = smb2_get_msg(work->response_buf);
+	rsp = smb_get_msg(work->response_buf);
 
 	rsp->StructureSize = cpu_to_le16(24);
 	if (!br_info->open_trunc &&
@@ -776,7 +776,7 @@ static void __smb2_lease_break_noti(stru
 		goto out;
 	}
 
-	rsp_hdr = smb2_get_msg(work->response_buf);
+	rsp_hdr = smb_get_msg(work->response_buf);
 	memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2);
 	rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER;
 	rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE;
@@ -790,7 +790,7 @@ static void __smb2_lease_break_noti(stru
 	rsp_hdr->SessionId = 0;
 	memset(rsp_hdr->Signature, 0, 16);
 
-	rsp = smb2_get_msg(work->response_buf);
+	rsp = smb_get_msg(work->response_buf);
 	rsp->StructureSize = cpu_to_le16(44);
 	rsp->Epoch = br_info->epoch;
 	rsp->Flags = 0;
fs/smb/server/smb2pdu.c +35 -35

240 lines not on this page · raw message

fs/smb/server/smb2pdu.h +0 -9
@@ -496,15 +496,6 @@ int smb2_ioctl(struct ksmbd_work *work);
 int smb2_oplock_break(struct ksmbd_work *work);
 int smb2_notify(struct ksmbd_work *ksmbd_work);
 
-/*
- * Get the body of the smb2 message excluding the 4 byte rfc1002 headers
- * from request/response buffer.
- */
-static inline void *smb2_get_msg(void *buf)
-{
-	return buf + 4;
-}
-
 #define POSIX_TYPE_FILE		0
 #define POSIX_TYPE_DIR		1
 #define POSIX_TYPE_SYMLINK	2
fs/smb/server/smb_common.c +3 -3
@@ -163,7 +163,7 @@ bool ksmbd_smb_request(struct ksmbd_conn
 	if (conn->request_buf[0] != 0)
 		return false;
 
-	proto = (__le32 *)smb2_get_msg(conn->request_buf);
+	proto = (__le32 *)smb_get_msg(conn->request_buf);
 	if (*proto == SMB2_COMPRESSION_TRANSFORM_ID) {
 		pr_err_ratelimited("smb2 compression not support yet");
 		return false;
@@ -259,14 +259,14 @@ int ksmbd_lookup_dialect_by_id(__le16 *c
 static int ksmbd_negotiate_smb_dialect(void *buf)
 {
 	int smb_buf_length = get_rfc1002_len(buf);
-	__le32 proto = ((struct smb2_hdr *)smb2_get_msg(buf))->ProtocolId;
+	__le32 proto = ((struct smb2_hdr *)smb_get_msg(buf))->ProtocolId;
 
 	if (proto == SMB2_PROTO_NUMBER) {
 		struct smb2_negotiate_req *req;
 		int smb2_neg_size =
 			offsetof(struct smb2_negotiate_req, Dialects);
 
-		req = (struct smb2_negotiate_req *)smb2_get_msg(buf);
+		req = (struct smb2_negotiate_req *)smb_get_msg(buf);
 		if (smb2_neg_size > smb_buf_length)
 			goto err_out;
 
fs/smb/server/smb_common.h +9 -0
@@ -421,4 +421,13 @@ unsigned int ksmbd_server_side_copy_max_
 unsigned int ksmbd_server_side_copy_max_total_size(void);
 bool is_asterisk(char *p);
 __le32 smb_map_generic_desired_access(__le32 daccess);
+
+/*
+ * Get the body of the smb message excluding the 4 byte rfc1002 headers
+ * from request/response buffer.
+ */
+static inline void *smb_get_msg(void *buf)
+{
+	return buf + 4;
+}
 #endif /* __SMB_SERVER_COMMON_H__ */
author maintainer
#91 lore raw

PATCH 91/166 smb/server: fix minimum SMB1 PDU size

In reply to Greg Kroah-Hartman

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

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

From: ChenXiaoSong <[email protected]>

[ Upstream commit 3b9c30eb8f5aaad4a54cdfa470b74c0467cc71e8 ]

Since the RFC1002 header has been removed from struct smb_hdr, the minimum SMB1 PDU size should be updated as well.

Fixes 83bfbd0bb902 ("cifs: Remove the RFC1002 header from smb_hdr")

Stable-dep-of: cfc0b8e5080a ("ksmbd: validate minimum PDU size for transform requests")

---
 fs/smb/server/connection.c |    5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
fs/smb/server/connection.c +3 -2
@@ -376,7 +376,8 @@ bool ksmbd_conn_alive(struct ksmbd_conn
 	return true;
 }
 
-#define SMB1_MIN_SUPPORTED_HEADER_SIZE (sizeof(struct smb_hdr))
+/* "+2" for BCC field (ByteCount, 2 bytes) */
+#define SMB1_MIN_SUPPORTED_PDU_SIZE (sizeof(struct smb_hdr) + 2)
 #define SMB2_MIN_SUPPORTED_HEADER_SIZE (sizeof(struct smb2_hdr) + 4)
 
 /**
@@ -444,7 +445,7 @@ recheck:
 		if (pdu_size > MAX_STREAM_PROT_LEN)
 			break;
 
-		if (pdu_size < SMB1_MIN_SUPPORTED_HEADER_SIZE)
+		if (pdu_size < SMB1_MIN_SUPPORTED_PDU_SIZE)
 			break;
 
 		/* 4 for rfc1002 length field */
author maintainer
#92 lore raw

PATCH 92/166 smb/server: fix minimum SMB2 PDU size

In reply to Greg Kroah-Hartman

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

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

From: ChenXiaoSong <[email protected]>

[ Upstream commit 4c7d8eb9a79ae5400eac19c4f6f0815bff674452 ]

The minimum SMB2 PDU size should be updated to the size of struct smb2_pdu (that is, the size of struct smb2_hdr + 2).

Stable-dep-of: cfc0b8e5080a ("ksmbd: validate minimum PDU size for transform requests")

---
 fs/smb/server/connection.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
fs/smb/server/connection.c +2 -2
@@ -378,7 +378,7 @@ bool ksmbd_conn_alive(struct ksmbd_conn
 
 /* "+2" for BCC field (ByteCount, 2 bytes) */
 #define SMB1_MIN_SUPPORTED_PDU_SIZE (sizeof(struct smb_hdr) + 2)
-#define SMB2_MIN_SUPPORTED_HEADER_SIZE (sizeof(struct smb2_hdr) + 4)
+#define SMB2_MIN_SUPPORTED_PDU_SIZE (sizeof(struct smb2_pdu))
 
 /**
  * ksmbd_conn_handler_loop() - session thread to listen on new smb requests
@@ -478,7 +478,7 @@ recheck:
 
 		if (((struct smb2_hdr *)smb_get_msg(conn->request_buf))->ProtocolId ==
 		    SMB2_PROTO_NUMBER) {
-			if (pdu_size < SMB2_MIN_SUPPORTED_HEADER_SIZE)
+			if (pdu_size < SMB2_MIN_SUPPORTED_PDU_SIZE)
 				break;
 		}
 
author maintainer
#93 lore raw

PATCH 93/166 ksmbd: validate minimum PDU size for transform requests

In reply to Greg Kroah-Hartman

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

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

From: Namjae Jeon <[email protected]>

[ Upstream commit cfc0b8e5080aec87700774e8568765eaa4b7b92b ]

The receive path applies the minimum SMB2 PDU size check only when ProtocolId is SMB2_PROTO_NUMBER. A packet carrying SMB2_TRANSFORM_PROTO_NUM bypasses the check even when the negotiated dialect does not provide transform handling.

On an SMB 2.1 connection, a short transform packet therefore reaches init_smb2_rsp_hdr(), which interprets the request as a full SMB2 header and reads beyond the request allocation. The copied fields can then be returned to the unauthenticated client.

Compression transforms are converted to ordinary SMB2 messages before protocol validation. After that conversion, validate ordinary SMB2 requests against SMB2_MIN_SUPPORTED_PDU_SIZE and require encryption transform requests to contain both a transform header and an SMB2 header. This rejects truncated requests before work allocation.

Fixes 368ba06881c3 ("ksmbd: check the validation of pdu_size in ksmbd_conn_handler_loop")
---
 fs/smb/server/connection.c |   16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)
fs/smb/server/connection.c +11 -5
@@ -379,6 +379,8 @@ bool ksmbd_conn_alive(struct ksmbd_conn
 /* "+2" for BCC field (ByteCount, 2 bytes) */
 #define SMB1_MIN_SUPPORTED_PDU_SIZE (sizeof(struct smb_hdr) + 2)
 #define SMB2_MIN_SUPPORTED_PDU_SIZE (sizeof(struct smb2_pdu))
+#define SMB2_TRANSFORM_MIN_SUPPORTED_PDU_SIZE	\
+	(sizeof(struct smb2_transform_hdr) + sizeof(struct smb2_hdr))
 
 /**
  * ksmbd_conn_handler_loop() - session thread to listen on new smb requests
@@ -393,6 +395,7 @@ int ksmbd_conn_handler_loop(void *p)
 	struct ksmbd_conn *conn = (struct ksmbd_conn *)p;
 	struct ksmbd_transport *t = conn->transport;
 	unsigned int pdu_size, max_allowed_pdu_size, max_req;
+	__le32 proto;
 	char hdr_buf[4] = {0,};
 	int size;
 
@@ -476,11 +479,14 @@ recheck:
 		if (!ksmbd_smb_request(conn))
 			break;
 
-		if (((struct smb2_hdr *)smb_get_msg(conn->request_buf))->ProtocolId ==
-		    SMB2_PROTO_NUMBER) {
-			if (pdu_size < SMB2_MIN_SUPPORTED_PDU_SIZE)
-				break;
-		}
+		proto = *(__le32 *)smb_get_msg(conn->request_buf);
+		if (proto == SMB2_PROTO_NUMBER &&
+		    pdu_size < SMB2_MIN_SUPPORTED_PDU_SIZE)
+			break;
+
+		if (proto == SMB2_TRANSFORM_PROTO_NUM &&
+		    pdu_size < SMB2_TRANSFORM_MIN_SUPPORTED_PDU_SIZE)
+			break;
 
 		if (!default_conn_ops.process_fn) {
 			pr_err("No connection request callback\n");
author maintainer
#94 lore raw

PATCH 94/166 tcp: Pass flags to __tcp_send_ack

In reply to Greg Kroah-Hartman

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

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

From: Ilpo Järvinen <[email protected]>

[ Upstream commit 9866884ce8ef25338c5b33cbb97c2b5d92088528 ]

Accurate ECN needs to send custom flags to handle IP-ECN field reflection during handshake.

Stable-dep-of: a28c4fcbf774 ("tcp: challenge ACK for non-exact RST in SYN-RECEIVED")

---
 include/net/tcp.h     |    2 +-
 net/ipv4/bpf_tcp_ca.c |    2 +-
 net/ipv4/tcp_dctcp.h  |    2 +-
 net/ipv4/tcp_output.c |    6 +++---
 4 files changed, 6 insertions(+), 6 deletions(-)
include/net/tcp.h +1 -1
@@ -615,7 +615,7 @@ void tcp_send_fin(struct sock *sk);
 void tcp_send_active_reset(struct sock *sk, gfp_t priority);
 int tcp_send_synack(struct sock *);
 void tcp_push_one(struct sock *, unsigned int mss_now);
-void __tcp_send_ack(struct sock *sk, u32 rcv_nxt);
+void __tcp_send_ack(struct sock *sk, u32 rcv_nxt, u16 flags);
 void tcp_send_ack(struct sock *sk);
 void tcp_send_delayed_ack(struct sock *sk);
 void tcp_send_loss_probe(struct sock *sk);
net/ipv4/bpf_tcp_ca.c +1 -1
@@ -128,7 +128,7 @@ static int bpf_tcp_ca_btf_struct_access(
 BPF_CALL_2(bpf_tcp_send_ack, struct tcp_sock *, tp, u32, rcv_nxt)
 {
 	/* bpf_tcp_ca prog cannot have NULL tp */
-	__tcp_send_ack((struct sock *)tp, rcv_nxt);
+	__tcp_send_ack((struct sock *)tp, rcv_nxt, 0);
 	return 0;
 }
 
net/ipv4/tcp_dctcp.h +1 -1
@@ -28,7 +28,7 @@ static inline void dctcp_ece_ack_update(
 		 */
 		if (inet_csk(sk)->icsk_ack.pending & ICSK_ACK_TIMER) {
 			dctcp_ece_ack_cwr(sk, *ce_state);
-			__tcp_send_ack(sk, *prior_rcv_nxt);
+			__tcp_send_ack(sk, *prior_rcv_nxt, 0);
 		}
 		inet_csk(sk)->icsk_ack.pending |= ICSK_ACK_NOW;
 	}
net/ipv4/tcp_output.c +3 -3
@@ -4098,7 +4098,7 @@ void tcp_send_delayed_ack(struct sock *s
 }
 
 /* This routine sends an ack and also updates the window. */
-void __tcp_send_ack(struct sock *sk, u32 rcv_nxt)
+void __tcp_send_ack(struct sock *sk, u32 rcv_nxt, u16 flags)
 {
 	struct sk_buff *buff;
 
@@ -4127,7 +4127,7 @@ void __tcp_send_ack(struct sock *sk, u32
 
 	/* Reserve space for headers and prepare control bits. */
 	skb_reserve(buff, MAX_TCP_HEADER);
-	tcp_init_nondata_skb(buff, tcp_acceptable_seq(sk), TCPHDR_ACK);
+	tcp_init_nondata_skb(buff, tcp_acceptable_seq(sk), TCPHDR_ACK | flags);
 
 	/* We do not want pure acks influencing TCP Small Queues or fq/pacing
 	 * too much.
@@ -4142,7 +4142,7 @@ EXPORT_SYMBOL_GPL(__tcp_send_ack);
 
 void tcp_send_ack(struct sock *sk)
 {
-	__tcp_send_ack(sk, tcp_sk(sk)->rcv_nxt);
+	__tcp_send_ack(sk, tcp_sk(sk)->rcv_nxt, 0);
 }
 
 /* This routine sends a packet with an out of date sequence
author maintainer
#95 lore raw

PATCH 95/166 tcp: fast path functions later

In reply to Greg Kroah-Hartman

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

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

From: Ilpo Järvinen <[email protected]>

[ Upstream commit 61b2f7baa9779b12a7bf1b9800a3f2a2549a1315 ]

The following patch will use tcp_ecn_mode_accecn(), TCP_ACCECN_CEP_INIT_OFFSET, TCP_ACCECN_CEP_ACE_MASK in __tcp_fast_path_on() to make new flag for AccECN.

No functional changes.

Stable-dep-of: a28c4fcbf774 ("tcp: challenge ACK for non-exact RST in SYN-RECEIVED")

---
 include/net/tcp.h |   54 +++++++++++++++++++++++++++---------------------------
 1 file changed, 27 insertions(+), 27 deletions(-)
include/net/tcp.h +27 -27
@@ -715,33 +715,6 @@ static inline u32 __tcp_set_rto(const st
 	return usecs_to_jiffies((tp->srtt_us >> 3) + tp->rttvar_us);
 }
 
-static inline void __tcp_fast_path_on(struct tcp_sock *tp, u32 snd_wnd)
-{
-	/* mptcp hooks are only on the slow path */
-	if (sk_is_mptcp((struct sock *)tp))
-		return;
-
-	tp->pred_flags = htonl((tp->tcp_header_len << 26) |
-			       ntohl(TCP_FLAG_ACK) |
-			       snd_wnd);
-}
-
-static inline void tcp_fast_path_on(struct tcp_sock *tp)
-{
-	__tcp_fast_path_on(tp, tp->snd_wnd >> tp->rx_opt.snd_wscale);
-}
-
-static inline void tcp_fast_path_check(struct sock *sk)
-{
-	struct tcp_sock *tp = tcp_sk(sk);
-
-	if (RB_EMPTY_ROOT(&tp->out_of_order_queue) &&
-	    tp->rcv_wnd &&
-	    atomic_read(&sk->sk_rmem_alloc) < sk->sk_rcvbuf &&
-	    !tp->urg_data)
-		tcp_fast_path_on(tp);
-}
-
 u32 tcp_delack_max(const struct sock *sk);
 
 /* Compute the actual rto_min value */
@@ -1658,6 +1631,33 @@ static inline bool tcp_paws_reject(const
 	return true;
 }
 
+static inline void __tcp_fast_path_on(struct tcp_sock *tp, u32 snd_wnd)
+{
+	/* mptcp hooks are only on the slow path */
+	if (sk_is_mptcp((struct sock *)tp))
+		return;
+
+	tp->pred_flags = htonl((tp->tcp_header_len << 26) |
+			       ntohl(TCP_FLAG_ACK) |
+			       snd_wnd);
+}
+
+static inline void tcp_fast_path_on(struct tcp_sock *tp)
+{
+	__tcp_fast_path_on(tp, tp->snd_wnd >> tp->rx_opt.snd_wscale);
+}
+
+static inline void tcp_fast_path_check(struct sock *sk)
+{
+	struct tcp_sock *tp = tcp_sk(sk);
+
+	if (RB_EMPTY_ROOT(&tp->out_of_order_queue) &&
+	    tp->rcv_wnd &&
+	    atomic_read(&sk->sk_rmem_alloc) < sk->sk_rcvbuf &&
+	    !tp->urg_data)
+		tcp_fast_path_on(tp);
+}
+
 bool tcp_oow_rate_limited(struct net *net, const struct sk_buff *skb,
 			  int mib_idx, u32 *last_oow_ack_time);
 
author maintainer
#96 lore raw

PATCH 96/166 tcp: challenge ACK for non-exact RST in SYN-RECEIVED

In reply to Greg Kroah-Hartman

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

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

From: Yuxiang Yang <[email protected]>

[ Upstream commit a28c4fcbf774e23b4779cae468e3497a5ad1f4a1 ]

The SYN-RECEIVED request-socket path in tcp_check_req() accepts an in-window RST without requiring SEG.SEQ to exactly match RCV.NXT. A non-exact RST therefore removes the request instead of eliciting a challenge ACK.

RFC 9293 section 3.10.7.4 applies the RFC 5961 reset check in
SYN-RECEIVED: an exact RST resets the connection, while a non-exact in-window RST must trigger a challenge ACK and be dropped.

Apply that check before the ACK-field validation, following the RFC sequence-number, RST, then ACK processing order. Factor the per-netns challenge ACK quota out of tcp_send_challenge_ack() so request sockets can share it. Use the request socket's send_ack() callback and its own out-of-window ACK timestamp to send and rate-limit the response.

---
 include/net/tcp.h        |    2 +
 net/ipv4/tcp_input.c     |   56 ++++++++++++++++++++++++++++++++++++-----------
 net/ipv4/tcp_minisocks.c |   12 +++++++++-
 3 files changed, 56 insertions(+), 14 deletions(-)
include/net/tcp.h +2 -0
@@ -1660,6 +1660,8 @@ static inline void tcp_fast_path_check(s
 
 bool tcp_oow_rate_limited(struct net *net, const struct sk_buff *skb,
 			  int mib_idx, u32 *last_oow_ack_time);
+void tcp_reqsk_send_challenge_ack(struct sock *sk, struct sk_buff *skb,
+				  struct request_sock *req);
 
 static inline void tcp_mib_init(struct net *net)
 {
net/ipv4/tcp_input.c +43 -13

79 lines not on this page · raw message

net/ipv4/tcp_minisocks.c +11 -1
@@ -736,7 +736,7 @@ struct sock *tcp_check_req(struct sock *
 	 * elsewhere and is checked directly against the child socket rather
 	 * than req because user data may have been sent out.
 	 */
-	if ((flg & TCP_FLAG_ACK) && !fastopen &&
+	if ((flg & TCP_FLAG_ACK) && !(flg & TCP_FLAG_RST) && !fastopen &&
 	    (TCP_SKB_CB(skb)->ack_seq !=
 	     tcp_rsk(req)->snt_isn + 1))
 		return sk;
@@ -769,6 +769,16 @@ struct sock *tcp_check_req(struct sock *
 		flg &= ~TCP_FLAG_SYN;
 	}
 
+	/* RFC 5961 section 3.2, as clarified by RFC 9293 section
+	 * 3.10.7.4, requires a challenge ACK for a non-exact
+	 * in-window RST in SYN-RECEIVED.
+	 */
+	if ((flg & TCP_FLAG_RST) &&
+	    TCP_SKB_CB(skb)->seq != tcp_rsk(req)->rcv_nxt) {
+		tcp_reqsk_send_challenge_ack(sk, skb, req);
+		return NULL;
+	}
+
 	/* RFC793: "second check the RST bit" and
 	 *	   "fourth, check the SYN bit"
 	 */
author maintainer
#97 lore raw

PATCH 97/166 btrfs: add debug build only WARN

In reply to Greg Kroah-Hartman

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

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

From: David Sterba <[email protected]>

[ Upstream commit 3db15c6ca6feb2c5000a1cbb39c33866e0349abd ]

Add conditional WARN() wrapper that's enabled only in debug build. It should be used for unexpected conditions that should be noisy. Use it instead of ASSERT(0). As it will not lead to BUG() make sure that continuing is still possible, e.g. the error is handled anyway.

Stable-dep-of: 8bc4d7209611 ("btrfs: zoned: fix missing chunk metadata reservation")

---
 fs/btrfs/messages.h |    7 +++++++
 1 file changed, 7 insertions(+)
fs/btrfs/messages.h +7 -0
@@ -181,6 +181,13 @@ do {								\
 #define ASSERT(expr)	(void)(expr)
 #endif
 
+#ifdef CONFIG_BTRFS_DEBUG
+/* Verbose warning only under debug build. */
+#define DEBUG_WARN(args...)			WARN(1, KERN_ERR args)
+#else
+#define DEBUG_WARN(...)				do {} while(0)
+#endif
+
 __printf(5, 6)
 __cold
 void __btrfs_handle_fs_error(struct btrfs_fs_info *fs_info, const char *function,
author maintainer
#98 lore raw

PATCH 98/166 btrfs: add space_info argument to btrfs_chunk_alloc()

In reply to Greg Kroah-Hartman

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

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

From: Naohiro Aota <[email protected]>

[ Upstream commit 098a442d5b6d440602604dc1a88706a2a91bce4e ]

Take a btrfs_space_info argument in btrfs_chunk_alloc(). New block group will belong to that space_info.

Stable-dep-of: 8bc4d7209611 ("btrfs: zoned: fix missing chunk metadata reservation")

---
 fs/btrfs/block-group.c |   27 +++++++++++++++++----------
 fs/btrfs/block-group.h |    3 ++-
 fs/btrfs/extent-tree.c |    6 ++++--
 fs/btrfs/space-info.c  |    2 +-
 fs/btrfs/transaction.c |    5 +++--
 5 files changed, 27 insertions(+), 16 deletions(-)
fs/btrfs/block-group.c +17 -10
@@ -2929,6 +2929,7 @@ int btrfs_inc_block_group_ro(struct btrf
 			     bool do_chunk_alloc)
 {
 	struct btrfs_fs_info *fs_info = cache->fs_info;
+	struct btrfs_space_info *space_info = cache->space_info;
 	struct btrfs_trans_handle *trans;
 	struct btrfs_root *root = btrfs_block_group_root(fs_info);
 	u64 alloc_flags;
@@ -2981,7 +2982,7 @@ int btrfs_inc_block_group_ro(struct btrf
 		 */
 		alloc_flags = btrfs_get_alloc_profile(fs_info, cache->flags);
 		if (alloc_flags != cache->flags) {
-			ret = btrfs_chunk_alloc(trans, alloc_flags,
+			ret = btrfs_chunk_alloc(trans, space_info, alloc_flags,
 						CHUNK_ALLOC_FORCE);
 			/*
 			 * ENOSPC is allowed here, we may have enough space
@@ -3009,15 +3010,15 @@ int btrfs_inc_block_group_ro(struct btrf
 	    (cache->flags & BTRFS_BLOCK_GROUP_SYSTEM))
 		goto unlock_out;
 
-	alloc_flags = btrfs_get_alloc_profile(fs_info, cache->space_info->flags);
-	ret = btrfs_chunk_alloc(trans, alloc_flags, CHUNK_ALLOC_FORCE);
+	alloc_flags = btrfs_get_alloc_profile(fs_info, space_info->flags);
+	ret = btrfs_chunk_alloc(trans, space_info, alloc_flags, CHUNK_ALLOC_FORCE);
 	if (ret < 0)
 		goto out;
 	/*
 	 * We have allocated a new chunk. We also need to activate that chunk to
 	 * grant metadata tickets for zoned filesystem.
 	 */
-	ret = btrfs_zoned_activate_one_bg(fs_info, cache->space_info, true);
+	ret = btrfs_zoned_activate_one_bg(fs_info, space_info, true);
 	if (ret < 0)
 		goto out;
 
@@ -3875,8 +3876,15 @@ static int should_alloc_chunk(const stru
 int btrfs_force_chunk_alloc(struct btrfs_trans_handle *trans, u64 type)
 {
 	u64 alloc_flags = btrfs_get_alloc_profile(trans->fs_info, type);
+	struct btrfs_space_info *space_info;
+
+	space_info = btrfs_find_space_info(trans->fs_info, type);
+	if (!space_info) {
+		DEBUG_WARN();
+		return -EINVAL;
+	}
 
-	return btrfs_chunk_alloc(trans, alloc_flags, CHUNK_ALLOC_FORCE);
+	return btrfs_chunk_alloc(trans, space_info, alloc_flags, CHUNK_ALLOC_FORCE);
 }
 
 static struct btrfs_block_group *do_chunk_alloc(struct btrfs_trans_handle *trans, u64 flags)
@@ -4072,6 +4080,8 @@ out:
  *
  * This function, btrfs_chunk_alloc(), belongs to phase 1.
  *
+ * @space_info: specify which space_info the new chunk should belong to.
+ *
  * If @force is CHUNK_ALLOC_FORCE:
  *    - return 1 if it successfully allocates a chunk,
  *    - return errors including -ENOSPC otherwise.
@@ -4080,11 +4090,11 @@ out:
  *    - return 1 if it successfully allocates a chunk,
  *    - return errors including -ENOSPC otherwise.
  */
-int btrfs_chunk_alloc(struct btrfs_trans_handle *trans, u64 flags,
+int btrfs_chunk_alloc(struct btrfs_trans_handle *trans,
+		      struct btrfs_space_info *space_info, u64 flags,
 		      enum btrfs_chunk_alloc_enum force)
 {
 	struct btrfs_fs_info *fs_info = trans->fs_info;
-	struct btrfs_space_info *space_info;
 	struct btrfs_block_group *ret_bg;
 	bool wait_for_alloc = false;
 	bool should_alloc = false;
@@ -4123,9 +4133,6 @@ int btrfs_chunk_alloc(struct btrfs_trans
 	if (flags & BTRFS_BLOCK_GROUP_SYSTEM)
 		return -ENOSPC;
 
-	space_info = btrfs_find_space_info(fs_info, flags);
-	ASSERT(space_info);
-
 	do {
 		spin_lock(&space_info->lock);
 		if (force < space_info->force_alloc)
fs/btrfs/block-group.h +2 -1
@@ -327,7 +327,8 @@ int btrfs_add_reserved_bytes(struct btrf
 			     bool force_wrong_size_class);
 void btrfs_free_reserved_bytes(struct btrfs_block_group *cache,
 			       u64 num_bytes, int delalloc);
-int btrfs_chunk_alloc(struct btrfs_trans_handle *trans, u64 flags,
+int btrfs_chunk_alloc(struct btrfs_trans_handle *trans,
+		      struct btrfs_space_info *space_info, u64 flags,
 		      enum btrfs_chunk_alloc_enum force);
 int btrfs_force_chunk_alloc(struct btrfs_trans_handle *trans, u64 type);
 void check_system_chunk(struct btrfs_trans_handle *trans, const u64 type);
fs/btrfs/extent-tree.c +4 -2
@@ -4034,6 +4034,7 @@ static int can_allocate_chunk(struct btr
 static int find_free_extent_update_loop(struct btrfs_fs_info *fs_info,
 					struct btrfs_key *ins,
 					struct find_free_extent_ctl *ffe_ctl,
+					struct btrfs_space_info *space_info,
 					bool full_search)
 {
 	struct btrfs_root *root = fs_info->chunk_root;
@@ -4088,7 +4089,7 @@ static int find_free_extent_update_loop(
 				return ret;
 			}
 
-			ret = btrfs_chunk_alloc(trans, ffe_ctl->flags,
+			ret = btrfs_chunk_alloc(trans, space_info, ffe_ctl->flags,
 						CHUNK_ALLOC_FORCE_FOR_EXTENT);
 
 			/* Do not bail out on ENOSPC since we can do more. */
@@ -4488,7 +4489,8 @@ loop:
 	}
 	up_read(&space_info->groups_sem);
 
-	ret = find_free_extent_update_loop(fs_info, ins, ffe_ctl, full_search);
+	ret = find_free_extent_update_loop(fs_info, ins, ffe_ctl, space_info,
+					   full_search);
 	if (ret > 0)
 		goto search;
 
fs/btrfs/space-info.c +1 -1
@@ -807,7 +807,7 @@ static void flush_space(struct btrfs_fs_
 			ret = PTR_ERR(trans);
 			break;
 		}
-		ret = btrfs_chunk_alloc(trans,
+		ret = btrfs_chunk_alloc(trans, space_info,
 				btrfs_get_alloc_profile(fs_info, space_info->flags),
 				(state == ALLOC_CHUNK) ? CHUNK_ALLOC_NO_FORCE :
 					CHUNK_ALLOC_FORCE);
fs/btrfs/transaction.c +3 -2
@@ -729,9 +729,10 @@ got_it:
 	 * value here.
 	 */
 	if (do_chunk_alloc && num_bytes) {
-		u64 flags = h->block_rsv->space_info->flags;
+		struct btrfs_space_info *space_info = h->block_rsv->space_info;
+		u64 flags = space_info->flags;
 
-		btrfs_chunk_alloc(h, btrfs_get_alloc_profile(fs_info, flags),
+		btrfs_chunk_alloc(h, space_info, btrfs_get_alloc_profile(fs_info, flags),
 				  CHUNK_ALLOC_NO_FORCE);
 	}
 
author maintainer
#99 lore raw

PATCH 99/166 btrfs: remove fs_info argument from btrfs_zoned_activate_one_bg()

In reply to Greg Kroah-Hartman

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

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

From: Filipe Manana <[email protected]>

[ Upstream commit a232ff90d14657c8637c6e94b606bb5d700a2ecb ]

We don't need it since we can grab fs_info from the given space_info. So remove the fs_info argument.

Stable-dep-of: 8bc4d7209611 ("btrfs: zoned: fix missing chunk metadata reservation")

---
 fs/btrfs/block-group.c |    4 ++--
 fs/btrfs/zoned.c       |    5 ++---
 fs/btrfs/zoned.h       |    6 ++----
 3 files changed, 6 insertions(+), 9 deletions(-)
fs/btrfs/block-group.c +2 -2
@@ -3018,7 +3018,7 @@ int btrfs_inc_block_group_ro(struct btrf
 	 * We have allocated a new chunk. We also need to activate that chunk to
 	 * grant metadata tickets for zoned filesystem.
 	 */
-	ret = btrfs_zoned_activate_one_bg(fs_info, space_info, true);
+	ret = btrfs_zoned_activate_one_bg(space_info, true);
 	if (ret < 0)
 		goto out;
 
@@ -4284,7 +4284,7 @@ static void reserve_chunk_space(struct b
 			 * We have a new chunk. We also need to activate it for
 			 * zoned filesystem.
 			 */
-			ret = btrfs_zoned_activate_one_bg(fs_info, info, true);
+			ret = btrfs_zoned_activate_one_bg(info, true);
 			if (ret < 0)
 				return;
 
fs/btrfs/zoned.c +2 -3
@@ -2457,10 +2457,9 @@ int btrfs_zone_finish_one_bg(struct btrf
 	return ret < 0 ? ret : 1;
 }
 
-int btrfs_zoned_activate_one_bg(struct btrfs_fs_info *fs_info,
-				struct btrfs_space_info *space_info,
-				bool do_finish)
+int btrfs_zoned_activate_one_bg(struct btrfs_space_info *space_info, bool do_finish)
 {
+	struct btrfs_fs_info *fs_info = space_info->fs_info;
 	struct btrfs_block_group *bg;
 	int index;
 
fs/btrfs/zoned.h +2 -4
@@ -81,8 +81,7 @@ bool btrfs_zoned_should_reclaim(const st
 void btrfs_zoned_release_data_reloc_bg(struct btrfs_fs_info *fs_info, u64 logical,
 				       u64 length);
 int btrfs_zone_finish_one_bg(struct btrfs_fs_info *fs_info);
-int btrfs_zoned_activate_one_bg(struct btrfs_fs_info *fs_info,
-				struct btrfs_space_info *space_info, bool do_finish);
+int btrfs_zoned_activate_one_bg(struct btrfs_space_info *space_info, bool do_finish);
 void btrfs_check_active_zone_reservation(struct btrfs_fs_info *fs_info);
 #else /* CONFIG_BLK_DEV_ZONED */
 static inline int btrfs_get_dev_zone(struct btrfs_device *device, u64 pos,
@@ -253,8 +252,7 @@ static inline int btrfs_zone_finish_one_
 	return 1;
 }
 
-static inline int btrfs_zoned_activate_one_bg(struct btrfs_fs_info *fs_info,
-					      struct btrfs_space_info *space_info,
+static inline int btrfs_zoned_activate_one_bg(struct btrfs_space_info *space_info,
 					      bool do_finish)
 {
 	/* Consider all the block groups are active */
author maintainer
#100 lore raw

PATCH 100/166 btrfs: zoned: fix missing chunk metadata reservation

In reply to Greg Kroah-Hartman

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

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

From: Guanghui Yang <[email protected]>

[ Upstream commit 8bc4d7209611e8aa9d5409b6a4a86a9eb91b69a3 ]

reserve_chunk_space() stores the return value of btrfs_zoned_activate_one_bg() in ret. The helper can return 1 after successfully activating a block group, but ret is later used to decide whether to reserve metadata for chunk tree updates.

As a result, successful activation skips btrfs_block_rsv_add() and leaves trans->chunk_bytes_reserved unchanged. Use a separate variable for the activation result so positive success does not affect the later reservation. Keep activation failures in ret instead of returning early so the function uses the common tail path.

Fixes b6a98021e401 ("btrfs: zoned: activate necessary block group")
---
 fs/btrfs/block-group.c |   34 +++++++++++++++++++---------------
 1 file changed, 19 insertions(+), 15 deletions(-)
fs/btrfs/block-group.c +19 -15
@@ -4280,25 +4280,29 @@ static void reserve_chunk_space(struct b
 		if (IS_ERR(bg)) {
 			ret = PTR_ERR(bg);
 		} else {
+			int activate_ret;
+
 			/*
 			 * We have a new chunk. We also need to activate it for
 			 * zoned filesystem.
 			 */
-			ret = btrfs_zoned_activate_one_bg(info, true);
-			if (ret < 0)
-				return;
-
-			/*
-			 * If we fail to add the chunk item here, we end up
-			 * trying again at phase 2 of chunk allocation, at
-			 * btrfs_create_pending_block_groups(). So ignore
-			 * any error here. An ENOSPC here could happen, due to
-			 * the cases described at do_chunk_alloc() - the system
-			 * block group we just created was just turned into RO
-			 * mode by a scrub for example, or a running discard
-			 * temporarily removed its free space entries, etc.
-			 */
-			btrfs_chunk_alloc_add_chunk_item(trans, bg);
+			activate_ret = btrfs_zoned_activate_one_bg(info, true);
+			if (activate_ret < 0) {
+				ret = activate_ret;
+			} else {
+				/*
+				 * If we fail to add the chunk item here, we end
+				 * up trying again at phase 2 of chunk allocation,
+				 * at btrfs_create_pending_block_groups(). So
+				 * ignore any error here. An ENOSPC here could
+				 * happen, due to the cases described at
+				 * do_chunk_alloc() - the system block group we
+				 * just created was just turned into RO mode by a
+				 * scrub for example, or a running discard
+				 * temporarily removed its free space entries, etc.
+				 */
+				btrfs_chunk_alloc_add_chunk_item(trans, bg);
+			}
 		}
 	}
 
author maintainer
#101 lore raw

PATCH 101/166 mm: migrate_device: fix pte_pfn/pte_dirty called on non-present PTE

In reply to Greg Kroah-Hartman

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

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

From: Kefeng Wang <[email protected]>

[ Upstream commit 63867c82d0c0c2d182016a32b1cc0103116b0ea5 ]

pte_pfn() and pte_dirty() have undefined behaviour when called on a non-present PTE. In migrate_vma_collect_pmd(), these functions may be invoked on non-present entries (e.g., device-private entries), leading to potential crashes from pte_pfn() or incorrect dirty folio accounting from pte_dirty(). Fix both by guarding with pte_present() checks.

Fixes fd35ca3d12cc ("mm/migrate_device.c: copy pte dirty bit to page")
Fixes 6c287605fd56 ("mm: remember exclusively mapped anonymous pages with PG_anon_exclusive")
Cc Alistair Popple <[email protected]>
Cc Byungchul Park <[email protected]>
Cc David Hildenbrand <[email protected]>
Cc Gregory Price <[email protected]>
Cc "Huang, Ying" <[email protected]>
Cc Joshua Hahn <[email protected]>
Cc Matthew Brost <[email protected]>
Cc Rakie Kim <[email protected]>
Cc Ying Huang <[email protected]>
[ adapted `folio_test_anon(folio)`/`folio_mark_dirty(folio)` context lines to 6.6's `PageAnon(page)`/`folio_mark_dirty(page_folio(page))` spelling since the `folio` local doesn't exist ]
---
 mm/migrate_device.c |    5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
mm/migrate_device.c +3 -2
@@ -194,7 +194,8 @@ again:
 			bool anon_exclusive;
 			pte_t swp_pte;
 
-			flush_cache_page(vma, addr, pte_pfn(pte));
+			if (pte_present(pte))
+				flush_cache_page(vma, addr, pte_pfn(pte));
 			anon_exclusive = PageAnon(page) && PageAnonExclusive(page);
 			if (anon_exclusive) {
 				pte = ptep_clear_flush(vma, addr, ptep);
@@ -213,7 +214,7 @@ again:
 			migrate->cpages++;
 
 			/* Set the dirty flag on the folio now the pte is gone. */
-			if (pte_dirty(pte))
+			if (pte_present(pte) && pte_dirty(pte))
 				folio_mark_dirty(page_folio(page));
 
 			/* Setup special migration page table entry */
author maintainer
#102 lore raw

PATCH 102/166 ice: make use of DEFINE_FLEX() in ice_switch.c

In reply to Greg Kroah-Hartman

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

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

From: Przemek Kitszel <[email protected]>

[ Upstream commit e268b972270567e0414836919c5b9f8bc01651a4 ]

Use DEFINE_FLEX() macro for 1-elem flex array members of ice_switch.c

Stable-dep-of: cdc36db204ff ("Bluetooth: hci_sync: Fix advertising data UAFs")

---
 drivers/net/ethernet/intel/ice/ice_switch.c |   63 ++++++----------------------
 1 file changed, 14 insertions(+), 49 deletions(-)
drivers/net/ethernet/intel/ice/ice_switch.c +14 -49
@@ -1812,15 +1812,11 @@ ice_aq_alloc_free_vsi_list(struct ice_hw
 			   enum ice_sw_lkup_type lkup_type,
 			   enum ice_adminq_opc opc)
 {
-	struct ice_aqc_alloc_free_res_elem *sw_buf;
+	DEFINE_FLEX(struct ice_aqc_alloc_free_res_elem, sw_buf, elem, 1);
+	u16 buf_len = __struct_size(sw_buf);
 	struct ice_aqc_res_elem *vsi_ele;
-	u16 buf_len;
 	int status;
 
-	buf_len = struct_size(sw_buf, elem, 1);
-	sw_buf = devm_kzalloc(ice_hw_to_dev(hw), buf_len, GFP_KERNEL);
-	if (!sw_buf)
-		return -ENOMEM;
 	sw_buf->num_elems = cpu_to_le16(1);
 
 	if (lkup_type == ICE_SW_LKUP_MAC ||
@@ -1841,8 +1837,7 @@ ice_aq_alloc_free_vsi_list(struct ice_hw
 			sw_buf->res_type =
 				cpu_to_le16(ICE_AQC_RES_TYPE_VSI_LIST_PRUNE);
 	} else {
-		status = -EINVAL;
-		goto ice_aq_alloc_free_vsi_list_exit;
+		return -EINVAL;
 	}
 
 	if (opc == ice_aqc_opc_free_res)
@@ -1850,16 +1845,14 @@ ice_aq_alloc_free_vsi_list(struct ice_hw
 
 	status = ice_aq_alloc_free_res(hw, sw_buf, buf_len, opc);
 	if (status)
-		goto ice_aq_alloc_free_vsi_list_exit;
+		return status;
 
 	if (opc == ice_aqc_opc_alloc_res) {
 		vsi_ele = &sw_buf->elem[0];
 		*vsi_list_id = le16_to_cpu(vsi_ele->e.sw_resp);
 	}
 
-ice_aq_alloc_free_vsi_list_exit:
-	devm_kfree(ice_hw_to_dev(hw), sw_buf);
-	return status;
+	return 0;
 }
 
 /**
@@ -2089,15 +2082,10 @@ ice_aq_get_recipe_to_profile(struct ice_
  */
 int ice_alloc_recipe(struct ice_hw *hw, u16 *rid)
 {
-	struct ice_aqc_alloc_free_res_elem *sw_buf;
-	u16 buf_len;
+	DEFINE_FLEX(struct ice_aqc_alloc_free_res_elem, sw_buf, elem, 1);
+	u16 buf_len = __struct_size(sw_buf);
 	int status;
 
-	buf_len = struct_size(sw_buf, elem, 1);
-	sw_buf = kzalloc(buf_len, GFP_KERNEL);
-	if (!sw_buf)
-		return -ENOMEM;
-
 	sw_buf->num_elems = cpu_to_le16(1);
 	sw_buf->res_type = cpu_to_le16((ICE_AQC_RES_TYPE_RECIPE <<
 					ICE_AQC_RES_TYPE_S) |
@@ -2106,7 +2094,6 @@ int ice_alloc_recipe(struct ice_hw *hw,
 				       ice_aqc_opc_alloc_res);
 	if (!status)
 		*rid = le16_to_cpu(sw_buf->elem[0].e.sw_resp);
-	kfree(sw_buf);
 
 	return status;
 }
@@ -4438,28 +4425,19 @@ int
 ice_alloc_res_cntr(struct ice_hw *hw, u8 type, u8 alloc_shared, u16 num_items,
 		   u16 *counter_id)
 {
-	struct ice_aqc_alloc_free_res_elem *buf;
-	u16 buf_len;
+	DEFINE_FLEX(struct ice_aqc_alloc_free_res_elem, buf, elem, 1);
+	u16 buf_len = __struct_size(buf);
 	int status;
 
-	/* Allocate resource */
-	buf_len = struct_size(buf, elem, 1);
-	buf = kzalloc(buf_len, GFP_KERNEL);
-	if (!buf)
-		return -ENOMEM;
-
 	buf->num_elems = cpu_to_le16(num_items);
 	buf->res_type = cpu_to_le16(((type << ICE_AQC_RES_TYPE_S) &
 				      ICE_AQC_RES_TYPE_M) | alloc_shared);
 
 	status = ice_aq_alloc_free_res(hw, buf, buf_len, ice_aqc_opc_alloc_res);
 	if (status)
-		goto exit;
+		return status;
 
 	*counter_id = le16_to_cpu(buf->elem[0].e.sw_resp);
-
-exit:
-	kfree(buf);
 	return status;
 }
 
@@ -4475,16 +4453,10 @@ int
 ice_free_res_cntr(struct ice_hw *hw, u8 type, u8 alloc_shared, u16 num_items,
 		  u16 counter_id)
 {
-	struct ice_aqc_alloc_free_res_elem *buf;
-	u16 buf_len;
+	DEFINE_FLEX(struct ice_aqc_alloc_free_res_elem, buf, elem, 1);
+	u16 buf_len = __struct_size(buf);
 	int status;
 
-	/* Free resource */
-	buf_len = struct_size(buf, elem, 1);
-	buf = kzalloc(buf_len, GFP_KERNEL);
-	if (!buf)
-		return -ENOMEM;
-
 	buf->num_elems = cpu_to_le16(num_items);
 	buf->res_type = cpu_to_le16(((type << ICE_AQC_RES_TYPE_S) &
 				      ICE_AQC_RES_TYPE_M) | alloc_shared);
@@ -4494,7 +4466,6 @@ ice_free_res_cntr(struct ice_hw *hw, u8
 	if (status)
 		ice_debug(hw, ICE_DBG_SW, "counter resource could not be freed\n");
 
-	kfree(buf);
 	return status;
 }
 
@@ -4512,15 +4483,10 @@ ice_free_res_cntr(struct ice_hw *hw, u8
  */
 int ice_share_res(struct ice_hw *hw, u16 type, u8 shared, u16 res_id)
 {
-	struct ice_aqc_alloc_free_res_elem *buf;
-	u16 buf_len;
+	DEFINE_FLEX(struct ice_aqc_alloc_free_res_elem, buf, elem, 1);
+	u16 buf_len = __struct_size(buf);
 	int status;
 
-	buf_len = struct_size(buf, elem, 1);
-	buf = kzalloc(buf_len, GFP_KERNEL);
-	if (!buf)
-		return -ENOMEM;
-
 	buf->num_elems = cpu_to_le16(1);
 	if (shared)
 		buf->res_type = cpu_to_le16(((type << ICE_AQC_RES_TYPE_S) &
@@ -4538,7 +4504,6 @@ int ice_share_res(struct ice_hw *hw, u16
 		ice_debug(hw, ICE_DBG_SW, "Could not set resource type %u id %u to %s\n",
 			  type, res_id, shared ? "SHARED" : "DEDICATED");
 
-	kfree(buf);
 	return status;
 }
 
author maintainer
#103 lore raw

PATCH 103/166 ice: make ice_vsi_cfg_rxq() static

In reply to Greg Kroah-Hartman

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

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

From: Maciej Fijalkowski <[email protected]>

[ Upstream commit 3e5fb691faee1d43ee13d4ce2666b0a05e1555b3 ]

Currently, XSK control path in ice driver calls directly ice_vsi_cfg_rxq() whereas we have ice_vsi_cfg_single_rxq() for that purpose. Use the latter from XSK side and make ice_vsi_cfg_rxq() static.

ice_vsi_cfg_rxq() resides in ice_base.c and is rather big, so to reduce the code churn let us move two callers of it from ice_lib.c to ice_base.c.

Stable-dep-of: cdc36db204ff ("Bluetooth: hci_sync: Fix advertising data UAFs")

---
 drivers/net/ethernet/intel/ice/ice_base.c |   58 +++++++++++++++++++++++++++++-
 drivers/net/ethernet/intel/ice/ice_base.h |    3 +
 drivers/net/ethernet/intel/ice/ice_lib.c  |   56 ----------------------------
 drivers/net/ethernet/intel/ice/ice_lib.h  |    4 --
 drivers/net/ethernet/intel/ice/ice_xsk.c  |    2 -
 5 files changed, 60 insertions(+), 63 deletions(-)
drivers/net/ethernet/intel/ice/ice_base.c +57 -1
@@ -544,7 +544,7 @@ static unsigned int ice_get_frame_sz(str
  *
  * Return 0 on success and a negative value on error.
  */
-int ice_vsi_cfg_rxq(struct ice_rx_ring *ring)
+static int ice_vsi_cfg_rxq(struct ice_rx_ring *ring)
 {
 	struct device *dev = ice_pf_to_dev(ring->vsi->back);
 	u32 num_bufs = ICE_RX_DESC_UNUSED(ring);
@@ -636,6 +636,62 @@ int ice_vsi_cfg_rxq(struct ice_rx_ring *
 
 	return 0;
 }
+
+int ice_vsi_cfg_single_rxq(struct ice_vsi *vsi, u16 q_idx)
+{
+	if (q_idx >= vsi->num_rxq)
+		return -EINVAL;
+
+	return ice_vsi_cfg_rxq(vsi->rx_rings[q_idx]);
+}
+
+/**
+ * ice_vsi_cfg_frame_size - setup max frame size and Rx buffer length
+ * @vsi: VSI
+ */
+static void ice_vsi_cfg_frame_size(struct ice_vsi *vsi)
+{
+	if (!vsi->netdev || test_bit(ICE_FLAG_LEGACY_RX, vsi->back->flags)) {
+		vsi->max_frame = ICE_MAX_FRAME_LEGACY_RX;
+		vsi->rx_buf_len = ICE_RXBUF_1664;
+#if (PAGE_SIZE < 8192)
+	} else if (!ICE_2K_TOO_SMALL_WITH_PADDING &&
+		   (vsi->netdev->mtu <= ETH_DATA_LEN)) {
+		vsi->max_frame = ICE_RXBUF_1536 - NET_IP_ALIGN;
+		vsi->rx_buf_len = ICE_RXBUF_1536 - NET_IP_ALIGN;
+#endif
+	} else {
+		vsi->max_frame = ICE_AQ_SET_MAC_FRAME_SIZE_MAX;
+		vsi->rx_buf_len = ICE_RXBUF_3072;
+	}
+}
+
+/**
+ * ice_vsi_cfg_rxqs - Configure the VSI for Rx
+ * @vsi: the VSI being configured
+ *
+ * Return 0 on success and a negative value on error
+ * Configure the Rx VSI for operation.
+ */
+int ice_vsi_cfg_rxqs(struct ice_vsi *vsi)
+{
+	u16 i;
+
+	if (vsi->type == ICE_VSI_VF)
+		goto setup_rings;
+
+	ice_vsi_cfg_frame_size(vsi);
+setup_rings:
+	/* set up individual rings */
+	ice_for_each_rxq(vsi, i) {
+		int err = ice_vsi_cfg_rxq(vsi->rx_rings[i]);
+
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
 
 /**
  * __ice_vsi_get_qs - helper function for assigning queues from PF to VSI
drivers/net/ethernet/intel/ice/ice_base.h +2 -1
@@ -6,7 +6,8 @@
 
 #include "ice.h"
 
-int ice_vsi_cfg_rxq(struct ice_rx_ring *ring);
+int ice_vsi_cfg_single_rxq(struct ice_vsi *vsi, u16 q_idx);
+int ice_vsi_cfg_rxqs(struct ice_vsi *vsi);
 int __ice_vsi_get_qs(struct ice_qs_cfg *qs_cfg);
 int
 ice_vsi_ctrl_one_rx_ring(struct ice_vsi *vsi, bool ena, u16 rxq_idx, bool wait);
drivers/net/ethernet/intel/ice/ice_lib.c +0 -56

74 lines not on this page · raw message

drivers/net/ethernet/intel/ice/ice_lib.h +0 -4
@@ -54,12 +54,8 @@ bool ice_pf_state_is_nominal(struct ice_
 
 void ice_update_eth_stats(struct ice_vsi *vsi);
 
-int ice_vsi_cfg_single_rxq(struct ice_vsi *vsi, u16 q_idx);
-
 int ice_vsi_cfg_single_txq(struct ice_vsi *vsi, struct ice_tx_ring **tx_rings, u16 q_idx);
 
-int ice_vsi_cfg_rxqs(struct ice_vsi *vsi);
-
 int ice_vsi_cfg_lan_txqs(struct ice_vsi *vsi);
 
 void ice_vsi_cfg_msix(struct ice_vsi *vsi);
drivers/net/ethernet/intel/ice/ice_xsk.c +1 -1
@@ -252,7 +252,7 @@ static int ice_qp_ena(struct ice_vsi *vs
 		ice_tx_xsk_pool(vsi, q_idx);
 	}
 
-	err = ice_vsi_cfg_rxq(rx_ring);
+	err = ice_vsi_cfg_single_rxq(vsi, q_idx);
 	if (err)
 		goto free_buf;
 
author maintainer
#104 lore raw

PATCH 104/166 overflow: Change DEFINE_FLEX to take __counted_by member

In reply to Greg Kroah-Hartman

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

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

From: Kees Cook <[email protected]>

[ Upstream commit d8e45f2929b94099913eb66c3ebb18b5063e9421 ]

The norm should be flexible array structures with __counted_by annotations, so DEFINE_FLEX() is updated to expect that. Rename the non-annotated version to DEFINE_RAW_FLEX(), and update the few existing users. Additionally add selftests for the macros.

Stable-dep-of: cdc36db204ff ("Bluetooth: hci_sync: Fix advertising data UAFs")

---
 drivers/net/ethernet/intel/ice/ice_switch.c |   10 ++---
 include/linux/overflow.h                    |   52 ++++++++++++++++++++++++++++
 lib/overflow_kunit.c                        |   19 ++++++++++
 3 files changed, 76 insertions(+), 5 deletions(-)
drivers/net/ethernet/intel/ice/ice_switch.c +5 -5
@@ -1812,7 +1812,7 @@ ice_aq_alloc_free_vsi_list(struct ice_hw
 			   enum ice_sw_lkup_type lkup_type,
 			   enum ice_adminq_opc opc)
 {
-	DEFINE_FLEX(struct ice_aqc_alloc_free_res_elem, sw_buf, elem, 1);
+	DEFINE_RAW_FLEX(struct ice_aqc_alloc_free_res_elem, sw_buf, elem, 1);
 	u16 buf_len = __struct_size(sw_buf);
 	struct ice_aqc_res_elem *vsi_ele;
 	int status;
@@ -2082,7 +2082,7 @@ ice_aq_get_recipe_to_profile(struct ice_
  */
 int ice_alloc_recipe(struct ice_hw *hw, u16 *rid)
 {
-	DEFINE_FLEX(struct ice_aqc_alloc_free_res_elem, sw_buf, elem, 1);
+	DEFINE_RAW_FLEX(struct ice_aqc_alloc_free_res_elem, sw_buf, elem, 1);
 	u16 buf_len = __struct_size(sw_buf);
 	int status;
 
@@ -4425,7 +4425,7 @@ int
 ice_alloc_res_cntr(struct ice_hw *hw, u8 type, u8 alloc_shared, u16 num_items,
 		   u16 *counter_id)
 {
-	DEFINE_FLEX(struct ice_aqc_alloc_free_res_elem, buf, elem, 1);
+	DEFINE_RAW_FLEX(struct ice_aqc_alloc_free_res_elem, buf, elem, 1);
 	u16 buf_len = __struct_size(buf);
 	int status;
 
@@ -4453,7 +4453,7 @@ int
 ice_free_res_cntr(struct ice_hw *hw, u8 type, u8 alloc_shared, u16 num_items,
 		  u16 counter_id)
 {
-	DEFINE_FLEX(struct ice_aqc_alloc_free_res_elem, buf, elem, 1);
+	DEFINE_RAW_FLEX(struct ice_aqc_alloc_free_res_elem, buf, elem, 1);
 	u16 buf_len = __struct_size(buf);
 	int status;
 
@@ -4483,7 +4483,7 @@ ice_free_res_cntr(struct ice_hw *hw, u8
  */
 int ice_share_res(struct ice_hw *hw, u16 type, u8 shared, u16 res_id)
 {
-	DEFINE_FLEX(struct ice_aqc_alloc_free_res_elem, buf, elem, 1);
+	DEFINE_RAW_FLEX(struct ice_aqc_alloc_free_res_elem, buf, elem, 1);
 	u16 buf_len = __struct_size(buf);
 	int status;
 
include/linux/overflow.h +52 -0

56 lines not on this page · raw message

lib/overflow_kunit.c +19 -0
@@ -1113,6 +1113,24 @@ static void castable_to_type_test(struct
 #undef TEST_CASTABLE_TO_TYPE
 }
 
+struct foo {
+	int a;
+	u32 counter;
+	s16 array[] __counted_by(counter);
+};
+
+static void DEFINE_FLEX_test(struct kunit *test)
+{
+	DEFINE_RAW_FLEX(struct foo, two, array, 2);
+	DEFINE_FLEX(struct foo, eight, array, counter, 8);
+	DEFINE_FLEX(struct foo, empty, array, counter, 0);
+
+	KUNIT_EXPECT_EQ(test, __struct_size(two),
+			sizeof(struct foo) + sizeof(s16) + sizeof(s16));
+	KUNIT_EXPECT_EQ(test, __struct_size(eight), 24);
+	KUNIT_EXPECT_EQ(test, __struct_size(empty), sizeof(struct foo));
+}
+
 static struct kunit_case overflow_test_cases[] = {
 	KUNIT_CASE(u8_u8__u8_overflow_test),
 	KUNIT_CASE(s8_s8__s8_overflow_test),
@@ -1135,6 +1153,7 @@ static struct kunit_case overflow_test_c
 	KUNIT_CASE(overflows_type_test),
 	KUNIT_CASE(same_type_test),
 	KUNIT_CASE(castable_to_type_test),
+	KUNIT_CASE(DEFINE_FLEX_test),
 	{}
 };
 
author maintainer
#105 lore raw

PATCH 105/166 Bluetooth: hci_conn, hci_sync: Use __counted_by() to avoid -Wfamnae warnings

In reply to Greg Kroah-Hartman

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

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

From: "Gustavo A. R. Silva" <[email protected]>

[ Upstream commit c4585edf708edb5277a3cc4b8581ccb833f3307d ]

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 via CONFIG_UBSAN_BOUNDS (for array indexing) and CONFIG_FORTIFY_SOURCE (for strcpy/memcpy-family functions).

Also, -Wflex-array-member-not-at-end is coming in GCC-14, and we are getting ready to enable it globally.

So, use the DEFINE_FLEX() helper for multiple on-stack definitions of a flexible structure where the size of the flexible-array member is known at compile-time, and refactor the rest of the code, accordingly.

Notice that, due to the use of __counted_by() in struct hci_cp_le_create_cis, the for loop in function hci_cs_le_create_cis() had to be modified. Once the index i, through which cp->cis[i] is accessed, falls in the interval [0, cp->num_cis), cp->num_cis cannot be decremented all the way down to zero while accessing cp->cis[]:

net/bluetooth/hci_event.c:4310:

4310    for (i = 0; cp->num_cis; cp->num_cis--, i++) {
                ...

4314 handle = __le16_to_cpu(cp->cis[i].cis_handle);

otherwise, only half (one iteration before cp->num_cis == i) or half plus one (one iteration before cp->num_cis < i) of the items in the array will be accessed before running into an out-of-bounds issue. So, in order to avoid this, set cp->num_cis to zero just after the for loop.

Also, make use of aux_num_cis variable to update cmd->num_cis after a list_for_each_entry_rcu() loop.

With these changes, fix the following warnings:
net/bluetooth/hci_sync.c:1239:56: warning: structure containing a flexible array member is not at the end of another structure [-Wflex-array-member-not-at-end] net/bluetooth/hci_sync.c:1415:51: warning: structure containing a flexible array member is not at the end of another structure [-Wflex-array-member-not-at-end] net/bluetooth/hci_sync.c:1731:51: warning: structure containing a flexible array member is not at the end of another structure [-Wflex-array-member-not-at-end] net/bluetooth/hci_sync.c:6497:45: warning: structure containing a flexible array member is not at the end of another structure [-Wflex-array-member-not-at-end]

Stable-dep-of: cdc36db204ff ("Bluetooth: hci_sync: Fix advertising data UAFs")

---
 include/net/bluetooth/hci.h |    8 ++--
 net/bluetooth/hci_event.c   |    3 +
 net/bluetooth/hci_sync.c    |   84 +++++++++++++++++---------------------------
 3 files changed, 40 insertions(+), 55 deletions(-)
include/net/bluetooth/hci.h +4 -4
@@ -1949,7 +1949,7 @@ struct hci_cp_le_set_ext_adv_data {
 	__u8  operation;
 	__u8  frag_pref;
 	__u8  length;
-	__u8  data[];
+	__u8  data[] __counted_by(length);
 } __packed;
 
 #define HCI_OP_LE_SET_EXT_SCAN_RSP_DATA		0x2038
@@ -1958,7 +1958,7 @@ struct hci_cp_le_set_ext_scan_rsp_data {
 	__u8  operation;
 	__u8  frag_pref;
 	__u8  length;
-	__u8  data[];
+	__u8  data[] __counted_by(length);
 } __packed;
 
 #define HCI_OP_LE_SET_EXT_ADV_ENABLE		0x2039
@@ -1983,7 +1983,7 @@ struct hci_cp_le_set_per_adv_data {
 	__u8  handle;
 	__u8  operation;
 	__u8  length;
-	__u8  data[];
+	__u8  data[] __counted_by(length);
 } __packed;
 
 #define HCI_OP_LE_SET_PER_ADV_ENABLE		0x2040
@@ -2084,7 +2084,7 @@ struct hci_cis {
 
 struct hci_cp_le_create_cis {
 	__u8    num_cis;
-	struct hci_cis cis[];
+	struct hci_cis cis[] __counted_by(num_cis);
 } __packed;
 
 #define HCI_OP_LE_REMOVE_CIG			0x2065
net/bluetooth/hci_event.c +2 -1
@@ -4253,7 +4253,7 @@ static void hci_cs_le_create_cis(struct
 	hci_dev_lock(hdev);
 
 	/* Remove connection if command failed */
-	for (i = 0; cp->num_cis; cp->num_cis--, i++) {
+	for (i = 0; i < cp->num_cis; i++) {
 		struct hci_conn *conn;
 		u16 handle;
 
@@ -4269,6 +4269,7 @@ static void hci_cs_le_create_cis(struct
 			hci_conn_del(conn);
 		}
 	}
+	cp->num_cis = 0;
 
 	if (pending)
 		hci_le_create_cis_pending(hdev);
net/bluetooth/hci_sync.c +34 -50

188 lines not on this page · raw message

author maintainer
#106 lore raw

PATCH 106/166 Bluetooth: hci_core: Fix not handling hdev->le_num_of_adv_sets=1

In reply to Greg Kroah-Hartman

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

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

From: Luiz Augusto von Dentz <[email protected]>

[ Upstream commit e77f43d531af41e9ce299eab10dcae8fa5dbc293 ]

If hdev->le_num_of_adv_sets is set to 1 it means that only handle 0x00 can be used, but since the MGMT interface instances start from 1 (instance 0 means all instances in case of MGMT_OP_REMOVE_ADVERTISING) the code needs to map the instance to handle otherwise users will not be able to advertise as instance 1 would attempt to use handle 0x01.

Fixes 1d0fac2c38ed ("Bluetooth: Use controller sets when available")

Stable-dep-of: cdc36db204ff ("Bluetooth: hci_sync: Fix advertising data UAFs")

---
 include/net/bluetooth/hci_core.h |    1 +
 net/bluetooth/hci_core.c         |    9 +++++++++
 net/bluetooth/hci_sync.c         |   19 +++++++++----------
 3 files changed, 19 insertions(+), 10 deletions(-)
include/net/bluetooth/hci_core.h +1 -0
@@ -245,6 +245,7 @@ struct adv_info {
 	bool	periodic_enabled;
 	__u8	mesh;
 	__u8	instance;
+	__u8	handle;
 	__u32	flags;
 	__u16	timeout;
 	__u16	remaining_time;
net/bluetooth/hci_core.c +9 -0
@@ -1706,6 +1706,15 @@ struct adv_info *hci_add_adv_instance(st
 
 		adv->pending = true;
 		adv->instance = instance;
+
+		/* If controller support only one set and the instance is set to
+		 * 1 then there is no option other than using handle 0x00.
+		 */
+		if (hdev->le_num_of_adv_sets == 1 && instance == 1)
+			adv->handle = 0x00;
+		else
+			adv->handle = instance;
+
 		list_add(&adv->list, &hdev->adv_instances);
 		hdev->adv_instance_cnt++;
 	}
net/bluetooth/hci_sync.c +9 -10
@@ -1195,11 +1195,10 @@ static int hci_disable_ext_adv_instance_
 	struct hci_cp_ext_adv_set *set;
 	u8 data[sizeof(*cp) + sizeof(*set) * 1];
 	u8 size;
+	struct adv_info *adv = NULL;
 
 	/* If request specifies an instance that doesn't exist, fail */
 	if (instance > 0) {
-		struct adv_info *adv;
-
 		adv = hci_find_adv_instance(hdev, instance);
 		if (!adv)
 			return -EINVAL;
@@ -1218,7 +1217,7 @@ static int hci_disable_ext_adv_instance_
 	cp->num_of_sets = !!instance;
 	cp->enable = 0x00;
 
-	set->handle = instance;
+	set->handle = adv ? adv->handle : instance;
 
 	size = sizeof(*cp) + sizeof(*set) * cp->num_of_sets;
 
@@ -1312,7 +1311,7 @@ static int hci_set_ext_adv_data_sync(str
 	len = eir_create_adv_data(hdev, instance, pdu->data);
 
 	pdu->length = len;
-	pdu->handle = instance;
+	pdu->handle = adv ? adv->handle : instance;
 	pdu->operation = LE_SET_ADV_DATA_OP_COMPLETE;
 	pdu->frag_pref = LE_SET_ADV_DATA_NO_FRAG;
 
@@ -1465,7 +1464,7 @@ int hci_setup_ext_adv_instance_sync(stru
 
 	cp.own_addr_type = own_addr_type;
 	cp.channel_map = hdev->le_adv_channel_map;
-	cp.handle = instance;
+	cp.handle = adv ? adv->handle : instance;
 
 	if (flags & MGMT_ADV_FLAG_SEC_2M) {
 		cp.primary_phy = HCI_ADV_PHY_1M;
@@ -1523,7 +1522,7 @@ static int hci_set_ext_scan_rsp_data_syn
 
 	len = eir_create_scan_rsp(hdev, instance, pdu->data);
 
-	pdu->handle = instance;
+	pdu->handle = adv ? adv->handle : instance;
 	pdu->length = len;
 	pdu->operation = LE_SET_ADV_DATA_OP_COMPLETE;
 	pdu->frag_pref = LE_SET_ADV_DATA_NO_FRAG;
@@ -1605,7 +1604,7 @@ int hci_enable_ext_advertising_sync(stru
 
 	memset(set, 0, sizeof(*set));
 
-	set->handle = instance;
+	set->handle = adv ? adv->handle : instance;
 
 	/* Set duration per instance since controller is responsible for
 	 * scheduling it.
@@ -1684,10 +1683,10 @@ static int hci_set_per_adv_data_sync(str
 	DEFINE_FLEX(struct hci_cp_le_set_per_adv_data, pdu, data, length,
 		    HCI_MAX_PER_AD_LENGTH);
 	u8 len;
+	struct adv_info *adv = NULL;
 
 	if (instance) {
-		struct adv_info *adv = hci_find_adv_instance(hdev, instance);
-
+		adv = hci_find_adv_instance(hdev, instance);
 		if (!adv || !adv->periodic)
 			return 0;
 	}
@@ -1695,7 +1694,7 @@ static int hci_set_per_adv_data_sync(str
 	len = eir_create_per_adv_data(hdev, instance, pdu->data);
 
 	pdu->length = len;
-	pdu->handle = instance;
+	pdu->handle = adv ? adv->handle : instance;
 	pdu->operation = LE_SET_ADV_DATA_OP_COMPLETE;
 
 	return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_PER_ADV_DATA,
author maintainer
#107 lore raw

PATCH 107/166 Bluetooth: eir: Fix possible crashes on eir_create_adv_data

In reply to Greg Kroah-Hartman

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

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

From: Luiz Augusto von Dentz <[email protected]>

[ Upstream commit 47c03902269aff377f959dc3fd94a9733aa31d6e ]

eir_create_adv_data may attempt to add EIR_FLAGS and EIR_TX_POWER without checking if that would fit.

Fixes 01ce70b0a274 ("Bluetooth: eir: Move EIR/Adv Data functions to its own file")

Stable-dep-of: cdc36db204ff ("Bluetooth: hci_sync: Fix advertising data UAFs")

---
 net/bluetooth/eir.c      |    7 ++++---
 net/bluetooth/eir.h      |    2 +-
 net/bluetooth/hci_sync.c |    5 +++--
 3 files changed, 8 insertions(+), 6 deletions(-)
net/bluetooth/eir.c +4 -3
@@ -242,7 +242,7 @@ u8 eir_create_per_adv_data(struct hci_de
 	return ad_len;
 }
 
-u8 eir_create_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr)
+u8 eir_create_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr, u8 size)
 {
 	struct adv_info *adv = NULL;
 	u8 ad_len = 0, flags = 0;
@@ -286,7 +286,7 @@ u8 eir_create_adv_data(struct hci_dev *h
 		/* If flags would still be empty, then there is no need to
 		 * include the "Flags" AD field".
 		 */
-		if (flags) {
+		if (flags && (ad_len + eir_precalc_len(1) <= size)) {
 			ptr[0] = 0x02;
 			ptr[1] = EIR_FLAGS;
 			ptr[2] = flags;
@@ -316,7 +316,8 @@ skip_flags:
 		}
 
 		/* Provide Tx Power only if we can provide a valid value for it */
-		if (adv_tx_power != HCI_TX_POWER_INVALID) {
+		if (adv_tx_power != HCI_TX_POWER_INVALID &&
+		    (ad_len + eir_precalc_len(1) <= size)) {
 			ptr[0] = 0x02;
 			ptr[1] = EIR_TX_POWER;
 			ptr[2] = (u8)adv_tx_power;
net/bluetooth/eir.h +1 -1
@@ -9,7 +9,7 @@
 
 void eir_create(struct hci_dev *hdev, u8 *data);
 
-u8 eir_create_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr);
+u8 eir_create_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr, u8 size);
 u8 eir_create_scan_rsp(struct hci_dev *hdev, u8 instance, u8 *ptr);
 u8 eir_create_per_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr);
 
net/bluetooth/hci_sync.c +3 -2
@@ -1308,7 +1308,8 @@ static int hci_set_ext_adv_data_sync(str
 			return 0;
 	}
 
-	len = eir_create_adv_data(hdev, instance, pdu->data);
+	len = eir_create_adv_data(hdev, instance, pdu->data,
+				  HCI_MAX_EXT_AD_LENGTH);
 
 	pdu->length = len;
 	pdu->handle = adv ? adv->handle : instance;
@@ -1339,7 +1340,7 @@ static int hci_set_adv_data_sync(struct
 
 	memset(&cp, 0, sizeof(cp));
 
-	len = eir_create_adv_data(hdev, instance, cp.data);
+	len = eir_create_adv_data(hdev, instance, cp.data, sizeof(cp.data));
 
 	/* There's nothing to do if the data hasn't changed */
 	if (hdev->adv_data_len == len &&
author maintainer
#108 lore raw

PATCH 108/166 Bluetooth: hci_sync: Fix advertising data UAFs

In reply to Greg Kroah-Hartman

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

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

From: Chengfeng Ye <[email protected]>

[ Upstream commit cdc36db204ffd97b947d64374cf23a210dc74777 ]

hci_find_adv_instance() returns an adv_info pointer that is valid only while hdev->lock is held. The advertising command-sync paths perform instance lookups without that lock and, in some cases, retain the pointer while waiting for a controller response.

An advertising termination event can therefore interleave as follows:

hci_cmd_sync_work hci_rx_work
hci_find_adv_instance()
__hci_cmd_sync_status()
wait for controller reply hci_dev_lock()
hci_remove_adv_instance()
kfree(adv)
adv->scan_rsp_changed = false

KASAN reported:

  BUG: KASAN: slab-use-after-free in hci_set_ext_scan_rsp_data_sync+0x2e1/0x300
  Write of size 1 at addr ffff88810a45d21d by task kworker/u17:0/88
  Workqueue: hci0 hci_cmd_sync_work
  Call Trace:
   hci_set_ext_scan_rsp_data_sync+0x2e1/0x300
   hci_schedule_adv_instance_sync+0x390/0x4c0
   hci_cmd_sync_work+0x173/0x300
  Allocated by task 87:
   hci_add_adv_instance+0x538/0xac0
   add_advertising+0x885/0x1160
  Freed by task 89:
   kfree+0x131/0x3c0
   hci_remove_adv_instance+0x1d8/0x3b0
   hci_le_ext_adv_term_evt+0x17b/0x730

Protect the instance lookup and payload construction in the extended advertising, scan response, and periodic advertising data paths. Snapshot the advertising parameters under hdev->lock, but release the lock before waiting for the controller.

Clear advertising-data dirty bits before issuing their commands and restore them after a failure using a fresh lookup. Likewise, update the reported transmit power through a fresh lookup after the parameter command completes. No adv_info pointer then survives an HCI command wait.

Fixes cba6b758711c ("Bluetooth: hci_sync: Make use of hci_cmd_sync_queue set 2")
---
 net/bluetooth/hci_sync.c |  131 ++++++++++++++++++++++++++++++++++-------------
 1 file changed, 97 insertions(+), 34 deletions(-)
net/bluetooth/hci_sync.c +97 -34
@@ -1252,10 +1252,11 @@ static int hci_set_adv_set_random_addr_s
 }
 
 static int
-hci_set_ext_adv_params_sync(struct hci_dev *hdev, struct adv_info *adv,
+hci_set_ext_adv_params_sync(struct hci_dev *hdev, u8 instance,
 			    const struct hci_cp_le_set_ext_adv_params *cp,
 			    struct hci_rp_le_set_ext_adv_params *rp)
 {
+	struct adv_info *adv;
 	struct sk_buff *skb;
 
 	skb = __hci_cmd_sync(hdev, HCI_OP_LE_SET_EXT_ADV_PARAMS, sizeof(*cp),
@@ -1283,11 +1284,15 @@ hci_set_ext_adv_params_sync(struct hci_d
 
 	if (!rp->status) {
 		hdev->adv_addr_type = cp->own_addr_type;
-		if (!cp->handle) {
+		if (!instance) {
 			/* Store in hdev for instance 0 */
 			hdev->adv_tx_power = rp->tx_power;
-		} else if (adv) {
-			adv->tx_power = rp->tx_power;
+		} else {
+			hci_dev_lock(hdev);
+			adv = hci_find_adv_instance(hdev, instance);
+			if (adv)
+				adv->tx_power = rp->tx_power;
+			hci_dev_unlock(hdev);
 		}
 	}
 
@@ -1303,9 +1308,13 @@ static int hci_set_ext_adv_data_sync(str
 	int err;
 
 	if (instance) {
+		hci_dev_lock(hdev);
+
 		adv = hci_find_adv_instance(hdev, instance);
-		if (!adv || !adv->adv_data_changed)
+		if (!adv || !adv->adv_data_changed) {
+			hci_dev_unlock(hdev);
 			return 0;
+		}
 	}
 
 	len = eir_create_adv_data(hdev, instance, pdu->data,
@@ -1316,16 +1325,27 @@ static int hci_set_ext_adv_data_sync(str
 	pdu->operation = LE_SET_ADV_DATA_OP_COMPLETE;
 	pdu->frag_pref = LE_SET_ADV_DATA_NO_FRAG;
 
+	if (adv) {
+		adv->adv_data_changed = false;
+		hci_dev_unlock(hdev);
+	}
+
 	err = __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EXT_ADV_DATA,
 				    struct_size(pdu, data, len), pdu,
 				    HCI_CMD_TIMEOUT);
-	if (err)
+	if (err) {
+		if (instance) {
+			hci_dev_lock(hdev);
+			adv = hci_find_adv_instance(hdev, instance);
+			if (adv)
+				adv->adv_data_changed = true;
+			hci_dev_unlock(hdev);
+		}
+
 		return err;
+	}
 
-	/* Update data if the command succeed */
-	if (adv) {
-		adv->adv_data_changed = false;
-	} else {
+	if (!instance) {
 		memcpy(hdev->adv_data, pdu->data, len);
 		hdev->adv_data_len = len;
 	}
@@ -1379,22 +1399,22 @@ int hci_setup_ext_adv_instance_sync(stru
 	struct adv_info *adv;
 	bool secondary_adv;
 
-	if (instance > 0) {
-		adv = hci_find_adv_instance(hdev, instance);
-		if (!adv)
-			return -EINVAL;
-	} else {
-		adv = NULL;
-	}
-
 	/* Updating parameters of an active instance will return a
-	 * Command Disallowed error, so we must first disable the
-	 * instance if it is active.
+	 * Command Disallowed error, so disable it before taking a snapshot.
 	 */
-	if (adv) {
+	if (instance > 0) {
 		err = hci_disable_ext_adv_instance_sync(hdev, instance);
 		if (err)
 			return err;
+
+		hci_dev_lock(hdev);
+		adv = hci_find_adv_instance(hdev, instance);
+		if (!adv) {
+			hci_dev_unlock(hdev);
+			return -EINVAL;
+		}
+	} else {
+		adv = NULL;
 	}
 
 	flags = hci_adv_instance_flags(hdev, instance);
@@ -1405,8 +1425,11 @@ int hci_setup_ext_adv_instance_sync(stru
 	connectable = (flags & MGMT_ADV_FLAG_CONNECTABLE) ||
 		      mgmt_get_connectable(hdev);
 
-	if (!is_advertising_allowed(hdev, connectable))
+	if (!is_advertising_allowed(hdev, connectable)) {
+		if (instance)
+			hci_dev_unlock(hdev);
 		return -EPERM;
+	}
 
 	/* Set require_privacy to true only when non-connectable
 	 * advertising is used and it is not periodic.
@@ -1417,8 +1440,11 @@ int hci_setup_ext_adv_instance_sync(stru
 	err = hci_get_random_address(hdev, require_privacy,
 				     adv_use_rpa(hdev, flags), adv,
 				     &own_addr_type, &random_addr);
-	if (err < 0)
+	if (err < 0) {
+		if (instance)
+			hci_dev_unlock(hdev);
 		return err;
+	}
 
 	memset(&cp, 0, sizeof(cp));
 
@@ -1467,6 +1493,9 @@ int hci_setup_ext_adv_instance_sync(stru
 	cp.channel_map = hdev->le_adv_channel_map;
 	cp.handle = adv ? adv->handle : instance;
 
+	if (instance)
+		hci_dev_unlock(hdev);
+
 	if (flags & MGMT_ADV_FLAG_SEC_2M) {
 		cp.primary_phy = HCI_ADV_PHY_1M;
 		cp.secondary_phy = HCI_ADV_PHY_2M;
@@ -1479,12 +1508,12 @@ int hci_setup_ext_adv_instance_sync(stru
 		cp.secondary_phy = HCI_ADV_PHY_1M;
 	}
 
-	err = hci_set_ext_adv_params_sync(hdev, adv, &cp, &rp);
+	err = hci_set_ext_adv_params_sync(hdev, instance, &cp, &rp);
 	if (err)
 		return err;
 
 	/* Update adv data as tx power is known now */
-	err = hci_set_ext_adv_data_sync(hdev, cp.handle);
+	err = hci_set_ext_adv_data_sync(hdev, instance);
 	if (err)
 		return err;
 
@@ -1492,9 +1521,14 @@ int hci_setup_ext_adv_instance_sync(stru
 	     own_addr_type == ADDR_LE_DEV_RANDOM_RESOLVED) &&
 	    bacmp(&random_addr, BDADDR_ANY)) {
 		/* Check if random address need to be updated */
-		if (adv) {
-			if (!bacmp(&random_addr, &adv->random_addr))
+		if (instance) {
+			hci_dev_lock(hdev);
+			adv = hci_find_adv_instance(hdev, instance);
+			if (!adv || !bacmp(&random_addr, &adv->random_addr)) {
+				hci_dev_unlock(hdev);
 				return 0;
+			}
+			hci_dev_unlock(hdev);
 		} else {
 			if (!bacmp(&random_addr, &hdev->random_addr))
 				return 0;
@@ -1516,9 +1550,13 @@ static int hci_set_ext_scan_rsp_data_syn
 	int err;
 
 	if (instance) {
+		hci_dev_lock(hdev);
+
 		adv = hci_find_adv_instance(hdev, instance);
-		if (!adv || !adv->scan_rsp_changed)
+		if (!adv || !adv->scan_rsp_changed) {
+			hci_dev_unlock(hdev);
 			return 0;
+		}
 	}
 
 	len = eir_create_scan_rsp(hdev, instance, pdu->data);
@@ -1528,15 +1566,27 @@ static int hci_set_ext_scan_rsp_data_syn
 	pdu->operation = LE_SET_ADV_DATA_OP_COMPLETE;
 	pdu->frag_pref = LE_SET_ADV_DATA_NO_FRAG;
 
+	if (adv) {
+		adv->scan_rsp_changed = false;
+		hci_dev_unlock(hdev);
+	}
+
 	err = __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EXT_SCAN_RSP_DATA,
 				    struct_size(pdu, data, len), pdu,
 				    HCI_CMD_TIMEOUT);
-	if (err)
+	if (err) {
+		if (instance) {
+			hci_dev_lock(hdev);
+			adv = hci_find_adv_instance(hdev, instance);
+			if (adv)
+				adv->scan_rsp_changed = true;
+			hci_dev_unlock(hdev);
+		}
+
 		return err;
+	}
 
-	if (adv) {
-		adv->scan_rsp_changed = false;
-	} else {
+	if (!instance) {
 		memcpy(hdev->scan_rsp_data, pdu->data, len);
 		hdev->scan_rsp_data_len = len;
 	}
@@ -1551,8 +1601,14 @@ static int __hci_set_scan_rsp_data_sync(
 
 	memset(&cp, 0, sizeof(cp));
 
+	if (instance)
+		hci_dev_lock(hdev);
+
 	len = eir_create_scan_rsp(hdev, instance, cp.data);
 
+	if (instance)
+		hci_dev_unlock(hdev);
+
 	if (hdev->scan_rsp_data_len == len &&
 	    !memcmp(cp.data, hdev->scan_rsp_data, len))
 		return 0;
@@ -1687,9 +1743,13 @@ static int hci_set_per_adv_data_sync(str
 	struct adv_info *adv = NULL;
 
 	if (instance) {
+		hci_dev_lock(hdev);
+
 		adv = hci_find_adv_instance(hdev, instance);
-		if (!adv || !adv->periodic)
+		if (!adv || !adv->periodic) {
+			hci_dev_unlock(hdev);
 			return 0;
+		}
 	}
 
 	len = eir_create_per_adv_data(hdev, instance, pdu->data);
@@ -1698,6 +1758,9 @@ static int hci_set_per_adv_data_sync(str
 	pdu->handle = adv ? adv->handle : instance;
 	pdu->operation = LE_SET_ADV_DATA_OP_COMPLETE;
 
+	if (adv)
+		hci_dev_unlock(hdev);
+
 	return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_PER_ADV_DATA,
 				     struct_size(pdu, data, len), pdu,
 				     HCI_CMD_TIMEOUT);
@@ -6402,7 +6465,7 @@ static int hci_le_ext_directed_advertisi
 	if (err)
 		return err;
 
-	err = hci_set_ext_adv_params_sync(hdev, NULL, &cp, &rp);
+	err = hci_set_ext_adv_params_sync(hdev, 0, &cp, &rp);
 	if (err)
 		return err;
 
author maintainer
#109 lore raw

PATCH 109/166 ASoC: tas2562: Validate values for volume writes

In reply to Greg Kroah-Hartman

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

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

From: Mark Brown <[email protected]>

[ Upstream commit 8fb41964f7e4e4207c8999af2056894caa7a252a ]

tas2562_volume_control_put() does not do any validation of the control value written by userspace, it uses it to look up a value in a fixed size array which can easily be overflowed and then writes whatever value it gets back to the device. Add validation that we are loading a value we have in the array.

---
 sound/soc/codecs/tas2562.c |    9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)
sound/soc/codecs/tas2562.c +7 -2
@@ -473,10 +473,15 @@ static int tas2562_volume_control_put(st
 {
 	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
 	struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component);
-	int ret;
+	int ret, index;
 	u32 reg_val;
 
-	reg_val = float_vol_db_lookup[ucontrol->value.integer.value[0]/2];
+	index = ucontrol->value.integer.value[0] / 2;
+	if (index < 0 || index >= ARRAY_SIZE(float_vol_db_lookup))
+		return -EINVAL;
+
+	reg_val = float_vol_db_lookup[index];
+
 	/*
 	 * The device applies the 32-bit coefficient to the playback path on
 	 * the write to DVC_CFG4 (the LSB, book 0 page 2 reg 0x0F), so the
author maintainer
#110 lore raw

PATCH 110/166 igc: remove napi_synchronize() in igc_down()

In reply to Greg Kroah-Hartman

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

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

From: David Carlier <[email protected]>

[ Upstream commit 5ffab5b9589c50e4cfc0cf36ffd76c89422d4019 ]

When an AF_XDP zero-copy application is killed abruptly, the XSK pool is torn down but NAPI keeps polling. igc_clean_rx_irq_zc() then returns the full budget on every poll, so napi_complete_done() never clears NAPI_STATE_SCHED.

igc_down() calls napi_synchronize() before napi_disable(), so it spins forever waiting for that bit and the interface never goes down. Drop the napi_synchronize() and let napi_disable() do the job -- it sets NAPI_STATE_DISABLE, which forces the stuck poll to complete. Reorder it ahead of igc_set_queue_napi() so the NAPI mapping is cleared only after polling has stopped, matching the recent igb fix b1e067240379.

---
 drivers/net/ethernet/intel/igc/igc_main.c |    1 -
 1 file changed, 1 deletion(-)
drivers/net/ethernet/intel/igc/igc_main.c +0 -1
@@ -5085,7 +5085,6 @@ void igc_down(struct igc_adapter *adapte
 
 	for (i = 0; i < adapter->num_q_vectors; i++) {
 		if (adapter->q_vector[i]) {
-			napi_synchronize(&adapter->q_vector[i]->napi);
 			napi_disable(&adapter->q_vector[i]->napi);
 		}
 	}
author maintainer
#111 lore raw

PATCH 111/166 ksmbd: conn lock to serialize smb2 negotiate

In reply to Greg Kroah-Hartman

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

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

From: Namjae Jeon <[email protected]>

[ Upstream commit fe4ed2f09b492e3507615a053814daa8fafdecb1 ]

If client send parallel smb2 negotiate request on same connection, ksmbd_conn can be racy. smb2 negotiate handling that are not performance-related can be serialized with conn lock.

Stable-dep-of: cb469993b3a6 ("ksmbd: reject repeated SMB2 NEGOTIATE requests")

---
 fs/smb/server/smb2pdu.c |    2 ++
 1 file changed, 2 insertions(+)
fs/smb/server/smb2pdu.c +2 -0
@@ -1114,6 +1114,7 @@ int smb2_handle_negotiate(struct ksmbd_w
 		return rc;
 	}
 
+	ksmbd_conn_lock(conn);
 	smb2_buf_len = get_rfc1002_len(work->request_buf);
 	smb2_neg_size = offsetof(struct smb2_negotiate_req, Dialects);
 	if (smb2_neg_size > smb2_buf_len) {
@@ -1264,6 +1265,7 @@ int smb2_handle_negotiate(struct ksmbd_w
 	ksmbd_conn_set_need_setup(conn);
 
 err_out:
+	ksmbd_conn_unlock(conn);
 	if (rc)
 		rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
 
author maintainer
#112 lore raw

PATCH 112/166 ksmbd: reject repeated SMB2 NEGOTIATE requests

In reply to Greg Kroah-Hartman

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

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

From: Namjae Jeon <[email protected]>

[ Upstream commit cb469993b3a61a72653770856d37af616d72d05f ]

Unauthenticated client can send multiple successful SMB2 NEGOTIATE requests on one connection before SESSION_SETUP. While the connection is in KSMBD_SESS_NEED_SETUP, smb2_handle_negotiate() accepts another SMB3.1.1 NEGOTIATE and overwrites conn->preauth_info with a new allocation. Only the final allocation is freed when the connection is released, leaking one object for every additional successful request.

A repeated SMB2 NEGOTIATE after a dialect has been selected is a protocol violation. MS-SMB2 section 3.3.5.4 requires the server to disconnect without replying in this case. Set the connection exiting when rejecting the request, in addition to suppressing the response.

Reject SMB2 NEGOTIATE unless the connection is new or is waiting for the SMB2 NEGOTIATE that follows an SMB1 multi-protocol negotiate. Serialize both SMB1 and SMB2 negotiation paths under conn->srv_mutex, since they update connection-wide dialect and negotiation state.

Move the locking contract to ksmbd_smb_negotiate_common(), where the state and dialect are selected, and add ksmbd_conn_new() for consistent state access.

Fixes e2f34481b24d ("cifsd: add server-side procedures for SMB3")
---
 fs/smb/server/connection.h |    5 +++++
 fs/smb/server/smb2pdu.c    |   10 ++--------
 fs/smb/server/smb_common.c |   37 ++++++++++++++++++++++++++++++-------
 3 files changed, 37 insertions(+), 15 deletions(-)
fs/smb/server/connection.h +5 -0
@@ -194,6 +194,11 @@ void ksmbd_conn_r_count_dec(struct ksmbd
  * This is a hack. We will move status to a proper place once we land
  * a multi-sessions support.
  */
+static inline bool ksmbd_conn_new(struct ksmbd_conn *conn)
+{
+	return READ_ONCE(conn->status) == KSMBD_SESS_NEW;
+}
+
 static inline bool ksmbd_conn_good(struct ksmbd_conn *conn)
 {
 	return READ_ONCE(conn->status) == KSMBD_SESS_GOOD;
fs/smb/server/smb2pdu.c +2 -8
@@ -1095,6 +1095,8 @@ static __le32 deassemble_neg_contexts(st
  * smb2_handle_negotiate() - handler for smb2 negotiate command
  * @work:	smb work containing smb request buffer
  *
+ * The caller holds conn->srv_mutex.
+ *
  * Return:      0
  */
 int smb2_handle_negotiate(struct ksmbd_work *work)
@@ -1108,13 +1110,6 @@ int smb2_handle_negotiate(struct ksmbd_w
 
 	ksmbd_debug(SMB, "Received negotiate request\n");
 	conn->need_neg = false;
-	if (ksmbd_conn_good(conn)) {
-		pr_err("conn->tcp_status is already in CifsGood State\n");
-		work->send_no_response = 1;
-		return rc;
-	}
-
-	ksmbd_conn_lock(conn);
 	smb2_buf_len = get_rfc1002_len(work->request_buf);
 	smb2_neg_size = offsetof(struct smb2_negotiate_req, Dialects);
 	if (smb2_neg_size > smb2_buf_len) {
@@ -1265,7 +1260,6 @@ int smb2_handle_negotiate(struct ksmbd_w
 	ksmbd_conn_set_need_setup(conn);
 
 err_out:
-	ksmbd_conn_unlock(conn);
 	if (rc)
 		rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
 
fs/smb/server/smb_common.c +30 -7
@@ -592,23 +592,46 @@ int ksmbd_smb_negotiate_common(struct ks
 	struct ksmbd_conn *conn = work->conn;
 	int ret;
 
-	conn->dialect =
-		ksmbd_negotiate_smb_dialect(work->request_buf);
-	ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect);
-
 	if (command == SMB2_NEGOTIATE_HE) {
+		/*
+		 * An SMB2 NEGOTIATE is valid for a new connection, or after an
+		 * SMB1 multi-protocol negotiate has selected SMB2. Do not allow
+		 * a second SMB2 NEGOTIATE to replace connection-wide state
+		 * while a session setup is pending. KSMBD_SESS_NEED_RECONNECT
+		 * is a transient session state and does not restart transport
+		 * negotiation.
+		 */
+		ksmbd_conn_lock(conn);
+		if (!ksmbd_conn_new(conn) &&
+		    !ksmbd_conn_need_negotiate(conn)) {
+			work->send_no_response = 1;
+			ksmbd_conn_set_exiting(conn);
+			ksmbd_conn_unlock(conn);
+			return 0;
+		}
+
+		conn->dialect =
+			ksmbd_negotiate_smb_dialect(work->request_buf);
+		ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect);
 		ret = smb2_handle_negotiate(work);
+		ksmbd_conn_unlock(conn);
 		return ret;
 	}
 
 	if (command == SMB_COM_NEGOTIATE) {
+		ksmbd_conn_lock(conn);
+		conn->dialect =
+			ksmbd_negotiate_smb_dialect(work->request_buf);
+		ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect);
 		if (__smb2_negotiate(conn)) {
 			init_smb3_11_server(conn);
-			init_smb2_neg_rsp(work);
+			ret = init_smb2_neg_rsp(work);
 			ksmbd_debug(SMB, "Upgrade to SMB2 negotiation\n");
-			return 0;
+		} else {
+			ret = smb_handle_negotiate(work);
 		}
-		return smb_handle_negotiate(work);
+		ksmbd_conn_unlock(conn);
+		return ret;
 	}
 
 	pr_err("Unknown SMB negotiation command: %u\n", command);
author maintainer
#113 lore raw

PATCH 113/166 net: pktgen: fix code style (WARNING: Block comments)

In reply to Greg Kroah-Hartman

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

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

From: Peter Seiderer <[email protected]>

[ Upstream commit 870b856cb478bc02fffe4d89897e62c692efb09a ]

Fix checkpatch code style warnings:

WARNING: Block comments use a trailing */ on a separate line
+ * removal by worker thread */

WARNING: Block comments use * on subsequent lines
+ __u8 tos; /* six MSB of (former) IPv4 TOS
+ are for dscp codepoint */

WARNING: Block comments use a trailing */ on a separate line
+ are for dscp codepoint */

WARNING: Block comments use * on subsequent lines
+ __u8 traffic_class; /* ditto for the (former) Traffic Class in IPv6
+ (see RFC 3260, sec. 4) */

WARNING: Block comments use a trailing */ on a separate line
+ (see RFC 3260, sec. 4) */

WARNING: Block comments use * on subsequent lines
+ /* = {
+ 0x00, 0x80, 0xC8, 0x79, 0xB3, 0xCB,

WARNING: Block comments use * on subsequent lines
+ /* Field for thread to receive "posted" events terminate,
+ stop ifs etc. */

WARNING: Block comments use a trailing */ on a separate line
+ stop ifs etc. */

WARNING: Block comments should align the * on each line
+ * we go look for it ...
+*/

WARNING: Block comments use a trailing */ on a separate line
+ * we resolve the dst issue */

WARNING: Block comments use a trailing */ on a separate line
+ * with proc_create_data() */

Stable-dep-of: 817ff6efdb7f ("net: pktgen: fix proc entry use-after-free")

---
 net/core/pktgen.c |   39 +++++++++++++++++++++++----------------
 1 file changed, 23 insertions(+), 16 deletions(-)
net/core/pktgen.c +23 -16
@@ -284,7 +284,8 @@ struct pktgen_dev {
 	int pkt_overhead;	/* overhead for MPLS, VLANs, IPSEC etc */
 	int nfrags;
 	int removal_mark;	/* non-zero => the device is marked for
-				 * removal by worker thread */
+				 * removal by worker thread
+				 */
 
 	struct page *page;
 	u64 delay;		/* nano-seconds */
@@ -347,10 +348,12 @@ struct pktgen_dev {
 	__u16 udp_dst_max;	/* exclusive, dest UDP port */
 
 	/* DSCP + ECN */
-	__u8 tos;            /* six MSB of (former) IPv4 TOS
-				are for dscp codepoint */
-	__u8 traffic_class;  /* ditto for the (former) Traffic Class in IPv6
-				(see RFC 3260, sec. 4) */
+	__u8 tos;		/* six MSB of (former) IPv4 TOS
+				 * are for dscp codepoint
+				 */
+	__u8 traffic_class;	/* ditto for the (former) Traffic Class in IPv6
+				 * (see RFC 3260, sec. 4)
+				 */
 
 	/* IMIX */
 	unsigned int n_imix_entries;
@@ -390,12 +393,12 @@ struct pktgen_dev {
 
 	__u8 hh[14];
 	/* = {
-	   0x00, 0x80, 0xC8, 0x79, 0xB3, 0xCB,
-
-	   We fill in SRC address later
-	   0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-	   0x08, 0x00
-	   };
+	 * 0x00, 0x80, 0xC8, 0x79, 0xB3, 0xCB,
+	 *
+	 * We fill in SRC address later
+	 * 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 * 0x08, 0x00
+	 * };
 	 */
 	__u16 pad;		/* pad out the hh struct to an even 16 bytes */
 
@@ -459,7 +462,8 @@ struct pktgen_thread {
 	char result[512];
 
 	/* Field for thread to receive "posted" events terminate,
-	   stop ifs etc. */
+	 * stop ifs etc.
+	 */
 
 	u32 control;
 	int cpu;
@@ -2343,7 +2347,7 @@ static inline int f_pick(struct pktgen_d
 #ifdef CONFIG_XFRM
 /* If there was already an IPSEC SA, we keep it as is, else
  * we go look for it ...
-*/
+ */
 #define DUMMY_MARK 0
 static void get_ipsec_sa(struct pktgen_dev *pkt_dev, int flow)
 {
@@ -2642,7 +2646,8 @@ static int pktgen_output_ipsec(struct sk
 	if (!x)
 		return 0;
 	/* XXX: we dont support tunnel mode for now until
-	 * we resolve the dst issue */
+	 * we resolve the dst issue
+	 */
 	if ((x->props.mode != XFRM_MODE_TRANSPORT) && (pkt_dev->spi == 0))
 		return 0;
 
@@ -3710,7 +3715,8 @@ static int add_dev_to_thread(struct pktg
 	 * userspace on another CPU than the kthread.  The if_lock()
 	 * is used here to sync with concurrent instances of
 	 * _rem_dev_from_if_list() invoked via kthread, which is also
-	 * updating the if_list */
+	 * updating the if_list
+	 */
 	if_lock(t);
 
 	if (pkt_dev->pg_thread) {
@@ -3907,7 +3913,8 @@ static int pktgen_remove_device(struct p
 
 	/* Remove proc before if_list entry, because add_device uses
 	 * list to determine if interface already exist, avoid race
-	 * with proc_create_data() */
+	 * with proc_create_data()
+	 */
 	proc_remove(pkt_dev->entry);
 
 	/* And update the thread if_list */
author maintainer
#114 lore raw

PATCH 114/166 net: pktgen: fix proc entry use-after-free

In reply to Greg Kroah-Hartman

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

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

From: Chengfeng Ye <[email protected]>

[ Upstream commit 817ff6efdb7f484ea547218e11e17d8e43daa3b4 ]

pktgen_change_name() replaces pkt_dev->entry while holding t->if_lock. pktgen_remove_device() removes the same entry before _rem_dev_from_if_list() takes that lock.

This allows the following interleaving:

CPU 0 (NETDEV_CHANGENAME) CPU 1 (kpktgend)
if_lock(t)
proc_remove(pkt_dev->entry)

                                  proc_remove(pkt_dev->entry)

pkt_dev->entry = proc_create_data(...)
if_unlock(t)

The kthread can pass the stale proc_dir_entry to proc_remove() after the rename path has freed it. A reproducer with a widened race window reports:

  BUG: KASAN: slab-use-after-free in proc_remove+0x78/0x80
  Read of size 8 at addr ffff8881478fea70 by task kpktgend_0/67
  Call Trace:
   proc_remove+0x78/0x80
   pktgen_remove_device.isra.0+0x11c/0x4c0
   pktgen_thread_worker+0x1214/0x6bc0
   kthread+0x2c6/0x3b0
  Allocated by task 95:
   __proc_create+0x204/0x790
   proc_create_data+0x72/0xe0
   pktgen_thread_write+0xd61/0x1510
  Freed by task 28:
   kmem_cache_free+0xcb/0x3d0
   proc_free_inode+0x5b/0x80
   rcu_core+0x50a/0x1850
  The buggy address belongs to the object at ffff8881478fea00
   which belongs to the cache proc_dir_entry of size 192

Move proc_remove() into the if_lock-protected list removal helper. Keep it before list_del_rcu() to preserve the ordering required by add_device(). The rename path must then finish replacing the entry before removal, or it observes that the device is no longer on the list.

---
 net/core/pktgen.c |    4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)
net/core/pktgen.c +1 -3
@@ -3886,6 +3886,7 @@ static void _rem_dev_from_if_list(struct
 	struct pktgen_dev *p;
 
 	if_lock(t);
+	proc_remove(pkt_dev->entry);
 	list_for_each_safe(q, n, &t->if_list) {
 		p = list_entry(q, struct pktgen_dev, list);
 		if (p == pkt_dev)
@@ -3915,9 +3916,6 @@ static int pktgen_remove_device(struct p
 	 * list to determine if interface already exist, avoid race
 	 * with proc_create_data()
 	 */
-	proc_remove(pkt_dev->entry);
-
-	/* And update the thread if_list */
 	_rem_dev_from_if_list(t, pkt_dev);
 
 #ifdef CONFIG_XFRM
author maintainer
#115 lore raw

PATCH 115/166 veth: convert frag_list skbs before running XDP

In reply to Greg Kroah-Hartman

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

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

From: Matt Fleming <[email protected]>

[ Upstream commit d0d6415963040c401e7a7e4e482a698ba52448cb ]

A frag_list skb can reach veth with data_len set but nr_frags zero. veth_convert_skb_to_xdp_buff() only converts skbs that are shared, locked, have frags[], or do not have enough headroom. It later uses skb_is_nonlinear() to decide whether to set XDP_FLAGS_HAS_FRAGS and xdp_frags_size.

That exposes frag_list data to XDP as if it were stored in frags[], but frags[] is empty. AF_XDP copy mode can then trust the bogus XDP fragment metadata, walk an empty fragment entry, and crash in memcpy() from __xsk_rcv().

Route non-linear skbs through skb_pp_cow_data() before exposing them to XDP, and only advertise XDP frags when the resulting skb has frags[]. skb_copy_bits() already handles frag_list input, and skb_pp_cow_data() builds frags[] output with skb_add_rx_frag(), which is the representation XDP multi-buffer expects.

---
 drivers/net/veth.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/veth.c +2 -2
@@ -731,7 +731,7 @@ static int veth_convert_skb_to_xdp_buff(
 	u32 frame_sz;
 
 	if (skb_shared(skb) || skb_head_is_locked(skb) ||
-	    skb_shinfo(skb)->nr_frags ||
+	    skb_is_nonlinear(skb) ||
 	    skb_headroom(skb) < XDP_PACKET_HEADROOM) {
 		u32 size, len, max_head_size, off;
 		struct sk_buff *nskb;
@@ -808,7 +808,7 @@ static int veth_convert_skb_to_xdp_buff(
 	xdp_prepare_buff(xdp, skb->head, skb_headroom(skb),
 			 skb_headlen(skb), true);
 
-	if (skb_is_nonlinear(skb)) {
+	if (skb_shinfo(skb)->nr_frags) {
 		skb_shinfo(skb)->xdp_frags_size = skb->data_len;
 		xdp_buff_set_frags_flag(xdp);
 	} else {
author maintainer
#116 lore raw

PATCH 116/166 fs: dont block write during exec on pre-content watched files

In reply to Greg Kroah-Hartman

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

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

From: Amir Goldstein <[email protected]>

[ Upstream commit 0357ef03c94ef835bd44a0658b8edb672a9dbf51 ]

Commit 2a010c412853 ("fs: don't block i_writecount during exec") removed the legacy behavior of getting ETXTBSY on attempt to open and executable file for write while it is being executed.

This commit was reverted because an application that depends on this legacy behavior was broken by the change.

We need to allow HSM writing into executable files while executed to fill their content on-the-fly.

To that end, disable the ETXTBSY legacy behavior for files that are watched by pre-content events.

This change is not expected to cause regressions with existing systems which do not have any pre-content event listeners.

Stable-dep-of: db1856ea9196 ("binfmt_misc: restore write access when removing an entry")

---
 fs/binfmt_elf.c       |    4 ++--
 fs/binfmt_elf_fdpic.c |    4 ++--
 fs/exec.c             |    6 +++---
 include/linux/fs.h    |   12 ++++++++++++
 kernel/fork.c         |   12 ++++++------
 5 files changed, 25 insertions(+), 13 deletions(-)
fs/binfmt_elf.c +2 -2
@@ -1287,7 +1287,7 @@ out_free_interp:
 		}
 		reloc_func_desc = interp_load_addr;
 
-		allow_write_access(interpreter);
+		exe_file_allow_write_access(interpreter);
 		fput(interpreter);
 
 		kfree(interp_elf_ex);
@@ -1396,7 +1396,7 @@ out_free_dentry:
 	kfree(interp_elf_ex);
 	kfree(interp_elf_phdata);
 out_free_file:
-	allow_write_access(interpreter);
+	exe_file_allow_write_access(interpreter);
 	if (interpreter)
 		fput(interpreter);
 out_free_ph:
fs/binfmt_elf_fdpic.c +2 -2
@@ -398,7 +398,7 @@ static int load_elf_fdpic_binary(struct
 			goto error;
 		}
 
-		allow_write_access(interpreter);
+		exe_file_allow_write_access(interpreter);
 		fput(interpreter);
 		interpreter = NULL;
 	}
@@ -471,7 +471,7 @@ static int load_elf_fdpic_binary(struct
 
 error:
 	if (interpreter) {
-		allow_write_access(interpreter);
+		exe_file_allow_write_access(interpreter);
 		fput(interpreter);
 	}
 	kfree(interpreter_name);
fs/exec.c +3 -3
@@ -935,7 +935,7 @@ static struct file *do_open_execat(int f
 	    path_noexec(&file->f_path))
 		goto exit;
 
-	err = deny_write_access(file);
+	err = exe_file_deny_write_access(file);
 	if (err)
 		goto exit;
 
@@ -1521,7 +1521,7 @@ static void free_bprm(struct linux_binpr
 		abort_creds(bprm->cred);
 	}
 	if (bprm->file) {
-		allow_write_access(bprm->file);
+		exe_file_allow_write_access(bprm->file);
 		fput(bprm->file);
 	}
 	if (bprm->executable)
@@ -1824,7 +1824,7 @@ static int exec_binprm(struct linux_binp
 		bprm->file = bprm->interpreter;
 		bprm->interpreter = NULL;
 
-		allow_write_access(exec);
+		exe_file_allow_write_access(exec);
 		if (unlikely(bprm->have_execfd)) {
 			if (bprm->executable) {
 				fput(exec);
include/linux/fs.h +12 -0
@@ -2824,6 +2824,18 @@ static inline void allow_write_access(st
 	if (file)
 		atomic_inc(&file_inode(file)->i_writecount);
 }
+
+static inline int exe_file_deny_write_access(struct file *exe_file)
+{
+	return deny_write_access(exe_file);
+}
+static inline void exe_file_allow_write_access(struct file *exe_file)
+{
+	if (unlikely(!exe_file))
+		return;
+	allow_write_access(exe_file);
+}
+
 static inline bool inode_is_open_for_write(const struct inode *inode)
 {
 	return atomic_read(&inode->i_writecount) > 0;
kernel/fork.c +6 -6
@@ -640,8 +640,8 @@ static void dup_mm_exe_file(struct mm_st
 	 * We depend on the oldmm having properly denied write access to the
 	 * exe_file already.
 	 */
-	if (exe_file && deny_write_access(exe_file))
-		pr_warn_once("deny_write_access() failed in %s\n", __func__);
+	if (exe_file && exe_file_deny_write_access(exe_file))
+		pr_warn_once("exe_file_deny_write_access() failed in %s\n", __func__);
 }
 
 #ifdef CONFIG_MMU
@@ -1431,13 +1431,13 @@ int set_mm_exe_file(struct mm_struct *mm
 		 * We expect the caller (i.e., sys_execve) to already denied
 		 * write access, so this is unlikely to fail.
 		 */
-		if (unlikely(deny_write_access(new_exe_file)))
+		if (unlikely(exe_file_deny_write_access(new_exe_file)))
 			return -EACCES;
 		get_file(new_exe_file);
 	}
 	rcu_assign_pointer(mm->exe_file, new_exe_file);
 	if (old_exe_file) {
-		allow_write_access(old_exe_file);
+		exe_file_allow_write_access(old_exe_file);
 		fput(old_exe_file);
 	}
 	return 0;
@@ -1476,7 +1476,7 @@ int replace_mm_exe_file(struct mm_struct
 			return ret;
 	}
 
-	ret = deny_write_access(new_exe_file);
+	ret = exe_file_deny_write_access(new_exe_file);
 	if (ret)
 		return -EACCES;
 	get_file(new_exe_file);
@@ -1488,7 +1488,7 @@ int replace_mm_exe_file(struct mm_struct
 	mmap_write_unlock(mm);
 
 	if (old_exe_file) {
-		allow_write_access(old_exe_file);
+		exe_file_allow_write_access(old_exe_file);
 		fput(old_exe_file);
 	}
 	return 0;
author maintainer
#117 lore raw

PATCH 117/166 binfmt_misc: restore write access when removing an entry

In reply to Greg Kroah-Hartman

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

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

From: Christian Brauner <[email protected]>

[ Upstream commit db1856ea9196cf6e015d12199a34c0b9313c7bfa ]

Registering an entry with the MISC_FMT_OPEN_FILE flag opens the interpreter via open_exec() which denies write access to it for as long as the entry exists. Removing the entry closes the interpreter file via filp_close() but never restores write access, leaving the inode's i_writecount permanently negative. Opening the interpreter for writing keeps failing with ETXTBSY long after the entry is gone until the inode is evicted from the inode cache.

Commit 90f601b497d7 ("binfmt_misc: restore write access before closing files opened by open_exec()") fixed the same imbalance in the error path of bm_register_write() but the actual removal path has been leaking the write denial since the introduction of the flag.

Restore write access in put_binfmt_handler() before closing the interpreter file.

Fixes 948b701a607f ("binfmt_misc: add persistent opened binary handler for containers")
---
 fs/binfmt_misc.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
fs/binfmt_misc.c +3 -1
@@ -163,8 +163,10 @@ static Node *get_binfmt_handler(struct l
 static void put_binfmt_handler(Node *e)
 {
 	if (refcount_dec_and_test(&e->users)) {
-		if (e->flags & MISC_FMT_OPEN_FILE)
+		if (e->flags & MISC_FMT_OPEN_FILE) {
+			exe_file_allow_write_access(e->interp_file);
 			filp_close(e->interp_file, NULL);
+		}
 		kfree(e);
 	}
 }
author maintainer
#118 lore raw

PATCH 118/166 ice: fix VF interrupts cleanup

In reply to Greg Kroah-Hartman

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

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

From: Dawid Osuchowski <[email protected]>

[ Upstream commit fb096882095e5a8d6b5159e43793d4a38a0c5b1f ]

When a virtual function sends an IRQ map command, the PF will set up interrupts according to that request. However, because these interrupts are never reset, the next time Virtual Function initializes, the interrupts are still enabled for a given VF, which leads to performance degradation in certain cases due to interrupts being unexpectedly enabled and thus causing interrupt floods.

Fixes 1071a8358a28 ("ice: Implement virtchnl commands for AVF support")
[ changed the file path for the `ice_vf_ena_rxq_interrupt` hunk from `ice/virt/queues.c` to `ice_virtchnl.c`, which predates the upstream directory split ]
---
 drivers/net/ethernet/intel/ice/ice_vf_lib.c         |   27 ++++++++++++++++++++
 drivers/net/ethernet/intel/ice/ice_vf_lib_private.h |    1 
 drivers/net/ethernet/intel/ice/ice_virtchnl.c       |   21 +++++++++++++++
 3 files changed, 49 insertions(+)
drivers/net/ethernet/intel/ice/ice_vf_lib.c +27 -0
@@ -833,6 +833,30 @@ static void ice_notify_vf_reset(struct i
 }
 
 /**
+ * ice_reset_interrupts - clear all queue interrupt configuration for a VSI
+ * @vsi: the VSI whose interrupt registers should be cleared
+ *
+ * Zero the QINT_RQCTL and QINT_TQCTL registers for all allocated queues
+ * in the VSI. This clears the entire register including MSIX_INDX, ITR_INDX,
+ * CAUSE_ENA and NEXTQ fields, unlike ice_vf_dis_rxq_interrupt() which only
+ * clears the CAUSE_ENA bit.
+ */
+void ice_reset_interrupts(struct ice_vsi *vsi)
+{
+	struct ice_pf *pf = vsi->back;
+	struct ice_hw *hw = &pf->hw;
+	int i;
+
+	ice_for_each_alloc_rxq(vsi, i)
+		wr32(hw, QINT_RQCTL(vsi->rxq_map[i]), 0);
+
+	ice_for_each_alloc_txq(vsi, i)
+		wr32(hw, QINT_TQCTL(vsi->txq_map[i]), 0);
+
+	ice_flush(hw);
+}
+
+/**
  * ice_reset_vf - Reset a particular VF
  * @vf: pointer to the VF structure
  * @flags: flags controlling behavior of the reset
@@ -914,6 +938,9 @@ int ice_reset_vf(struct ice_vf *vf, u32
 
 	ice_dis_vf_qs(vf);
 
+	/* cleanup interrupt registers */
+	ice_reset_interrupts(vsi);
+
 	/* Call Disable LAN Tx queue AQ whether or not queues are
 	 * enabled. This is needed for successful completion of VFR.
 	 */
drivers/net/ethernet/intel/ice/ice_vf_lib_private.h +1 -0
@@ -26,6 +26,7 @@
 void ice_initialize_vf_entry(struct ice_vf *vf);
 void ice_deinitialize_vf_entry(struct ice_vf *vf);
 void ice_dis_vf_qs(struct ice_vf *vf);
+void ice_reset_interrupts(struct ice_vsi *vsi);
 int ice_check_vf_init(struct ice_vf *vf);
 enum virtchnl_status_code ice_err_to_virt_err(int err);
 struct ice_port_info *ice_vf_get_port_info(struct ice_vf *vf);
drivers/net/ethernet/intel/ice/ice_virtchnl.c +21 -0
@@ -1216,6 +1216,24 @@ static void ice_vf_ena_rxq_interrupt(str
 }
 
 /**
+ * ice_vf_dis_rxq_interrupt - disable Rx queue interrupt via QINT_RQCTL
+ * @vsi: VSI of the VF to configure
+ * @q_idx: VF queue index used to determine the queue in the PF's space
+ */
+static void ice_vf_dis_rxq_interrupt(struct ice_vsi *vsi, u32 q_idx)
+{
+	struct ice_hw *hw = &vsi->back->hw;
+	u32 pfq = vsi->rxq_map[q_idx];
+	u32 reg;
+
+	reg = rd32(hw, QINT_RQCTL(pfq));
+	reg &= ~QINT_RQCTL_CAUSE_ENA_M;
+	wr32(hw, QINT_RQCTL(pfq), reg);
+
+	ice_flush(hw);
+}
+
+/**
  * ice_vc_ena_qs_msg
  * @vf: pointer to the VF info
  * @msg: pointer to the msg buffer
@@ -1408,6 +1426,8 @@ static int ice_vc_dis_qs_msg(struct ice_
 			goto error_param;
 		}
 
+		for_each_set_bit(vf_q_id, &q_map, ICE_MAX_RSS_QS_PER_VF)
+			ice_vf_dis_rxq_interrupt(vsi, vf_q_id);
 		bitmap_zero(vf->rxq_ena, ICE_MAX_RSS_QS_PER_VF);
 	} else if (q_map) {
 		for_each_set_bit(vf_q_id, &q_map, ICE_MAX_RSS_QS_PER_VF) {
@@ -1428,6 +1448,7 @@ static int ice_vc_dis_qs_msg(struct ice_
 				goto error_param;
 			}
 
+			ice_vf_dis_rxq_interrupt(vsi, vf_q_id);
 			/* Clear enabled queues flag */
 			clear_bit(vf_q_id, vf->rxq_ena);
 		}
author maintainer
#119 lore raw

PATCH 119/166 vxlan: Do not alloc tstats manually

In reply to Greg Kroah-Hartman

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

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

From: Breno Leitao <[email protected]>

[ Upstream commit e28c5efc31397af17bc5a7d55b963f59bcde0166 ]

With commit 34d21de99cea9 ("net: Move {l,t,d}stats allocation to core and convert veth & vrf"), stats allocation could be done on net core instead of in this driver.

With this new approach, the driver doesn't have to bother with error handling (allocation failure checking, making sure free happens in the right spot, etc). This is core responsibility now.

Remove the allocation in the vxlan driver and leverage the network core allocation instead.

Stable-dep-of: b9553558b48d ("vxlan: use pskb_network_may_pull() for transmit path header pulls")

---
 drivers/net/vxlan/vxlan_core.c |   13 ++-----------
 1 file changed, 2 insertions(+), 11 deletions(-)
drivers/net/vxlan/vxlan_core.c +2 -11
@@ -2990,15 +2990,9 @@ static int vxlan_init(struct net_device
 			return err;
 	}
 
-	dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
-	if (!dev->tstats) {
-		err = -ENOMEM;
-		goto err_vnigroup_uninit;
-	}
-
 	err = gro_cells_init(&vxlan->gro_cells, dev);
 	if (err)
-		goto err_free_percpu;
+		goto err_vnigroup_uninit;
 
 	err = vxlan_mdb_init(vxlan);
 	if (err)
@@ -3009,8 +3003,6 @@ static int vxlan_init(struct net_device
 
 err_gro_cells_destroy:
 	gro_cells_destroy(&vxlan->gro_cells);
-err_free_percpu:
-	free_percpu(dev->tstats);
 err_vnigroup_uninit:
 	if (vxlan->cfg.flags & VXLAN_F_VNIFILTER)
 		vxlan_vnigroup_uninit(vxlan);
@@ -3041,8 +3033,6 @@ static void vxlan_uninit(struct net_devi
 	gro_cells_destroy(&vxlan->gro_cells);
 
 	vxlan_fdb_delete_default(vxlan, vxlan->cfg.vni);
-
-	free_percpu(dev->tstats);
 }
 
 /* Start ageing timer and join group when device is brought up */
@@ -3269,6 +3259,7 @@ static void vxlan_setup(struct net_devic
 	dev->min_mtu = ETH_MIN_MTU;
 	dev->max_mtu = ETH_MAX_MTU;
 
+	dev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS;
 	INIT_LIST_HEAD(&vxlan->next);
 
 	timer_setup(&vxlan->age_timer, vxlan_cleanup, TIMER_DEFERRABLE);
author maintainer
#120 lore raw

PATCH 120/166 net: core,vrf: Change pcpu_dstat fields to u64_stats_t

In reply to Greg Kroah-Hartman

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

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

From: Jeremy Kerr <[email protected]>

[ Upstream commit fa59dc2f6fc616fde3941f62ccd4adcaa21ccd47 ]

The pcpu_sw_netstats and pcpu_lstats structs both contain a set of u64_stats_t fields for individual stats, but pcpu_dstats uses u64s instead.

Make this consistent by using u64_stats_t across all stats types.

The per-cpu dstats are only used by the vrf driver at present, so update that driver as part of this change.

Stable-dep-of: b9553558b48d ("vxlan: use pskb_network_may_pull() for transmit path header pulls")

---
 drivers/net/vrf.c         |   38 ++++++++++++++++++++++----------------
 include/linux/netdevice.h |   12 ++++++------
 2 files changed, 28 insertions(+), 22 deletions(-)
drivers/net/vrf.c +22 -16
@@ -126,8 +126,8 @@ static void vrf_rx_stats(struct net_devi
 	struct pcpu_dstats *dstats = this_cpu_ptr(dev->dstats);
 
 	u64_stats_update_begin(&dstats->syncp);
-	dstats->rx_packets++;
-	dstats->rx_bytes += len;
+	u64_stats_inc(&dstats->rx_packets);
+	u64_stats_add(&dstats->rx_bytes, len);
 	u64_stats_update_end(&dstats->syncp);
 }
 
@@ -150,11 +150,11 @@ static void vrf_get_stats64(struct net_d
 		dstats = per_cpu_ptr(dev->dstats, i);
 		do {
 			start = u64_stats_fetch_begin(&dstats->syncp);
-			tbytes = dstats->tx_bytes;
-			tpkts = dstats->tx_packets;
-			tdrops = dstats->tx_drops;
-			rbytes = dstats->rx_bytes;
-			rpkts = dstats->rx_packets;
+			tbytes = u64_stats_read(&dstats->tx_bytes);
+			tpkts = u64_stats_read(&dstats->tx_packets);
+			tdrops = u64_stats_read(&dstats->tx_drops);
+			rbytes = u64_stats_read(&dstats->rx_bytes);
+			rpkts = u64_stats_read(&dstats->rx_packets);
 		} while (u64_stats_fetch_retry(&dstats->syncp, start));
 		stats->tx_bytes += tbytes;
 		stats->tx_packets += tpkts;
@@ -408,10 +408,15 @@ static int vrf_local_xmit(struct sk_buff
 
 	skb->protocol = eth_type_trans(skb, dev);
 
-	if (likely(__netif_rx(skb) == NET_RX_SUCCESS))
+	if (likely(__netif_rx(skb) == NET_RX_SUCCESS)) {
 		vrf_rx_stats(dev, len);
-	else
-		this_cpu_inc(dev->dstats->rx_drops);
+	} else {
+		struct pcpu_dstats *dstats = this_cpu_ptr(dev->dstats);
+
+		u64_stats_update_begin(&dstats->syncp);
+		u64_stats_inc(&dstats->rx_drops);
+		u64_stats_update_end(&dstats->syncp);
+	}
 
 	return NETDEV_TX_OK;
 }
@@ -599,19 +604,20 @@ static netdev_tx_t is_ip_tx_frame(struct
 
 static netdev_tx_t vrf_xmit(struct sk_buff *skb, struct net_device *dev)
 {
+	struct pcpu_dstats *dstats = this_cpu_ptr(dev->dstats);
+
 	int len = skb->len;
 	netdev_tx_t ret = is_ip_tx_frame(skb, dev);
 
+	u64_stats_update_begin(&dstats->syncp);
 	if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) {
-		struct pcpu_dstats *dstats = this_cpu_ptr(dev->dstats);
 
-		u64_stats_update_begin(&dstats->syncp);
-		dstats->tx_packets++;
-		dstats->tx_bytes += len;
-		u64_stats_update_end(&dstats->syncp);
+		u64_stats_inc(&dstats->tx_packets);
+		u64_stats_add(&dstats->tx_bytes, len);
 	} else {
-		this_cpu_inc(dev->dstats->tx_drops);
+		u64_stats_inc(&dstats->tx_drops);
 	}
+	u64_stats_update_end(&dstats->syncp);
 
 	return ret;
 }
include/linux/netdevice.h +6 -6
@@ -2748,12 +2748,12 @@ struct pcpu_sw_netstats {
 } __aligned(4 * sizeof(u64));
 
 struct pcpu_dstats {
-	u64			rx_packets;
-	u64			rx_bytes;
-	u64			rx_drops;
-	u64			tx_packets;
-	u64			tx_bytes;
-	u64			tx_drops;
+	u64_stats_t		rx_packets;
+	u64_stats_t		rx_bytes;
+	u64_stats_t		rx_drops;
+	u64_stats_t		tx_packets;
+	u64_stats_t		tx_bytes;
+	u64_stats_t		tx_drops;
 	struct u64_stats_sync	syncp;
 } __aligned(8 * sizeof(u64));
 
author maintainer
#121 lore raw

PATCH 121/166 vrf: Make pcpu_dstats update functions available to other modules.

In reply to Greg Kroah-Hartman

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

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

From: Guillaume Nault <[email protected]>

[ Upstream commit 18eabadd73ae60023ab05e376246bd725fb0c113 ]

Currently vrf is the only module that uses NETDEV_PCPU_STAT_DSTATS. In order to make this kind of statistics available to other modules, we need to define the update functions in netdevice.h.

Therefore, let's define dev_dstats_*() functions for RX and TX packet updates (packets, bytes and drops). Use these new functions in vrf.c instead of vrf_rx_stats() and the other manual counter updates.

While there, update the type of the "len" variables to "unsigned int", so that there're aligned with both skb->len and the new dstats update functions.

Stable-dep-of: b9553558b48d ("vxlan: use pskb_network_may_pull() for transmit path header pulls")

---
 drivers/net/vrf.c         |   49 +++++++++++++---------------------------------
 include/linux/netdevice.h |   40 +++++++++++++++++++++++++++++++++++++
 2 files changed, 54 insertions(+), 35 deletions(-)
drivers/net/vrf.c +14 -35
@@ -121,16 +121,6 @@ struct net_vrf {
 	int			ifindex;
 };
 
-static void vrf_rx_stats(struct net_device *dev, int len)
-{
-	struct pcpu_dstats *dstats = this_cpu_ptr(dev->dstats);
-
-	u64_stats_update_begin(&dstats->syncp);
-	u64_stats_inc(&dstats->rx_packets);
-	u64_stats_add(&dstats->rx_bytes, len);
-	u64_stats_update_end(&dstats->syncp);
-}
-
 static void vrf_tx_error(struct net_device *vrf_dev, struct sk_buff *skb)
 {
 	vrf_dev->stats.tx_errors++;
@@ -395,7 +385,7 @@ static bool qdisc_tx_is_default(const st
 static int vrf_local_xmit(struct sk_buff *skb, struct net_device *dev,
 			  struct dst_entry *dst)
 {
-	int len = skb->len;
+	unsigned int len = skb->len;
 
 	skb_orphan(skb);
 
@@ -408,15 +398,10 @@ static int vrf_local_xmit(struct sk_buff
 
 	skb->protocol = eth_type_trans(skb, dev);
 
-	if (likely(__netif_rx(skb) == NET_RX_SUCCESS)) {
-		vrf_rx_stats(dev, len);
-	} else {
-		struct pcpu_dstats *dstats = this_cpu_ptr(dev->dstats);
-
-		u64_stats_update_begin(&dstats->syncp);
-		u64_stats_inc(&dstats->rx_drops);
-		u64_stats_update_end(&dstats->syncp);
-	}
+	if (likely(__netif_rx(skb) == NET_RX_SUCCESS))
+		dev_dstats_rx_add(dev, len);
+	else
+		dev_dstats_rx_dropped(dev);
 
 	return NETDEV_TX_OK;
 }
@@ -604,20 +589,14 @@ static netdev_tx_t is_ip_tx_frame(struct
 
 static netdev_tx_t vrf_xmit(struct sk_buff *skb, struct net_device *dev)
 {
-	struct pcpu_dstats *dstats = this_cpu_ptr(dev->dstats);
-
-	int len = skb->len;
-	netdev_tx_t ret = is_ip_tx_frame(skb, dev);
-
-	u64_stats_update_begin(&dstats->syncp);
-	if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) {
+	unsigned int len = skb->len;
+	netdev_tx_t ret;
 
-		u64_stats_inc(&dstats->tx_packets);
-		u64_stats_add(&dstats->tx_bytes, len);
-	} else {
-		u64_stats_inc(&dstats->tx_drops);
-	}
-	u64_stats_update_end(&dstats->syncp);
+	ret = is_ip_tx_frame(skb, dev);
+	if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN))
+		dev_dstats_tx_add(dev, len);
+	else
+		dev_dstats_tx_dropped(dev);
 
 	return ret;
 }
@@ -1400,7 +1379,7 @@ static struct sk_buff *vrf_ip6_rcv(struc
 	if (!is_ndisc) {
 		struct net_device *orig_dev = skb->dev;
 
-		vrf_rx_stats(vrf_dev, skb->len);
+		dev_dstats_rx_add(vrf_dev, skb->len);
 		skb->dev = vrf_dev;
 		skb->skb_iif = vrf_dev->ifindex;
 
@@ -1456,7 +1435,7 @@ static struct sk_buff *vrf_ip_rcv(struct
 		goto out;
 	}
 
-	vrf_rx_stats(vrf_dev, skb->len);
+	dev_dstats_rx_add(vrf_dev, skb->len);
 
 	if (!list_empty(&vrf_dev->ptype_all)) {
 		int err;
include/linux/netdevice.h +40 -0
@@ -2797,6 +2797,46 @@ static inline void dev_lstats_add(struct
 	u64_stats_update_end(&lstats->syncp);
 }
 
+static inline void dev_dstats_rx_add(struct net_device *dev,
+				     unsigned int len)
+{
+	struct pcpu_dstats *dstats = this_cpu_ptr(dev->dstats);
+
+	u64_stats_update_begin(&dstats->syncp);
+	u64_stats_inc(&dstats->rx_packets);
+	u64_stats_add(&dstats->rx_bytes, len);
+	u64_stats_update_end(&dstats->syncp);
+}
+
+static inline void dev_dstats_rx_dropped(struct net_device *dev)
+{
+	struct pcpu_dstats *dstats = this_cpu_ptr(dev->dstats);
+
+	u64_stats_update_begin(&dstats->syncp);
+	u64_stats_inc(&dstats->rx_drops);
+	u64_stats_update_end(&dstats->syncp);
+}
+
+static inline void dev_dstats_tx_add(struct net_device *dev,
+				     unsigned int len)
+{
+	struct pcpu_dstats *dstats = this_cpu_ptr(dev->dstats);
+
+	u64_stats_update_begin(&dstats->syncp);
+	u64_stats_inc(&dstats->tx_packets);
+	u64_stats_add(&dstats->tx_bytes, len);
+	u64_stats_update_end(&dstats->syncp);
+}
+
+static inline void dev_dstats_tx_dropped(struct net_device *dev)
+{
+	struct pcpu_dstats *dstats = this_cpu_ptr(dev->dstats);
+
+	u64_stats_update_begin(&dstats->syncp);
+	u64_stats_inc(&dstats->tx_drops);
+	u64_stats_update_end(&dstats->syncp);
+}
+
 #define __netdev_alloc_pcpu_stats(type, gfp)				\
 ({									\
 	typeof(type) __percpu *pcpu_stats = alloc_percpu_gfp(type, gfp);\
author maintainer
#122 lore raw

PATCH 122/166 vxlan: Handle stats using NETDEV_PCPU_STAT_DSTATS.

In reply to Greg Kroah-Hartman

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

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

From: Guillaume Nault <[email protected]>

[ Upstream commit be226352e8dc77d3313c096b2d8e7f69bf6980fc ]

VXLAN uses the TSTATS infrastructure (dev_sw_netstats_*()) for RX and TX packet counters. It also uses the device core stats (dev_core_stats_*()) for RX and TX drops.

Let's consolidate that using the DSTATS infrastructure, which can handle both packet counters and packet drops. Statistics that don't fit DSTATS are still updated atomically with DEV_STATS_INC().

While there, convert the "len" variable of vxlan_encap_bypass() to unsigned int, to respect the types of skb->len and dev_dstats_[rt]x_add().

Stable-dep-of: b9553558b48d ("vxlan: use pskb_network_may_pull() for transmit path header pulls")

---
 drivers/net/vxlan/vxlan_core.c |   28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)
drivers/net/vxlan/vxlan_core.c +14 -14
@@ -1798,13 +1798,13 @@ static int vxlan_rcv(struct sock *sk, st
 
 	if (unlikely(!(vxlan->dev->flags & IFF_UP))) {
 		rcu_read_unlock();
-		dev_core_stats_rx_dropped_inc(vxlan->dev);
+		dev_dstats_rx_dropped(vxlan->dev);
 		vxlan_vnifilter_count(vxlan, vni, vninode,
 				      VXLAN_VNI_STATS_RX_DROPS, 0);
 		goto drop;
 	}
 
-	dev_sw_netstats_rx_add(vxlan->dev, skb->len);
+	dev_dstats_rx_add(vxlan->dev, skb->len);
 	vxlan_vnifilter_count(vxlan, vni, vninode, VXLAN_VNI_STATS_RX, skb->len);
 	gro_cells_receive(&vxlan->gro_cells, skb);
 
@@ -1858,7 +1858,7 @@ static int arp_reduce(struct net_device
 		goto out;
 
 	if (!pskb_may_pull(skb, arp_hdr_len(dev))) {
-		dev_core_stats_tx_dropped_inc(dev);
+		dev_dstats_tx_dropped(dev);
 		vxlan_vnifilter_count(vxlan, vni, NULL,
 				      VXLAN_VNI_STATS_TX_DROPS, 0);
 		goto out;
@@ -1916,7 +1916,7 @@ static int arp_reduce(struct net_device
 		reply->pkt_type = PACKET_HOST;
 
 		if (netif_rx(reply) == NET_RX_DROP) {
-			dev_core_stats_rx_dropped_inc(dev);
+			dev_dstats_rx_dropped(dev);
 			vxlan_vnifilter_count(vxlan, vni, NULL,
 					      VXLAN_VNI_STATS_RX_DROPS, 0);
 		}
@@ -2077,7 +2077,7 @@ static int neigh_reduce(struct net_devic
 			goto out;
 
 		if (netif_rx(reply) == NET_RX_DROP) {
-			dev_core_stats_rx_dropped_inc(dev);
+			dev_dstats_rx_dropped(dev);
 			vxlan_vnifilter_count(vxlan, vni, NULL,
 					      VXLAN_VNI_STATS_RX_DROPS, 0);
 		}
@@ -2370,8 +2370,8 @@ static void vxlan_encap_bypass(struct sk
 {
 	union vxlan_addr loopback;
 	union vxlan_addr *remote_ip = &dst_vxlan->default_dst.remote_ip;
+	unsigned int len = skb->len;
 	struct net_device *dev;
-	int len = skb->len;
 
 	skb->pkt_type = PACKET_HOST;
 	skb->encapsulation = 0;
@@ -2398,16 +2398,16 @@ static void vxlan_encap_bypass(struct sk
 	if ((dst_vxlan->cfg.flags & VXLAN_F_LEARN) && snoop)
 		vxlan_snoop(dev, &loopback, eth_hdr(skb)->h_source, 0, vni);
 
-	dev_sw_netstats_tx_add(src_vxlan->dev, 1, len);
+	dev_dstats_tx_add(src_vxlan->dev, len);
 	vxlan_vnifilter_count(src_vxlan, vni, NULL, VXLAN_VNI_STATS_TX, len);
 
 	if (__netif_rx(skb) == NET_RX_SUCCESS) {
-		dev_sw_netstats_rx_add(dst_vxlan->dev, len);
+		dev_dstats_rx_add(dst_vxlan->dev, len);
 		vxlan_vnifilter_count(dst_vxlan, vni, NULL, VXLAN_VNI_STATS_RX,
 				      len);
 	} else {
 drop:
-		dev_core_stats_rx_dropped_inc(dev);
+		dev_dstats_rx_dropped(dev);
 		vxlan_vnifilter_count(dst_vxlan, vni, NULL,
 				      VXLAN_VNI_STATS_RX_DROPS, 0);
 	}
@@ -2700,7 +2700,7 @@ out_unlock:
 	return;
 
 drop:
-	dev_core_stats_tx_dropped_inc(dev);
+	dev_dstats_tx_dropped(dev);
 	vxlan_vnifilter_count(vxlan, vni, NULL, VXLAN_VNI_STATS_TX_DROPS, 0);
 	dev_kfree_skb(skb);
 	return;
@@ -2745,7 +2745,7 @@ static void vxlan_xmit_nh(struct sk_buff
 	return;
 
 drop:
-	dev_core_stats_tx_dropped_inc(dev);
+	dev_dstats_tx_dropped(dev);
 	vxlan_vnifilter_count(netdev_priv(dev), vni, NULL,
 			      VXLAN_VNI_STATS_TX_DROPS, 0);
 	dev_kfree_skb(skb);
@@ -2783,7 +2783,7 @@ static netdev_tx_t vxlan_xmit_nhid(struc
 	return NETDEV_TX_OK;
 
 drop:
-	dev_core_stats_tx_dropped_inc(dev);
+	dev_dstats_tx_dropped(dev);
 	vxlan_vnifilter_count(netdev_priv(dev), vni, NULL,
 			      VXLAN_VNI_STATS_TX_DROPS, 0);
 	dev_kfree_skb(skb);
@@ -2881,7 +2881,7 @@ static netdev_tx_t vxlan_xmit(struct sk_
 			    !is_multicast_ether_addr(eth->h_dest))
 				vxlan_fdb_miss(vxlan, eth->h_dest);
 
-			dev_core_stats_tx_dropped_inc(dev);
+			dev_dstats_tx_dropped(dev);
 			vxlan_vnifilter_count(vxlan, vni, NULL,
 					      VXLAN_VNI_STATS_TX_DROPS, 0);
 			kfree_skb(skb);
@@ -3259,7 +3259,7 @@ static void vxlan_setup(struct net_devic
 	dev->min_mtu = ETH_MIN_MTU;
 	dev->max_mtu = ETH_MAX_MTU;
 
-	dev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS;
+	dev->pcpu_stat_type = NETDEV_PCPU_STAT_DSTATS;
 	INIT_LIST_HEAD(&vxlan->next);
 
 	timer_setup(&vxlan->age_timer, vxlan_cleanup, TIMER_DEFERRABLE);
author maintainer
#123 lore raw

PATCH 123/166 vxlan: use pskb_network_may_pull() for transmit path header pulls

In reply to Greg Kroah-Hartman

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

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

From: Eric Dumazet <[email protected]>

[ Upstream commit b9553558b48db54ac9273e6b98d7263ef5c1a329 ]

In vxlan_xmit(), arp_reduce(), and vxlan_mdb_entry_skb_get(), pskb_may_pull() was being called to verify the availability of network layer headers (ARP, IPv6/ND, IP/IPv6 MDB keys).

However, during transmit skb->data points to the MAC header, so skb_network_offset(skb) is ETH_HLEN (14 bytes). Using pskb_may_pull(skb, len) only checks len bytes from skb->data rather than skb_network_offset(skb) + len, which can leave part of the network header in non-linear frags.

Replace these remaining pskb_may_pull() calls with pskb_network_may_pull() to properly account for the MAC header offset.

Fixes e4f67addf158 ("add DOVE extensions for VXLAN")
Fixes f564f45c4518 ("vxlan: add ipv6 proxy support")
Fixes 0f83e69f44bf ("vxlan: Add MDB data path support")
---
 drivers/net/vxlan/vxlan_core.c |    6 +++---
 drivers/net/vxlan/vxlan_mdb.c  |    4 ++--
 2 files changed, 5 insertions(+), 5 deletions(-)
drivers/net/vxlan/vxlan_core.c +3 -3
@@ -1857,7 +1857,7 @@ static int arp_reduce(struct net_device
 	if (dev->flags & IFF_NOARP)
 		goto out;
 
-	if (!pskb_may_pull(skb, arp_hdr_len(dev))) {
+	if (!pskb_network_may_pull(skb, arp_hdr_len(dev))) {
 		dev_dstats_tx_dropped(dev);
 		vxlan_vnifilter_count(vxlan, vni, NULL,
 				      VXLAN_VNI_STATS_TX_DROPS, 0);
@@ -2831,8 +2831,8 @@ static netdev_tx_t vxlan_xmit(struct sk_
 			return arp_reduce(dev, skb, vni);
 #if IS_ENABLED(CONFIG_IPV6)
 		else if (ntohs(eth->h_proto) == ETH_P_IPV6 &&
-			 pskb_may_pull(skb, sizeof(struct ipv6hdr) +
-					    sizeof(struct nd_msg)) &&
+			 pskb_network_may_pull(skb, sizeof(struct ipv6hdr) +
+						    sizeof(struct nd_msg)) &&
 			 ipv6_hdr(skb)->nexthdr == IPPROTO_ICMPV6) {
 			struct nd_msg *m = (struct nd_msg *)(ipv6_hdr(skb) + 1);
 
drivers/net/vxlan/vxlan_mdb.c +2 -2
@@ -1326,7 +1326,7 @@ struct vxlan_mdb_entry *vxlan_mdb_entry_
 
 	switch (skb->protocol) {
 	case htons(ETH_P_IP):
-		if (!pskb_may_pull(skb, sizeof(struct iphdr)))
+		if (!pskb_network_may_pull(skb, sizeof(struct iphdr)))
 			return NULL;
 		group.dst.sa.sa_family = AF_INET;
 		group.dst.sin.sin_addr.s_addr = ip_hdr(skb)->daddr;
@@ -1335,7 +1335,7 @@ struct vxlan_mdb_entry *vxlan_mdb_entry_
 		break;
 #if IS_ENABLED(CONFIG_IPV6)
 	case htons(ETH_P_IPV6):
-		if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
+		if (!pskb_network_may_pull(skb, sizeof(struct ipv6hdr)))
 			return NULL;
 		group.dst.sa.sa_family = AF_INET6;
 		group.dst.sin6.sin6_addr = ipv6_hdr(skb)->daddr;
author maintainer
#124 lore raw

PATCH 124/166 i2c: bcm-iproc: remove printout on handled timeouts

In reply to Greg Kroah-Hartman

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

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

From: Wolfram Sang <[email protected]>

[ Upstream commit 796e2c260187e32530cf343546ba1cdf2e2f5491 ]

I2C and SMBus timeouts are not something the user needs to be informed about on controller level. The client driver may know if that really is a problem and give more detailed information to the user. The controller should just pass this information upwards. Remove the printout.

Stable-dep-of: 98f2e9e6d6f9 ("i2c: iproc: reset bus after timeout if START_BUSY is stuck")

---
 drivers/i2c/busses/i2c-bcm-iproc.c |    2 --
 1 file changed, 2 deletions(-)
drivers/i2c/busses/i2c-bcm-iproc.c +0 -2
@@ -811,8 +811,6 @@ static int bcm_iproc_i2c_xfer_wait(struc
 	}
 
 	if (!time_left && !iproc_i2c->xfer_is_done) {
-		dev_err(iproc_i2c->device, "transaction timed out\n");
-
 		/* flush both TX/RX FIFOs */
 		val = BIT(M_FIFO_RX_FLUSH_SHIFT) | BIT(M_FIFO_TX_FLUSH_SHIFT);
 		iproc_i2c_wr_reg(iproc_i2c, M_FIFO_CTRL_OFFSET, val);
author maintainer
#125 lore raw

PATCH 125/166 i2c: iproc: reset bus after timeout if START_BUSY is stuck

In reply to Greg Kroah-Hartman

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

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

From: Jonas Gorski <[email protected]>

[ Upstream commit 98f2e9e6d6f91a6abb43f166b244b428ba85fa2b ]

If a transaction times out, the START_BUSY signal can stay up, and subsequent transactaction attempts will fail as the bus is still considered busy.

I can easily trigger this by attempting to read from an address with no device, e.g. when running i2cdetect. After the first read times out, all subsequent read attempts return busy.

To get to a working state again, the controller needs to be reset to clear the START_BUSY signal. So check for START_BUSY still asserted on a timeout, and do reset in case it is,

This is also done by the original non-upstream iproc-smbus driver implementation [1].

Works around situations like:

    bcm-iproc-2c 1803b000.i2c: transaction timed out
    bcm-iproc-2c 1803b000.i2c: bus is busy
    bcm-iproc-2c 1803b000.i2c: bus is busy
    bcm-iproc-2c 1803b000.i2c: bus is busy
    bcm-iproc-2c 1803b000.i2c: bus is busy
    bcm-iproc-2c 1803b000.i2c: bus is busy
    ...

where the bus never recovers after a timeout.

[1] https://github.com/opencomputeproject/onie/blob/master/patches/kernel/3.2.69/driver-iproc-smbus.patch

---
 drivers/i2c/busses/i2c-bcm-iproc.c |   11 +++++++++++
 1 file changed, 11 insertions(+)
drivers/i2c/busses/i2c-bcm-iproc.c +11 -0
@@ -811,6 +811,17 @@ static int bcm_iproc_i2c_xfer_wait(struc
 	}
 
 	if (!time_left && !iproc_i2c->xfer_is_done) {
+		/*
+		 * The controller may fail to clear START_BUSY after a timeout,
+		 * reset the controller to recover in that case.
+		 */
+		if (!!(iproc_i2c_rd_reg(iproc_i2c, M_CMD_OFFSET) &
+		       BIT(M_CMD_START_BUSY_SHIFT))) {
+			bcm_iproc_i2c_enable_disable(iproc_i2c, false);
+			bcm_iproc_i2c_init(iproc_i2c);
+			bcm_iproc_i2c_enable_disable(iproc_i2c, true);
+		}
+
 		/* flush both TX/RX FIFOs */
 		val = BIT(M_FIFO_RX_FLUSH_SHIFT) | BIT(M_FIFO_TX_FLUSH_SHIFT);
 		iproc_i2c_wr_reg(iproc_i2c, M_FIFO_CTRL_OFFSET, val);
author maintainer
#126 lore raw

PATCH 126/166 can: rcar_canfd: change the initializing flow for clocks and resets

In reply to Greg Kroah-Hartman

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

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

From: Tu Nguyen <[email protected]>

[ Upstream commit bef9004c5b91debfceaea2841855a4ebe81ff2b3 ]

Testing CANFD on RZ/G3E shows that many registers do not reset to their initial values with the current flow of deasserting resets first and then enabling clocks.

Based on the HW manual, clocks should be supplied first and the resets deasserted afterward.

section 7.4.3 Procedure for Activating Modules: RZ/G2L section 4.4.9.3 Procedure for Starting up Units: RZ/G3E

So, update the order of the initializing flow for resets and clocks to match the hardware manual, resetting all CANFD registers to their initial values. Also update rcar_canfd_global_deinit() to assert resets before disabling clocks, so the teardown path mirrors the new init ordering.

---
 drivers/net/can/rcar/rcar_canfd.c |   28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)
drivers/net/can/rcar/rcar_canfd.c +14 -14
@@ -1999,27 +1999,27 @@ static int rcar_canfd_probe(struct platf
 		}
 	}
 
-	err = reset_control_reset(gpriv->rstc1);
-	if (err)
-		goto fail_dev;
-	err = reset_control_reset(gpriv->rstc2);
-	if (err) {
-		reset_control_assert(gpriv->rstc1);
-		goto fail_dev;
-	}
-
 	/* Enable peripheral clock for register access */
 	err = clk_prepare_enable(gpriv->clkp);
 	if (err) {
 		dev_err(dev, "failed to enable peripheral clock: %pe\n",
 			ERR_PTR(err));
-		goto fail_reset;
+		goto fail_dev;
+	}
+
+	err = reset_control_reset(gpriv->rstc1);
+	if (err)
+		goto fail_clk;
+	err = reset_control_reset(gpriv->rstc2);
+	if (err) {
+		reset_control_assert(gpriv->rstc1);
+		goto fail_clk;
 	}
 
 	err = rcar_canfd_reset_controller(gpriv);
 	if (err) {
 		dev_err(dev, "reset controller failed: %pe\n", ERR_PTR(err));
-		goto fail_clk;
+		goto fail_reset;
 	}
 
 	/* Controller in Global reset & Channel reset mode */
@@ -2070,11 +2070,11 @@ fail_channel:
 		rcar_canfd_channel_remove(gpriv, ch);
 fail_mode:
 	rcar_canfd_disable_global_interrupts(gpriv);
-fail_clk:
-	clk_disable_unprepare(gpriv->clkp);
 fail_reset:
 	reset_control_assert(gpriv->rstc1);
 	reset_control_assert(gpriv->rstc2);
+fail_clk:
+	clk_disable_unprepare(gpriv->clkp);
 fail_dev:
 	return err;
 }
@@ -2094,9 +2094,9 @@ static void rcar_canfd_remove(struct pla
 
 	/* Enter global sleep mode */
 	rcar_canfd_set_bit(gpriv->base, RCANFD_GCTR, RCANFD_GCTR_GSLPR);
-	clk_disable_unprepare(gpriv->clkp);
 	reset_control_assert(gpriv->rstc1);
 	reset_control_assert(gpriv->rstc2);
+	clk_disable_unprepare(gpriv->clkp);
 }
 
 static int __maybe_unused rcar_canfd_suspend(struct device *dev)
author maintainer
#127 lore raw

PATCH 127/166 drm/amd/pm: fix torn gpu metrics reads

In reply to Greg Kroah-Hartman

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

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

From: Yang Wang <[email protected]>

[ Upstream commit 048f4541b71fb19645fb79d6e62e6e4da23a4035 ]

amdgpu_dpm_get_gpu_metrics() returns a pointer to the shared metrics cache after dropping adev->pm.mutex. The sysfs path then copies from that pointer. Another reader can refresh the cache in place during the copy and return a snapshot containing data from two generations.

Pass caller-provided storage through the DPM interface and copy the metrics while the mutex is held. This keeps the cache pointer private and makes each sysfs read observe one complete sample.

Fixes 25c933b1c4fc ("drm/amd/powerplay: add new sysfs interface for retrieving gpu metrics(V2)")
(cherry picked from commit 862333bb48693ecafcae25af0c9d9ec31015ac77)
---
 drivers/gpu/drm/amd/pm/amdgpu_dpm.c     |   12 +++++++++---
 drivers/gpu/drm/amd/pm/amdgpu_pm.c      |    8 +-------
 drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h |    3 ++-
 3 files changed, 12 insertions(+), 11 deletions(-)
drivers/gpu/drm/amd/pm/amdgpu_dpm.c +9 -3
@@ -1255,17 +1255,23 @@ int amdgpu_dpm_set_power_profile_mode(st
 	return ret;
 }
 
-int amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void **table)
+ssize_t amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void *buf,
+				   size_t size)
 {
 	const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs;
-	int ret = 0;
+	void *table;
+	ssize_t ret;
 
 	if (!pp_funcs->get_gpu_metrics)
 		return 0;
 
 	mutex_lock(&adev->pm.mutex);
 	ret = pp_funcs->get_gpu_metrics(adev->powerplay.pp_handle,
-					table);
+					&table);
+	if (ret > 0) {
+		ret = min_t(ssize_t, ret, size);
+		memcpy(buf, table, ret);
+	}
 	mutex_unlock(&adev->pm.mutex);
 
 	return ret;
drivers/gpu/drm/amd/pm/amdgpu_pm.c +1 -7
@@ -1781,7 +1781,6 @@ static ssize_t amdgpu_get_gpu_metrics(st
 {
 	struct drm_device *ddev = dev_get_drvdata(dev);
 	struct amdgpu_device *adev = drm_to_adev(ddev);
-	void *gpu_metrics;
 	ssize_t size = 0;
 	int ret;
 
@@ -1796,15 +1795,10 @@ static ssize_t amdgpu_get_gpu_metrics(st
 		return ret;
 	}
 
-	size = amdgpu_dpm_get_gpu_metrics(adev, &gpu_metrics);
+	size = amdgpu_dpm_get_gpu_metrics(adev, buf, PAGE_SIZE - 1);
 	if (size <= 0)
 		goto out;
 
-	if (size >= PAGE_SIZE)
-		size = PAGE_SIZE - 1;
-
-	memcpy(buf, gpu_metrics, size);
-
 out:
 	pm_runtime_mark_last_busy(ddev->dev);
 	pm_runtime_put_autosuspend(ddev->dev);
drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h +2 -1
@@ -493,7 +493,8 @@ int amdgpu_dpm_get_power_profile_mode(st
 				      char *buf);
 int amdgpu_dpm_set_power_profile_mode(struct amdgpu_device *adev,
 				      long *input, uint32_t size);
-int amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void **table);
+ssize_t amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void *buf,
+				   size_t size);
 int amdgpu_dpm_get_fan_control_mode(struct amdgpu_device *adev,
 				    uint32_t *fan_mode);
 int amdgpu_dpm_set_fan_speed_pwm(struct amdgpu_device *adev,
author maintainer
#128 lore raw

PATCH 128/166 drm/amd/pm: fix pptable use-after-free

In reply to Greg Kroah-Hartman

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

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

From: Yang Wang <[email protected]>

[ Upstream commit bb493058c35c8676e48269ab6732688ea733d23c ]

amdgpu_dpm_get_pp_table() returns a pointer to a driver-owned power table after dropping adev->pm.mutex. The sysfs path then copies from that pointer. A concurrent pp_table write can replace and free the allocation during the copy, causing a use-after-free.

Change the DPM interface to copy into caller-provided storage while the mutex is held. Keep the size-only query for attribute discovery without exposing the driver-owned pointer.

Fixes 1684d3ba4885 ("drm/amd/amdgpu: change pptable output format from ASCII to binary")
(cherry picked from commit f6eed7acfd30099ef7baeb6ba45bb59daad80631)
[ kept 6.18's existing `if (!pp_funcs->get_pp_table) return 0;` guard instead of upstream's SR-IOV/SCPM `-EOPNOTSUPP` guard and dropped the `default_attr_update()` hunk whose `pp_table` branch doesn't exist yet ]
---
 drivers/gpu/drm/amd/pm/amdgpu_dpm.c     |   15 +++++++++++++--
 drivers/gpu/drm/amd/pm/amdgpu_pm.c      |    8 +-------
 drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h |    3 ++-
 3 files changed, 16 insertions(+), 10 deletions(-)
drivers/gpu/drm/amd/pm/amdgpu_dpm.c +13 -2
@@ -996,17 +996,28 @@ int amdgpu_dpm_dispatch_task(struct amdg
 	return ret;
 }
 
-int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char **table)
+int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char *table,
+			    size_t size)
 {
 	const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs;
+	char *pptable = NULL;
 	int ret = 0;
 
+	if ((!table && size) || (table && !size))
+		return -EINVAL;
+
 	if (!pp_funcs->get_pp_table)
 		return 0;
 
 	mutex_lock(&adev->pm.mutex);
 	ret = pp_funcs->get_pp_table(adev->powerplay.pp_handle,
-				     table);
+				     &pptable);
+	if (ret > 0 && !pptable) {
+		ret = -EINVAL;
+	} else if (ret > 0 && table) {
+		ret = min_t(size_t, ret, size);
+		memcpy(table, pptable, ret);
+	}
 	mutex_unlock(&adev->pm.mutex);
 
 	return ret;
drivers/gpu/drm/amd/pm/amdgpu_pm.c +1 -7
@@ -496,7 +496,6 @@ static ssize_t amdgpu_get_pp_table(struc
 {
 	struct drm_device *ddev = dev_get_drvdata(dev);
 	struct amdgpu_device *adev = drm_to_adev(ddev);
-	char *table = NULL;
 	int size, ret;
 
 	if (amdgpu_in_reset(adev))
@@ -510,7 +509,7 @@ static ssize_t amdgpu_get_pp_table(struc
 		return ret;
 	}
 
-	size = amdgpu_dpm_get_pp_table(adev, &table);
+	size = amdgpu_dpm_get_pp_table(adev, buf, PAGE_SIZE - 1);
 
 	pm_runtime_mark_last_busy(ddev->dev);
 	pm_runtime_put_autosuspend(ddev->dev);
@@ -518,11 +517,6 @@ static ssize_t amdgpu_get_pp_table(struc
 	if (size <= 0)
 		return size;
 
-	if (size >= PAGE_SIZE)
-		size = PAGE_SIZE - 1;
-
-	memcpy(buf, table, size);
-
 	return size;
 }
 
drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h +2 -1
@@ -463,7 +463,8 @@ int amdgpu_dpm_get_pp_num_states(struct
 int amdgpu_dpm_dispatch_task(struct amdgpu_device *adev,
 			      enum amd_pp_task task_id,
 			      enum amd_pm_state_type *user_state);
-int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char **table);
+int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char *table,
+			    size_t size);
 int amdgpu_dpm_set_fine_grain_clk_vol(struct amdgpu_device *adev,
 				      uint32_t type,
 				      long *input,
author maintainer
#129 lore raw

PATCH 129/166 drm/amdgpu: move debug_vm handling to amdgpu_cs_parser_fini

In reply to Greg Kroah-Hartman

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

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

From: Pierre-Eric Pelloux-Prayer <[email protected]>

[ Upstream commit d8726ef11512754a68c0ab53c57634a569b8feff ]

The commit referenced below restarts the CS if the validation is still in progress. When debug_vm is enabled, all BOs from the CS are invalidated so we will hit an infinite loop.

To avoid that, defer BO invalidation to amdgpu_cs_parser_fini.

Fixes 59720bfd8c6d ("drm/amdgpu: restart the CS if some parts of the VM are still invalidated")
(cherry picked from commit 8c990ee9daa295462df24982ce6878db997a380a)
[ adjusted `adev->debug_vm` back to the global `amdgpu_vm_debug` param and restored the `adev` argument to `amdgpu_vm_bo_invalidate()` calls ]
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c |   30 ++++++++++++++++--------------
 1 file changed, 16 insertions(+), 14 deletions(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +16 -14
@@ -1182,19 +1182,6 @@ static int amdgpu_cs_vm_handling(struct
 		job->vm_pd_addr = amdgpu_gmc_pd_addr(vm->root.bo);
 	}
 
-	if (amdgpu_vm_debug) {
-		/* Invalidate all BOs to test for userspace bugs */
-		amdgpu_bo_list_for_each_entry(e, p->bo_list) {
-			struct amdgpu_bo *bo = e->bo;
-
-			/* ignore duplicates */
-			if (!bo)
-				continue;
-
-			amdgpu_vm_bo_invalidate(adev, bo, false);
-		}
-	}
-
 	return 0;
 }
 
@@ -1383,6 +1370,8 @@ static int amdgpu_cs_submit(struct amdgp
 /* Cleanup the parser structure */
 static void amdgpu_cs_parser_fini(struct amdgpu_cs_parser *parser)
 {
+	struct amdgpu_device *adev = parser->adev;
+	struct amdgpu_bo_list_entry *e;
 	unsigned int i;
 
 	amdgpu_sync_free(&parser->sync);
@@ -1398,8 +1387,21 @@ static void amdgpu_cs_parser_fini(struct
 
 	if (parser->ctx)
 		amdgpu_ctx_put(parser->ctx);
-	if (parser->bo_list)
+	if (parser->bo_list) {
+		if (amdgpu_vm_debug) {
+			/* Invalidate all BOs to test for userspace bugs */
+			amdgpu_bo_list_for_each_entry(e, parser->bo_list) {
+				struct amdgpu_bo *bo = e->bo;
+
+				/* ignore duplicates */
+				if (!bo)
+					continue;
+
+				amdgpu_vm_bo_invalidate(adev, bo, false);
+			}
+		}
 		amdgpu_bo_list_put(parser->bo_list);
+	}
 
 	for (i = 0; i < parser->nchunks; i++)
 		kvfree(parser->chunks[i].kdata);