Skip to contentSkip to conversation

7.1.11-rc1 review

PATCH In review Greg Kroah-Hartman 14 participants 115 messages Aug 25 – Aug 28

Subsystems (3)
lore
Share & tools
lore

Greg Kroah-Hartman

author maintainer
#0 lore raw

cover 7.1.11-rc1 review

This is the start of the stable review cycle for the 7.1.11 release. There are 101 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 Thu, 27 Aug 2026 13:25:02 +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/v7.x/stable-review/patch-7.1.11-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-7.1.y
and the diffstat can be found below.

thanks,

greg k-h

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

Greg Kroah-Hartman <[email protected]>

    Linux 7.1.11-rc1

Laxman Acharya Padhya <[email protected]>
Bluetooth: hci_aml: validate firmware segment lengths

Ali Ahmet Memis <[email protected]>
Bluetooth: MGMT: reject HCI_CMD_SYNC params_len above 255

Ali Ahmet Memis <[email protected]>
Bluetooth: ISO: zero the sockaddr before returning it in getname

Ali Ahmet Memis <[email protected]>
Bluetooth: ISO: do not force BT_LISTEN after a failed BIG sync

Chengfeng Ye <[email protected]>
Bluetooth: hci_sync: Fix accept list UAF during suspend

Laxman Acharya Padhya <[email protected]>
Bluetooth: hci_event: validate LE Set CIG Parameters response

Chengfeng Ye <[email protected]>
Bluetooth: hci_event: fix LE list UAF on reset

Linus Torvalds <[email protected]>
drm/xe: Don't hand out the flat CCS storage as usable VRAM

Jose Villaseñor Montfort <[email protected]>
HID: input: read battery capacity from its actual report offset

Michael Bommarito <[email protected]>
HID: hyperv: validate initial device info bounds

Ibrahim Hashimov <[email protected]>
HID: uclogic: fix use-after-free of inrange_timer on remove

Haoxiang Li <[email protected]>
HID: sensor: custom: Fix use-after-free in enable_sensor

Raman Varabets <[email protected]>
HID: ft260: fix stack-use-after-return write in I2C read race

Jann Horn <[email protected]>
HID: core: fix number/pointer type confusion on long items

Jann Horn <[email protected]>
HID: rapoo: fix missing hid_is_usb() check

Jiangshan Yi <[email protected]>
HID: nintendo: stop device IO before hid_hw_stop on probe failure

Jiangshan Yi <[email protected]>
HID: nintendo: register input device after capabilities are set

Ibrahim Hashimov <[email protected]>
HID: nintendo: fix out-of-bounds read in joycon_ctlr_read_handler()

Jann Horn <[email protected]>
HID: huawei: fix missing hid_is_usb() check

Jann Horn <[email protected]>
HID: asus: fix missing hid_is_usb() check

Peter Zijlstra <[email protected]>
futex: Fix might_sleep() warning in futex_pivot_pending()

Hyunwoo Kim <[email protected]>

    futex: Fix race on the initial mm->futex.phash.ref allocation

Yao Kai <[email protected]>
futex: Fix race in futex_pivot_pending() during private hash resize

Thomas Gleixner <[email protected]>
futex/pi: Plug private futex exec() race

Thomas Gleixner <[email protected]>
futex: Sanitize and document task_struct::futex::state transitions

Kyle Zeng <[email protected]>
futex/pi: Reject cross-mm private futex owners

Anand Khoje <[email protected]>
net/ionic: avoid OOB TX partner lookup for hwstamp RXQ

Baul Lee <[email protected]>

    HID: pidff: fix OOB write when hid->inputs is empty

Baul Lee <[email protected]>

    HID: core: fix OOB read of field->usage in hid_set_field()

Jose Villaseñor Montfort <[email protected]>

    HID: magicmouse: do not keep a stale msc->input if no input is claimed

Christopher Kodama <[email protected]>
HID: magicmouse: re-enable multitouch after reset-resume

Jose Villaseñor Montfort <[email protected]>
HID: magicmouse: prevent unbounded recursion in magicmouse_raw_event()

Andrei Fed <[email protected]>
HID: magicmouse: fix battery reporting for Bluetooth Magic Trackpad USB-C

Bryam Vargas <[email protected]>
selinux: require a class's permission values to cover its permission count

Christian Göttsche <[email protected]>
selinux: more strict policy parsing

Christian Göttsche <[email protected]>
selinux: use u16 for security classes

Shardul Bankar <[email protected]>
mptcp: pm: fix memory leak from alloc-during-teardown race

Matthieu Baerts (NGI0) <[email protected]>
mptcp: pm: uniform announced addresses helpers

Matthieu Baerts (NGI0) <[email protected]>
mptcp: pm: rename add_entry structure to add_addr

Lijo Lazar <[email protected]>
drm/amdgpu: Allocate coredump ring buffers per ring

Mikhail Gavrilov <[email protected]>
drm/amdgpu: fix recursive ww_mutex acquire in amdgpu_devcoredump_format

Melbin K Mathew <[email protected]>
fbdev: serialize mode sysfs access with lock_fb_info()

Thomas Zimmermann <[email protected]>
fbdev: Wrap user-invoked calls to fb_set_var() in helper

Yifei Gao <[email protected]>
nvmet: pci-epf: put CQ ref on create_cq mapping failure

Shin'ichiro Kawasaki <[email protected]>
nvmet: pci-epf: fix use-after-free in nvmet_pci_epf_exec_iod_work()

Greg Kroah-Hartman <[email protected]>
nvmet-tcp: Do not WARN on remotely-controlled oversized SGL allocations

Ibrahim Hashimov <[email protected]>
nvmet-tcp: bound SGL data length before allocating command buffers

Jiang HongHui <[email protected]>
nvmet-fc: fix invalid free in LS IOD error path

Bryam Vargas <[email protected]>
nvmet-auth: zero the AUTH_RECEIVE response buffer

Griffin Kroah-Hartman <[email protected]>
dmaengine: fsl-edma: Add error handling for devm_kasprintf

Griffin Kroah-Hartman <[email protected]>
mailbox: mchp-ipc-sbi: Add null check for devm_kasprintf()

Luxiao Xu <[email protected]>
ipv6: fix use-after-free in ip6_finish_output2()

Yong Wang <[email protected]>
ipv4: reject undersized MTUs in ip_do_fragment()

Linmao Li <[email protected]>
nfc: nci: free destination parameters when closing a connection

Samuel Page <[email protected]>
nfc: nci: fix uninit-value in the RF discover/activated NTF handlers

Samuel Page <[email protected]>
nfc: nci: fix out-of-bounds write in nci_target_auto_activated()

Bryam Vargas <[email protected]>
nfc: nci: add data_len bound checks to activation parameter extractors

Doruk Tan Ozturk <[email protected]>
nfc: st21nfca: validate ATR_REQ length against the received frame

Xu Rao <[email protected]>
nfc: pn533: purge fragmented skbs during cleanup

Doruk Tan Ozturk <[email protected]>
nfc: llcp: reject PDUs shorter than the LLCP header

Muhammad Bilal <[email protected]>
nfc: llcp: fix OOB read and u8 offset wrap in TLV parsers

Doruk Tan Ozturk <[email protected]>
nfc: llcp: bound the connect_sn TLV walk to the skb

Pengpeng Hou <[email protected]>
nfc: microread: validate target discovery payload lengths

Bryam Vargas <[email protected]>
nfc: fdp: bound the device-reported read length and fix an skb leak

Doruk Tan Ozturk <[email protected]>
nfc: digital: clamp SENSF_RES length to the destination buffer

Maarten Lankhorst <[email protected]>
drm/xe: Fix DPT allocation paths.

Ville Syrjälä <[email protected]>
drm/i915: Introduce struct intel_fb_pin_params

Ville Syrjälä <[email protected]>
drm/i915: Track fence region ID in plane state

Jani Nikula <[email protected]>
drm/i915/pin: s/dev_priv/i915/ and drop struct drm_device usage

Yun Zhou <[email protected]>
xfs: restore nofs context unconditionally in xfs_trans_roll

Hongling Zeng <[email protected]>
xfs: validate attr entry pointer before field access

Jiazi Liu <[email protected]>
ext4: fix incorrect function call when initializing s_resgid

Eric Biggers <[email protected]>
ext4: don't enable DAX on new encrypted files

Guanghui Yang <[email protected]>
ext4: propagate errors from fast commit range replay

Jia Zhu <[email protected]>
ext4: avoid tail write_begin walk for uptodate folios

Guanghui Yang <[email protected]>
ext4: clear error before retrying inode xattr space fallback

Ryusuke Konishi <[email protected]>
nilfs2: reject invalid block index in GC ioctl

Matthias Goergens <[email protected]>
ext4: stop retrying saturated xattr cache entries

Tetsuo Handa <[email protected]>
kcov: fix data corruption and race conditions on PREEMPT_RT

Rik van Riel <[email protected]>
null_blk: fix UBSAN shift-out-of-bounds when zone_size is 0 or overflows

Ian Bridges <[email protected]>
ocfs2: fix missing metadata reservation for large xattrs

Jens Axboe <[email protected]>
io_uring: defer eventfd signaling when queued from a wakeup handler

Jens Axboe <[email protected]>
io_uring/uring_cmd: don't skip completion for a synchronous multishot cmd

Ali Ahmet Memis <[email protected]>
io_uring/rsrc: fix folio size overflow in io_vec_fill_bvec()

Vishnu Razdan <[email protected]>
io_uring/io-wq: fix worker accounting when canceling creation callbacks

Woraphat Khiaodaeng <[email protected]>
io_uring/cmd: fix iovec leak when the async cmd is not recycled

Takashi Iwai <[email protected]>
ALSA: dummy: Check card index validity at probe

Jens Axboe <[email protected]>
io_uring/futex: only mark private futex waits as inflight

Jens Axboe <[email protected]>
io_uring/futex: don't mark futex wake requests as inflight

Guixin Liu <[email protected]>
nvmet: fix NULL pointer dereference in nvmet_execute_identify_nslist()

Griffin Kroah-Hartman <[email protected]>
rndis_host: add overflow check in rndis_rx_fixup()

Geoffrey D. Bennett <[email protected]>
ALSA: scarlett2: Use a private URB for the notification endpoint

Geoffrey D. Bennett <[email protected]>
ALSA: FCP: Use a private URB for the notification endpoint

Peiyang He <[email protected]>
iommu/iommufd: Fix NULL pointer deref in iommufd_ioas_change_process when racing with iopt_map_file_pages

Shameer Kolothum <[email protected]>
iommu/tegra241-cmdqv: Fix CMD_SYNC use-after-free on teardown

Ali Ahmet Memis <[email protected]>
Bluetooth: RFCOMM: take rfcomm_mutex for the deferred setup accept

Steffen Persvold <[email protected]>
PCI: host-generic: Fix NULL pointer dereference on 32-bit CAM systems

Darrick J. Wong <[email protected]>
xfs: rtsummary scrub should treat rtbitmap corruption errors as an xref error

Christoph Hellwig <[email protected]>
xfs: add a xchk_ip_set_corrupt helper

Darrick J. Wong <[email protected]>
xfs: don't livelock in scrub on a circular unlinked list

Darrick J. Wong <[email protected]>
xfs: hoist per-bucket unlinked list check to helper

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

Diffstat:

 Makefile                                           |   4 +-
 drivers/block/null_blk/zoned.c                     |  10 +-
 drivers/bluetooth/hci_aml.c                        |  18 +-
 drivers/dma/fsl-edma-main.c                        |   2 +
 drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c   | 265 ++++++++++++---------
 drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.h   |   3 +-
 drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c             |  91 ++++---
 drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h             |   2 +-
 drivers/gpu/drm/i915/display/intel_display_types.h |   5 +-
 drivers/gpu/drm/i915/display/intel_fb_pin.c        | 107 +++++----
 drivers/gpu/drm/i915/display/intel_fb_pin.h        |  17 +-
 drivers/gpu/drm/i915/display/intel_fbc.c           |  11 +-
 drivers/gpu/drm/i915/display/intel_fbdev.c         |  20 +-
 drivers/gpu/drm/i915/display/intel_plane.c         |   3 +-
 drivers/gpu/drm/i915/i915_initial_plane.c          |   2 +-
 drivers/gpu/drm/xe/display/xe_fb_pin.c             |  74 +++---
 drivers/gpu/drm/xe/display/xe_initial_plane.c      |   6 +-
 drivers/gpu/drm/xe/xe_vram.c                       |  23 +-
 drivers/hid/Kconfig                                |   1 +
 drivers/hid/hid-asus.c                             |   2 +-
 drivers/hid/hid-core.c                             |  11 +-
 drivers/hid/hid-ft260.c                            |  25 +-
 drivers/hid/hid-huawei.c                           |   5 +-
 drivers/hid/hid-hyperv.c                           |  27 ++-
 drivers/hid/hid-input.c                            |  20 +-
 drivers/hid/hid-magicmouse.c                       |  73 +++++-
 drivers/hid/hid-nintendo.c                         |  22 +-
 drivers/hid/hid-rapoo.c                            |   2 +-
 drivers/hid/hid-sensor-custom.c                    |  21 +-
 drivers/hid/hid-uclogic-core.c                     |  12 +-
 drivers/hid/usbhid/hid-pidff.c                     |  13 +-
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c        |   2 +
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h        |   1 +
 drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c     |  15 +-
 drivers/iommu/iommufd/ioas.c                       |   4 +
 drivers/mailbox/mailbox-mchp-ipc-sbi.c             |   2 +
 drivers/net/ethernet/pensando/ionic/ionic_lif.c    |  17 +-
 drivers/net/ethernet/pensando/ionic/ionic_txrx.c   |   7 +-
 drivers/net/usb/rndis_host.c                       |   6 +-
 drivers/nfc/fdp/i2c.c                              |  27 +++
 drivers/nfc/microread/microread.c                  |  31 ++-
 drivers/nfc/pn533/pn533.c                          |   1 +
 drivers/nfc/st21nfca/dep.c                         |   3 +
 drivers/nvme/target/admin-cmd.c                    |   2 +-
 drivers/nvme/target/fabrics-cmd-auth.c             |   2 +-
 drivers/nvme/target/fc.c                           |   2 +-
 drivers/nvme/target/pci-epf.c                      |  10 +-
 drivers/nvme/target/tcp.c                          |  19 +-
 drivers/pci/controller/pci-host-generic.c          |  11 +-
 drivers/pci/ecam.c                                 |  13 +
 drivers/video/fbdev/core/fb_chrdev.c               |   6 +-
 drivers/video/fbdev/core/fbcon.c                   |   2 -
 drivers/video/fbdev/core/fbmem.c                   |  13 +
 drivers/video/fbdev/core/fbsysfs.c                 |  48 +++-
 drivers/video/fbdev/ps3fb.c                        |   5 +-
 drivers/video/fbdev/sh_mobile_lcdcfb.c             |   5 +-
 fs/exec.c                                          |   8 +-
 fs/ext4/crypto.c                                   |  40 ++--
 fs/ext4/fast_commit.c                              |  16 +-
 fs/ext4/ialloc.c                                   |   4 +
 fs/ext4/inode.c                                    |  11 +-
 fs/ext4/super.c                                    |   2 +-
 fs/ext4/xattr.c                                    |   7 +-
 fs/nilfs2/ioctl.c                                  |  20 +-
 fs/ocfs2/xattr.c                                   |  18 +-
 fs/xfs/libxfs/xfs_attr_leaf.c                      |  14 ++
 fs/xfs/scrub/agheader.c                            |  94 ++++++--
 fs/xfs/scrub/agheader_repair.c                     |  17 +-
 fs/xfs/scrub/bmap.c                                |   8 +-
 fs/xfs/scrub/common.c                              |  10 +-
 fs/xfs/scrub/common.h                              |   2 +
 fs/xfs/scrub/dir.c                                 |   2 +-
 fs/xfs/scrub/dirtree.c                             |   4 +-
 fs/xfs/scrub/metapath.c                            |   6 +-
 fs/xfs/scrub/nlinks.c                              |  12 +-
 fs/xfs/scrub/parent.c                              |   6 +-
 fs/xfs/scrub/rtbitmap.c                            |  12 +-
 fs/xfs/scrub/rtsummary.c                           |  12 +-
 fs/xfs/xfs_trans.c                                 |  16 +-
 include/linux/fb.h                                 |   2 +
 include/linux/futex.h                              |   2 +
 include/linux/hid.h                                |   2 +
 include/linux/io_uring_types.h                     |   8 +
 include/linux/pci-ecam.h                           |   3 +
 include/linux/sched.h                              |   8 +
 include/linux/wait.h                               |   1 +
 include/linux/wait_bit.h                           |   1 +
 io_uring/eventfd.c                                 |   8 +-
 io_uring/eventfd.h                                 |   2 +-
 io_uring/futex.c                                   |  32 ++-
 io_uring/futex.h                                   |   1 +
 io_uring/io-wq.c                                   |   9 +-
 io_uring/io_uring.c                                |   2 +-
 io_uring/opdef.c                                   |   2 +-
 io_uring/poll.c                                    |  23 +-
 io_uring/rsrc.c                                    |   2 +-
 io_uring/tw.c                                      |   2 +-
 io_uring/uring_cmd.c                               |   8 +-
 io_uring/waitid.c                                  |   2 +-
 kernel/futex/core.c                                | 126 ++++++----
 kernel/futex/futex.h                               |   9 +
 kernel/futex/pi.c                                  | 125 +++++++---
 kernel/kcov.c                                      |  90 +++----
 kernel/sched/wait.c                                |  15 ++
 kernel/sched/wait_bit.c                            |  14 +-
 lib/Kconfig.debug                                  |   5 +-
 net/bluetooth/hci_event.c                          |   8 +-
 net/bluetooth/hci_sync.c                           |  46 +++-
 net/bluetooth/iso.c                                |  32 ++-
 net/bluetooth/mgmt.c                               |   8 +
 net/bluetooth/rfcomm/core.c                        |  24 +-
 net/ipv4/ip_output.c                               |   4 +
 net/ipv6/ip6_output.c                              |   2 +
 net/mptcp/options.c                                |   2 +-
 net/mptcp/pm.c                                     |  68 +++---
 net/mptcp/pm_kernel.c                              |  10 +-
 net/mptcp/pm_userspace.c                           |   8 +-
 net/mptcp/protocol.h                               |  21 +-
 net/mptcp/subflow.c                                |   4 +-
 net/nfc/digital_technology.c                       |   2 +
 net/nfc/llcp_commands.c                            |  18 +-
 net/nfc/llcp_core.c                                |  15 +-
 net/nfc/nci/ntf.c                                  |  36 ++-
 net/nfc/nci/rsp.c                                  |   1 +
 security/selinux/include/security.h                |   1 +
 security/selinux/ss/avtab.c                        |  35 ++-
 security/selinux/ss/avtab.h                        |  13 +
 security/selinux/ss/conditional.c                  |  18 +-
 security/selinux/ss/constraint.h                   |   1 +
 security/selinux/ss/policydb.c                     | 248 +++++++++++++++----
 security/selinux/ss/policydb.h                     |  31 ++-
 security/selinux/ss/services.c                     |   8 +-
 sound/drivers/dummy.c                              |   6 +
 sound/usb/fcp.c                                    |  38 +--
 sound/usb/mixer.c                                  |   6 +
 sound/usb/mixer.h                                  |   2 +
 sound/usb/mixer_scarlett2.c                        |  98 +++++---
 137 files changed, 1957 insertions(+), 853 deletions(-)
author maintainer
#1 lore raw

PATCH 1/101 xfs: hoist per-bucket unlinked list check to helper

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

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

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

[ Upstream commit 7cdafd8f10ebdf745ba6046b9fa67490c343a17f ]

In the next patch we're going to make this loop more exciting, so hoist the code to a helper function to reduce clutter in the resulting code.

Stable-dep-of: 527eaaefddb6 ("xfs: don't livelock in scrub on a circular unlinked list")

---
 fs/xfs/scrub/agheader.c |   62 +++++++++++++++++++++++++++++++-----------------
 1 file changed, 41 insertions(+), 21 deletions(-)
fs/xfs/scrub/agheader.c +41 -21
@@ -933,6 +933,42 @@ xchk_agi_xref(
 }
 
 /*
+ * Walk the incore unlinked list for a particular AGI bucket to construct
+ * the unlinked inode bitmap for later reconstruction of the unlinked list.
+ * Returns 1 if we should keep checking, or 0 to stop checking.
+ */
+static int
+xchk_iunlink_bucket(
+	struct xfs_scrub		*sc,
+	unsigned int			bucket,
+	xfs_agino_t			agino)
+{
+	while (agino != NULLAGINO) {
+		struct xfs_inode	*ip;
+
+		if (agino % XFS_AGI_UNLINKED_BUCKETS != bucket) {
+			xchk_block_set_corrupt(sc, sc->sa.agi_bp);
+			return 0;
+		}
+
+		ip = xfs_iunlink_lookup(sc->sa.pag, agino);
+		if (!ip) {
+			xchk_block_set_corrupt(sc, sc->sa.agi_bp);
+			return 0;
+		}
+
+		if (!xfs_inode_on_unlinked_list(ip)) {
+			xchk_block_set_corrupt(sc, sc->sa.agi_bp);
+			return 0;
+		}
+
+		agino = ip->i_next_unlinked;
+	}
+
+	return 1;
+}
+
+/*
  * Check the unlinked buckets for links to bad inodes.  We hold the AGI, so
  * there cannot be any threads updating unlinked list pointers in this AG.
  */
@@ -942,30 +978,14 @@ xchk_iunlink(
 	struct xfs_agi		*agi)
 {
 	unsigned int		i;
-	struct xfs_inode	*ip;
 
 	for (i = 0; i < XFS_AGI_UNLINKED_BUCKETS; i++) {
-		xfs_agino_t	agino = be32_to_cpu(agi->agi_unlinked[i]);
+		int		ret;
 
-		while (agino != NULLAGINO) {
-			if (agino % XFS_AGI_UNLINKED_BUCKETS != i) {
-				xchk_block_set_corrupt(sc, sc->sa.agi_bp);
-				return;
-			}
-
-			ip = xfs_iunlink_lookup(sc->sa.pag, agino);
-			if (!ip) {
-				xchk_block_set_corrupt(sc, sc->sa.agi_bp);
-				return;
-			}
-
-			if (!xfs_inode_on_unlinked_list(ip)) {
-				xchk_block_set_corrupt(sc, sc->sa.agi_bp);
-				return;
-			}
-
-			agino = ip->i_next_unlinked;
-		}
+		ret = xchk_iunlink_bucket(sc, i,
+				be32_to_cpu(agi->agi_unlinked[i]));
+		if (ret < 1)
+			return;
 	}
 }
 
author maintainer
#2 lore raw

PATCH 2/101 xfs: dont livelock in scrub on a circular unlinked list

In reply to Greg Kroah-Hartman

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

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

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

[ Upstream commit 527eaaefddb6ec5c83a06c9a1559960dd6361753 ]

LOLLM points out that online fsck can livelock if an unlinked inode list contains a loop. Use a bitmap to detect cycles.

Fixes a12890aebb8959 ("xfs: scrub the AGI")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/scrub/agheader.c        |   44 +++++++++++++++++++++++++++++++++--------
 fs/xfs/scrub/agheader_repair.c |   17 +++++++++++++--
 2 files changed, 51 insertions(+), 10 deletions(-)
fs/xfs/scrub/agheader.c +36 -8
@@ -18,6 +18,8 @@
 #include "xfs_inode.h"
 #include "scrub/scrub.h"
 #include "scrub/common.h"
+#include "scrub/bitmap.h"
+#include "scrub/agino_bitmap.h"
 
 int
 xchk_setup_agheader(
@@ -935,7 +937,8 @@ xchk_agi_xref(
 /*
  * Walk the incore unlinked list for a particular AGI bucket to construct
  * the unlinked inode bitmap for later reconstruction of the unlinked list.
- * Returns 1 if we should keep checking, or 0 to stop checking.
+ * Returns 1 if we should keep checking, 0 to stop checking, or a negative
+ * errno.
  */
 static int
 xchk_iunlink_bucket(
@@ -943,36 +946,57 @@ xchk_iunlink_bucket(
 	unsigned int			bucket,
 	xfs_agino_t			agino)
 {
+	struct xagino_bitmap		seen;
+	int				ret;
+
+	xagino_bitmap_init(&seen);
+
 	while (agino != NULLAGINO) {
 		struct xfs_inode	*ip;
+		unsigned int		len = 1;
 
 		if (agino % XFS_AGI_UNLINKED_BUCKETS != bucket) {
 			xchk_block_set_corrupt(sc, sc->sa.agi_bp);
-			return 0;
+			goto bad;
+		}
+
+		if (xagino_bitmap_test(&seen, agino, &len)) {
+			xchk_block_set_corrupt(sc, sc->sa.agi_bp);
+			goto bad;
 		}
 
 		ip = xfs_iunlink_lookup(sc->sa.pag, agino);
 		if (!ip) {
 			xchk_block_set_corrupt(sc, sc->sa.agi_bp);
-			return 0;
+			goto bad;
 		}
 
 		if (!xfs_inode_on_unlinked_list(ip)) {
 			xchk_block_set_corrupt(sc, sc->sa.agi_bp);
-			return 0;
+			goto bad;
 		}
 
+		ret = xagino_bitmap_set(&seen, agino, 1);
+		if (ret)
+			goto out_bitmap;
+
 		agino = ip->i_next_unlinked;
 	}
+	ret = 1;
 
-	return 1;
+out_bitmap:
+	xagino_bitmap_destroy(&seen);
+	return ret;
+bad:
+	ret = 0;
+	goto out_bitmap;
 }
 
 /*
  * Check the unlinked buckets for links to bad inodes.  We hold the AGI, so
  * there cannot be any threads updating unlinked list pointers in this AG.
  */
-STATIC void
+STATIC int
 xchk_iunlink(
 	struct xfs_scrub	*sc,
 	struct xfs_agi		*agi)
@@ -985,8 +1009,10 @@ xchk_iunlink(
 		ret = xchk_iunlink_bucket(sc, i,
 				be32_to_cpu(agi->agi_unlinked[i]));
 		if (ret < 1)
-			return;
+			return ret;
 	}
+
+	return 0;
 }
 
 /* Scrub the AGI. */
@@ -1073,7 +1099,9 @@ xchk_agi(
 	if (pag->pagi_freecount != be32_to_cpu(agi->agi_freecount))
 		xchk_block_set_corrupt(sc, sc->sa.agi_bp);
 
-	xchk_iunlink(sc, agi);
+	error = xchk_iunlink(sc, agi);
+	if (error)
+		goto out;
 
 	xchk_agi_xref(sc);
 out:
fs/xfs/scrub/agheader_repair.c +15 -2
@@ -1096,18 +1096,22 @@ xrep_iunlink_walk_ondisk_bucket(
 	struct xrep_agi		*ragi,
 	unsigned int		bucket)
 {
+	struct xagino_bitmap	seen;
 	struct xfs_scrub	*sc = ragi->sc;
 	struct xfs_agi		*agi = ragi->agi_bp->b_addr;
 	xfs_agino_t		prev_agino = NULLAGINO;
 	xfs_agino_t		next_agino;
 	int			error = 0;
 
+	xagino_bitmap_init(&seen);
+
 	next_agino = be32_to_cpu(agi->agi_unlinked[bucket]);
 	while (next_agino != NULLAGINO) {
 		xfs_agino_t	agino = next_agino;
+		unsigned int	len = 1;
 
 		if (xchk_should_terminate(ragi->sc, &error))
-			return error;
+			goto out_bitmap;
 
 		trace_xrep_iunlink_walk_ondisk_bucket(sc->sa.pag, bucket,
 				prev_agino, agino);
@@ -1115,6 +1119,9 @@ xrep_iunlink_walk_ondisk_bucket(
 		if (bucket != agino % XFS_AGI_UNLINKED_BUCKETS)
 			break;
 
+		if (xagino_bitmap_test(&seen, agino, &len))
+			break;
+
 		next_agino = xrep_iunlink_next(sc, agino);
 		if (!next_agino) {
 			error = xrep_iunlink_reload_next(ragi, prev_agino,
@@ -1123,10 +1130,16 @@ xrep_iunlink_walk_ondisk_bucket(
 				break;
 		}
 
+		error = xagino_bitmap_set(&seen, agino, 1);
+		if (error)
+			goto out_bitmap;
+
 		prev_agino = agino;
 	}
 
-	return 0;
+out_bitmap:
+	xagino_bitmap_destroy(&seen);
+	return error;
 }
 
 /* Decide if this is an unlinked inode in this AG. */
author maintainer
#3 lore raw

PATCH 3/101 xfs: add a xchk_ip_set_corrupt helper

In reply to Greg Kroah-Hartman

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

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

From: Christoph Hellwig <[email protected]>

[ Upstream commit aa7cfdb6e16b474328c79736f9b9dad09addc81c ]

Add a smaller wrapper to set a inode corrupted by the xfs_inode pointer.

Stable-dep-of: 8ed78104722b ("xfs: rtsummary scrub should treat rtbitmap corruption errors as an xref error")

---
 fs/xfs/scrub/bmap.c      |    8 ++++----
 fs/xfs/scrub/common.c    |   10 +++++-----
 fs/xfs/scrub/common.h    |    2 ++
 fs/xfs/scrub/dir.c       |    2 +-
 fs/xfs/scrub/dirtree.c   |    4 ++--
 fs/xfs/scrub/metapath.c  |    6 +++---
 fs/xfs/scrub/nlinks.c    |   12 ++++++------
 fs/xfs/scrub/parent.c    |    6 +++---
 fs/xfs/scrub/rtbitmap.c  |   12 ++++++------
 fs/xfs/scrub/rtsummary.c |   12 ++++++------
 10 files changed, 38 insertions(+), 36 deletions(-)
fs/xfs/scrub/bmap.c +4 -4
@@ -1040,7 +1040,7 @@ xchk_bmap(
 	case XFS_COW_FORK:
 		/* No CoW forks filesystem doesn't support out of place writes */
 		if (!xfs_has_reflink(mp) && !xfs_has_zoned(mp)) {
-			xchk_ino_set_corrupt(sc, sc->ip->i_ino);
+			xchk_ip_set_corrupt(sc, sc->ip);
 			return 0;
 		}
 		break;
@@ -1052,7 +1052,7 @@ xchk_bmap(
 		 * attr here.
 		 */
 		if (!xfs_has_attr(mp))
-			xchk_ino_set_corrupt(sc, sc->ip->i_ino);
+			xchk_ip_set_corrupt(sc, sc->ip);
 		break;
 	default:
 		ASSERT(whichfork == XFS_DATA_FORK);
@@ -1137,7 +1137,7 @@ xchk_bmap_data(
 	int			error;
 
 	if (xchk_file_looks_zapped(sc, XFS_SICK_INO_BMBTD_ZAPPED)) {
-		xchk_ino_set_corrupt(sc, sc->ip->i_ino);
+		xchk_ip_set_corrupt(sc, sc->ip);
 		return 0;
 	}
 
@@ -1165,7 +1165,7 @@ xchk_bmap_attr(
 	 * returning immediately.
 	 */
 	if (xchk_file_looks_zapped(sc, XFS_SICK_INO_BMBTA_ZAPPED)) {
-		xchk_ino_set_corrupt(sc, sc->ip->i_ino);
+		xchk_ip_set_corrupt(sc, sc->ip);
 		return 0;
 	}
 
fs/xfs/scrub/common.c +5 -5
@@ -1110,7 +1110,7 @@ xchk_install_live_inode(
 	struct xfs_inode	*ip)
 {
 	if (!igrab(VFS_I(ip))) {
-		xchk_ino_set_corrupt(sc, ip->i_ino);
+		xchk_ip_set_corrupt(sc, ip);
 		return -EFSCORRUPTED;
 	}
 
@@ -1439,13 +1439,13 @@ xchk_metadata_inode_forks(
 
 	/* Metadata inodes don't live on the rt device. */
 	if (sc->ip->i_diflags & XFS_DIFLAG_REALTIME) {
-		xchk_ino_set_corrupt(sc, sc->ip->i_ino);
+		xchk_ip_set_corrupt(sc, sc->ip);
 		return 0;
 	}
 
 	/* They should never participate in reflink. */
 	if (xfs_is_reflink_inode(sc->ip)) {
-		xchk_ino_set_corrupt(sc, sc->ip->i_ino);
+		xchk_ip_set_corrupt(sc, sc->ip);
 		return 0;
 	}
 
@@ -1462,7 +1462,7 @@ xchk_metadata_inode_forks(
 				&error))
 			return error;
 		if (shared)
-			xchk_ino_set_corrupt(sc, sc->ip->i_ino);
+			xchk_ip_set_corrupt(sc, sc->ip);
 	}
 
 	/*
@@ -1471,7 +1471,7 @@ xchk_metadata_inode_forks(
 	 */
 	if (xfs_inode_hasattr(sc->ip)) {
 		if (!xfs_has_metadir(sc->mp)) {
-			xchk_ino_set_corrupt(sc, sc->ip->i_ino);
+			xchk_ip_set_corrupt(sc, sc->ip);
 			return 0;
 		}
 
fs/xfs/scrub/common.h +2 -0
@@ -32,6 +32,8 @@ void xchk_set_corrupt(struct xfs_scrub *
 void xchk_block_set_corrupt(struct xfs_scrub *sc,
 		struct xfs_buf *bp);
 void xchk_ino_set_corrupt(struct xfs_scrub *sc, xfs_ino_t ino);
+#define xchk_ip_set_corrupt(_sc, _ip) \
+	xchk_ino_set_corrupt((_sc), (_ip)->i_ino)
 void xchk_fblock_set_corrupt(struct xfs_scrub *sc, int whichfork,
 		xfs_fileoff_t offset);
 #ifdef CONFIG_XFS_QUOTA
fs/xfs/scrub/dir.c +1 -1
@@ -1080,7 +1080,7 @@ xchk_directory(
 
 	/* Plausible size? */
 	if (sc->ip->i_disk_size < xfs_dir2_sf_hdr_size(0)) {
-		xchk_ino_set_corrupt(sc, sc->ip->i_ino);
+		xchk_ip_set_corrupt(sc, sc->ip);
 		return 0;
 	}
 
fs/xfs/scrub/dirtree.c +2 -2
@@ -979,10 +979,10 @@ xchk_dirtree(
 	xchk_dirtree_evaluate(dl, &oc);
 	if (xchk_dirtree_parentless(dl)) {
 		if (oc.good || oc.bad || oc.suspect)
-			xchk_ino_set_corrupt(sc, sc->ip->i_ino);
+			xchk_ip_set_corrupt(sc, sc->ip);
 	} else {
 		if (oc.bad || oc.good + oc.suspect != 1)
-			xchk_ino_set_corrupt(sc, sc->ip->i_ino);
+			xchk_ip_set_corrupt(sc, sc->ip);
 		if (oc.suspect)
 			xchk_ino_xref_set_corrupt(sc, sc->ip->i_ino);
 	}
fs/xfs/scrub/metapath.c +3 -3
@@ -314,7 +314,7 @@ xchk_metapath(
 
 	/* Parent required to do anything else. */
 	if (mpath->dp == NULL) {
-		xchk_ino_set_corrupt(sc, sc->ip->i_ino);
+		xchk_ip_set_corrupt(sc, sc->ip);
 		return 0;
 	}
 
@@ -329,7 +329,7 @@ xchk_metapath(
 	trace_xchk_metapath_lookup(sc, mpath->path, mpath->dp, ino);
 	if (error == -ENOENT) {
 		/* No directory entry at all */
-		xchk_ino_set_corrupt(sc, sc->ip->i_ino);
+		xchk_ip_set_corrupt(sc, sc->ip);
 		error = 0;
 		goto out_ilock;
 	}
@@ -337,7 +337,7 @@ xchk_metapath(
 		goto out_ilock;
 	if (ino != sc->ip->i_ino) {
 		/* Pointing to wrong inode */
-		xchk_ino_set_corrupt(sc, sc->ip->i_ino);
+		xchk_ip_set_corrupt(sc, sc->ip);
 	}
 
 out_ilock:
fs/xfs/scrub/nlinks.c +6 -6
@@ -719,7 +719,7 @@ xchk_nlinks_compare_inode(
 	 * count, but it will let them decrease it.
 	 */
 	if (total_links > XFS_NLINK_PINNED) {
-		xchk_ino_set_corrupt(sc, ip->i_ino);
+		xchk_ip_set_corrupt(sc, ip);
 		goto out_corrupt;
 	} else if (total_links > XFS_MAXLINK) {
 		xchk_ino_set_warning(sc, ip->i_ino);
@@ -727,7 +727,7 @@ xchk_nlinks_compare_inode(
 
 	/* Link counts should match. */
 	if (total_links != actual_nlink) {
-		xchk_ino_set_corrupt(sc, ip->i_ino);
+		xchk_ip_set_corrupt(sc, ip);
 		goto out_corrupt;
 	}
 
@@ -748,7 +748,7 @@ xchk_nlinks_compare_inode(
 		 * back references.
 		 */
 		if (obs.backrefs != 0) {
-			xchk_ino_set_corrupt(sc, ip->i_ino);
+			xchk_ip_set_corrupt(sc, ip);
 			goto out_corrupt;
 		}
 
@@ -757,7 +757,7 @@ xchk_nlinks_compare_inode(
 		 * children.
 		 */
 		if (obs.children != 0) {
-			xchk_ino_set_corrupt(sc, ip->i_ino);
+			xchk_ip_set_corrupt(sc, ip);
 			goto out_corrupt;
 		}
 	}
@@ -770,7 +770,7 @@ xchk_nlinks_compare_inode(
 		 * the root directory.
 		 */
 		if (obs.parents != 1) {
-			xchk_ino_set_corrupt(sc, ip->i_ino);
+			xchk_ip_set_corrupt(sc, ip);
 			goto out_corrupt;
 		}
 	} else if (actual_nlink > 0) {
@@ -779,7 +779,7 @@ xchk_nlinks_compare_inode(
 		 * least one parent.
 		 */
 		if (obs.parents == 0) {
-			xchk_ino_set_corrupt(sc, ip->i_ino);
+			xchk_ip_set_corrupt(sc, ip);
 			goto out_corrupt;
 		}
 	}
fs/xfs/scrub/parent.c +3 -3
@@ -729,10 +729,10 @@ xchk_parent_count_pptrs(
 			pp->pptrs_found++;
 
 		if (VFS_I(sc->ip)->i_nlink == 0 && pp->pptrs_found > 0)
-			xchk_ino_set_corrupt(sc, sc->ip->i_ino);
+			xchk_ip_set_corrupt(sc, sc->ip);
 		else if (VFS_I(sc->ip)->i_nlink > 0 &&
 			 pp->pptrs_found == 0)
-			xchk_ino_set_corrupt(sc, sc->ip->i_ino);
+			xchk_ip_set_corrupt(sc, sc->ip);
 	} else {
 		/*
 		 * Starting with metadir, we allow checking of parent pointers
@@ -743,7 +743,7 @@ xchk_parent_count_pptrs(
 			pp->pptrs_found++;
 
 		if (VFS_I(sc->ip)->i_nlink != pp->pptrs_found)
-			xchk_ino_set_corrupt(sc, sc->ip->i_ino);
+			xchk_ip_set_corrupt(sc, sc->ip);
 	}
 
 	return 0;
fs/xfs/scrub/rtbitmap.c +6 -6
@@ -200,13 +200,13 @@ xchk_rtbitmap(
 
 	/* Is sb_rextents correct? */
 	if (mp->m_sb.sb_rextents != rtb->rextents) {
-		xchk_ino_set_corrupt(sc, rbmip->i_ino);
+		xchk_ip_set_corrupt(sc, rbmip);
 		return 0;
 	}
 
 	/* Is sb_rextslog correct? */
 	if (mp->m_sb.sb_rextslog != rtb->rextslog) {
-		xchk_ino_set_corrupt(sc, rbmip->i_ino);
+		xchk_ip_set_corrupt(sc, rbmip);
 		return 0;
 	}
 
@@ -215,17 +215,17 @@ xchk_rtbitmap(
 	 * case can we exceed 4bn bitmap blocks since the super field is a u32.
 	 */
 	if (rtb->rbmblocks > U32_MAX) {
-		xchk_ino_set_corrupt(sc, rbmip->i_ino);
+		xchk_ip_set_corrupt(sc, rbmip);
 		return 0;
 	}
 	if (mp->m_sb.sb_rbmblocks != rtb->rbmblocks) {
-		xchk_ino_set_corrupt(sc, rbmip->i_ino);
+		xchk_ip_set_corrupt(sc, rbmip);
 		return 0;
 	}
 
 	/* The bitmap file length must be aligned to an fsblock. */
 	if (rbmip->i_disk_size & mp->m_blockmask) {
-		xchk_ino_set_corrupt(sc, rbmip->i_ino);
+		xchk_ip_set_corrupt(sc, rbmip);
 		return 0;
 	}
 
@@ -235,7 +235,7 @@ xchk_rtbitmap(
 	 * file can be larger than sb_rbmblocks.
 	 */
 	if (rbmip->i_disk_size < XFS_FSB_TO_B(mp, rtb->rbmblocks)) {
-		xchk_ino_set_corrupt(sc, rbmip->i_ino);
+		xchk_ip_set_corrupt(sc, rbmip);
 		return 0;
 	}
 
fs/xfs/scrub/rtsummary.c +6 -6
@@ -314,25 +314,25 @@ xchk_rtsummary(
 
 	/* Is sb_rextents correct? */
 	if (mp->m_sb.sb_rextents != rts->rextents) {
-		xchk_ino_set_corrupt(sc, rbmip->i_ino);
+		xchk_ip_set_corrupt(sc, rbmip);
 		return 0;
 	}
 
 	/* Is m_rsumlevels correct? */
 	if (mp->m_rsumlevels != rts->rsumlevels) {
-		xchk_ino_set_corrupt(sc, rsumip->i_ino);
+		xchk_ip_set_corrupt(sc, rsumip);
 		return 0;
 	}
 
 	/* Is m_rsumsize correct? */
 	if (mp->m_rsumblocks != rts->rsumblocks) {
-		xchk_ino_set_corrupt(sc, rsumip->i_ino);
+		xchk_ip_set_corrupt(sc, rsumip);
 		return 0;
 	}
 
 	/* The summary file length must be aligned to an fsblock. */
 	if (rsumip->i_disk_size & mp->m_blockmask) {
-		xchk_ino_set_corrupt(sc, rsumip->i_ino);
+		xchk_ip_set_corrupt(sc, rsumip);
 		return 0;
 	}
 
@@ -342,7 +342,7 @@ xchk_rtsummary(
 	 * the file can be larger than rsumsize.
 	 */
 	if (rsumip->i_disk_size < XFS_FSB_TO_B(mp, rts->rsumblocks)) {
-		xchk_ino_set_corrupt(sc, rsumip->i_ino);
+		xchk_ip_set_corrupt(sc, rsumip);
 		return 0;
 	}
 
@@ -358,7 +358,7 @@ xchk_rtsummary(
 		 * EFSCORRUPTED means the rtbitmap is corrupt, which is an xref
 		 * error since we're checking the summary file.
 		 */
-		xchk_ino_set_corrupt(sc, rbmip->i_ino);
+		xchk_ip_set_corrupt(sc, rbmip);
 		return 0;
 	}
 	if (error)
author maintainer
#4 lore raw

PATCH 4/101 xfs: rtsummary scrub should treat rtbitmap corruption errors as an xref error

In reply to Greg Kroah-Hartman

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

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

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

[ Upstream commit 8ed78104722b57df69478b0b1608a408c1036085 ]

LOLLM notes the discrepancy between the comment saying that this is an xref error and the code, which marks a (non-xref) corruption error. This appears to be a regression.

Fixes e3088ae2dcae3c ("xfs: move RT bitmap and summary information to the rtgroup")
Assisted-by LOLLM # finding obvious bugs
[ changed `xchk_ip_xref_set_corrupt(sc, rbmip)` to `xchk_ino_xref_set_corrupt(sc, rbmip->i_ino)` since the renamed helper is absent ]
---
 fs/xfs/scrub/rtsummary.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/xfs/scrub/rtsummary.c +1 -1
@@ -358,7 +358,7 @@ xchk_rtsummary(
 		 * EFSCORRUPTED means the rtbitmap is corrupt, which is an xref
 		 * error since we're checking the summary file.
 		 */
-		xchk_ip_set_corrupt(sc, rbmip);
+		xchk_ino_xref_set_corrupt(sc, rbmip->i_ino);
 		return 0;
 	}
 	if (error)
author maintainer
#5 lore raw

PATCH 5/101 PCI: host-generic: Fix NULL pointer dereference on 32-bit CAM systems

In reply to Greg Kroah-Hartman

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

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

From: Steffen Persvold <[email protected]>

commit 008cb88edb41f3c7c8e0ed763ff9f26719830984 upstream.

On 32-bit systems the config space is too large to ioremap in one go, so pci_ecam_create() maps each bus segment separately and relies on the ->add_bus callback (pci_ecam_add_bus) to populate the per-bus mapping in cfg->winp[]. pci_ecam_map_bus() then uses that mapping as the base for every config access.

The generic ECAM ops (pci_generic_ecam_ops) already provide the ->add_bus and ->remove_bus callbacks, but the CAM (legacy) ops in pci-host-generic.c do not. As a result, on a 32-bit host using "pci-host-cam-generic" the per-bus mapping is never set up and the first config read dereferences a NULL base, crashing during bus enumeration:

 Unable to handle kernel NULL pointer dereference at virtual address 00000800
 Oops [#1]
 CPU: 0 PID: 1 Comm: swapper Not tainted 6.9.7+ #43
 Hardware name: Digilent Nexys-Video-A7 RV32 (DT)
 epc : pci_generic_config_read+0x40/0xb0
  ra : pci_generic_config_read+0x2c/0xb0
 [<c038db9c>] pci_generic_config_read+0x40/0xb0
 [<c038da04>] pci_bus_read_config_dword+0x50/0xb0
 [<c0391e94>] pci_bus_generic_read_dev_vendor_id+0x3c/0x1ec
 [<c039245c>] pci_scan_single_device+0xa4/0x11c
 [<c0392570>] pci_scan_slot+0x9c/0x23c
 [<c039388c>] pci_scan_child_bus_extend+0x58/0x2f4
 [<c0393db0>] pci_scan_root_bus_bridge+0x64/0xe8
 [<c0393e54>] pci_host_probe+0x20/0xc8
 [<c03bc6f4>] pci_host_common_probe+0x144/0x1e4

Fix this by giving the CAM ops the same ->add_bus/->remove_bus callbacks. Since pci_ecam_add_bus() and pci_ecam_remove_bus() are static to ecam.c, move the CAM ops definition there as pci_generic_cam_ops (mirroring pci_generic_ecam_ops) and export it for pci-host-generic.c to reference.

Fixes 8fe55ef23387 ("PCI: Dynamically map ECAM regions")
[mani: removed timestamp from log]
---
 drivers/pci/controller/pci-host-generic.c |   11 +----------
 drivers/pci/ecam.c                        |   13 +++++++++++++
 include/linux/pci-ecam.h                  |    3 +++
 3 files changed, 17 insertions(+), 10 deletions(-)
drivers/pci/controller/pci-host-generic.c +1 -10
@@ -16,15 +16,6 @@
 
 #include "pci-host-common.h"
 
-static const struct pci_ecam_ops gen_pci_cfg_cam_bus_ops = {
-	.bus_shift	= 16,
-	.pci_ops	= {
-		.map_bus	= pci_ecam_map_bus,
-		.read		= pci_generic_config_read,
-		.write		= pci_generic_config_write,
-	}
-};
-
 static bool pci_dw_valid_device(struct pci_bus *bus, unsigned int devfn)
 {
 	struct pci_config_window *cfg = bus->sysdata;
@@ -60,7 +51,7 @@ static const struct pci_ecam_ops pci_dw_
 
 static const struct of_device_id gen_pci_of_match[] = {
 	{ .compatible = "pci-host-cam-generic",
-	  .data = &gen_pci_cfg_cam_bus_ops },
+	  .data = &pci_generic_cam_ops },
 
 	{ .compatible = "pci-host-ecam-generic",
 	  .data = &pci_generic_ecam_ops },
drivers/pci/ecam.c +13 -0
@@ -208,6 +208,19 @@ const struct pci_ecam_ops pci_generic_ec
 };
 EXPORT_SYMBOL_GPL(pci_generic_ecam_ops);
 
+/* CAM ops */
+const struct pci_ecam_ops pci_generic_cam_ops = {
+	.bus_shift	= 16,
+	.pci_ops	= {
+		.add_bus	= pci_ecam_add_bus,
+		.remove_bus	= pci_ecam_remove_bus,
+		.map_bus	= pci_ecam_map_bus,
+		.read		= pci_generic_config_read,
+		.write		= pci_generic_config_write,
+	}
+};
+EXPORT_SYMBOL_GPL(pci_generic_cam_ops);
+
 #if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)
 /* ECAM ops for 32-bit access only (non-compliant) */
 const struct pci_ecam_ops pci_32b_ops = {
include/linux/pci-ecam.h +3 -0
@@ -81,6 +81,9 @@ void __iomem *pci_ecam_map_bus(struct pc
 /* default ECAM ops */
 extern const struct pci_ecam_ops pci_generic_ecam_ops;
 
+/* default CAM ops */
+extern const struct pci_ecam_ops pci_generic_cam_ops;
+
 #if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)
 extern const struct pci_ecam_ops pci_32b_ops;	/* 32-bit accesses only */
 extern const struct pci_ecam_ops pci_32b_read_ops; /* 32-bit read only */
author maintainer
#6 lore raw

PATCH 6/101 Bluetooth: RFCOMM: take rfcomm_mutex for the deferred setup accept

In reply to Greg Kroah-Hartman

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

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

From: Ali Ahmet Memis <[email protected]>

commit 43a556b2fd43f2df6dded59c2e26560a27874c24 upstream.

rfcomm_sock_recvmsg() completes a deferred setup by calling rfcomm_dlc_accept() without holding any RFCOMM lock:

	if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) {
		rfcomm_dlc_accept(d);
		return 0;
	}

and rfcomm_dlc_accept() dereferences the session on its first line:

	struct sock *sk = d->session->sock->sk;

Every other path that touches d->session runs under rfcomm_mutex:
rfcomm_dlc_open(), rfcomm_dlc_close(), rfcomm_dlc_exists(), rfcomm_dlc_send_rpn(), and the RFCOMM thread through rfcomm_process_sessions(). rfcomm_connect_ind() is even documented as "called under rfcomm_lock()". This call site is the only one that skips it.

The RFCOMM_DEFER_SETUP bit looks like it serialises the accept against teardown, since __rfcomm_dlc_close() returns early when it wins the test_and_clear. But rfcomm_recv_disc() forces the state first:

	d->state = BT_CLOSED;
	__rfcomm_dlc_close(d, err);

and the early return only covers BT_CONNECT, BT_CONFIG, BT_OPEN and BT_CONNECT2. With the state already BT_CLOSED that switch does not match, the bit is never consulted, and __rfcomm_dlc_close() falls through to rfcomm_dlc_unlink(), which sets d->session = NULL.

So a remote DISC on a deferred dlc clears the session while leaving RFCOMM_DEFER_SETUP set. The next recvmsg() then passes the test_and_clear and dereferences a NULL session. No timing window is
needed: once the DISC has been processed, the dereference is unconditional.

Give rfcomm_dlc_accept() the same shape as rfcomm_dlc_open() and rfcomm_dlc_close(): an exported wrapper that takes rfcomm_mutex and re-checks the session, around a __rfcomm_dlc_accept() that the two in-core callers, which already hold the mutex, keep using.

Reproduced on a KASAN + PROVE_LOCKING kernel with a BR/EDR peer emulated over /dev/vhci: the peer brings up an ACL link, opens L2CAP on the RFCOMM PSM, starts a session, opens a dlc on a channel bound with BT_DEFER_SETUP, and sends DISC after the socket is accepted. recv() on the accepted socket then hits:

  Oops: general protection fault
  KASAN: null-ptr-deref in range [0x0000000000000010-0x0000000000000017]
  RIP: 0010:rfcomm_dlc_accept+0x54/0x350
  Call Trace:
    rfcomm_sock_recvmsg+0x1cd/0x230
    sock_recvmsg+0x166/0x1c0
    __sys_recvfrom+0x20d/0x300

0x10 is the offset of sock in struct rfcomm_session. With this patch the same run completes with recv() returning 0 and no report, and lockdep stays quiet, confirming rfcomm_mutex is still taken before lock_sock on this path as it is on the thread side.

Fixes bb23c0ab8246 ("Bluetooth: Add support for deferring RFCOMM connection setup")
---
 net/bluetooth/rfcomm/core.c |   24 +++++++++++++++++++++---
 1 file changed, 21 insertions(+), 3 deletions(-)
net/bluetooth/rfcomm/core.c +21 -3
@@ -1334,7 +1334,10 @@ static struct rfcomm_session *rfcomm_rec
 	return s;
 }
 
-void rfcomm_dlc_accept(struct rfcomm_dlc *d)
+/* Must be called with rfcomm_mutex held, so that the session cannot be
+ * unlinked from under us.
+ */
+static void __rfcomm_dlc_accept(struct rfcomm_dlc *d)
 {
 	struct sock *sk = d->session->sock->sk;
 	struct l2cap_conn *conn = l2cap_pi(sk)->chan->conn;
@@ -1356,6 +1359,21 @@ void rfcomm_dlc_accept(struct rfcomm_dlc
 	rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig);
 }
 
+void rfcomm_dlc_accept(struct rfcomm_dlc *d)
+{
+	rfcomm_lock();
+
+	/* rfcomm_recv_disc() sets the dlc state to BT_CLOSED before calling
+	 * __rfcomm_dlc_close(), so the RFCOMM_DEFER_SETUP handshake there is
+	 * skipped and the session can already be unlinked by the time the
+	 * deferred accept runs from rfcomm_sock_recvmsg().
+	 */
+	if (d->session)
+		__rfcomm_dlc_accept(d);
+
+	rfcomm_unlock();
+}
+
 static void rfcomm_check_accept(struct rfcomm_dlc *d)
 {
 	if (rfcomm_check_security(d)) {
@@ -1368,7 +1386,7 @@ static void rfcomm_check_accept(struct r
 			d->state_change(d, 0);
 			rfcomm_dlc_unlock(d);
 		} else
-			rfcomm_dlc_accept(d);
+			__rfcomm_dlc_accept(d);
 	} else {
 		set_bit(RFCOMM_AUTH_PENDING, &d->flags);
 		rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
@@ -1956,7 +1974,7 @@ static void rfcomm_process_dlcs(struct r
 					d->state_change(d, 0);
 					rfcomm_dlc_unlock(d);
 				} else
-					rfcomm_dlc_accept(d);
+					__rfcomm_dlc_accept(d);
 			}
 			continue;
 		} else if (test_and_clear_bit(RFCOMM_AUTH_REJECT, &d->flags)) {
author maintainer
#7 lore raw

PATCH 7/101 iommu/tegra241-cmdqv: Fix CMD_SYNC use-after-free on teardown

In reply to Greg Kroah-Hartman

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

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

From: Shameer Kolothum <[email protected]>

commit 9ff145a25c5c8a26b06ef7cf558fb536b18bba6d upstream.

arm_smmu_impl_remove() is registered as a devres action in arm_smmu_impl_probe(), before arm_smmu_init_queues() allocates smmu->cmdq.q.base. On a devres unwind, whether a failed probe or an unbind, the queue is freed first and arm_smmu_impl_remove() then runs tegra241_cmdqv_remove_vintf(), whose VINTF deinit issues a CMD_SYNC on the freed memory.

Observed during testing with a QEMU hack that makes the VCMDQ fail to enable, so the impl reset fails and probe aborts into the devres unwind:

platform NVDA200C:00: tegra241_cmdqv: VINTF0: VCMDQ0/LVCMDQ0: failed to enable, STATUS=0x00000000 platform NVDA200C:00: tegra241_cmdqv: VINTF0: VCMDQ0/LVCMDQ0: GERRORN=0x0, GERROR=0x4, CONS=0x0 platform NVDA200C:00: tegra241_cmdqv: VINTF0: VCMDQ0/LVCMDQ0: uncleared error detected, resetting arm-smmu-v3 arm-smmu-v3.0.auto: failed to reset impl
arm-smmu-v3 arm-smmu-v3.0.auto: probe with driver arm-smmu-v3 failed with error -110

 Unable to handle kernel paging request at virtual address ffff8000891e0098
 ...
 Internal error: Oops: 0000000096000047 [#1] SMP
 ...
 Call trace:
  arm_smmu_cmdq_issue_cmdlist+0x320/0x6fc (P)
  tegra241_vcmdq_hw_deinit+0x98/0x168
  tegra241_vintf_hw_deinit+0x5c/0x1b0
  tegra241_cmdqv_remove_vintf+0x34/0xec
  tegra241_cmdqv_remove+0x40/0x9c
  arm_smmu_impl_remove+0x20/0x30
  devm_action_release+0x14/0x20
  devres_release_all+0xa8/0x110
  device_unbind_cleanup+0x18/0x84
  really_probe+0x1f0/0x29c

Drop the VINTF deinit from tegra241_cmdqv_remove_vintf() so the unwind no longer touches the freed queue. Quiesce the VINTFs earlier instead. Add a device_disable() impl op and run it from arm_smmu_disable_action() while the CMDQ is still up. That handles a live unbind. A failed reset is already handled because tegra241_vintf_hw_init() deinits the VINTF on its own error path. tegra241_cmdqv_remove_vintf() is also used by the iommufd viommu destroy path, so quiesce there too.

Fixes 4dc0d12474f9 ("iommu/tegra241-cmdqv: Add user-space use support")
---
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c    |    2 ++
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h    |    1 +
 drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c |   15 +++++++++++++--
 3 files changed, 16 insertions(+), 2 deletions(-)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +2 -0
@@ -5591,6 +5591,8 @@ static void arm_smmu_device_shutdown(str
 {
 	struct arm_smmu_device *smmu = platform_get_drvdata(pdev);
 
+	if (smmu->impl_ops && smmu->impl_ops->device_disable)
+		smmu->impl_ops->device_disable(smmu);
 	arm_smmu_device_disable(smmu);
 }
 
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +1 -0
@@ -804,6 +804,7 @@ struct arm_smmu_strtab_cfg {
 
 struct arm_smmu_impl_ops {
 	int (*device_reset)(struct arm_smmu_device *smmu);
+	void (*device_disable)(struct arm_smmu_device *smmu);
 	void (*device_remove)(struct arm_smmu_device *smmu);
 	int (*init_structures)(struct arm_smmu_device *smmu);
 	struct arm_smmu_cmdq *(*get_secondary_cmdq)(
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c +13 -2
@@ -761,8 +761,6 @@ static void tegra241_cmdqv_remove_vintf(
 	struct tegra241_vintf *vintf = cmdqv->vintfs[idx];
 	u16 lidx;
 
-	tegra241_vintf_hw_deinit(vintf);
-
 	/* Remove LVCMDQ resources */
 	for (lidx = 0; lidx < vintf->cmdqv->num_lvcmdqs_per_vintf; lidx++)
 		if (vintf->lvcmdqs[lidx])
@@ -779,6 +777,17 @@ static void tegra241_cmdqv_remove_vintf(
 	}
 }
 
+static void tegra241_cmdqv_hw_disable(struct arm_smmu_device *smmu)
+{
+	struct tegra241_cmdqv *cmdqv =
+		container_of(smmu, struct tegra241_cmdqv, smmu);
+	u16 idx;
+
+	for (idx = 0; idx < cmdqv->num_vintfs; idx++)
+		if (cmdqv->vintfs[idx])
+			tegra241_vintf_hw_deinit(cmdqv->vintfs[idx]);
+}
+
 static void tegra241_cmdqv_remove(struct arm_smmu_device *smmu)
 {
 	struct tegra241_cmdqv *cmdqv =
@@ -844,6 +853,7 @@ static struct arm_smmu_impl_ops tegra241
 	/* For in-kernel use */
 	.get_secondary_cmdq = tegra241_cmdqv_get_cmdq,
 	.device_reset = tegra241_cmdqv_hw_reset,
+	.device_disable = tegra241_cmdqv_hw_disable,
 	.device_remove = tegra241_cmdqv_remove,
 	/* For user-space use */
 	.hw_info = tegra241_cmdqv_hw_info,
@@ -1152,6 +1162,7 @@ static void tegra241_cmdqv_destroy_vintf
 	if (vintf->mmap_offset)
 		iommufd_viommu_destroy_mmap(&vintf->vsmmu.core,
 					    vintf->mmap_offset);
+	tegra241_vintf_hw_deinit(vintf);
 	tegra241_cmdqv_remove_vintf(vintf->cmdqv, vintf->idx);
 }
 
author maintainer
#8 lore raw

PATCH 8/101 iommu/iommufd: Fix NULL pointer deref in iommufd_ioas_change_process when racing with iopt_map_file_pages

In reply to Greg Kroah-Hartman

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

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

From: Peiyang He <[email protected]>

commit d616de490ec0242dcf78f02f1adf7baa035c4d0d upstream.

iommufd_ioas_change_process() iterates every IOAS area while only holding every IOAS iova_rwsem, so it assumes every area has a non-NULL pages pointer. That assumption can be false when it runs concurrently with iopt_map_file_pages().

iopt_map_pages() executes in two phases. It first creates the area and inserts it into the interval tree under iova_rwsem, with area->pages still NULL. It then drops iova_rwsem and later fills area->pages under domains_rwsem. This leaves a window between area creation and area->pages fill where a concurrent iommufd_ioas_change_process() can observe the area and dereference a NULL area->pages pointer, leading to a NULL pointer dereference:

BUG: kernel NULL pointer dereference, address: 00000000000000c0
#PF: supervisor read access in kernel mode
#PF: error_code(0x0000) - not-present page
PGD 4b655067 P4D 4b655067 PUD 0
Oops: Oops: 0000 [#1] SMP NOPTI
CPU: 0 UID: 0 PID: 11841 Comm: syz.1.628 Not tainted 7.1.0 #3 PREEMPT(full)
Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
RIP: 0010:iommufd_ioas_change_process+0x419/0xd50 drivers/iommu/iommufd/ioas.c:538
Code: 48 89 c3 48 85 c0 0f 84 cc 00 00 00 e8 10 f5 cb fd 48 8d 7b 68 e8 a7 b5 eb fd 48 8b 6b 68 48 8d bd c0 00 00 00 e8 17 b2 eb fd <8b> ad c0 00 00 00 bf 01 00 00 00 89 ee e8 85 ef cb fd 83 fd 01 74
RSP: 0018:ffffc90015c17d28 EFLAGS: 00010246
RAX: ffff8880186d5328 RBX: ffff88801d25e240 RCX: 0000000080000000
RDX: 00000000000002d7 RSI: ffffffff83ba9e10 RDI: 00000000000000c0
RBP: 0000000000000000 R08: ffffffff8e781eb8 R09: 0000000000000000
R10: 00000000000000c0 R11: ffffffff83ba9e29 R12: ffff88802e216008
R13: ffff88802e216000 R14: 0000000000000001 R15: 0000000000000000
FS:  00007f4aea3f66c0(0000) GS:ffff8880b1fa1000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00000000000000c0 CR3: 000000004b75c000 CR4: 0000000000350ef0
Call Trace:
 <TASK>
 iommufd_fops_ioctl+0x287/0x400 drivers/iommu/iommufd/main.c:533
 vfs_ioctl fs/ioctl.c:51 [inline]
 __do_sys_ioctl fs/ioctl.c:597 [inline]
 __se_sys_ioctl fs/ioctl.c:583 [inline]
 __x64_sys_ioctl+0x120/0x170 fs/ioctl.c:583
 x64_sys_call+0x1092/0x1fb0 arch/x86/include/generated/asm/syscalls_64.h:17
 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
 do_syscall_64+0x10a/0x680 arch/x86/entry/syscall_64.c:94
 entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f4aec1a82bd
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 b0 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007f4aea3f6018 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
RAX: ffffffffffffffda RBX: 00007f4aec436090 RCX: 00007f4aec1a82bd
RDX: 0000200000000180 RSI: 0000000000003b92 RDI: 0000000000000003
RBP: 00007f4aec250295 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007f4aec436128 R14: 00007f4aec436090 R15: 00007ffd04ef23e0
 </TASK>
Modules linked in:
CR2: 00000000000000c0
---[ end trace 0000000000000000 ]---
RIP: 0010:iommufd_ioas_change_process+0x419/0xd50 drivers/iommu/iommufd/ioas.c:538
Code: 48 89 c3 48 85 c0 0f 84 cc 00 00 00 e8 10 f5 cb fd 48 8d 7b 68 e8 a7 b5 eb fd 48 8b 6b 68 48 8d bd c0 00 00 00 e8 17 b2 eb fd <8b> ad c0 00 00 00 bf 01 00 00 00 89 ee e8 85 ef cb fd 83 fd 01 74
RSP: 0018:ffffc90015c17d28 EFLAGS: 00010246
RAX: ffff8880186d5328 RBX: ffff88801d25e240 RCX: 0000000080000000
RDX: 00000000000002d7 RSI: ffffffff83ba9e10 RDI: 00000000000000c0
RBP: 0000000000000000 R08: ffffffff8e781eb8 R09: 0000000000000000
R10: 00000000000000c0 R11: ffffffff83ba9e29 R12: ffff88802e216008
R13: ffff88802e216000 R14: 0000000000000001 R15: 0000000000000000
FS:  00007f4aea3f66c0(0000) GS:ffff8880b1fa1000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00000000000000c0 CR3: 000000004b75c000 CR4: 0000000000350ef0
----------------
Code disassembly (best guess):
   0:	48 89 c3             	mov    %rax,%rbx
   3:	48 85 c0             	test   %rax,%rax
   6:	0f 84 cc 00 00 00    	je     0xd8
   c:	e8 10 f5 cb fd       	call   0xfdcbf521
  11:	48 8d 7b 68          	lea    0x68(%rbx),%rdi
  15:	e8 a7 b5 eb fd       	call   0xfdebb5c1
  1a:	48 8b 6b 68          	mov    0x68(%rbx),%rbp
  1e:	48 8d bd c0 00 00 00 	lea    0xc0(%rbp),%rdi
  25:	e8 17 b2 eb fd       	call   0xfdebb241
* 2a:	8b ad c0 00 00 00    	mov    0xc0(%rbp),%ebp <-- trapping instruction
  30:	bf 01 00 00 00       	mov    $0x1,%edi
  35:	89 ee                	mov    %ebp,%esi
  37:	e8 85 ef cb fd       	call   0xfdcbefc1
  3c:	83 fd 01             	cmp    $0x1,%ebp
  3f:	74                   	.byte 0x74

Fix by mirroring the handling logic in iopt_unmap_iova_range(), i.e. return -EBUSY if area->pages is NULL.

---
 drivers/iommu/iommufd/ioas.c |    4 ++++
 1 file changed, 4 insertions(+)
drivers/iommu/iommufd/ioas.c +4 -0
@@ -535,6 +535,10 @@ int iommufd_ioas_change_process(struct i
 		return rc;
 
 	for_each_ioas_area(&ioas_list, index, ioas, area)  {
+		if (!area->pages) {
+			rc = -EBUSY;
+			goto out;
+		}
 		if (area->pages->type != IOPT_ADDRESS_FILE) {
 			rc = -EINVAL;
 			goto out;
author maintainer
#9 lore raw

PATCH 9/101 ALSA: FCP: Use a private URB for the notification endpoint

In reply to Greg Kroah-Hartman

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

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

From: Geoffrey D. Bennett <[email protected]>

commit 918b8d231c571c50a00efe92ffc8404a537a0490 upstream.

fcp_init_notify() used mixer->urb, which snd_usb_mixer_status_create() allocates for the optional UAC2 status interrupt endpoint and mixer.c kills, resubmits and frees. On a device with that endpoint, fcp_init_notify()'s "already set up" early return fires on the status URB and returns success without doing anything. No FCP notification URB is submitted, and cmd_done is left zeroed because it is initialised past that early return and nowhere else. fcp_init() then issues init1_opcode and wait_for_completion_timeout() would crash adding to the zeroed wait.head. fcp_cleanup_urb() would also kill and free mixer.c's status URB.

Use a separate URB in fcp_data, and initialise cmd_done in fcp_init_private() where fcp_data is allocated. fcp_init_notify() is reached again after suspend via fcp_reinit(), and the URB kill path in fcp_notify() completes cmd_done, leaving a stale count that would satisfy the next command's wait before the device ACKs. Use reinit_completion() to clear it.

Fixes 46757a3e7d50 ("ALSA: FCP: Add Focusrite Control Protocol driver")
Assisted-by Claude:claude-opus-5
---
 sound/usb/fcp.c |   38 +++++++++++++++++++++-----------------
 1 file changed, 21 insertions(+), 17 deletions(-)
sound/usb/fcp.c +21 -17
@@ -82,6 +82,7 @@ struct fcp_data {
 	struct mutex mutex;         /* serialise access to the device */
 	struct completion cmd_done; /* wait for command completion */
 	struct file *file;          /* hwdep file */
+	struct urb *urb;            /* FCP notification endpoint */
 
 	struct fcp_notify notify;
 
@@ -190,7 +191,7 @@ static int fcp_usb(struct usb_mixer_inte
 	const int max_retries = 5;
 	int err;
 
-	if (!mixer->urb)
+	if (!private->urb)
 		return -ENODEV;
 
 	struct fcp_usb_packet *req __free(kfree) = NULL;
@@ -305,7 +306,7 @@ static int fcp_reinit(struct usb_mixer_i
 {
 	struct fcp_data *private = mixer->private_data;
 
-	if (mixer->urb)
+	if (private->urb)
 		return 0;
 
 	void *step0_resp __free(kfree) =
@@ -901,13 +902,15 @@ static int fcp_hwdep_init(struct usb_mix
 
 static void fcp_cleanup_urb(struct usb_mixer_interface *mixer)
 {
-	if (!mixer->urb)
+	struct fcp_data *private = mixer->private_data;
+
+	if (!private->urb)
 		return;
 
-	usb_kill_urb(mixer->urb);
-	kfree(mixer->urb->transfer_buffer);
-	usb_free_urb(mixer->urb);
-	mixer->urb = NULL;
+	usb_kill_urb(private->urb);
+	kfree(private->urb->transfer_buffer);
+	usb_free_urb(private->urb);
+	private->urb = NULL;
 }
 
 static void fcp_private_free(struct usb_mixer_interface *mixer)
@@ -978,37 +981,37 @@ static int fcp_init_notify(struct usb_mi
 	int err;
 
 	/* Already set up */
-	if (mixer->urb)
+	if (private->urb)
 		return 0;
 
 	if (usb_pipe_type_check(dev, pipe))
 		return -EINVAL;
 
-	mixer->urb = usb_alloc_urb(0, GFP_KERNEL);
-	if (!mixer->urb)
+	private->urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!private->urb)
 		return -ENOMEM;
 
 	transfer_buffer = kmalloc(private->wMaxPacketSize, GFP_KERNEL);
 	if (!transfer_buffer) {
-		usb_free_urb(mixer->urb);
-		mixer->urb = NULL;
+		usb_free_urb(private->urb);
+		private->urb = NULL;
 		return -ENOMEM;
 	}
 
-	usb_fill_int_urb(mixer->urb, dev, pipe,
+	usb_fill_int_urb(private->urb, dev, pipe,
 			 transfer_buffer, private->wMaxPacketSize,
 			 fcp_notify, mixer, private->bInterval);
 
-	init_completion(&private->cmd_done);
+	reinit_completion(&private->cmd_done);
 
-	err = usb_submit_urb(mixer->urb, GFP_KERNEL);
+	err = usb_submit_urb(private->urb, GFP_KERNEL);
 	if (err) {
 		usb_audio_err(mixer->chip,
 			      "%s: usb_submit_urb failed: %d\n",
 			      __func__, err);
 		kfree(transfer_buffer);
-		usb_free_urb(mixer->urb);
-		mixer->urb = NULL;
+		usb_free_urb(private->urb);
+		private->urb = NULL;
 	}
 
 	return err;
@@ -1059,6 +1062,7 @@ static int fcp_init_private(struct usb_m
 		return -ENOMEM;
 
 	mutex_init(&private->mutex);
+	init_completion(&private->cmd_done);
 	init_waitqueue_head(&private->notify.queue);
 	spin_lock_init(&private->notify.lock);
 
author maintainer
#10 lore raw

PATCH 10/101 ALSA: scarlett2: Use a private URB for the notification endpoint

In reply to Greg Kroah-Hartman

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

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

From: Geoffrey D. Bennett <[email protected]>

commit cd17d6ff7b7d2b1dd9bcc80ae7b4a83773f918c6 upstream.

scarlett2_init_notify() used mixer->urb, which snd_usb_mixer_status_create() allocates for the UAC2 status interrupt endpoint and mixer.c manages. On a device with that endpoint, the "already in use" check fires on the status URB and returns 0 for success without doing anything. No notification URB is submitted, and cmd_done is left zeroed because it is initialised past that check and nowhere else. scarlett2_usb_init() then issues SCARLETT2_USB_INIT_1 and wait_for_completion_timeout() would crash adding to the zeroed wait.head.

Use a separate URB in scarlett2_data, as done for FCP, and initialise cmd_done in scarlett2_init_private(). mixer.c was also freeing the URB in snd_usb_mixer_free() and resubmitting it in snd_usb_mixer_activate(), so scarlett2 must now do both: add scarlett2_cleanup_urb(), called from private_free and private_suspend, and a private_resume callback to re-establish the URB after resume. scarlett2_init_notify() is reached from there, and the URB kill path in scarlett2_notify() completes cmd_done, leaving a stale count that would satisfy the next command's wait before the device ACKs. Use reinit_completion() to clear it.

Also free the URB if the transfer buffer allocation fails, and both if usb_submit_urb() fails. Move scarlett2_init_notify() up next to scarlett2_cleanup_urb() so scarlett2_init_private() can reference it without a forward declaration.

Fixes 1b65088958ca ("ALSA: scarlett2: Implement handling of the ACK notification")
Assisted-by Claude:claude-opus-5
---
 sound/usb/mixer.c           |    6 ++
 sound/usb/mixer.h           |    2 
 sound/usb/mixer_scarlett2.c |   98 ++++++++++++++++++++++++++++----------------
 3 files changed, 71 insertions(+), 35 deletions(-)
sound/usb/mixer.c +6 -0
@@ -3935,6 +3935,12 @@ int snd_usb_mixer_resume(struct usb_mixe
 	struct usb_mixer_elem_list *list;
 	int id, err;
 
+	if (mixer->private_resume) {
+		err = mixer->private_resume(mixer);
+		if (err < 0)
+			return err;
+	}
+
 	/* restore cached mixer values */
 	for (id = 0; id < MAX_ID_ELEMS; id++) {
 		for_each_mixer_elem(list, mixer, id) {
sound/usb/mixer.h +2 -0
@@ -18,6 +18,7 @@ struct usb_mixer_interface {
 	struct usb_host_interface *hostif;
 	struct list_head list;
 	unsigned int ignore_ctl_error;
+	/* UAC2 status interrupt endpoint; owned by mixer.c */
 	struct urb *urb;
 	/* array[MAX_ID_ELEMS], indexed by unit id */
 	struct usb_mixer_elem_list **id_elems;
@@ -42,6 +43,7 @@ struct usb_mixer_interface {
 	void *private_data;
 	void (*private_free)(struct usb_mixer_interface *mixer);
 	void (*private_suspend)(struct usb_mixer_interface *mixer);
+	int (*private_resume)(struct usb_mixer_interface *mixer);
 };
 
 #define MAX_CHANNELS	64	/* max logical channels */
sound/usb/mixer_scarlett2.c +63 -35

139 lines not on this page · raw message

author maintainer
#11 lore raw

PATCH 11/101 rndis_host: add overflow check in rndis_rx_fixup()

In reply to Greg Kroah-Hartman

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

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

From: Griffin Kroah-Hartman <[email protected]>

commit 965a251f23ff69cfb4486974d4532e9bb551c7fc upstream.

Add an overflow check to ensure that data_offset + data_len + 8 does not wrap, which would enable an OOB read of the USB data buffer.

---
 drivers/net/usb/rndis_host.c |    6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
drivers/net/usb/rndis_host.c +5 -1
@@ -14,6 +14,7 @@
 #include <linux/usb/cdc.h>
 #include <linux/usb/usbnet.h>
 #include <linux/usb/rndis_host.h>
+#include <linux/overflow.h>
 
 
 /*
@@ -506,6 +507,7 @@ int rndis_rx_fixup(struct usbnet *dev, s
 		struct rndis_data_hdr	*hdr = (void *)skb->data;
 		struct sk_buff		*skb2;
 		u32			msg_type, msg_len, data_offset, data_len;
+		u32			overflow_check;
 
 		msg_type = le32_to_cpu(hdr->msg_type);
 		msg_len = le32_to_cpu(hdr->msg_len);
@@ -514,7 +516,9 @@ int rndis_rx_fixup(struct usbnet *dev, s
 
 		/* don't choke if we see oob, per-packet data, etc */
 		if (unlikely(msg_type != RNDIS_MSG_PACKET || skb->len < msg_len
-				|| (data_offset + data_len + 8) > msg_len)) {
+				|| (data_offset + data_len + 8) > msg_len
+				|| check_add_overflow(data_offset, data_len, &overflow_check)
+				|| check_add_overflow(overflow_check, 8, &overflow_check))) {
 			dev->net->stats.rx_frame_errors++;
 			netdev_dbg(dev->net, "bad rndis message %d/%d/%d/%d, len %d\n",
 				   le32_to_cpu(hdr->msg_type),
author maintainer
#12 lore raw

PATCH 12/101 nvmet: fix NULL pointer dereference in nvmet_execute_identify_nslist()

In reply to Greg Kroah-Hartman

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

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

From: Guixin Liu <[email protected]>

commit 79aba4c9403419d822972d2851f2a96a2c0531cf upstream.

When a host issues an Identify command with CNS 07h (Active Namespace ID List for a specific I/O Command Set), nvmet_execute_identify_nslist() is called with match_css set. The command-set filter dereferences req->ns, but this handler never calls nvmet_req_find_ns(), so req->ns is always NULL (nvmet_req_init() resets it to NULL). As soon as an enabled namespace with an NSID greater than the requested value exists, req->ns->csi dereferences a NULL pointer and oopses.

Besides the crash, the comparison is logically wrong: to filter the list by command set it must test the command set of the namespace being iterated, not a single fixed value. Use the loop variable ns->csi.

Fixes 61c9967cd634 ("nvmet: implement active command set ns list")
---
 drivers/nvme/target/admin-cmd.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/nvme/target/admin-cmd.c +1 -1
@@ -958,7 +958,7 @@ static void nvmet_execute_identify_nslis
 	nvmet_for_each_enabled_ns(&ctrl->subsys->namespaces, idx, ns) {
 		if (ns->nsid <= min_nsid)
 			continue;
-		if (match_css && req->ns->csi != req->cmd->identify.csi)
+		if (match_css && ns->csi != req->cmd->identify.csi)
 			continue;
 		list[i++] = cpu_to_le32(ns->nsid);
 		if (i == buf_size / sizeof(__le32))
author maintainer
#13 lore raw

PATCH 13/101 io_uring/futex: dont mark futex wake requests as inflight

In reply to Greg Kroah-Hartman

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

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

From: Jens Axboe <[email protected]>

commit 73e7019097473fc9f83a334ef2c6ab3343709fef upstream.

Commit 079afb081c42 ("io_uring/futex: mark wait requests as inflight") added inflight tracking to ensure that do_exit() -> io_uring_files_cancel() finds and cancels pending futex waits before the mm goes away, as a private futex wait depends on the mm private futex hash staying alive for the duration of the request. However, as io_futex_prep() is shared between FUTEX_WAIT and FUTEX_WAKE, wake requests got marked as inflight as well.

A futex wake executes fully inline at issue time and never depends on the mm staying alive after completion, hence there's no need to track it. Kill it.

---
 io_uring/futex.c |   11 +++++++++++
 io_uring/futex.h |    1 +
 io_uring/opdef.c |    2 +-
 3 files changed, 13 insertions(+), 1 deletion(-)
io_uring/futex.c +11 -0
@@ -149,6 +149,17 @@ int io_futex_prep(struct io_kiocb *req,
 	    !futex_validate_input(iof->futex_flags, iof->futex_mask))
 		return -EINVAL;
 
+	return 0;
+}
+
+int io_futex_wait_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
+{
+	int ret;
+
+	ret = io_futex_prep(req, sqe);
+	if (unlikely(ret))
+		return ret;
+
 	/* Mark as inflight, so file exit cancelation will find it */
 	io_req_track_inflight(req);
 	return 0;
io_uring/futex.h +1 -0
@@ -3,6 +3,7 @@
 #include "cancel.h"
 
 int io_futex_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe);
+int io_futex_wait_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe);
 int io_futexv_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe);
 int io_futex_wait(struct io_kiocb *req, unsigned int issue_flags);
 int io_futexv_wait(struct io_kiocb *req, unsigned int issue_flags);
io_uring/opdef.c +1 -1
@@ -465,7 +465,7 @@ const struct io_issue_def io_issue_defs[
 	},
 	[IORING_OP_FUTEX_WAIT] = {
 #if defined(CONFIG_FUTEX)
-		.prep			= io_futex_prep,
+		.prep			= io_futex_wait_prep,
 		.issue			= io_futex_wait,
 #else
 		.prep			= io_eopnotsupp_prep,
author maintainer
#14 lore raw

PATCH 14/101 io_uring/futex: only mark private futex waits as inflight

In reply to Greg Kroah-Hartman

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

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

From: Jens Axboe <[email protected]>

commit 4d327bbd1cd2485783553b6b2b401aa475f90dd0 upstream.

Inflight tracking of futex wait requests exists to ensure that do_exit() -> io_uring_files_cancel() cancels them before the mm goes away, as a private futex wait depends on the mm private futex hash staying alive for the duration of the request.

Shared futexes have no such dependency. A FLAGS_SHARED request always resolves to either an inode based key or an mm-shared key, both of which fail futex_key_is_private() and hence always hash into the global futex hash, whose lifetime isn't tied to the mm.

Only mark vectored futex waits as inflight if the futex is private.

---
 io_uring/futex.c |   17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)
io_uring/futex.c +13 -4
@@ -154,14 +154,16 @@ int io_futex_prep(struct io_kiocb *req,
 
 int io_futex_wait_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
 {
+	struct io_futex *iof = io_kiocb_to_cmd(req, struct io_futex);
 	int ret;
 
 	ret = io_futex_prep(req, sqe);
 	if (unlikely(ret))
 		return ret;
 
-	/* Mark as inflight, so file exit cancelation will find it */
-	io_req_track_inflight(req);
+	/* inflight tracking only needed for mm private hash */
+	if (!(iof->futex_flags & FLAGS_SHARED))
+		io_req_track_inflight(req);
 	return 0;
 }
 
@@ -186,6 +188,7 @@ int io_futexv_prep(struct io_kiocb *req,
 {
 	struct io_futex *iof = io_kiocb_to_cmd(req, struct io_futex);
 	struct io_futexv_data *ifd;
+	unsigned int i;
 	int ret;
 
 	/* No flags or mask supported for waitv */
@@ -210,8 +213,14 @@ int io_futexv_prep(struct io_kiocb *req,
 		return ret;
 	}
 
-	/* Mark as inflight, so file exit cancelation will find it */
-	io_req_track_inflight(req);
+	/* inflight tracking only needed for mm private hash */
+	for (i = 0; i < iof->futex_nr; i++) {
+		if (!(ifd->futexv[i].w.flags & FLAGS_SHARED)) {
+			io_req_track_inflight(req);
+			break;
+		}
+	}
+
 	iof->futexv_unqueued = 0;
 	req->flags |= REQ_F_ASYNC_DATA;
 	req->async_data = ifd;
author maintainer
#15 lore raw

PATCH 15/101 ALSA: dummy: Check card index validity at probe

In reply to Greg Kroah-Hartman

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

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

From: Takashi Iwai <[email protected]>

commit 02442d5fe8ee365a084b055d4fa81a0c1abfc3fd upstream.

snd_dummy_probe() blindly trusts that the given devptr->id value is within the proper card index range. It's OK for the devices the driver itself creates at the module probe time, but if the device is bound manually via sysfs interface, this could be -1 as "none", and this leads to OOB access for index[] and other parameters.

Add a sanity check for the card index and warn/correct it if it's a value out of the range.

---
 sound/drivers/dummy.c |    6 ++++++
 1 file changed, 6 insertions(+)
sound/drivers/dummy.c +6 -0
@@ -1017,6 +1017,12 @@ static int snd_dummy_probe(struct platfo
 	int idx, err;
 	int dev = devptr->id;
 
+	if (dev < 0 || dev >= SNDRV_CARDS) {
+		dev_warn(&devptr->dev,
+			 "Invalid card index %d, using default 0\n", dev);
+		dev = 0;
+	}
+
 	err = snd_devm_card_new(&devptr->dev, index[dev], id[dev], THIS_MODULE,
 				sizeof(struct snd_dummy), &card);
 	if (err < 0)
author maintainer
#16 lore raw

PATCH 16/101 io_uring/cmd: fix iovec leak when the async cmd is not recycled

In reply to Greg Kroah-Hartman

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

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

From: Woraphat Khiaodaeng <[email protected]>

commit bb34ae5da3365699d53a756f4c96b6ea9f8ba0c1 upstream.

An io_async_cmd carries an iovec array in ->vec.iovec, allocated when the vec has to grow and kept across recycling through ctx->cmd_cache. On two paths nothing frees it and io_clean_op()'s kfree(req->async_data) drops the io_async_cmd without it.

io_req_uring_cleanup() clears the async data flags only when io_alloc_cache_put() succeeds, and the cache holds IO_ALLOC_CACHE_MAX == 128 entries, so once it is full the put fails and the vec is left behind. An NVMe passthrough workload gets there without doing anything unusual:
nvme_uring_cmd_io() returns -EIOCBQUEUED, so the io_async_cmd stays attached for the lifetime of the command and the live object count tracks the queue depth. Above 128 the puts start failing.

->cleanup is the last chance to free an inherited vec, since io_req_uring_cleanup() returns early for an io-wq issued command and is not called at all for one completed without ever being issued. But io_clean_op() calls ->cleanup only if REQ_F_NEED_CLEANUP is set, and for uring_cmd that happens only where the vec has to grow, so a command reusing a large enough cached vec never sets it. io_rw_alloc_async() and io_msg_alloc_async() flag an inherited vec for exactly this reason; io_uring_cmd_prep() does not.

Flag an inherited vec in io_uring_cmd_prep(), and free the vec when the cache put fails, as io_req_rw_cleanup() does.

The leak is invisible under KASAN, where io_alloc_cache_vec_kasan() frees the vec unconditionally.

---
 io_uring/uring_cmd.c |    4 ++++
 1 file changed, 4 insertions(+)
io_uring/uring_cmd.c +4 -0
@@ -38,6 +38,8 @@ static void io_req_uring_cleanup(struct
 	if (io_alloc_cache_put(&req->ctx->cmd_cache, ac)) {
 		ioucmd->sqe = NULL;
 		io_req_async_data_clear(req, REQ_F_NEED_CLEANUP);
+	} else {
+		io_vec_free(&ac->vec);
 	}
 }
 
@@ -208,6 +210,8 @@ int io_uring_cmd_prep(struct io_kiocb *r
 	ac = io_uring_alloc_async_data(&req->ctx->cmd_cache, req);
 	if (!ac)
 		return -ENOMEM;
+	if (ac->vec.iovec)
+		req->flags |= REQ_F_NEED_CLEANUP;
 	ioucmd->sqe = sqe;
 	return 0;
 }
author maintainer
#17 lore raw

PATCH 17/101 io_uring/io-wq: fix worker accounting when canceling creation callbacks

In reply to Greg Kroah-Hartman

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

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

From: Vishnu Razdan <[email protected]>

commit 297b5ccea4acacaa47c150f043bce695202afbf1 upstream.

create_worker_cb() reserves an io-wq worker slot only after its task-work callback runs. If the callback is canceled before then, io_worker_cancel_cb() still decrements acct->nr_workers. When an existing worker retires with its creation callback pending, that worker has already decremented the same account's worker count.

The resulting undercount permits worker creation beyond the account's configured limit. On an AST2600 OpenBMC system, an unchanged sensor daemon reached 4,291 threads with the original kernel. With an equivalent downstream fix, 25 passive samples under its normal workload showed 6-9 threads.

Decrement nr_workers only when the canceled callback is not create_worker_cb(). Continuation callbacks still release their reserved slot, and both callback types retain the existing running-count, reference-count, and create-state cleanup.

Fixes 1d5f5ea7cb7d ("io-wq: remove worker to owner tw dependency")
Assisted-by Codex:gpt-5.6-sol
---
 io_uring/io-wq.c |    9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)
io_uring/io-wq.c +6 -3
@@ -211,9 +211,12 @@ static void io_worker_cancel_cb(struct i
 	struct io_wq *wq = worker->wq;
 
 	atomic_dec(&acct->nr_running);
-	raw_spin_lock(&acct->workers_lock);
-	acct->nr_workers--;
-	raw_spin_unlock(&acct->workers_lock);
+	/* create_worker_cb() has not reserved a worker slot yet. */
+	if (worker->create_work.func != create_worker_cb) {
+		raw_spin_lock(&acct->workers_lock);
+		acct->nr_workers--;
+		raw_spin_unlock(&acct->workers_lock);
+	}
 	io_worker_ref_put(wq);
 	clear_bit_unlock(0, &worker->create_state);
 	io_worker_release(worker);
author maintainer
#18 lore raw

PATCH 18/101 io_uring/rsrc: fix folio size overflow in io_vec_fill_bvec()

In reply to Greg Kroah-Hartman

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

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

From: Ali Ahmet Memis <[email protected]>

commit 3f3a6a16bbe8bde76532d9415438f8cdef439e5d upstream.

io_vec_fill_bvec() computes the folio size with a plain int 1:

	unsigned long folio_size = 1 << imu->folio_shift;

imu->folio_shift is unsigned int and comes from folio_shift() of the folio backing the registered buffer, so it can be 32 or more on a 64 bit kernel. Shifting int 1 that far is undefined, and on x86 and arm64 the count is taken modulo 32, so a shift of 34 yields 4 rather than 16G. Every other folio_shift shift in this file already uses 1UL.

The result is that the segment estimate and the fill loop disagree. io_estimate_bvec_size() sizes the bvec array with the real shift:

	max_segs += (iov[i].iov_len >> shift) + 2;

so a 1M iovec on a 16G folio is charged 2 segments, while io_vec_fill_bvec() then walks the same iovec in folio_size chunks of 4 bytes and writes res_bvec[bvec_idx] a quarter of a million times, past the end of the array it was given. src_bvec is advanced once per iteration as well, so imu->bvec is read past its end at the same time. validate_fixed_range() only checks that the range is inside the registered buffer and does not bound the segment count.

Reaching it needs a folio with a shift of at least 32, which means a gigantic hugetlb page: 16G on arm64 with 64K pages, where CONT_PMD_SHIFT is 34 and hugetlb_add_hstate(CONT_PMD_SHIFT - PAGE_SHIFT) registers that size, and likewise on powerpc. x86_64 tops out at 1G, so a shift of 30, which still fits in int and is unaffected.

Use 1UL, as the rest of the file does.

Fixes 9ef4cbbcb4ac ("io_uring: add infra for importing vectored reg buffers")
---
 io_uring/rsrc.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
io_uring/rsrc.c +1 -1
@@ -1332,7 +1332,7 @@ static int io_vec_fill_bvec(int ddir, st
 				struct iovec *iovec, unsigned nr_iovs,
 				struct iou_vec *vec)
 {
-	unsigned long folio_size = 1 << imu->folio_shift;
+	unsigned long folio_size = 1UL << imu->folio_shift;
 	unsigned long folio_mask = folio_size - 1;
 	struct bio_vec *res_bvec = vec->bvec;
 	size_t total_len = 0;
author maintainer
#19 lore raw

PATCH 19/101 io_uring/uring_cmd: dont skip completion for a synchronous multishot cmd

In reply to Greg Kroah-Hartman

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

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

From: Jens Axboe <[email protected]>

commit 360941242f09437a1e07cbed9b5a96663ffeabb6 upstream.

If IORING_URING_CMD_MULTISHOT is set, io_uring_cmd() treats any non-negative return from ->uring_cmd() as the driver having taken ownership of the request and returns IOU_ISSUE_SKIP_COMPLETE. But nothing guarantees that the driver did so, and any handler that just completes the command inline and returns 0 or a positive result then leaves the request orphaned, leaking the io_kiocb, the async data, and the file reference.

The special case isn't needed. ublk returns -EIOCBQUEUED for the multishot fetch command, which is passed through as-is, and the poll driven socket timestamp command returns -EAGAIN. Kill it, a multishot handler that wants to hang on to the request must return -EIOCBQUEUED or -EAGAIN like any other command.

---
 io_uring/uring_cmd.c |    4 ----
 1 file changed, 4 deletions(-)
io_uring/uring_cmd.c +0 -4
@@ -273,10 +273,6 @@ int io_uring_cmd(struct io_kiocb *req, u
 	}
 
 	ret = file->f_op->uring_cmd(ioucmd, issue_flags);
-	if (ioucmd->flags & IORING_URING_CMD_MULTISHOT) {
-		if (ret >= 0)
-			return IOU_ISSUE_SKIP_COMPLETE;
-	}
 	if (ret == -EAGAIN) {
 		ioucmd->flags |= IORING_URING_CMD_REISSUE;
 		return ret;
author maintainer
#20 lore raw

PATCH 20/101 io_uring: defer eventfd signaling when queued from a wakeup handler

In reply to Greg Kroah-Hartman

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

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

From: Jens Axboe <[email protected]>

commit cd305ee3633a45fcf5f3a5d83f99f3cb77d87b6e upstream.

io_req_local_work_add() signals the CQ ring eventfd inline when it is the one to push the first entry onto ->work_list. For DEFER_TASKRUN rings that add is frequently done from a waitqueue wakeup handler, where an arbitrary waitqueue lock is held.

eventfd_signal_mask() only refuses to recurse when current->in_eventfd is set, but that bit is set by eventfd_signal_mask() itself. If the wake chain starts somewhere else, signal goes out inline and can feed back into epoll.

Add IOU_F_TWQ_IN_WAKE, set it on the task_work add done from the three waitqueue callbacks, and use it to force io_eventfd_signal() down the existing call_rcu_hurry() deferral instead of signaling inline.

Fixes 21a091b970cd ("io_uring: signal registered eventfd to process deferred task work")
---
 include/linux/io_uring_types.h |    8 ++++++++
 io_uring/eventfd.c             |    8 ++++----
 io_uring/eventfd.h             |    2 +-
 io_uring/futex.c               |    4 ++--
 io_uring/io_uring.c            |    2 +-
 io_uring/poll.c                |   23 ++++++++++++-----------
 io_uring/tw.c                  |    2 +-
 io_uring/waitid.c              |    2 +-
 8 files changed, 30 insertions(+), 21 deletions(-)
include/linux/io_uring_types.h +8 -0
@@ -20,6 +20,14 @@ enum {
 	 * It's also ignored unless IORING_SETUP_DEFER_TASKRUN is set.
 	 */
 	IOU_F_TWQ_LAZY_WAKE			= 1,
+
+	/*
+	 * Set when task_work is queued from a waitqueue wakeup handler, where
+	 * an arbitrary provider waitqueue lock is held. Signaling the CQ ring
+	 * eventfd inline from there can recurse back into that lock through
+	 * epoll, so the eventfd signal must be deferred.
+	 */
+	IOU_F_TWQ_IN_WAKE			= 2,
 };
 
 enum io_uring_cmd_flags {
io_uring/eventfd.c +4 -4
@@ -51,9 +51,9 @@ static void io_eventfd_do_signal(struct
 /*
  * Returns true if the caller should put the ev_fd reference, false if not.
  */
-static bool __io_eventfd_signal(struct io_ev_fd *ev_fd)
+static bool __io_eventfd_signal(struct io_ev_fd *ev_fd, bool defer)
 {
-	if (eventfd_signal_allowed()) {
+	if (!defer && eventfd_signal_allowed()) {
 		eventfd_signal_mask(ev_fd->cq_ev_fd, EPOLL_URING_WAKE);
 		return true;
 	}
@@ -73,7 +73,7 @@ static bool io_eventfd_trigger(struct io
 	return !ev_fd->eventfd_async || io_wq_current_is_worker();
 }
 
-void io_eventfd_signal(struct io_ring_ctx *ctx, bool cqe_event)
+void io_eventfd_signal(struct io_ring_ctx *ctx, bool cqe_event, bool defer)
 {
 	bool skip = false;
 	struct io_ev_fd *ev_fd;
@@ -113,7 +113,7 @@ void io_eventfd_signal(struct io_ring_ct
 		spin_unlock(&ctx->completion_lock);
 	}
 
-	if (skip || __io_eventfd_signal(ev_fd))
+	if (skip || __io_eventfd_signal(ev_fd, defer))
 		io_eventfd_put(ev_fd);
 }
 
io_uring/eventfd.h +1 -1
@@ -5,4 +5,4 @@ int io_eventfd_register(struct io_ring_c
 			unsigned int eventfd_async);
 int io_eventfd_unregister(struct io_ring_ctx *ctx);
 
-void io_eventfd_signal(struct io_ring_ctx *ctx, bool cqe_event);
+void io_eventfd_signal(struct io_ring_ctx *ctx, bool cqe_event, bool defer);
io_uring/futex.c +2 -2
@@ -181,7 +181,7 @@ static void io_futex_wakev_fn(struct wak
 
 	io_req_set_res(req, 0, 0);
 	req->io_task_work.func = io_futexv_complete;
-	io_req_task_work_add(req);
+	__io_req_task_work_add(req, IOU_F_TWQ_IN_WAKE);
 }
 
 int io_futexv_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
@@ -237,7 +237,7 @@ static void io_futex_wake_fn(struct wake
 
 	io_req_set_res(req, 0, 0);
 	req->io_task_work.func = io_futex_complete;
-	io_req_task_work_add(req);
+	__io_req_task_work_add(req, IOU_F_TWQ_IN_WAKE);
 }
 
 int io_futexv_wait(struct io_kiocb *req, unsigned int issue_flags)
io_uring/io_uring.c +1 -1
@@ -483,7 +483,7 @@ void __io_commit_cqring_flush(struct io_
 	if (ctx->int_flags & IO_RING_F_OFF_TIMEOUT_USED)
 		io_flush_timeouts(ctx);
 	if (ctx->int_flags & IO_RING_F_HAS_EVFD)
-		io_eventfd_signal(ctx, true);
+		io_eventfd_signal(ctx, true, false);
 }
 
 static inline void __io_cq_lock(struct io_ring_ctx *ctx)
io_uring/poll.c +12 -11
@@ -208,9 +208,9 @@ enum {
 	IOU_POLL_REQUEUE = 4,
 };
 
-static void __io_poll_execute(struct io_kiocb *req, int mask)
+static void __io_poll_execute(struct io_kiocb *req, int mask, unsigned tw_flags)
 {
-	unsigned flags = 0;
+	unsigned flags = tw_flags;
 
 	io_req_set_res(req, mask, 0);
 	req->io_task_work.func = io_poll_task_func;
@@ -218,14 +218,15 @@ static void __io_poll_execute(struct io_
 	trace_io_uring_task_add(req, mask);
 
 	if (!(req->flags & REQ_F_POLL_NO_LAZY))
-		flags = IOU_F_TWQ_LAZY_WAKE;
+		flags |= IOU_F_TWQ_LAZY_WAKE;
 	__io_req_task_work_add(req, flags);
 }
 
-static inline void io_poll_execute(struct io_kiocb *req, int res)
+static inline void io_poll_execute(struct io_kiocb *req, int res,
+				   unsigned tw_flags)
 {
 	if (io_poll_get_ownership(req))
-		__io_poll_execute(req, res);
+		__io_poll_execute(req, res, tw_flags);
 }
 
 /*
@@ -344,7 +345,7 @@ void io_poll_task_func(struct io_tw_req
 	if (ret == IOU_POLL_NO_ACTION) {
 		return;
 	} else if (ret == IOU_POLL_REQUEUE) {
-		__io_poll_execute(req, 0);
+		__io_poll_execute(req, 0, 0);
 		return;
 	}
 	io_poll_remove_entries(req);
@@ -383,7 +384,7 @@ static void io_poll_cancel_req(struct io
 {
 	io_poll_mark_cancelled(req);
 	/* kick tw, which should complete the request */
-	io_poll_execute(req, 0);
+	io_poll_execute(req, 0, 0);
 }
 
 #define IO_ASYNC_POLL_COMMON	(EPOLLONESHOT | EPOLLPRI)
@@ -392,7 +393,7 @@ static __cold int io_pollfree_wake(struc
 {
 	io_poll_mark_cancelled(req);
 	/* we have to kick tw in case it's not already */
-	io_poll_execute(req, 0);
+	io_poll_execute(req, 0, IOU_F_TWQ_IN_WAKE);
 	io_poll_remove_waitq(poll);
 	return 1;
 }
@@ -430,7 +431,7 @@ static int io_poll_wake(struct wait_queu
 			else
 				req->flags &= ~REQ_F_SINGLE_POLL;
 		}
-		__io_poll_execute(req, mask);
+		__io_poll_execute(req, mask, IOU_F_TWQ_IN_WAKE);
 	}
 	return 1;
 }
@@ -618,7 +619,7 @@ static int __io_arm_poll_handler(struct
 
 	if (mask && (poll->events & EPOLLET) &&
 	    io_poll_can_finish_inline(req, ipt)) {
-		__io_poll_execute(req, mask);
+		__io_poll_execute(req, mask, 0);
 		return 0;
 	}
 	io_napi_add(req);
@@ -629,7 +630,7 @@ static int __io_arm_poll_handler(struct
 		 * poll was waken up, queue up a tw, it'll deal with it.
 		 */
 		if (atomic_cmpxchg(&req->poll_refs, 1, 0) != 1)
-			__io_poll_execute(req, 0);
+			__io_poll_execute(req, 0, 0);
 	}
 	return 0;
 }
io_uring/tw.c +1 -1
@@ -223,7 +223,7 @@ void io_req_local_work_add(struct io_kio
 	if (!head) {
 		io_ctx_mark_taskrun(ctx);
 		if (data_race(ctx->int_flags) & IO_RING_F_HAS_EVFD)
-			io_eventfd_signal(ctx, false);
+			io_eventfd_signal(ctx, false, flags & IOU_F_TWQ_IN_WAKE);
 	}
 
 	nr_wait = atomic_read(&ctx->cq_wait_nr);
io_uring/waitid.c +1 -1
@@ -253,7 +253,7 @@ static int io_waitid_wait(struct wait_qu
 		return 1;
 
 	req->io_task_work.func = io_waitid_cb;
-	io_req_task_work_add(req);
+	__io_req_task_work_add(req, IOU_F_TWQ_IN_WAKE);
 	return 1;
 }
 
author maintainer
#21 lore raw

PATCH 21/101 ocfs2: fix missing metadata reservation for large xattrs

In reply to Greg Kroah-Hartman

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

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

From: Ian Bridges <[email protected]>

commit 0cdc7dde00ec63ac714271fa8b2918d630b8da1a upstream.

[BUG]
lsetxattr() panics the kernel when setting a large xattr value on a fragmented filesystem where the file already has an external xattr block.

[CAUSE]
ocfs2_calc_xattr_set_need() never reserves metadata blocks for a new xattr value's extent tree when the file already has an external xattr block. The not_found path leaves meta_add at zero, so meta_ac is NULL when ocfs2_xattr_extend_allocation() runs.

A new value root has room for a single extent record. On a fragmented filesystem, the allocator cannot satisfy the xattr value in one contiguous run, so each non-contiguous run requires its own extent record. When the value root's extent list is full and meta_ac is NULL, ocfs2_add_clusters_in_btree() returns RESTART_META, and ocfs2_xattr_extend_allocation() hits BUG_ON(why == RESTART_META).

[FIX]
The case where no xattr block exists yet already calls ocfs2_extend_meta_needed(&def_xv.xv.xr_list) to reserve value tree metadata. Add the same reservation to the case where an xattr block already exists, making the two cases consistent.

Replace the BUG_ON with a -ENOSPC return so that if RESTART_META is returned despite the reservation, the error propagates to userspace instead of panicking the kernel.

Fixes a78f9f466894 ("ocfs2: make xattr extension work with new local alloc reservation.")
Reported-by [email protected]
Cc Mark Fasheh <[email protected]>
Cc Joel Becker <[email protected]>
Cc Junxiao Bi <[email protected]>
Cc Changwei Ge <[email protected]>
Cc Jun Piao <[email protected]>
Cc Heming Zhao <[email protected]>
---
 fs/ocfs2/xattr.c |   18 ++++++++++++------
 1 file changed, 12 insertions(+), 6 deletions(-)
fs/ocfs2/xattr.c +12 -6
@@ -740,12 +740,10 @@ static int ocfs2_xattr_extend_allocation
 					 prev_clusters;
 
 		if (why != RESTART_NONE && clusters_to_add) {
-			/*
-			 * We can only fail in case the alloc file doesn't give
-			 * up enough clusters.
-			 */
-			BUG_ON(why == RESTART_META);
-
+			if (why == RESTART_META) {
+				status = -ENOSPC;
+				break;
+			}
 			credits = ocfs2_calc_extend_credits(inode->i_sb,
 							    &vb->vb_xv->xr_list);
 			status = ocfs2_extend_trans(handle, credits);
@@ -3242,6 +3240,14 @@ meta_guess:
 			credits += OCFS2_SUBALLOC_ALLOC + 1;
 
 		/*
+		 * Reserve metadata for the new xattr's value extent tree.
+		 * The not_found path above adds credits for this tree but
+		 * omits meta_add, leaving meta_ac NULL for large values.
+		 */
+		if (xi->xi_value_len > OCFS2_XATTR_INLINE_SIZE)
+			meta_add += ocfs2_extend_meta_needed(&def_xv.xv.xr_list);
+
+		/*
 		 * This cluster will be used either for new bucket or for
 		 * new xattr block.
 		 * If the cluster size is the same as the bucket size, one
author maintainer
#22 lore raw

PATCH 22/101 null_blk: fix UBSAN shift-out-of-bounds when zone_size is 0 or overflows

In reply to Greg Kroah-Hartman

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

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

From: Rik van Riel <[email protected]>

commit 95491fb05105b61050cb623a5e0227eb26aa3525 upstream.

null_zone_no() does sect >> ilog2(dev->zone_size_sects). When zone_size_sects is 0, ilog2(0) returns -1, producing shift exponent -1 which UBSAN reports as shift-out-of-bounds.

  UBSAN: shift-out-of-bounds in drivers/block/null_blk/zoned.c:21:14
  shift exponent -1 is negative
  Call Trace:
   null_zone_no drivers/block/null_blk/zoned.c:21 [inline]
   null_process_zoned_cmd+0xf76/0xf80 drivers/block/null_blk/zoned.c:728
   null_handle_cmd drivers/block/null_blk/main.c:1455 [inline]
   null_queue_rq+0x8bc/0xe70 drivers/block/null_blk/main.c:1703
   __blk_mq_issue_directly block/blk-mq.c:2694 [inline]
   blk_mq_try_issue_directly+0x3f4/0x880 block/blk-mq.c:2754
   blk_mq_submit_bio+0x20c0/0x2a40 block/blk-mq.c:3208
   submit_bio_noacct_nocheck+0x2f4/0xa40 block/blk-core.c:790
   block_read_full_folio+0x7a6/0x810 fs/buffer.c:2463
   filemap_read_folio+0x12c/0x3a0 mm/filemap.c:2510
   read_part_sector+0xb6/0x2b0 block/partitions/core.c:724
   adfspart_check_ICS+0xb1/0x960 block/partitions/acorn.c:357
   check_partition block/partitions/core.c:143 [inline]
   blk_add_partitions block/partitions/core.c:591 [inline]
   bdev_disk_changed+0x851/0x17a0 block/partitions/core.c:695
   blkdev_get_whole+0x372/0x510 block/bdev.c:751
   add_disk_final block/genhd.c:412 [inline]
   add_disk_fwnode+0x24b/0x3a0 block/genhd.c:606
   null_add_dev+0x130b/0x1d70 drivers/block/null_blk/main.c:2052
   nullb_device_power_store+0x240/0x380 drivers/block/null_blk/main.c:501
   configfs_write_iter+0x337/0x430 fs/configfs/file.c:229

Syzkaller triggers this by creating a zoned null_blk device via configfs. The Call Trace shows configfs_write_iter in configfs/file.c handling a write to power file, which calls nullb_device_power_store in main.c, which calls null_add_dev in main.c, which calls add_disk in genhd.c, which triggers partition scan via bdev_disk_changed in partitions/core.c.

A zoned null_blk device with zone_size 0 should not be legal. Existing code tries to reject it via is_power_of_2() check in zoned.c and !zone_size check in main.c, but syzkaller can still reach null_zone_no() with zone_size_sects 0 via two paths:

1. Direct 0 via configfs: zone_size attribute store in main.c has NULLB_DEVICE_ATTR(zone_size, ulong, NULL) with no validation callback, so echo 0 > zone_size succeeds before power store. If zoned is false at power store time, the !zone_size check in main.c is skipped, and later zoned set true leaves zone_size 0.

2. Large value overflow: mb_to_sects() in zoned.c does (sector_t)mb * SZ_1M >> SECTOR_SHIFT which is mb * 2048. If mb is 1UL << 53 (9PB), mb * 2048 overflows 64-bit to 0. The value is power-of-two so is_power_of_2() passes, but mb_to_sects() returns 0.

Check for zero zone_size explicitly in null_init_zoned_dev() in zoned.c, returning -EINVAL with "must be non-zero power-of-two". Check for zero zone_size_sects after mb_to_sects() conversion, returning -EINVAL for overflow case. Keep defensive check in null_zone_no() returning 0 for zero sectors to avoid shift out-of-bounds even if zero slips through.

This change should be safe because zone_size is set once in null_init_zoned_dev() under device lock and never changes after, and 0 is never valid for a zoned device. Returning -EINVAL at init time fails device creation early with clear error, while defensive return 0 in null_zone_no() makes zoned command fail via offline zone check. No new locking is introduced.

---
 drivers/block/null_blk/zoned.c |   10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)
drivers/block/null_blk/zoned.c +8 -2
@@ -18,6 +18,8 @@ static inline sector_t mb_to_sects(unsig
 
 static inline unsigned int null_zone_no(struct nullb_device *dev, sector_t sect)
 {
+	if (WARN_ON_ONCE(!dev->zone_size_sects))
+		return 0;
 	return sect >> ilog2(dev->zone_size_sects);
 }
 
@@ -56,8 +58,8 @@ int null_init_zoned_dev(struct nullb_dev
 	sector_t sector = 0;
 	unsigned int i;
 
-	if (!is_power_of_2(dev->zone_size)) {
-		pr_err("zone_size must be power-of-two\n");
+	if (!dev->zone_size || !is_power_of_2(dev->zone_size)) {
+		pr_err("zone_size must be non-zero power-of-two\n");
 		return -EINVAL;
 	}
 	if (dev->zone_size > dev->size) {
@@ -88,6 +90,10 @@ int null_init_zoned_dev(struct nullb_dev
 	zone_capacity_sects = mb_to_sects(dev->zone_capacity);
 	dev_capacity_sects = mb_to_sects(dev->size);
 	dev->zone_size_sects = mb_to_sects(dev->zone_size);
+	if (!dev->zone_size_sects) {
+		pr_err("zone_size too large or too small, leads to zero sectors\n");
+		return -EINVAL;
+	}
 	dev->nr_zones = round_up(dev_capacity_sects, dev->zone_size_sects)
 		>> ilog2(dev->zone_size_sects);
 
author maintainer
#23 lore raw

PATCH 23/101 kcov: fix data corruption and race conditions on PREEMPT_RT

In reply to Greg Kroah-Hartman

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

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

From: Tetsuo Handa <[email protected]>

commit 2eed77fdcb0cc48e8eccb2bcd4b7f2c6d650e84c upstream.

syzbot is reporting KCOV state corruption on PREEMPT_RT kernels, for the temporary storage used for saving/restoring remote KCOV state is currently allocated as the per-CPU area.

On PREEMPT_RT kernels, softirq handlers run as preemptible task threads (e.g., ksoftirqd). If a softirq context preempts a task running a remote KCOV session, it safely saves the task's state into the per-CPU area. However, if that softirq thread is subsequently preempted by a higher- priority softirq thread on the same CPU, the second softirq will overwrite the same per-CPU area, permanently destroying the original task's KCOV state.

Fix this data corruption by moving the temporary storage from the per-CPU area to the per-thread area. Since each softirq thread now owns its own task context, nested softirq preemption no longer causes data overwrites.

Note that while the temporary storage is now on a per-thread basis, the per-CPU kcov_percpu_data.lock must be retained, for we need to ensure that kcov_remote_start() and kcov_remote_stop() operate atomically without racing against asynchronous interrupts that manipulate the current task's KCOV state.

It is likely that GFP_KERNEL allocation by vmalloc_node() in kcov_init() has already called panic() before returning NULL, for there will be no OOM-killable userspace processes when __init function of built-in module runs. But this patch also fixes crashing the kernel when vmalloc_node() in kcov_init() returned NULL, for kcov_init() left per-CPU irq_area == NULL but kcov_remote_start() depends on per-CPU irq_area != NULL, resulting in

(1) doing vmalloc() in kcov_remote_start() despite !in_task() context

(2) out-of-array-bounds access if (1) succeeded but

      kcov->remote_size < CONFIG_KCOV_IRQ_AREA_SIZE

(3) always leak memory allocated by (1), eventually killing all OOM-killable userspace processes

problems.

---
 include/linux/sched.h |    8 ++++
 kernel/kcov.c         |   90 ++++++++++++++++++++++++++------------------------
 lib/Kconfig.debug     |    5 +-
 3 files changed, 58 insertions(+), 45 deletions(-)
include/linux/sched.h +8 -0
@@ -1525,6 +1525,14 @@ struct task_struct {
 
 	/* Collect coverage from softirq context: */
 	unsigned int			kcov_softirq;
+
+	/* Temporary storage for preempting remote coverage collection: */
+	unsigned int			kcov_saved_mode;
+	unsigned int			kcov_saved_size;
+	void				*kcov_saved_area;
+	struct kcov			*kcov_saved_kcov;
+	int				kcov_saved_sequence;
+
 #endif
 
 #ifdef CONFIG_MEMCG_V1
kernel/kcov.c +47 -43

169 lines not on this page · raw message

lib/Kconfig.debug +3 -2
@@ -2247,10 +2247,11 @@ config KCOV_INSTRUMENT_ALL
 config KCOV_IRQ_AREA_SIZE
 	hex "Size of interrupt coverage collection area in words"
 	depends on KCOV
+	range 0x80 0x1000000
 	default 0x40000
 	help
-	  KCOV uses preallocated per-cpu areas to collect coverage from
-	  soft interrupts. This specifies the size of those areas in the
+	  KCOV uses preallocated areas to collect coverage from soft
+	  interrupts. This specifies the size of those areas in the
 	  number of unsigned long words.
 
 config KCOV_SELFTEST
author maintainer
#24 lore raw

PATCH 24/101 ext4: stop retrying saturated xattr cache entries

In reply to Greg Kroah-Hartman

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

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

From: Matthias Goergens <[email protected]>

commit 54b6bd40898de7906acb2bccc9a96d1b8e6b4323 upstream.

ext4_xattr_block_set() retries when a cache entry selected for reuse has a saturated reference count after taking the buffer lock. The retry returns to the mbcache lookup without making that entry ineligible, so it can select the same unusable entry indefinitely. A task spinning there can hold the parent directory's i_rwsem and leave concurrent rmdir callers blocked.

Normally a reusable entry has a reference count below EXT4_XATTR_REFCOUNT_MAX because the count and MBE_REUSABLE_B are updated under the same buffer lock. A corrupted filesystem can violate that invariant. The syzbot reproducer reports allocator and xattr corruption before triggering this retry loop.

Check the untrusted on-disk count before incrementing it, avoiding overflow, and clear MBE_REUSABLE_B when it is already saturated. The next lookup then skips the entry that was just proven unusable. This mirrors the normal transition at EXT4_XATTR_REFCOUNT_MAX; the release path marks the entry reusable again on the exact 1024-to-1023 transition.

Using the same QEMU harness and guest parameters, current unpatched Linux hung in 6 of 8 420-second trials with the do_rmdir signature; representative NMI backtraces caught the owner spinning in ext4_xattr_block_set(). The patched kernel completed 28 of 28 trials without a hung-task report; the final twelve trials exercised the reviewed overflow-safe form of the change. syzbot's patch testing also completed without reproducing the hang.

---
 fs/ext4/xattr.c |    6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
fs/ext4/xattr.c +4 -2
@@ -2075,12 +2075,13 @@ inserted:
 				 * stable so we can check the additional
 				 * reference fits.
 				 */
-				ref = le32_to_cpu(BHDR(new_bh)->h_refcount) + 1;
-				if (ref > EXT4_XATTR_REFCOUNT_MAX) {
+				ref = le32_to_cpu(BHDR(new_bh)->h_refcount);
+				if (ref >= EXT4_XATTR_REFCOUNT_MAX) {
 					/*
 					 * Undo everything and check mbcache
 					 * again.
 					 */
+					clear_bit(MBE_REUSABLE_B, &ce->e_flags);
 					unlock_buffer(new_bh);
 					dquot_free_block(inode,
 							 EXT4_C2B(EXT4_SB(sb),
@@ -2091,6 +2092,7 @@ inserted:
 					new_bh = NULL;
 					goto inserted;
 				}
+				ref++;
 				BHDR(new_bh)->h_refcount = cpu_to_le32(ref);
 				if (ref == EXT4_XATTR_REFCOUNT_MAX)
 					clear_bit(MBE_REUSABLE_B, &ce->e_flags);
author maintainer
#25 lore raw

PATCH 25/101 nilfs2: reject invalid block index in GC ioctl

In reply to Greg Kroah-Hartman

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

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

From: Ryusuke Konishi <[email protected]>

commit a1735eae55448bc79c2da6593455791e886f6ed8 upstream.

Syzbot reported list corruption caused by a double list_add_tail() call on bh->b_assoc_buffers within nilfs_lookup_dirty_data_buffers().

Analysis revealed that the root cause was the insertion of a page/folio with a page index of ULONG_MAX into the page cache via the GC ioctl. filemap_get_folios_tag(), called by nilfs_lookup_dirty_data_buffers(), repeatedly detects a dirty folio with a page index of ULONG_MAX due to index wrap-around, leading to duplicate processing of dirty buffers.

As a preparatory step, the GC ioctl loads the page/folio of the block to be moved during GC and inserts it into the page cache based on information in the nilfs_vdesc structure passed as an argument. Normally, this does not cause issues because the user-space GC library configures the nilfs_vdesc structure properly. However, since there is no range check on the parameters determining the page index, a request with artificially crafted parameters -- such as those generated by Syzbot -- can result in a page/folio being inserted with a page index of ULONG_MAX, triggering the above problem.

This resolves the issue by checking the ranges of 'vd_offset' and 'vd_vblocknr' in the nilfs_vdesc structure that determine the page index, thereby preventing the invalid page/folio insertions.

---
 fs/nilfs2/ioctl.c |   20 ++++++++++++++++++--
 1 file changed, 18 insertions(+), 2 deletions(-)
fs/nilfs2/ioctl.c +18 -2
@@ -527,6 +527,7 @@ static int nilfs_ioctl_get_bdescs(struct
  * Return: 0 on success, or one of the following negative error codes on
  * failure:
  * * %-EEXIST	- Block conflict detected.
+ * * %-EINVAL	- Invalid virtual block descriptor.
  * * %-EIO	- I/O error.
  * * %-ENOENT	- Requested block doesn't exist.
  * * %-ENOMEM	- Insufficient memory available.
@@ -536,15 +537,30 @@ static int nilfs_ioctl_move_inode_block(
 					struct list_head *buffers)
 {
 	struct buffer_head *bh;
+	__u64 limit_blkidx = (__u64)inode->i_sb->s_maxbytes >> inode->i_blkbits;
 	int ret;
 
-	if (vdesc->vd_flags == 0)
+	/*
+	 * vblocknr 0 is reserved as an invalid pointer.  Also, limit_blkidx
+	 * ensures that the page index converted from vd_vblocknr never
+	 * overflows the page cache limit and respects the architecture's bmap
+	 * key width.
+	 */
+	if (unlikely(vdesc->vd_vblocknr == 0 ||
+			vdesc->vd_vblocknr >= limit_blkidx))
+		return -EINVAL;
+
+	if (vdesc->vd_flags == 0) {
+		if (unlikely(vdesc->vd_offset >= limit_blkidx))
+			return -EINVAL;
+
 		ret = nilfs_gccache_submit_read_data(
 			inode, vdesc->vd_offset, vdesc->vd_blocknr,
 			vdesc->vd_vblocknr, &bh);
-	else
+	} else {
 		ret = nilfs_gccache_submit_read_node(
 			inode, vdesc->vd_blocknr, vdesc->vd_vblocknr, &bh);
+	}
 
 	if (unlikely(ret < 0)) {
 		if (ret == -ENOENT)
author maintainer
#26 lore raw

PATCH 26/101 ext4: clear error before retrying inode xattr space fallback

In reply to Greg Kroah-Hartman

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

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

From: Guanghui Yang <[email protected]>

commit 409a7f12a0933ff2c617fa814c76cef0bd1d457a upstream.

When ext4_xattr_make_inode_space() returns -ENOSPC, ext4_expand_extra_isize_ea() can retry the expansion with s_min_extra_isize. If that retry succeeds by finding enough ibody free space, control jumps directly to the shift label.

The previous -ENOSPC is still stored in error in that path, so the function can update i_extra_isize but still return -ENOSPC to the caller. Clear error before retrying so a successful fallback expansion returns success.

Reproduced with an ext4 image using 1 KiB blocks, project quota support, 256-byte inodes, and min_extra_isize/want_extra_isize set to 32. FS_IOC_FSSETXATTR failures dropped from 802 to 86 after the fix.

---
 fs/ext4/xattr.c |    1 +
 1 file changed, 1 insertion(+)
fs/ext4/xattr.c +1 -0
@@ -2841,6 +2841,7 @@ retry:
 		    s_min_extra_isize) {
 			tried_min_extra_isize++;
 			new_extra_isize = s_min_extra_isize;
+			error = 0;
 			goto retry;
 		}
 		goto cleanup;
author maintainer
#27 lore raw

PATCH 27/101 ext4: avoid tail write_begin walk for uptodate folios

In reply to Greg Kroah-Hartman

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

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

From: Jia Zhu <[email protected]>

commit d09811183db2891776dbf0c0f1094540e29938f6 upstream.

Ext4 buffered writes into large folios also pay a full buffer_head walk in ext4_block_write_begin(). For a small overwrite of an existing cached folio, the folio is already uptodate and the write only needs to prepare the buffers through the written range. Walking the suffix still makes the write_begin cost proportional to the folio size.

Before ext4 enabled large folios for regular files, the same loop was bounded by a single page of buffers. That commit made the existing full-folio walk visible as a regression for cached small overwrites.

The suffix walk is needed for non-uptodate folios, where ext4 may have to submit reads for partial blocks, preserve new-buffer cleanup, and run error zeroing. Keep those folios on the old full walk.

For already-uptodate folios, keep the walk starting at the first buffer rather than seeking directly to from. This preserves the existing prefix buffer state handling. Stop once block_start reaches the end of the write range, because the skipped suffix would only repeat the outside-range uptodate handling for buffers beyond @to.

On current master, the libMicro ext4 large-folio overwrite test shows the following full-series result. Results are median usecs/call over 10 runs, lower is better:

case        nofix     this series   improvement
write_u1k   1.418     0.3405        76.0%
write_u10k  1.887     0.4175        77.9%
pwrite_u1k  1.6775    0.3390        79.8%
pwrite_u10k 1.9035    0.4130        78.3%
Fixes 7ac67301e82f0 ("ext4: enable large folio for regular file")
Cc [email protected] # v6.16+
---
 fs/ext4/inode.c |   11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)
fs/ext4/inode.c +6 -5
@@ -1182,6 +1182,7 @@ int ext4_block_write_begin(handle_t *han
 	int nr_wait = 0;
 	int i;
 	bool should_journal_data = ext4_should_journal_data(inode);
+	bool folio_uptodate = folio_test_uptodate(folio);
 
 	BUG_ON(!folio_test_locked(folio));
 	BUG_ON(to > folio_size(folio));
@@ -1193,13 +1194,13 @@ int ext4_block_write_begin(handle_t *han
 		head = create_empty_buffers(folio, blocksize, 0);
 	block = EXT4_PG_TO_LBLK(inode, folio->index);
 
-	for (bh = head, block_start = 0; bh != head || !block_start;
+	for (bh = head, block_start = 0;
+	     block_start < to || (!folio_uptodate && bh != head);
 	    block++, block_start = block_end, bh = bh->b_this_page) {
 		block_end = block_start + blocksize;
 		if (block_end <= from || block_start >= to) {
-			if (folio_test_uptodate(folio)) {
+			if (folio_uptodate)
 				set_buffer_uptodate(bh);
-			}
 			continue;
 		}
 		if (WARN_ON_ONCE(buffer_new(bh)))
@@ -1220,7 +1221,7 @@ int ext4_block_write_begin(handle_t *han
 				if (should_journal_data)
 					do_journal_get_write_access(handle,
 								    inode, bh);
-				if (folio_test_uptodate(folio)) {
+				if (folio_uptodate) {
 					/*
 					 * Unlike __block_write_begin() we leave
 					 * dirtying of new uptodate buffers to
@@ -1237,7 +1238,7 @@ int ext4_block_write_begin(handle_t *han
 				continue;
 			}
 		}
-		if (folio_test_uptodate(folio)) {
+		if (folio_uptodate) {
 			set_buffer_uptodate(bh);
 			continue;
 		}
author maintainer
#28 lore raw

PATCH 28/101 ext4: propagate errors from fast commit range replay

In reply to Greg Kroah-Hartman

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

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

From: Guanghui Yang <[email protected]>

commit d8b8dd3530bf41e14b118702cdaf9de64bb96885 upstream.

ext4_fc_replay() stops replaying fast commit tags only when a tag handler returns a negative error. However, ext4_fc_replay_add_range() and ext4_fc_replay_del_range() currently return 0 from their common exit paths even after internal failures.

This hides errors from ext4_fc_record_modified_inode(), ext4_map_blocks(), ext4_find_extent(), ext4_ext_insert_extent(), ext4_ext_replay_update_ex(), and ext4_ext_remove_space(). As a result, a failed ADD_RANGE or DEL_RANGE replay can be treated as successful and the replay code may continue with subsequent fast commit tags.

This is particularly problematic for DEL_RANGE because it may already have marked blocks as free before ext4_ext_remove_space() fails. If the error is swallowed, replay may continue from a partially applied range operation.

Return the saved error from the common exit paths and make the ERR_PTR() cases in ADD_RANGE store PTR_ERR() before jumping to out.

---
 fs/ext4/fast_commit.c |   16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)
fs/ext4/fast_commit.c +12 -4
@@ -1757,8 +1757,11 @@ static int ext4_fc_replay_add_range(stru
 		if (ret == 0) {
 			/* Range is not mapped */
 			path = ext4_find_extent(inode, cur, path, 0);
-			if (IS_ERR(path))
+			if (IS_ERR(path)) {
+				ret = PTR_ERR(path);
+				path = NULL;
 				goto out;
+			}
 			memset(&newex, 0, sizeof(newex));
 			newex.ee_block = cpu_to_le32(cur);
 			ext4_ext_store_pblock(
@@ -1770,8 +1773,11 @@ static int ext4_fc_replay_add_range(stru
 			path = ext4_ext_insert_extent(NULL, inode,
 						      path, &newex, 0);
 			up_write((&EXT4_I(inode)->i_data_sem));
-			if (IS_ERR(path))
+			if (IS_ERR(path)) {
+				ret = PTR_ERR(path);
+				path = NULL;
 				goto out;
+			}
 			goto next;
 		}
 
@@ -1818,10 +1824,11 @@ next:
 	}
 	ext4_ext_replay_shrink_inode(inode, i_size_read(inode) >>
 					sb->s_blocksize_bits);
+	ret = 0;
 out:
 	ext4_free_ext_path(path);
 	iput(inode);
-	return 0;
+	return ret;
 }
 
 /* Replay DEL_RANGE tag */
@@ -1881,9 +1888,10 @@ ext4_fc_replay_del_range(struct super_bl
 	ext4_ext_replay_shrink_inode(inode,
 		i_size_read(inode) >> sb->s_blocksize_bits);
 	ext4_mark_inode_dirty(NULL, inode);
+	ret = 0;
 out:
 	iput(inode);
-	return 0;
+	return ret;
 }
 
 static void ext4_fc_set_bitmaps_and_counters(struct super_block *sb)
author maintainer
#29 lore raw

PATCH 29/101 ext4: dont enable DAX on new encrypted files

In reply to Greg Kroah-Hartman

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

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

From: Eric Biggers <[email protected]>

commit da32af420d6d466e247c43ac0b829edeac7ae0ad upstream.

Currently, when a new encrypted regular file is created, the call to ext4_set_inode_flags(inode, init=true) in __ext4_new_inode() is made before EXT4_INODE_ENCRYPT is set. As a result, it can set S_DAX if the filesystem is mounted with "-o dax=always".

EXT4_INODE_ENCRYPT then actually gets set a bit later in __ext4_new_inode(), when it calls fscrypt_set_context() which calls ext4_set_context(). ext4_set_context() sets EXT4_INODE_ENCRYPT and calls ext4_set_inode_flags(inode, init=false) to set S_ENCRYPTED too.

This was intended to clear S_DAX as well. However, this was broken by commit 043546e46dc7 ("fs/ext4: Only change S_DAX on inode load"). This causes data written to the file to bypass encryption, also causing xfstests failures such as generic/548 (when "-o dax=always" is used).

Fix this by simplifying the flow by making __ext4_new_inode() set EXT4_INODE_ENCRYPT earlier. This makes it take effect in ext4_set_inode_flags(inode, init=true), making S_DAX never be set.

Similarly, make EXT4_STATE_MAY_INLINE_DATA never be set in the first place on new encrypted inodes. Then it doesn't need to be cleared.

As a result of these simplifications, ext4_set_context() no longer needs to change inode flags or state when 'handle != NULL'. Remove that too.

---
 fs/ext4/crypto.c |   40 ++++++++++++++++++++--------------------
 fs/ext4/ialloc.c |    4 ++++
 2 files changed, 24 insertions(+), 20 deletions(-)
fs/ext4/crypto.c +20 -20
@@ -144,7 +144,13 @@ static int ext4_set_context(struct inode
 	if (inode->i_ino == EXT4_ROOT_INO)
 		return -EPERM;
 
-	if (WARN_ON_ONCE(IS_DAX(inode) && i_size_read(inode)))
+	/*
+	 * For new encrypted inodes, S_DAX is never set in the first place.
+	 *
+	 * For existing inodes, this is called only on empty directories.  ext4
+	 * never sets S_DAX on directories.
+	 */
+	if (WARN_ON_ONCE(IS_DAX(inode)))
 		return -EINVAL;
 
 	if (ext4_test_inode_flag(inode, EXT4_INODE_DAX))
@@ -164,27 +170,24 @@ static int ext4_set_context(struct inode
 
 	if (handle) {
 		/*
+		 * __ext4_new_inode() should have already set the encrypt flag
+		 * on the inode and avoided enabling inline data.
+		 */
+		if (WARN_ON_ONCE(!IS_ENCRYPTED(inode)))
+			return -EINVAL;
+		if (WARN_ON_ONCE(ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA)))
+			return -EINVAL;
+		/*
 		 * Since the inode is new it is ok to pass the
 		 * XATTR_CREATE flag. This is necessary to match the
 		 * remaining journal credits check in the set_handle
 		 * function with the credits allocated for the new
 		 * inode.
 		 */
-		res = ext4_xattr_set_handle(handle, inode,
-					    EXT4_XATTR_INDEX_ENCRYPTION,
-					    EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
-					    ctx, len, XATTR_CREATE);
-		if (!res) {
-			ext4_set_inode_flag(inode, EXT4_INODE_ENCRYPT);
-			ext4_clear_inode_state(inode,
-					EXT4_STATE_MAY_INLINE_DATA);
-			/*
-			 * Update inode->i_flags - S_ENCRYPTED will be enabled,
-			 * S_DAX may be disabled
-			 */
-			ext4_set_inode_flags(inode, false);
-		}
-		return res;
+		return ext4_xattr_set_handle(handle, inode,
+					     EXT4_XATTR_INDEX_ENCRYPTION,
+					     EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
+					     ctx, len, XATTR_CREATE);
 	}
 
 	res = dquot_initialize(inode);
@@ -205,10 +208,7 @@ retry:
 				    ctx, len, 0);
 	if (!res) {
 		ext4_set_inode_flag(inode, EXT4_INODE_ENCRYPT);
-		/*
-		 * Update inode->i_flags - S_ENCRYPTED will be enabled,
-		 * S_DAX may be disabled
-		 */
+		/* Update inode->i_flags to set S_ENCRYPTED. */
 		ext4_set_inode_flags(inode, false);
 		res = ext4_mark_inode_dirty(handle, inode);
 		if (res)
fs/ext4/ialloc.c +4 -0
@@ -995,6 +995,8 @@ struct inode *__ext4_new_inode(struct mn
 		err = fscrypt_prepare_new_inode(dir, inode, &encrypt);
 		if (err)
 			goto out;
+		if (encrypt)
+			i_flags |= EXT4_ENCRYPT_FL;
 	}
 
 	err = dquot_initialize(inode);
@@ -1304,6 +1306,8 @@ got:
 	ei->i_extra_isize = sbi->s_want_extra_isize;
 	ei->i_inline_off = 0;
 	if (ext4_has_feature_inline_data(sb) &&
+	    /* Encrypted inodes cannot have inline data */
+	    !(ei->i_flags & EXT4_ENCRYPT_FL) &&
 	    (!(ei->i_flags & (EXT4_DAX_FL|EXT4_EA_INODE_FL)) || S_ISDIR(mode)))
 		ext4_set_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA);
 	ret = inode;
author maintainer
#30 lore raw

PATCH 30/101 ext4: fix incorrect function call when initializing s_resgid

In reply to Greg Kroah-Hartman

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

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

From: Jiazi Liu <[email protected]>

commit c87abbab6147dcc5aa1fd8f2a61734d58d8b99ec upstream.

In __ext4_fill_super(), s_resgid is initialized by calling ext4_get_resuid() instead of ext4_get_resgid(), resulting in the reserved GID being set to the same value as the reserved UID rather than the value stored in the superblock.

Fixes 12c84dd4d308 ("ext4: add support for 32-bit default reserved uid and gid values")
---
 fs/ext4/super.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/ext4/super.c +1 -1
@@ -5365,7 +5365,7 @@ static int __ext4_fill_super(struct fs_c
 	ext4_set_def_opts(sb, es);
 
 	sbi->s_resuid = make_kuid(&init_user_ns, ext4_get_resuid(es));
-	sbi->s_resgid = make_kgid(&init_user_ns, ext4_get_resuid(es));
+	sbi->s_resgid = make_kgid(&init_user_ns, ext4_get_resgid(es));
 	sbi->s_commit_interval = JBD2_DEFAULT_MAX_COMMIT_AGE * HZ;
 	sbi->s_min_batch_time = EXT4_DEF_MIN_BATCH_TIME;
 	sbi->s_max_batch_time = EXT4_DEF_MAX_BATCH_TIME;
author maintainer
#31 lore raw

PATCH 31/101 xfs: validate attr entry pointer before field access

In reply to Greg Kroah-Hartman

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

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

From: Hongling Zeng <[email protected]>

commit b7eea80be25f3334f131d52982b3131aba77b97d upstream.

xfs_attr3_leaf_verify_entry() accesses lentry/rentry fields (namelen, valuelen) before checking if the entry pointer itself is within bounds. If nameidx is crafted to point near the end of the buffer, these field accesses can read out-of-bounds before the bounds check at name_end > buf_end is performed.

Add explicit bounds checks for entry pointers before accessing their fields. Use offsetof() to check that the start of the flexible array member (nameval/name) is within bounds, which ensures all preceding fields are safe to access.

Fixes c84760659dcf2 ("xfs: check attribute leaf block structure")
Cc <[email protected]> # v5.5
---
 fs/xfs/libxfs/xfs_attr_leaf.c |   14 ++++++++++++++
 1 file changed, 14 insertions(+)
fs/xfs/libxfs/xfs_attr_leaf.c +14 -0
@@ -325,6 +325,13 @@ xfs_attr3_leaf_verify_entry(
 	 */
 	if (ent->flags & XFS_ATTR_LOCAL) {
 		lentry = xfs_attr3_leaf_name_local(leaf, idx);
+
+		/* Validate lentry pointer is within bounds before field access */
+		if ((char *)lentry >= buf_end)
+			return __this_address;
+		if ((char *)lentry + offsetof(struct xfs_attr_leaf_name_local, nameval) > buf_end)
+			return __this_address;
+
 		namesize = xfs_attr_leaf_entsize_local(lentry->namelen,
 				be16_to_cpu(lentry->valuelen));
 		name_end = (char *)lentry + namesize;
@@ -332,6 +339,13 @@ xfs_attr3_leaf_verify_entry(
 			return __this_address;
 	} else {
 		rentry = xfs_attr3_leaf_name_remote(leaf, idx);
+
+		/* Validate rentry pointer is within bounds before field access */
+		if ((char *)rentry >= buf_end)
+			return __this_address;
+		if ((char *)rentry + offsetof(struct xfs_attr_leaf_name_remote, name) > buf_end)
+			return __this_address;
+
 		namesize = xfs_attr_leaf_entsize_remote(rentry->namelen);
 		name_end = (char *)rentry + namesize;
 		if (rentry->namelen == 0)
author maintainer
#32 lore raw

PATCH 32/101 xfs: restore nofs context unconditionally in xfs_trans_roll

In reply to Greg Kroah-Hartman

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

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

From: Yun Zhou <[email protected]>

commit 0241ea5fb0fe86d2a673163b2f5815111aadc7f7 upstream.

When __xfs_trans_commit() fails in xfs_trans_roll(), the NOFS context is cleared but only restored in the success path. This leaves the error path without nofs protection, causing a circular lock dependency between xfs_nondir_ilock_class and fs_reclaim:

       CPU0                    CPU1
       ----                    ----
  lock(&xfs_nondir_ilock_class);
                               lock(fs_reclaim);
                               lock(&xfs_nondir_ilock_class);

lock(fs_reclaim);

Fix this by moving xfs_trans_set_context() before the error check so that nofs context is always restored on the new transaction.

---
 fs/xfs/xfs_trans.c | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)
fs/xfs/xfs_trans.c +9 -7
index 7bfbd9f6f0df..1b36cf12d4e3 100644
@@ -1029,6 +1029,15 @@ xfs_trans_roll(
 	 * duplicate transaction that gets returned.
 	 */
 	error = __xfs_trans_commit(tp, true);
+
+	tp = *tpp;
+	/*
+	 * __xfs_trans_commit cleared the NOFS flag by calling into
+	 * xfs_trans_free.  Set it again here before doing memory
+	 * allocations.
+	 */
+	xfs_trans_set_context(tp);
+
 	if (error)
 		return error;
 
@@ -1040,13 +1049,6 @@ xfs_trans_roll(
 	 * either nothing be locked across this call, or that anything that is
 	 * locked be logged in the prior and the next transactions.
 	 */
-	tp = *tpp;
-	/*
-	 * __xfs_trans_commit cleared the NOFS flag by calling into
-	 * xfs_trans_free.  Set it again here before doing memory
-	 * allocations.
-	 */
-	xfs_trans_set_context(tp);
 	error = xfs_log_regrant(tp->t_mountp, tp->t_ticket);
 	if (error)
 		return error;
Signature · 1 line
2.55.0
author maintainer
#33 lore raw

PATCH 33/101 drm/i915/pin: s/dev_priv/i915/ and drop struct drm_device usage

In reply to Greg Kroah-Hartman

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

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

From: Jani Nikula <[email protected]>

[ Upstream commit 3823bf019d3b31ccd3e79860b7a3e20c719a1db4 ]

Improve consistency within i915_fb_pin.c by replacing dev_priv naming with i915, and dropping struct drm_device usage.

Stable-dep-of: fc6487579083 ("drm/xe: Fix DPT allocation paths.")

---
 drivers/gpu/drm/i915/display/intel_fb_pin.c |   24 +++++++++++-------------
 1 file changed, 11 insertions(+), 13 deletions(-)
drivers/gpu/drm/i915/display/intel_fb_pin.c +11 -13
@@ -29,9 +29,8 @@ intel_fb_pin_to_dpt(const struct drm_fra
 		    unsigned long *out_flags,
 		    struct intel_dpt *dpt)
 {
-	struct drm_device *dev = fb->dev;
-	struct intel_display *display = to_intel_display(dev);
-	struct drm_i915_private *dev_priv = to_i915(dev);
+	struct intel_display *display = to_intel_display(fb->dev);
+	struct drm_i915_private *i915 = to_i915(fb->dev);
 	struct drm_gem_object *_obj = intel_fb_bo(fb);
 	struct drm_i915_gem_object *obj = to_intel_bo(_obj);
 	struct i915_address_space *vm = i915_dpt_to_vm(dpt);
@@ -43,7 +42,7 @@ intel_fb_pin_to_dpt(const struct drm_fra
 	 * We are not syncing against the binding (and potential migrations)
 	 * below, so this vm must never be async.
 	 */
-	if (drm_WARN_ON(&dev_priv->drm, vm->bind_async_flags))
+	if (drm_WARN_ON(&i915->drm, vm->bind_async_flags))
 		return ERR_PTR(-EINVAL);
 
 	if (WARN_ON(!i915_gem_object_is_framebuffer(obj)))
@@ -56,7 +55,7 @@ intel_fb_pin_to_dpt(const struct drm_fra
 		if (ret)
 			continue;
 
-		if (HAS_LMEM(dev_priv)) {
+		if (HAS_LMEM(i915)) {
 			unsigned int flags = obj->flags;
 
 			/*
@@ -118,9 +117,8 @@ intel_fb_pin_to_ggtt(const struct drm_fr
 		     bool uses_fence,
 		     unsigned long *out_flags)
 {
-	struct drm_device *dev = fb->dev;
-	struct intel_display *display = to_intel_display(dev);
-	struct drm_i915_private *dev_priv = to_i915(dev);
+	struct intel_display *display = to_intel_display(fb->dev);
+	struct drm_i915_private *i915 = to_i915(fb->dev);
 	struct drm_gem_object *_obj = intel_fb_bo(fb);
 	struct drm_i915_gem_object *obj = to_intel_bo(_obj);
 	struct ref_tracker *wakeref;
@@ -129,10 +127,10 @@ intel_fb_pin_to_ggtt(const struct drm_fr
 	unsigned int pinctl;
 	int ret;
 
-	if (drm_WARN_ON(dev, !i915_gem_object_is_framebuffer(obj)))
+	if (drm_WARN_ON(&i915->drm, !i915_gem_object_is_framebuffer(obj)))
 		return ERR_PTR(-EINVAL);
 
-	if (drm_WARN_ON(dev, alignment && !is_power_of_2(alignment)))
+	if (drm_WARN_ON(&i915->drm, alignment && !is_power_of_2(alignment)))
 		return ERR_PTR(-EINVAL);
 
 	/*
@@ -163,7 +161,7 @@ retry:
 	ret = i915_gem_object_lock(obj, &ww);
 	if (!ret && phys_alignment)
 		ret = i915_gem_object_attach_phys(obj, phys_alignment);
-	else if (!ret && HAS_LMEM(dev_priv))
+	else if (!ret && HAS_LMEM(i915))
 		ret = i915_gem_object_migrate(obj, &ww, INTEL_REGION_LMEM_0);
 	if (!ret)
 		ret = i915_gem_object_pin_pages(obj);
@@ -264,7 +262,7 @@ intel_plane_fb_vtd_guard(const struct in
 int intel_plane_pin_fb(struct intel_plane_state *plane_state,
 		       const struct intel_plane_state *old_plane_state)
 {
-	struct intel_display *display = to_intel_display(plane_state);
+	struct drm_i915_private *i915 = to_i915(plane_state->uapi.plane->dev);
 	struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane);
 	const struct intel_framebuffer *fb =
 		to_intel_framebuffer(plane_state->hw.fb);
@@ -308,7 +306,7 @@ int intel_plane_pin_fb(struct intel_plan
 		 * The DPT object contains only one vma, and there is no VT-d
 		 * guard, so the VMA's offset within the DPT is always 0.
 		 */
-		drm_WARN_ON(display->drm, i915_dpt_offset(plane_state->dpt_vma));
+		drm_WARN_ON(&i915->drm, i915_dpt_offset(plane_state->dpt_vma));
 	}
 
 	/*
author maintainer
#34 lore raw

PATCH 34/101 drm/i915: Track fence region ID in plane state

In reply to Greg Kroah-Hartman

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

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

From: Ville Syrjälä <[email protected]>

[ Upstream commit eb3dd9640731ae3ace871190f80474a8e7bae121 ]

Get rid of the needlessly complicated PLANE_HAS_FENCE + intel_parent_vma_fence_id() dance by simply tracking the fence_id directly in the plane state.

v2: Don't request the redundant fence for fbdev (Jani)

    Assign *out_fence_id with '=' instead of '|=' (Sashiko,Jani)

Stable-dep-of: fc6487579083 ("drm/xe: Fix DPT allocation paths.")

---
 drivers/gpu/drm/i915/display/intel_display_types.h |    5 +--
 drivers/gpu/drm/i915/display/intel_fb_pin.c        |   33 +++++++++++----------
 drivers/gpu/drm/i915/display/intel_fb_pin.h        |    5 +--
 drivers/gpu/drm/i915/display/intel_fbc.c           |   11 +------
 drivers/gpu/drm/i915/display/intel_fbdev.c         |    9 +----
 drivers/gpu/drm/i915/display/intel_plane.c         |    3 +
 drivers/gpu/drm/i915/i915_initial_plane.c          |    2 -
 drivers/gpu/drm/xe/display/xe_fb_pin.c             |    8 ++---
 drivers/gpu/drm/xe/display/xe_initial_plane.c      |    2 -
 9 files changed, 37 insertions(+), 41 deletions(-)
drivers/gpu/drm/i915/display/intel_display_types.h +3 -2
@@ -684,14 +684,15 @@ struct intel_plane_state {
 
 	struct i915_vma *ggtt_vma;
 	struct i915_vma *dpt_vma;
-	unsigned long flags;
-#define PLANE_HAS_FENCE BIT(0)
 
 	struct intel_fb_view view;
 
 	/* for legacy cursor fb unpin */
 	struct drm_vblank_work unpin_work;
 
+	/* fenced region ID (-1 if none) */
+	s8 fence_id;
+
 	/* Plane pxp decryption state */
 	bool decrypt;
 
drivers/gpu/drm/i915/display/intel_fb_pin.c +18 -15
@@ -26,7 +26,6 @@ static struct i915_vma *
 intel_fb_pin_to_dpt(const struct drm_framebuffer *fb,
 		    const struct i915_gtt_view *view,
 		    unsigned int alignment,
-		    unsigned long *out_flags,
 		    struct intel_dpt *dpt)
 {
 	struct intel_display *display = to_intel_display(fb->dev);
@@ -114,8 +113,7 @@ intel_fb_pin_to_ggtt(const struct drm_fr
 		     unsigned int alignment,
 		     unsigned int phys_alignment,
 		     unsigned int vtd_guard,
-		     bool uses_fence,
-		     unsigned long *out_flags)
+		     int *out_fence_id)
 {
 	struct intel_display *display = to_intel_display(fb->dev);
 	struct drm_i915_private *i915 = to_i915(fb->dev);
@@ -175,7 +173,10 @@ retry:
 		goto err_unpin;
 	}
 
-	if (uses_fence && i915_vma_is_map_and_fenceable(vma)) {
+	if (out_fence_id)
+		*out_fence_id = -1;
+
+	if (out_fence_id && i915_vma_is_map_and_fenceable(vma)) {
 		/*
 		 * Install a fence for tiled scan-out. Pre-i965 always needs a
 		 * fence, whereas 965+ only requires a fence if using
@@ -201,7 +202,7 @@ retry:
 		ret = 0;
 
 		if (vma->fence)
-			*out_flags |= PLANE_HAS_FENCE;
+			*out_fence_id = vma->fence->id;
 	}
 
 	i915_vma_get(vma);
@@ -223,9 +224,9 @@ err:
 	return vma;
 }
 
-void intel_fb_unpin_vma(struct i915_vma *vma, unsigned long flags)
+void intel_fb_unpin_vma(struct i915_vma *vma, int fence_id)
 {
-	if (flags & PLANE_HAS_FENCE)
+	if (fence_id >= 0)
 		i915_vma_unpin_fence(vma);
 	i915_vma_unpin(vma);
 	i915_vma_put(vma);
@@ -269,17 +270,18 @@ int intel_plane_pin_fb(struct intel_plan
 	struct i915_vma *vma;
 
 	if (!intel_fb_uses_dpt(&fb->base)) {
+		int fence_id = -1;
+
 		vma = intel_fb_pin_to_ggtt(&fb->base, &plane_state->view.gtt,
 					   intel_plane_fb_min_alignment(plane_state),
 					   intel_plane_fb_min_phys_alignment(plane_state),
 					   intel_plane_fb_vtd_guard(plane_state),
-					   intel_plane_uses_fence(plane_state),
-					   &plane_state->flags);
+					   intel_plane_uses_fence(plane_state) ? &fence_id : NULL);
 		if (IS_ERR(vma))
 			return PTR_ERR(vma);
 
 		plane_state->ggtt_vma = vma;
-
+		plane_state->fence_id = fence_id;
 	} else {
 		unsigned int alignment = intel_plane_fb_min_alignment(plane_state);
 
@@ -290,8 +292,7 @@ int intel_plane_pin_fb(struct intel_plan
 		plane_state->ggtt_vma = vma;
 
 		vma = intel_fb_pin_to_dpt(&fb->base, &plane_state->view.gtt,
-					  alignment, &plane_state->flags,
-					  fb->dpt);
+					  alignment, fb->dpt);
 		if (IS_ERR(vma)) {
 			i915_dpt_unpin_from_ggtt(fb->dpt);
 			plane_state->ggtt_vma = NULL;
@@ -336,12 +337,14 @@ void intel_plane_unpin_fb(struct intel_p
 
 	if (!intel_fb_uses_dpt(&fb->base)) {
 		vma = fetch_and_zero(&old_plane_state->ggtt_vma);
-		if (vma)
-			intel_fb_unpin_vma(vma, old_plane_state->flags);
+		if (vma) {
+			intel_fb_unpin_vma(vma, old_plane_state->fence_id);
+			old_plane_state->fence_id = -1;
+		}
 	} else {
 		vma = fetch_and_zero(&old_plane_state->dpt_vma);
 		if (vma)
-			intel_fb_unpin_vma(vma, old_plane_state->flags);
+			intel_fb_unpin_vma(vma, -1);
 
 		vma = fetch_and_zero(&old_plane_state->ggtt_vma);
 		if (vma)
drivers/gpu/drm/i915/display/intel_fb_pin.h +2 -3
@@ -20,10 +20,9 @@ intel_fb_pin_to_ggtt(const struct drm_fr
 		     unsigned int alignment,
 		     unsigned int phys_alignment,
 		     unsigned int vtd_guard,
-		     bool uses_fence,
-		     unsigned long *out_flags);
+		     int *out_fence_id);
 
-void intel_fb_unpin_vma(struct i915_vma *vma, unsigned long flags);
+void intel_fb_unpin_vma(struct i915_vma *vma, int fence_id);
 
 int intel_plane_pin_fb(struct intel_plane_state *new_plane_state,
 		       const struct intel_plane_state *old_plane_state);
drivers/gpu/drm/i915/display/intel_fbc.c +3 -8
@@ -1458,13 +1458,10 @@ static void intel_fbc_update_state(struc
 
 	fbc_state->fence_y_offset = intel_plane_fence_y_offset(plane_state);
 
-	drm_WARN_ON(display->drm, plane_state->flags & PLANE_HAS_FENCE &&
+	drm_WARN_ON(display->drm, plane_state->fence_id >= 0 &&
 		    !intel_fbc_has_fences(display));
 
-	if (plane_state->flags & PLANE_HAS_FENCE)
-		fbc_state->fence_id = intel_parent_vma_fence_id(display, plane_state->ggtt_vma);
-	else
-		fbc_state->fence_id = -1;
+	fbc_state->fence_id = plane_state->fence_id;
 
 	fbc_state->cfb_stride = intel_fbc_cfb_stride(plane_state);
 	fbc_state->cfb_size = intel_fbc_cfb_size(plane_state);
@@ -1487,9 +1484,7 @@ static bool intel_fbc_is_fence_ok(const
 	 * so have no fence associated with it) due to aperture constraints
 	 * at the time of pinning.
 	 */
-	return DISPLAY_VER(display) >= 9 ||
-		(plane_state->flags & PLANE_HAS_FENCE &&
-		 intel_parent_vma_fence_id(display, plane_state->ggtt_vma) != -1);
+	return DISPLAY_VER(display) >= 9 || plane_state->fence_id >= 0;
 }
 
 static bool intel_fbc_is_cfb_ok(const struct intel_plane_state *plane_state)
drivers/gpu/drm/i915/display/intel_fbdev.c +3 -6
@@ -60,7 +60,6 @@
 struct intel_fbdev {
 	struct intel_framebuffer *fb;
 	struct i915_vma *vma;
-	unsigned long vma_flags;
 };
 
 static struct intel_fbdev *to_intel_fbdev(struct drm_fb_helper *fb_helper)
@@ -141,7 +140,7 @@ static void intel_fbdev_fb_destroy(struc
 	 * the info->screen_base mmaping. Leaking the VMA is simpler than
 	 * trying to rectify all the possible error paths leading here.
 	 */
-	intel_fb_unpin_vma(ifbdev->vma, ifbdev->vma_flags);
+	intel_fb_unpin_vma(ifbdev->vma, -1);
 	drm_framebuffer_remove(fb_helper->fb);
 
 	drm_client_release(&fb_helper->client);
@@ -269,7 +268,6 @@ int intel_fbdev_driver_fbdev_probe(struc
 	struct fb_info *info = helper->info;
 	struct ref_tracker *wakeref;
 	struct i915_vma *vma;
-	unsigned long flags = 0;
 	bool prealloc = false;
 	struct drm_gem_object *obj;
 	int ret;
@@ -314,7 +312,7 @@ int intel_fbdev_driver_fbdev_probe(struc
 				   fb->min_alignment, 0,
 				   intel_fb_view_vtd_guard(&fb->base, &fb->normal_view,
 							   DRM_MODE_ROTATE_0),
-				   false, &flags);
+				   NULL);
 	if (IS_ERR(vma)) {
 		ret = PTR_ERR(vma);
 		goto out_unlock;
@@ -345,14 +343,13 @@ int intel_fbdev_driver_fbdev_probe(struc
 	drm_dbg_kms(display->drm, "allocated %dx%d fb\n", fb->base.width, fb->base.height);
 	ifbdev->fb = fb;
 	ifbdev->vma = vma;
-	ifbdev->vma_flags = flags;
 
 	intel_display_rpm_put(display, wakeref);
 
 	return 0;
 
 out_unpin:
-	intel_fb_unpin_vma(vma, flags);
+	intel_fb_unpin_vma(vma, -1);
 out_unlock:
 	intel_display_rpm_put(display, wakeref);
 
drivers/gpu/drm/i915/display/intel_plane.c +2 -1
@@ -70,6 +70,7 @@ static void intel_plane_state_reset(stru
 	__drm_atomic_helper_plane_state_reset(&plane_state->uapi, &plane->base);
 
 	plane_state->scaler_id = -1;
+	plane_state->fence_id = -1;
 }
 
 struct intel_plane *intel_plane_alloc(void)
@@ -137,7 +138,7 @@ intel_plane_duplicate_state(struct drm_p
 
 	intel_state->ggtt_vma = NULL;
 	intel_state->dpt_vma = NULL;
-	intel_state->flags = 0;
+	intel_state->fence_id = -1;
 	intel_state->damage = DRM_RECT_INIT(0, 0, 0, 0);
 
 	/* add reference to fb */
drivers/gpu/drm/i915/i915_initial_plane.c +1 -1
@@ -268,7 +268,7 @@ i915_initial_plane_setup(struct drm_plan
 	plane_state->ggtt_vma = i915_vma_get(vma);
 	if (intel_plane_uses_fence(plane_state) &&
 	    i915_vma_pin_fence(vma) == 0 && vma->fence)
-		plane_state->flags |= PLANE_HAS_FENCE;
+		plane_state->fence_id = vma->fence->id;
 
 	plane_state->surf = i915_ggtt_offset(plane_state->ggtt_vma);
 
drivers/gpu/drm/xe/display/xe_fb_pin.c +4 -4
@@ -418,15 +418,15 @@ intel_fb_pin_to_ggtt(const struct drm_fr
 		     unsigned int alignment,
 		     unsigned int phys_alignment,
 		     unsigned int vtd_guard,
-		     bool uses_fence,
-		     unsigned long *out_flags)
+		     int *out_fence_id)
 {
-	*out_flags = 0;
+	if (out_fence_id)
+		*out_fence_id = -1;
 
 	return __xe_pin_fb_vma(to_intel_framebuffer(fb), view, alignment);
 }
 
-void intel_fb_unpin_vma(struct i915_vma *vma, unsigned long flags)
+void intel_fb_unpin_vma(struct i915_vma *vma, int fence_id)
 {
 	__xe_unpin_fb_vma(vma);
 }
drivers/gpu/drm/xe/display/xe_initial_plane.c +1 -1
@@ -153,7 +153,7 @@ xe_initial_plane_setup(struct drm_plane_
 	struct i915_vma *vma;
 
 	vma = intel_fb_pin_to_ggtt(fb, &plane_state->view.gtt,
-				   0, 0, 0, false, &plane_state->flags);
+				   0, 0, 0, NULL);
 	if (IS_ERR(vma))
 		return PTR_ERR(vma);
 
author maintainer
#35 lore raw

PATCH 35/101 drm/i915: Introduce struct intel_fb_pin_params

In reply to Greg Kroah-Hartman

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

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

From: Ville Syrjälä <[email protected]>

[ Upstream commit a89b7f72f20db1ae1a65e97881e785639db4480a ]

We need to pass a lot of information between the display driver and the core driver to get framebuffers mapped into GGTT/DPT correctly. Rather than passing around a swarm of integers and boolean as function arguments, let's collect it all into a structure (struct intel_fb_pin_params).

Start by moving the gtt view, alignment, phys_alignment, and vtd_guard there. Going forward additional things need to added as well (mainly various boolean flags).

Stable-dep-of: fc6487579083 ("drm/xe: Fix DPT allocation paths.")

---
 drivers/gpu/drm/i915/display/intel_fb_pin.c   |   54 ++++++++++++++------------
 drivers/gpu/drm/i915/display/intel_fb_pin.h   |   12 +++--
 drivers/gpu/drm/i915/display/intel_fbdev.c    |   13 +++---
 drivers/gpu/drm/xe/display/xe_fb_pin.c        |   39 +++++++++---------
 drivers/gpu/drm/xe/display/xe_initial_plane.c |    6 +-
 5 files changed, 69 insertions(+), 55 deletions(-)
drivers/gpu/drm/i915/display/intel_fb_pin.c +30 -24
@@ -24,9 +24,8 @@
 
 static struct i915_vma *
 intel_fb_pin_to_dpt(const struct drm_framebuffer *fb,
-		    const struct i915_gtt_view *view,
-		    unsigned int alignment,
-		    struct intel_dpt *dpt)
+		    struct intel_dpt *dpt,
+		    const struct intel_fb_pin_params *pin_params)
 {
 	struct intel_display *display = to_intel_display(fb->dev);
 	struct drm_i915_private *i915 = to_i915(fb->dev);
@@ -75,19 +74,20 @@ intel_fb_pin_to_dpt(const struct drm_fra
 		if (ret)
 			continue;
 
-		vma = i915_vma_instance(obj, vm, view);
+		vma = i915_vma_instance(obj, vm, pin_params->view);
 		if (IS_ERR(vma)) {
 			ret = PTR_ERR(vma);
 			continue;
 		}
 
-		if (i915_vma_misplaced(vma, 0, alignment, 0)) {
+		if (i915_vma_misplaced(vma, 0, pin_params->alignment, 0)) {
 			ret = i915_vma_unbind(vma);
 			if (ret)
 				continue;
 		}
 
-		ret = i915_vma_pin_ww(vma, &ww, 0, alignment, PIN_GLOBAL);
+		ret = i915_vma_pin_ww(vma, &ww, 0, pin_params->alignment,
+				      PIN_GLOBAL);
 		if (ret)
 			continue;
 	}
@@ -96,7 +96,8 @@ intel_fb_pin_to_dpt(const struct drm_fra
 		goto err;
 	}
 
-	vma->display_alignment = max(vma->display_alignment, alignment);
+	vma->display_alignment = max(vma->display_alignment,
+				     pin_params->alignment);
 
 	i915_gem_object_flush_if_display(obj);
 
@@ -109,10 +110,7 @@ err:
 
 struct i915_vma *
 intel_fb_pin_to_ggtt(const struct drm_framebuffer *fb,
-		     const struct i915_gtt_view *view,
-		     unsigned int alignment,
-		     unsigned int phys_alignment,
-		     unsigned int vtd_guard,
+		     const struct intel_fb_pin_params *pin_params,
 		     int *out_fence_id)
 {
 	struct intel_display *display = to_intel_display(fb->dev);
@@ -128,7 +126,8 @@ intel_fb_pin_to_ggtt(const struct drm_fr
 	if (drm_WARN_ON(&i915->drm, !i915_gem_object_is_framebuffer(obj)))
 		return ERR_PTR(-EINVAL);
 
-	if (drm_WARN_ON(&i915->drm, alignment && !is_power_of_2(alignment)))
+	if (drm_WARN_ON(&i915->drm, pin_params->alignment &&
+			!is_power_of_2(pin_params->alignment)))
 		return ERR_PTR(-EINVAL);
 
 	/*
@@ -157,8 +156,8 @@ intel_fb_pin_to_ggtt(const struct drm_fr
 	i915_gem_ww_ctx_init(&ww, true);
 retry:
 	ret = i915_gem_object_lock(obj, &ww);
-	if (!ret && phys_alignment)
-		ret = i915_gem_object_attach_phys(obj, phys_alignment);
+	if (!ret && pin_params->phys_alignment)
+		ret = i915_gem_object_attach_phys(obj, pin_params->phys_alignment);
 	else if (!ret && HAS_LMEM(i915))
 		ret = i915_gem_object_migrate(obj, &ww, INTEL_REGION_LMEM_0);
 	if (!ret)
@@ -166,8 +165,10 @@ retry:
 	if (ret)
 		goto err;
 
-	vma = i915_gem_object_pin_to_display_plane(obj, &ww, alignment,
-						   vtd_guard, view, pinctl);
+	vma = i915_gem_object_pin_to_display_plane(obj, &ww,
+						   pin_params->alignment,
+						   pin_params->vtd_guard,
+						   pin_params->view, pinctl);
 	if (IS_ERR(vma)) {
 		ret = PTR_ERR(vma);
 		goto err_unpin;
@@ -270,12 +271,15 @@ int intel_plane_pin_fb(struct intel_plan
 	struct i915_vma *vma;
 
 	if (!intel_fb_uses_dpt(&fb->base)) {
+		struct intel_fb_pin_params pin_params = {
+			.view = &plane_state->view.gtt,
+			.alignment = intel_plane_fb_min_alignment(plane_state),
+			.phys_alignment = intel_plane_fb_min_phys_alignment(plane_state),
+			.vtd_guard = intel_plane_fb_vtd_guard(plane_state),
+		};
 		int fence_id = -1;
 
-		vma = intel_fb_pin_to_ggtt(&fb->base, &plane_state->view.gtt,
-					   intel_plane_fb_min_alignment(plane_state),
-					   intel_plane_fb_min_phys_alignment(plane_state),
-					   intel_plane_fb_vtd_guard(plane_state),
+		vma = intel_fb_pin_to_ggtt(&fb->base, &pin_params,
 					   intel_plane_uses_fence(plane_state) ? &fence_id : NULL);
 		if (IS_ERR(vma))
 			return PTR_ERR(vma);
@@ -283,16 +287,18 @@ int intel_plane_pin_fb(struct intel_plan
 		plane_state->ggtt_vma = vma;
 		plane_state->fence_id = fence_id;
 	} else {
-		unsigned int alignment = intel_plane_fb_min_alignment(plane_state);
+		struct intel_fb_pin_params pin_params = {
+			.view = &plane_state->view.gtt,
+			.alignment = intel_plane_fb_min_alignment(plane_state),
+		};
 
-		vma = i915_dpt_pin_to_ggtt(fb->dpt, alignment / 512);
+		vma = i915_dpt_pin_to_ggtt(fb->dpt, pin_params.alignment / 512);
 		if (IS_ERR(vma))
 			return PTR_ERR(vma);
 
 		plane_state->ggtt_vma = vma;
 
-		vma = intel_fb_pin_to_dpt(&fb->base, &plane_state->view.gtt,
-					  alignment, fb->dpt);
+		vma = intel_fb_pin_to_dpt(&fb->base, fb->dpt, &pin_params);
 		if (IS_ERR(vma)) {
 			i915_dpt_unpin_from_ggtt(fb->dpt);
 			plane_state->ggtt_vma = NULL;
drivers/gpu/drm/i915/display/intel_fb_pin.h +8 -4
@@ -14,12 +14,16 @@ struct intel_plane_state;
 struct i915_gtt_view;
 struct iosys_map;
 
+struct intel_fb_pin_params {
+	const struct i915_gtt_view *view;
+	unsigned int alignment;
+	unsigned int phys_alignment;
+	unsigned int vtd_guard;
+};
+
 struct i915_vma *
 intel_fb_pin_to_ggtt(const struct drm_framebuffer *fb,
-		     const struct i915_gtt_view *view,
-		     unsigned int alignment,
-		     unsigned int phys_alignment,
-		     unsigned int vtd_guard,
+		     const struct intel_fb_pin_params *pin_params,
 		     int *out_fence_id);
 
 void intel_fb_unpin_vma(struct i915_vma *vma, int fence_id);
drivers/gpu/drm/i915/display/intel_fbdev.c +8 -5
@@ -264,6 +264,7 @@ int intel_fbdev_driver_fbdev_probe(struc
 {
 	struct intel_display *display = to_intel_display(helper->dev);
 	struct intel_fbdev *ifbdev = to_intel_fbdev(helper);
+	struct intel_fb_pin_params pin_params = {};
 	struct intel_framebuffer *fb = ifbdev->fb;
 	struct fb_info *info = helper->info;
 	struct ref_tracker *wakeref;
@@ -308,11 +309,13 @@ int intel_fbdev_driver_fbdev_probe(struc
 	 * This also validates that any existing fb inherited from the
 	 * BIOS is suitable for own access.
 	 */
-	vma = intel_fb_pin_to_ggtt(&fb->base, &fb->normal_view.gtt,
-				   fb->min_alignment, 0,
-				   intel_fb_view_vtd_guard(&fb->base, &fb->normal_view,
-							   DRM_MODE_ROTATE_0),
-				   NULL);
+	pin_params.view = &fb->normal_view.gtt;
+	pin_params.alignment = fb->min_alignment;
+	pin_params.vtd_guard = intel_fb_view_vtd_guard(&fb->base,
+						       &fb->normal_view,
+						       DRM_MODE_ROTATE_0);
+
+	vma = intel_fb_pin_to_ggtt(&fb->base, &pin_params, NULL);
 	if (IS_ERR(vma)) {
 		ret = PTR_ERR(vma);
 		goto out_unlock;
drivers/gpu/drm/xe/display/xe_fb_pin.c +19 -20
@@ -140,14 +140,14 @@ write_dpt_remapped(struct xe_bo *bo,
 }
 
 static int __xe_pin_fb_vma_dpt(const struct intel_framebuffer *fb,
-			       const struct i915_gtt_view *view,
-			       struct i915_vma *vma,
-			       unsigned int alignment)
+			       const struct intel_fb_pin_params *pin_params,
+			       struct i915_vma *vma)
 {
 	struct xe_device *xe = to_xe_device(fb->base.dev);
 	struct xe_tile *tile0 = xe_device_get_root_tile(xe);
 	struct xe_ggtt *ggtt = tile0->mem.ggtt;
 	struct drm_gem_object *obj = intel_fb_bo(&fb->base);
+	const struct i915_gtt_view *view = pin_params->view;
 	struct xe_bo *bo = gem_to_xe_bo(obj), *dpt;
 	u32 dpt_size, size = bo->ttm.base.size;
 
@@ -168,7 +168,7 @@ static int __xe_pin_fb_vma_dpt(const str
 						   XE_BO_FLAG_VRAM0 |
 						   XE_BO_FLAG_GGTT |
 						   XE_BO_FLAG_PAGETABLE,
-						   alignment, false);
+						   pin_params->alignment, false);
 	else
 		dpt = xe_bo_create_pin_map_at_novm(xe, tile0,
 						   dpt_size,  ~0ull,
@@ -176,7 +176,7 @@ static int __xe_pin_fb_vma_dpt(const str
 						   XE_BO_FLAG_STOLEN |
 						   XE_BO_FLAG_GGTT |
 						   XE_BO_FLAG_PAGETABLE,
-						   alignment, false);
+						   pin_params->alignment, false);
 	if (IS_ERR(dpt))
 		dpt = xe_bo_create_pin_map_at_novm(xe, tile0,
 						   dpt_size,  ~0ull,
@@ -185,7 +185,7 @@ static int __xe_pin_fb_vma_dpt(const str
 						   XE_BO_FLAG_GGTT |
 						   XE_BO_FLAG_PAGETABLE |
 						   XE_BO_FLAG_FORCE_WC,
-						   alignment, false);
+						   pin_params->alignment, false);
 	if (IS_ERR(dpt))
 		return PTR_ERR(dpt);
 
@@ -269,11 +269,11 @@ static void write_ggtt_rotated_node(stru
 }
 
 static int __xe_pin_fb_vma_ggtt(const struct intel_framebuffer *fb,
-				const struct i915_gtt_view *view,
-				struct i915_vma *vma,
-				unsigned int alignment)
+				const struct intel_fb_pin_params *pin_params,
+				struct i915_vma *vma)
 {
 	struct drm_gem_object *obj = intel_fb_bo(&fb->base);
+	const struct i915_gtt_view *view = pin_params->view;
 	struct xe_bo *bo = gem_to_xe_bo(obj);
 	struct xe_device *xe = to_xe_device(fb->base.dev);
 	struct xe_tile *tile0 = xe_device_get_root_tile(xe);
@@ -319,8 +319,7 @@ static int __xe_pin_fb_vma_ggtt(const st
 }
 
 static struct i915_vma *__xe_pin_fb_vma(const struct intel_framebuffer *fb,
-					const struct i915_gtt_view *view,
-					unsigned int alignment)
+					const struct intel_fb_pin_params *pin_params)
 {
 	struct drm_device *dev = fb->base.dev;
 	struct xe_device *xe = to_xe_device(dev);
@@ -377,9 +376,9 @@ static struct i915_vma *__xe_pin_fb_vma(
 
 	vma->bo = bo;
 	if (intel_fb_uses_dpt(&fb->base))
-		ret = __xe_pin_fb_vma_dpt(fb, view, vma, alignment);
+		ret = __xe_pin_fb_vma_dpt(fb, pin_params, vma);
 	else
-		ret = __xe_pin_fb_vma_ggtt(fb, view, vma,  alignment);
+		ret = __xe_pin_fb_vma_ggtt(fb, pin_params, vma);
 	if (ret)
 		goto err_unpin;
 
@@ -414,16 +413,13 @@ static void __xe_unpin_fb_vma(struct i91
 
 struct i915_vma *
 intel_fb_pin_to_ggtt(const struct drm_framebuffer *fb,
-		     const struct i915_gtt_view *view,
-		     unsigned int alignment,
-		     unsigned int phys_alignment,
-		     unsigned int vtd_guard,
+		     const struct intel_fb_pin_params *pin_params,
 		     int *out_fence_id)
 {
 	if (out_fence_id)
 		*out_fence_id = -1;
 
-	return __xe_pin_fb_vma(to_intel_framebuffer(fb), view, alignment);
+	return __xe_pin_fb_vma(to_intel_framebuffer(fb), pin_params);
 }
 
 void intel_fb_unpin_vma(struct i915_vma *vma, int fence_id)
@@ -475,7 +471,10 @@ int intel_plane_pin_fb(struct intel_plan
 	struct i915_vma *vma;
 	struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
 	struct intel_plane *plane = to_intel_plane(new_plane_state->uapi.plane);
-	unsigned int alignment = plane->min_alignment(plane, fb, 0);
+	struct intel_fb_pin_params pin_params = {
+		.view = &new_plane_state->view.gtt,
+		.alignment = plane->min_alignment(plane, fb, 0),
+	};
 
 	if (reuse_vma(new_plane_state, old_plane_state))
 		return 0;
@@ -484,7 +483,7 @@ int intel_plane_pin_fb(struct intel_plan
 	drm_WARN_ON(bo->ttm.base.dev, !(bo->flags & XE_BO_FLAG_FORCE_WC) &&
 		    bo->ttm.type != ttm_bo_type_sg);
 
-	vma = __xe_pin_fb_vma(intel_fb, &new_plane_state->view.gtt, alignment);
+	vma = __xe_pin_fb_vma(intel_fb, &pin_params);
 
 	if (IS_ERR(vma))
 		return PTR_ERR(vma);
drivers/gpu/drm/xe/display/xe_initial_plane.c +4 -2
@@ -151,9 +151,11 @@ xe_initial_plane_setup(struct drm_plane_
 {
 	struct intel_plane_state *plane_state = to_intel_plane_state(_plane_state);
 	struct i915_vma *vma;
+	struct intel_fb_pin_params pin_params = {
+		.view = &plane_state->view.gtt,
+	};
 
-	vma = intel_fb_pin_to_ggtt(fb, &plane_state->view.gtt,
-				   0, 0, 0, NULL);
+	vma = intel_fb_pin_to_ggtt(fb, &pin_params, NULL);
 	if (IS_ERR(vma))
 		return PTR_ERR(vma);
 
author maintainer
#36 lore raw

PATCH 36/101 drm/xe: Fix DPT allocation paths.

In reply to Greg Kroah-Hartman

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

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

From: Maarten Lankhorst <[email protected]>

[ Upstream commit fc648757908304aedbad74f74bf58192aec383db ]

Remove the fallback for VRAM to system memory, I tested it and that doesn't work at all, only a black screen with pipe fault errors were observed.

On systems with media GT, extra latency is added when accessing stolen memory when the GT is in MC6. Since we additionally aren't counting how much memory is used for stolen and we could in theory fill up the entire stolen area with DPT's, avoid using stolen and only use the default memory region.

Using stolen may also result in random system hangs under load.

Fixes 775d0adc01a5 ("drm/xe/fbdev: Limit the usage of stolen for LNL+")
Cc <[email protected]> # v6.12+
(cherry picked from commit a196406a3831291598fe8e73245914f7acffdfe0)
---
 drivers/gpu/drm/xe/display/xe_fb_pin.c |   33 ++++++++-------------------------
 1 file changed, 8 insertions(+), 25 deletions(-)
drivers/gpu/drm/xe/display/xe_fb_pin.c +8 -25
@@ -161,31 +161,14 @@ static int __xe_pin_fb_vma_dpt(const str
 		dpt_size = ALIGN(intel_rotation_info_size(&view->rotated) * 8,
 				 XE_PAGE_SIZE);
 
-	if (IS_DGFX(xe))
-		dpt = xe_bo_create_pin_map_at_novm(xe, tile0,
-						   dpt_size, ~0ull,
-						   ttm_bo_type_kernel,
-						   XE_BO_FLAG_VRAM0 |
-						   XE_BO_FLAG_GGTT |
-						   XE_BO_FLAG_PAGETABLE,
-						   pin_params->alignment, false);
-	else
-		dpt = xe_bo_create_pin_map_at_novm(xe, tile0,
-						   dpt_size,  ~0ull,
-						   ttm_bo_type_kernel,
-						   XE_BO_FLAG_STOLEN |
-						   XE_BO_FLAG_GGTT |
-						   XE_BO_FLAG_PAGETABLE,
-						   pin_params->alignment, false);
-	if (IS_ERR(dpt))
-		dpt = xe_bo_create_pin_map_at_novm(xe, tile0,
-						   dpt_size,  ~0ull,
-						   ttm_bo_type_kernel,
-						   XE_BO_FLAG_SYSTEM |
-						   XE_BO_FLAG_GGTT |
-						   XE_BO_FLAG_PAGETABLE |
-						   XE_BO_FLAG_FORCE_WC,
-						   pin_params->alignment, false);
+	dpt = xe_bo_create_pin_map_at_novm(xe, tile0,
+					   dpt_size,  ~0ull,
+					   ttm_bo_type_kernel,
+					   XE_BO_FLAG_VRAM_IF_DGFX(tile0) |
+					   XE_BO_FLAG_GGTT |
+					   XE_BO_FLAG_PAGETABLE |
+					   XE_BO_FLAG_FORCE_WC,
+					   pin_params->alignment, false);
 	if (IS_ERR(dpt))
 		return PTR_ERR(dpt);
 
author maintainer
#37 lore raw

PATCH 37/101 nfc: digital: clamp SENSF_RES length to the destination buffer

In reply to Greg Kroah-Hartman

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

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

From: Doruk Tan Ozturk <[email protected]>

commit 344a56d7c8e0f3cbaff0bcb1bcd95a1a1db24b16 upstream.

digital_in_recv_sensf_res() memcpy()s resp->len bytes from a remote NFC-F device response into the NFC_SENSF_RES_MAXSIZE-byte target.sensf_res field without an upper-bound check. A nearby malicious NFC-F device can send an oversized SENSF_RES response to overflow the stack-local struct nfc_target.

Clamp resp->len to NFC_SENSF_RES_MAXSIZE before the copy.

Found by 0sec automated security-research tooling (https://0sec.ai).

---
 net/nfc/digital_technology.c |    2 ++
 1 file changed, 2 insertions(+)
net/nfc/digital_technology.c +2 -0
@@ -778,6 +778,8 @@ static void digital_in_recv_sensf_res(st
 
 	sensf_res = (struct digital_sensf_res *)resp->data;
 
+	resp->len = min_t(unsigned int, resp->len, NFC_SENSF_RES_MAXSIZE);
+
 	memcpy(target.sensf_res, sensf_res, resp->len);
 	target.sensf_res_len = resp->len;
 
author maintainer
#38 lore raw

PATCH 38/101 nfc: fdp: bound the device-reported read length and fix an skb leak

In reply to Greg Kroah-Hartman

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

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

From: Bryam Vargas <[email protected]>

commit 7ad21dcfeb5181af0c3ee2608808c0c0a5283aa1 upstream.

fdp_nci_i2c_read() takes the next packet length from two device-supplied bytes and never validates it. The value is a u16 used as the i2c_master_recv() count into a 261-byte on-stack buffer: a malicious, counterfeit or malfunctioning controller (or an i2c bus interposer) can drive it far past the buffer for a stack out-of-bounds write that clobbers the canary and return address, or below the minimum frame size (directly, or by truncating the computed sum) so the header/LRC strip and the next length read run past a short receive. Reject a length outside [FDP_NCI_I2C_MIN_PAYLOAD, FDP_NCI_I2C_MAX_PAYLOAD], as a corrupted packet already is, and force resynchronization.

The same loop allocates one data skb per iteration and assumes a length packet followed by a data packet; a device that sends two data packets in one call leaks the first skb when the second allocation overwrites it. Free a previously allocated skb before allocating the next.

---
 drivers/nfc/fdp/i2c.c |   27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)
drivers/nfc/fdp/i2c.c +27 -0
@@ -166,9 +166,36 @@ static int fdp_nci_i2c_read(struct fdp_i
 		/* Packet that contains a length */
 		if (tmp[0] == 0 && tmp[1] == 0) {
 			phy->next_read_size = (tmp[2] << 8) + tmp[3] + 3;
+
+			/*
+			 * next_read_size is taken from the device and is used
+			 * as the i2c_master_recv() count for the next packet
+			 * and as the data skb size. A value above the receive
+			 * buffer overflows tmp[]; one below the minimum frame
+			 * size runs the header/LRC strip and the length-field
+			 * read past a short receive. Either way the packet is
+			 * corrupt: drop it and force resynchronization.
+			 */
+			if (phy->next_read_size < FDP_NCI_I2C_MIN_PAYLOAD ||
+			    phy->next_read_size > FDP_NCI_I2C_MAX_PAYLOAD) {
+				dev_dbg(&client->dev, "%s: corrupted packet\n",
+					__func__);
+				phy->next_read_size = FDP_NCI_I2C_MIN_PAYLOAD;
+				goto flush;
+			}
 		} else {
 			phy->next_read_size = FDP_NCI_I2C_MIN_PAYLOAD;
 
+			/*
+			 * Only one data packet is delivered per call; if the
+			 * device sends another, do not overwrite and leak the
+			 * skb allocated for the previous one.
+			 */
+			if (*skb) {
+				kfree_skb(*skb);
+				*skb = NULL;
+			}
+
 			*skb = alloc_skb(len, GFP_KERNEL);
 			if (*skb == NULL) {
 				r = -ENOMEM;
author maintainer
#39 lore raw

PATCH 39/101 nfc: microread: validate target discovery payload lengths

In reply to Greg Kroah-Hartman

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

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

From: Pengpeng Hou <[email protected]>

commit 25519469972ef57c3edb1805dabd6c5612b90211 upstream.

microread_target_discovered() parses target discovery payloads from skb->data according to the HCI gate. The fixed field offsets and UID copies were checked only against the destination nfc_target buffers, not against the actual skb length.

Validate that each gate-specific payload contains the fixed fields and UID bytes before reading or copying them.

Fixes cfad1ba87150 ("NFC: Initial support for Inside Secure microread")
---
 drivers/nfc/microread/microread.c |   31 +++++++++++++++++++++++++++++--
 1 file changed, 29 insertions(+), 2 deletions(-)
drivers/nfc/microread/microread.c +29 -2
@@ -483,13 +483,19 @@ static void microread_target_discovered(
 
 	switch (gate) {
 	case MICROREAD_GATE_ID_MREAD_ISO_A:
+		if (skb->len <= MICROREAD_EMCF_A_LEN) {
+			r = -EINVAL;
+			goto exit_free;
+		}
+
 		targets->supported_protocols =
 		      nfc_hci_sak_to_protocol(skb->data[MICROREAD_EMCF_A_SAK]);
 		targets->sens_res =
 			 be16_to_cpu(*(u16 *)&skb->data[MICROREAD_EMCF_A_ATQA]);
 		targets->sel_res = skb->data[MICROREAD_EMCF_A_SAK];
 		targets->nfcid1_len = skb->data[MICROREAD_EMCF_A_LEN];
-		if (targets->nfcid1_len > sizeof(targets->nfcid1)) {
+		if (targets->nfcid1_len > sizeof(targets->nfcid1) ||
+		    targets->nfcid1_len > skb->len - MICROREAD_EMCF_A_UID) {
 			r = -EINVAL;
 			goto exit_free;
 		}
@@ -497,13 +503,19 @@ static void microread_target_discovered(
 		       targets->nfcid1_len);
 		break;
 	case MICROREAD_GATE_ID_MREAD_ISO_A_3:
+		if (skb->len <= MICROREAD_EMCF_A3_LEN) {
+			r = -EINVAL;
+			goto exit_free;
+		}
+
 		targets->supported_protocols =
 		      nfc_hci_sak_to_protocol(skb->data[MICROREAD_EMCF_A3_SAK]);
 		targets->sens_res =
 			 be16_to_cpu(*(u16 *)&skb->data[MICROREAD_EMCF_A3_ATQA]);
 		targets->sel_res = skb->data[MICROREAD_EMCF_A3_SAK];
 		targets->nfcid1_len = skb->data[MICROREAD_EMCF_A3_LEN];
-		if (targets->nfcid1_len > sizeof(targets->nfcid1)) {
+		if (targets->nfcid1_len > sizeof(targets->nfcid1) ||
+		    targets->nfcid1_len > skb->len - MICROREAD_EMCF_A3_UID) {
 			r = -EINVAL;
 			goto exit_free;
 		}
@@ -511,11 +523,21 @@ static void microread_target_discovered(
 		       targets->nfcid1_len);
 		break;
 	case MICROREAD_GATE_ID_MREAD_ISO_B:
+		if (skb->len < MICROREAD_EMCF_B_UID + 4) {
+			r = -EINVAL;
+			goto exit_free;
+		}
+
 		targets->supported_protocols = NFC_PROTO_ISO14443_B_MASK;
 		memcpy(targets->nfcid1, &skb->data[MICROREAD_EMCF_B_UID], 4);
 		targets->nfcid1_len = 4;
 		break;
 	case MICROREAD_GATE_ID_MREAD_NFC_T1:
+		if (skb->len < MICROREAD_EMCF_T1_UID + 4) {
+			r = -EINVAL;
+			goto exit_free;
+		}
+
 		targets->supported_protocols = NFC_PROTO_JEWEL_MASK;
 		targets->sens_res =
 			le16_to_cpu(*(u16 *)&skb->data[MICROREAD_EMCF_T1_ATQA]);
@@ -523,6 +545,11 @@ static void microread_target_discovered(
 		targets->nfcid1_len = 4;
 		break;
 	case MICROREAD_GATE_ID_MREAD_NFC_T3:
+		if (skb->len < MICROREAD_EMCF_T3_UID + 8) {
+			r = -EINVAL;
+			goto exit_free;
+		}
+
 		targets->supported_protocols = NFC_PROTO_FELICA_MASK;
 		memcpy(targets->nfcid1, &skb->data[MICROREAD_EMCF_T3_UID], 8);
 		targets->nfcid1_len = 8;
author maintainer
#40 lore raw

PATCH 40/101 nfc: llcp: bound the connect_sn TLV walk to the skb

In reply to Greg Kroah-Hartman

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

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

From: Doruk Tan Ozturk <[email protected]>

commit 55c68ac93e7dacc0f5f608b9c39dd4ff48cf28e8 upstream.

Commit 27256cdb290e ("nfc: llcp: bound SNL TLV parsing to the skb and add length checks") fixed the unbounded TLV walk in nfc_llcp_recv_snl(), and commit d8bd2dedbde5 ("nfc: llcp: fix OOB read and u8 offset wrap in TLV parsers") subsequently bounded nfc_llcp_parse_gb_tlv() and nfc_llcp_parse_connection_tlv(). One sibling parser sharing the same pattern remains unbounded: nfc_llcp_connect_sn().

nfc_llcp_connect_sn() walks a TLV list, reading a two-byte header (type, length) followed by length bytes of value, without checking that the two header bytes or the declared length stay within the buffer. It returns a pointer to a service name of up to 255 bytes that may point past the end of the skb; it is subsequently consumed by memcmp() in nfc_llcp_sock_from_sn(). In addition tlv_array_len was computed as "skb->len - LLCP_HEADER_SIZE" in size_t, so a CONNECT/CC frame shorter than the LLCP header underflows to a huge length and the walk runs far past the buffer.

nfc_llcp_connect_sn() is reachable from nfc_llcp_recv_connect() and nfc_llcp_recv_cc(), i.e. from received CONNECT and CC PDUs. A nearby NFC device can reach this without authentication; LLCP link activation happens automatically after NFC-DEP, and the nfc_llcp_rx_skb() dispatcher applies no minimum-length guard.

Walk the TLV list by pointer, bounded by skb_tail_pointer(skb), and validate each declared length before use, matching the approach already used for nfc_llcp_recv_snl(). Starting the walk at &skb->data[LLCP_HEADER_SIZE] against the tail pointer also removes the size_t underflow for short frames.

Found by 0sec automated security-research tooling (https://0sec.ai).

---
 net/nfc/llcp_core.c |   10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)
net/nfc/llcp_core.c +6 -4
@@ -849,13 +849,16 @@ static struct nfc_llcp_sock *nfc_llcp_so
 static const u8 *nfc_llcp_connect_sn(const struct sk_buff *skb, size_t *sn_len)
 {
 	u8 type, length;
-	const u8 *tlv = &skb->data[2];
-	size_t tlv_array_len = skb->len - LLCP_HEADER_SIZE, offset = 0;
+	const u8 *tlv = &skb->data[LLCP_HEADER_SIZE];
+	const u8 *tlv_end = skb_tail_pointer(skb);
 
-	while (offset < tlv_array_len) {
+	while (tlv + 2 < tlv_end) {
 		type = tlv[0];
 		length = tlv[1];
 
+		if (tlv + 2 + length > tlv_end)
+			break;
+
 		pr_debug("type 0x%x length %d\n", type, length);
 
 		if (type == LLCP_TLV_SN) {
@@ -863,7 +866,6 @@ static const u8 *nfc_llcp_connect_sn(con
 			return &tlv[2];
 		}
 
-		offset += length + 2;
 		tlv += length + 2;
 	}
 
author maintainer
#41 lore raw

PATCH 41/101 nfc: llcp: fix OOB read and u8 offset wrap in TLV parsers

In reply to Greg Kroah-Hartman

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

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

From: Muhammad Bilal <[email protected]>

commit 78b20c8eeacd2e44a2d8a4cb5316d3c521d90911 upstream.

nfc_llcp_parse_gb_tlv() and nfc_llcp_parse_connection_tlv() contain three related bugs in their TLV parsing loops:

1. 'offset' is declared u8 but tlv_array_len is u16. When TLV data advances offset past 255 it silently wraps to zero, causing infinite loops or double-processing of buffer data.

2. Before reading tlv[0] (type) and tlv[1] (length) there is no check that offset+2 <= tlv_array_len. A truncated TLV causes an OOB read of one byte past the buffer end.

3. After reading the length field, the value bytes are accessed without checking offset+2+length <= tlv_array_len. A crafted length=0xFF on a short buffer causes up to 255 bytes of OOB read past the buffer end.

Both functions are reachable without authentication via nfc_llcp_set_remote_gb() which feeds remote LLCP general bytes directly into nfc_llcp_parse_gb_tlv() with no additional validation.

Fix all three issues by widening offset from u8 to u16 and adding bounds checks for both the TLV header and value field before each access.

---
 net/nfc/llcp_commands.c |   18 ++++++++++++++++--
 1 file changed, 16 insertions(+), 2 deletions(-)
net/nfc/llcp_commands.c +16 -2
@@ -193,7 +193,8 @@ int nfc_llcp_parse_gb_tlv(struct nfc_llc
 			  const u8 *tlv_array, u16 tlv_array_len)
 {
 	const u8 *tlv = tlv_array;
-	u8 type, length, offset = 0;
+	u8 type, length;
+	u16 offset = 0;
 
 	pr_debug("TLV array length %d\n", tlv_array_len);
 
@@ -201,9 +202,15 @@ int nfc_llcp_parse_gb_tlv(struct nfc_llc
 		return -ENODEV;
 
 	while (offset < tlv_array_len) {
+		if (offset + 2 > tlv_array_len)
+			return -EINVAL;
+
 		type = tlv[0];
 		length = tlv[1];
 
+		if (offset + 2 + length > tlv_array_len)
+			return -EINVAL;
+
 		pr_debug("type 0x%x length %d\n", type, length);
 
 		switch (type) {
@@ -243,7 +250,8 @@ int nfc_llcp_parse_connection_tlv(struct
 				  const u8 *tlv_array, u16 tlv_array_len)
 {
 	const u8 *tlv = tlv_array;
-	u8 type, length, offset = 0;
+	u8 type, length;
+	u16 offset = 0;
 
 	pr_debug("TLV array length %d\n", tlv_array_len);
 
@@ -251,9 +259,15 @@ int nfc_llcp_parse_connection_tlv(struct
 		return -ENOTCONN;
 
 	while (offset < tlv_array_len) {
+		if (offset + 2 > tlv_array_len)
+			return -EINVAL;
+
 		type = tlv[0];
 		length = tlv[1];
 
+		if (offset + 2 + length > tlv_array_len)
+			return -EINVAL;
+
 		pr_debug("type 0x%x length %d\n", type, length);
 
 		switch (type) {
author maintainer
#42 lore raw

PATCH 42/101 nfc: llcp: reject PDUs shorter than the LLCP header

In reply to Greg Kroah-Hartman

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

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

From: Doruk Tan Ozturk <[email protected]>

commit 95674f506c6376d6722a23144c9acd26609771ed upstream.

Every LLCP PDU begins with a two-byte header (DSAP/SSAP + PTYPE), but the receive path never checked that a frame is at least LLCP_HEADER_SIZE bytes before parsing it.

nfc_llcp_rx_skb() reads the header via nfc_llcp_ptype()/nfc_llcp_dsap()/ nfc_llcp_ssap(), which dereference pdu->data[0] and pdu->data[1], and a CONNECT or CC PDU then computes

	tlv_array_len = skb->len - LLCP_HEADER_SIZE;

as a size_t and hands it to the TLV walk. When the frame is shorter than the header the subtraction wraps to a huge value and the walk runs far past the buffer, an out-of-bounds read.

A nearby NFC device can reach this without authentication; LLCP link activation happens automatically after NFC-DEP.

Guard the common receive choke point __nfc_llcp_recv(), shared by both the target (nfc_llcp_data_received()) and initiator (nfc_llcp_recv()) paths, so a short skb is dropped before the rx_work worker parses it. Use pskb_may_pull() rather than a skb->len test so the two header bytes are guaranteed to sit in the skb linear area even for a non-linear skb, matching how the sibling NCI and HCI receive paths validate their headers.

Reproduced with a KFENCE out-of-bounds read via /dev/virtual_nci on linux-next.

Found by 0sec automated security-research tooling (https://0sec.ai).

---
 net/nfc/llcp_core.c |    5 +++++
 1 file changed, 5 insertions(+)
net/nfc/llcp_core.c +5 -0
@@ -1554,6 +1554,11 @@ static void nfc_llcp_rx_work(struct work
 
 static void __nfc_llcp_recv(struct nfc_llcp_local *local, struct sk_buff *skb)
 {
+	if (!pskb_may_pull(skb, LLCP_HEADER_SIZE)) {
+		kfree_skb(skb);
+		return;
+	}
+
 	local->rx_pending = skb;
 	timer_delete(&local->link_timer);
 	schedule_work(&local->rx_work);
author maintainer
#43 lore raw

PATCH 43/101 nfc: pn533: purge fragmented skbs during cleanup

In reply to Greg Kroah-Hartman

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

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

From: Xu Rao <[email protected]>

commit 5718fc62198c38c2de5316020a90506f9e75e0bb upstream.

pn53x_common_clean() purges resp_q before freeing the common PN533 state, but it leaves fragment_skb untouched. The fragmentation helpers queue transmit fragments there while sending large initiator or target-mode frames, and those skbs remain owned by the driver until they are sent or discarded.

If the device is removed while fragments are still queued, the common cleanup path frees the PN533 state without releasing the queued fragment skbs, leaking them.

Purge fragment_skb during cleanup alongside resp_q.

Fixes 963a82e07d4e ("NFC: pn533: Split large Tx frames in chunks")
---
 drivers/nfc/pn533/pn533.c |    1 +
 1 file changed, 1 insertion(+)
drivers/nfc/pn533/pn533.c +1 -0
@@ -2799,6 +2799,7 @@ void pn53x_common_clean(struct pn533 *pr
 	destroy_workqueue(priv->wq);
 
 	skb_queue_purge(&priv->resp_q);
+	skb_queue_purge(&priv->fragment_skb);
 
 	list_for_each_entry_safe(cmd, n, &priv->cmd_queue, queue) {
 		list_del(&cmd->queue);
author maintainer
#44 lore raw

PATCH 44/101 nfc: st21nfca: validate ATR_REQ length against the received frame

In reply to Greg Kroah-Hartman

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

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

From: Doruk Tan Ozturk <[email protected]>

commit 5cdcca5d62a66eda6b774110a44cba67bc1a8d1d upstream.

st21nfca_tm_recv_atr_req() checks that the received ATR_REQ frame is at least ST21NFCA_ATR_REQ_MIN_SIZE and that the self-declared atr_req->length is at least sizeof(struct st21nfca_atr_req), but never checks that atr_req->length does not exceed the actual received length (skb->len).

st21nfca_tm_send_atr_res() then trusts the declared length:

	gb_len = atr_req->length - sizeof(struct st21nfca_atr_req);
	...
	memcpy(atr_res->gbi, atr_req->gbi, gb_len);

so an RF peer that sends a short frame but sets atr_req->length larger than the frame makes gb_len exceed the general bytes actually present, and the memcpy reads out of bounds past the received skb. Those bytes are placed in the ATR_RES and sent back to the peer (kernel-memory disclosure to a proximity attacker); a larger declared length is an out-of-bounds read (DoS).

Reject frames whose declared length exceeds the received length. The adjacent nfc_tm_activated() path in the same function already derives its general-bytes length from skb->len rather than the declared field.

Found by 0sec (https://0sec.ai) using automated source analysis; the missing bound is evident from source. Compile-tested.

Fixes 1892bf844ea0 ("NFC: st21nfca: Adding P2P support to st21nfca in Initiator & Target mode")
Assisted-by 0sec:claude-opus-4-8
---
 drivers/nfc/st21nfca/dep.c |    3 +++
 1 file changed, 3 insertions(+)
drivers/nfc/st21nfca/dep.c +3 -0
@@ -205,6 +205,9 @@ static int st21nfca_tm_recv_atr_req(stru
 	if (atr_req->length < sizeof(struct st21nfca_atr_req))
 		return -EPROTO;
 
+	if (atr_req->length > skb->len)
+		return -EPROTO;
+
 	r = st21nfca_tm_send_atr_res(hdev, atr_req);
 	if (r)
 		return r;
author maintainer
#45 lore raw

PATCH 45/101 nfc: nci: add data_len bound checks to activation parameter extractors

In reply to Greg Kroah-Hartman

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

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

From: Bryam Vargas <[email protected]>

commit 0428fa2c22e2ba0cff766d3b80d461e149102045 upstream.

nci_extract_activation_params_iso_dep() and nci_extract_activation_params_nfc_dep() read an inner length byte from the NCI RF_INTF_ACTIVATED_NTF payload and use it to memcpy() into fixed kernel buffers, but neither function receives the caller-validated activation_params_len. A crafted NCI notification with activation_params_len=1 and an inner length byte of up to 20 (NFC-A) or 50 (NFC-B) causes memcpy() to read that many bytes past the one valid byte in the activation params region -- a slab out-of-bounds read of kernel memory adjacent to the NCI skb.

The sibling nci_extract_rf_params_*() family was given equivalent protection by commit 571dcbeb8e63 ("net: nfc: nci: Fix parameter validation for packet data"), but the two activation parameter extractors were not updated at that time.

Add a data_len parameter to both functions, guard against an empty region before consuming the inner length byte, decrement the remaining count after consuming it, and clamp the copy length to what is actually available. Update both call sites to pass ntf.activation_params_len, which is already validated against the skb at ntf.c:801.

Fixes e8c0dacd9836 ("NFC: Update names and structs to NCI spec 1.0 d18")
---
 net/nfc/nci/ntf.c |   26 ++++++++++++++++++++++----
 1 file changed, 22 insertions(+), 4 deletions(-)
net/nfc/nci/ntf.c +22 -4
@@ -525,15 +525,19 @@ static int nci_rf_discover_ntf_packet(st
 
 static int nci_extract_activation_params_iso_dep(struct nci_dev *ndev,
 						 struct nci_rf_intf_activated_ntf *ntf,
-						 const __u8 *data)
+						 const __u8 *data, __u8 data_len)
 {
 	struct activation_params_nfca_poll_iso_dep *nfca_poll;
 	struct activation_params_nfcb_poll_iso_dep *nfcb_poll;
 
 	switch (ntf->activation_rf_tech_and_mode) {
 	case NCI_NFC_A_PASSIVE_POLL_MODE:
+		if (data_len < 1)
+			return NCI_STATUS_RF_PROTOCOL_ERROR;
 		nfca_poll = &ntf->activation_params.nfca_poll_iso_dep;
 		nfca_poll->rats_res_len = min_t(__u8, *data++, NFC_ATS_MAXSIZE);
+		data_len--;
+		nfca_poll->rats_res_len = min_t(__u8, nfca_poll->rats_res_len, data_len);
 		pr_debug("rats_res_len %d\n", nfca_poll->rats_res_len);
 		if (nfca_poll->rats_res_len > 0) {
 			memcpy(nfca_poll->rats_res,
@@ -542,8 +546,12 @@ static int nci_extract_activation_params
 		break;
 
 	case NCI_NFC_B_PASSIVE_POLL_MODE:
+		if (data_len < 1)
+			return NCI_STATUS_RF_PROTOCOL_ERROR;
 		nfcb_poll = &ntf->activation_params.nfcb_poll_iso_dep;
 		nfcb_poll->attrib_res_len = min_t(__u8, *data++, 50);
+		data_len--;
+		nfcb_poll->attrib_res_len = min_t(__u8, nfcb_poll->attrib_res_len, data_len);
 		pr_debug("attrib_res_len %d\n", nfcb_poll->attrib_res_len);
 		if (nfcb_poll->attrib_res_len > 0) {
 			memcpy(nfcb_poll->attrib_res,
@@ -562,7 +570,7 @@ static int nci_extract_activation_params
 
 static int nci_extract_activation_params_nfc_dep(struct nci_dev *ndev,
 						 struct nci_rf_intf_activated_ntf *ntf,
-						 const __u8 *data)
+						 const __u8 *data, __u8 data_len)
 {
 	struct activation_params_poll_nfc_dep *poll;
 	struct activation_params_listen_nfc_dep *listen;
@@ -570,9 +578,13 @@ static int nci_extract_activation_params
 	switch (ntf->activation_rf_tech_and_mode) {
 	case NCI_NFC_A_PASSIVE_POLL_MODE:
 	case NCI_NFC_F_PASSIVE_POLL_MODE:
+		if (data_len < 1)
+			return NCI_STATUS_RF_PROTOCOL_ERROR;
 		poll = &ntf->activation_params.poll_nfc_dep;
 		poll->atr_res_len = min_t(__u8, *data++,
 					  NFC_ATR_RES_MAXSIZE - 2);
+		data_len--;
+		poll->atr_res_len = min_t(__u8, poll->atr_res_len, data_len);
 		pr_debug("atr_res_len %d\n", poll->atr_res_len);
 		if (poll->atr_res_len > 0)
 			memcpy(poll->atr_res, data, poll->atr_res_len);
@@ -580,9 +592,13 @@ static int nci_extract_activation_params
 
 	case NCI_NFC_A_PASSIVE_LISTEN_MODE:
 	case NCI_NFC_F_PASSIVE_LISTEN_MODE:
+		if (data_len < 1)
+			return NCI_STATUS_RF_PROTOCOL_ERROR;
 		listen = &ntf->activation_params.listen_nfc_dep;
 		listen->atr_req_len = min_t(__u8, *data++,
 					    NFC_ATR_REQ_MAXSIZE - 2);
+		data_len--;
+		listen->atr_req_len = min_t(__u8, listen->atr_req_len, data_len);
 		pr_debug("atr_req_len %d\n", listen->atr_req_len);
 		if (listen->atr_req_len > 0)
 			memcpy(listen->atr_req, data, listen->atr_req_len);
@@ -806,12 +822,14 @@ static int nci_rf_intf_activated_ntf_pac
 		switch (ntf.rf_interface) {
 		case NCI_RF_INTERFACE_ISO_DEP:
 			err = nci_extract_activation_params_iso_dep(ndev,
-								    &ntf, data);
+								    &ntf, data,
+								    ntf.activation_params_len);
 			break;
 
 		case NCI_RF_INTERFACE_NFC_DEP:
 			err = nci_extract_activation_params_nfc_dep(ndev,
-								    &ntf, data);
+								    &ntf, data,
+								    ntf.activation_params_len);
 			break;
 
 		case NCI_RF_INTERFACE_FRAME:
author maintainer
#46 lore raw

PATCH 46/101 nfc: nci: fix out-of-bounds write in nci_target_auto_activated()

In reply to Greg Kroah-Hartman

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

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

From: Samuel Page <[email protected]>

commit ac200079db50af81e6b04d058b33ec92901d8edd upstream.

nci_target_auto_activated() appends a target to the fixed-size array ndev->targets[NCI_MAX_DISCOVERED_TARGETS] and increments ndev->n_targets without first checking the array is full; unlike its sibling nci_add_new_target(), which bails out when n_targets already equals NCI_MAX_DISCOVERED_TARGETS.

ndev->n_targets is only cleared by nci_clear_target_list(), so an NFCC that repeatedly re-runs discovery (RF_DISCOVER_RSP, which re-enters NCI_DISCOVERY without clearing the target list) and reports an auto-activated target (RF_INTF_ACTIVATED_NTF) drives n_targets past the limit. The append then writes a struct nfc_target past the end of the array (a slab out-of-bounds write), and nfc_targets_found() goes on to walk the array with the inflated count:

  BUG: KASAN: slab-out-of-bounds in nci_add_new_protocol+0x94/0x2ac [nci]
  Write of size 2 at addr ffff0000c7299a18 by task kworker/u8:0/12
  Workqueue: nfc0_nci_rx_wq nci_rx_work [nci]
  Call trace:
   nci_add_new_protocol+0x94/0x2ac [nci]
   nci_ntf_packet+0xddc/0x11a0 [nci]
   nci_rx_work+0x15c/0x1e0 [nci]
   process_one_work+0x2dc/0x500
   worker_thread+0x240/0x460
   kthread+0x1c0/0x1d0
   ret_from_fork+0x10/0x20

  The buggy address belongs to the cache kmalloc-2k of size 2048
  The buggy address is located 1024 bytes to the right of
  allocated 1560-byte region [ffff0000c7299000, ffff0000c7299618)

Guard nci_target_auto_activated() with the same check used by nci_add_new_target().

Fixes 019c4fbaa790 ("NFC: Add NCI multiple targets support")
Assisted-by Bynario AI
---
 net/nfc/nci/ntf.c |    6 ++++++
 1 file changed, 6 insertions(+)
net/nfc/nci/ntf.c +6 -0
@@ -619,6 +619,12 @@ static void nci_target_auto_activated(st
 	struct nfc_target *target;
 	int rc;
 
+	/* This is a new target, check if we've enough room */
+	if (ndev->n_targets == NCI_MAX_DISCOVERED_TARGETS) {
+		pr_debug("not enough room, ignoring new target...\n");
+		return;
+	}
+
 	target = &ndev->targets[ndev->n_targets];
 
 	rc = nci_add_new_protocol(ndev, target, ntf->rf_protocol,
author maintainer
#47 lore raw

PATCH 47/101 nfc: nci: fix uninit-value in the RF discover/activated NTF handlers

In reply to Greg Kroah-Hartman

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

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

From: Samuel Page <[email protected]>

commit 8cbe06c1e699c0a165dae5093a2550e65f914818 upstream.

nci_rf_discover_ntf_packet() and nci_rf_intf_activated_ntf_packet() each parse a notification into an on-stack struct (nci_rf_discover_ntf / nci_rf_intf_activated_ntf) that is not initialised. The RF technology-specific parameters are only extracted when rf_tech_specific_params_len is non-zero, so a notification that reports a zero length leaves the rf_tech_specific_params union uninitialised - and both handlers then pass it to nci_add_new_protocol(), which reads it:

- discover: nci_add_new_target() -> nci_add_new_protocol();
- activated: nci_target_auto_activated() -> nci_add_new_protocol().

nci_add_new_protocol() uses nfca_poll->nfcid1_len as both a branch condition and a memcpy() length and copies nfcid1/sens_res/sel_res into ndev->targets, which is later exposed to user space via NFC_CMD_GET_TARGET.

  BUG: KMSAN: uninit-value in nci_add_new_protocol+0x624/0x6c0
   nci_add_new_protocol+0x624/0x6c0
   nci_ntf_packet+0x25b2/0x3c30
   nci_rx_work+0x318/0x5d0
   process_scheduled_works+0x84b/0x17a0
   worker_thread+0xc10/0x11b0
   kthread+0x376/0x500
  Local variable ntf.i created at:
   nci_ntf_packet+0xbc2/0x3c30

Zero-initialise both on-stack notifications so the union reads back as zero when no technology-specific parameters are present.

Fixes 019c4fbaa790 ("NFC: Add NCI multiple targets support")
Fixes e8c0dacd9836 ("NFC: Update names and structs to NCI spec 1.0 d18")
Assisted-by Bynario AI
---
 net/nfc/nci/ntf.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
net/nfc/nci/ntf.c +2 -2
@@ -440,7 +440,7 @@ void nci_clear_target_list(struct nci_de
 static int nci_rf_discover_ntf_packet(struct nci_dev *ndev,
 				      const struct sk_buff *skb)
 {
-	struct nci_rf_discover_ntf ntf;
+	struct nci_rf_discover_ntf ntf = {};
 	const __u8 *data;
 	bool add_target = true;
 
@@ -710,7 +710,7 @@ static int nci_rf_intf_activated_ntf_pac
 					    const struct sk_buff *skb)
 {
 	struct nci_conn_info *conn_info;
-	struct nci_rf_intf_activated_ntf ntf;
+	struct nci_rf_intf_activated_ntf ntf = {};
 	const __u8 *data;
 	int err = NCI_STATUS_OK;
 
author maintainer
#48 lore raw

PATCH 48/101 nfc: nci: free destination parameters when closing a connection

In reply to Greg Kroah-Hartman

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

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

From: Linmao Li <[email protected]>

commit 2e65bafdfd3a8bba972b3d17b6a57816557530fc upstream.

When a connection is closed, nci_core_conn_close_rsp_packet() frees conn_info but not conn_info->dest_params, which is a separate devm allocation. Each connect/close cycle leaks one dest_params until the NFC device is removed. Free dest_params along with conn_info.

Fixes 9b8d1a4cf2aa ("nfc: nci: Add an additional parameter to identify a connection id")
---
 net/nfc/nci/rsp.c |    1 +
 1 file changed, 1 insertion(+)
net/nfc/nci/rsp.c +1 -0
@@ -336,6 +336,7 @@ static void nci_core_conn_close_rsp_pack
 			list_del(&conn_info->list);
 			if (conn_info == ndev->rf_conn_info)
 				ndev->rf_conn_info = NULL;
+			devm_kfree(&ndev->nfc_dev->dev, conn_info->dest_params);
 			devm_kfree(&ndev->nfc_dev->dev, conn_info);
 		}
 	}
author maintainer
#49 lore raw

PATCH 49/101 ipv4: reject undersized MTUs in ip_do_fragment()

In reply to Greg Kroah-Hartman

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

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

From: Yong Wang <[email protected]>

commit c0726f0caf8c6b3208552949e17d23634a2f3129 upstream.

ip_do_fragment() subtracts the IPv4 header length from the effective MTU and passes the resulting payload MTU to ip_frag_next().

If the effective MTU is smaller than hlen + 8, ip_frag_next() rounds the fragment payload length down to zero. The fragmentation state then never makes forward progress: state->left, state->ptr and state->offset stay unchanged while ip_do_fragment() keeps allocating and transmitting header-only fragments until the softlockup detector fires.

This is reproducible with a route installed using "mtu lock 20", but it is also reproducible without route MTU lock, for example by forwarding a packet to a device whose MTU is 20.

Fix it in ip_do_fragment() by rejecting mtu < hlen + 8 with -EMSGSIZE, matching the existing IPv6 fragmentation check.

---
 net/ipv4/ip_output.c |    4 ++++
 1 file changed, 4 insertions(+)
net/ipv4/ip_output.c +4 -0
@@ -791,6 +791,10 @@ int ip_do_fragment(struct net *net, stru
 	 */
 
 	hlen = iph->ihl * 4;
+	if (mtu < hlen + 8) {
+		err = -EMSGSIZE;
+		goto fail;
+	}
 	mtu = mtu - hlen;	/* Size of data space */
 	IPCB(skb)->flags |= IPSKB_FRAG_COMPLETE;
 	ll_rs = LL_RESERVED_SPACE(rt->dst.dev);
author maintainer
#50 lore raw

PATCH 50/101 ipv6: fix use-after-free in ip6_finish_output2()

In reply to Greg Kroah-Hartman

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

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

From: Luxiao Xu <[email protected]>

commit d0d48d999b0eee6bb176ef4e39d9be868fa80f7e upstream.

ip6_finish_output2() caches a pointer to the IPv6 destination address (daddr) before invoking lwtunnel_xmit(). The LWT-BPF transmit path or other encapsulation operations within lwtunnel_xmit() can reallocate the skb head, freeing the memory that daddr points to. When lwtunnel_xmit() returns LWTUNNEL_XMIT_CONTINUE, the function continues to use the stale daddr pointer to compute the nexthop and to look up or create the neighbour entry. This results in a use-after-free read, which can leak sensitive kernel data, pollute the neighbour table with arbitrary values, misdirect traffic, or crash the system.

Fix this by re-fetching the IPv6 header and the destination address pointer after lwtunnel_xmit() returns LWTUNNEL_XMIT_CONTINUE, ensuring that the subsequent nexthop computation and neighbour lookup operate on valid memory.

---
 net/ipv6/ip6_output.c |    2 ++
 1 file changed, 2 insertions(+)
net/ipv6/ip6_output.c +2 -0
@@ -116,6 +116,8 @@ static int ip6_finish_output2(struct net
 
 		if (res != LWTUNNEL_XMIT_CONTINUE)
 			return res;
+		hdr = ipv6_hdr(skb);
+		daddr = &hdr->daddr;
 	}
 
 	IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
author maintainer
#51 lore raw

PATCH 51/101 mailbox: mchp-ipc-sbi: Add null check for devm_kasprintf()

In reply to Greg Kroah-Hartman

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

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

From: Griffin Kroah-Hartman <[email protected]>

commit b37c4d0a2fd90c0c31223acd37f763eb8953ed1a upstream.

Add a check to see if devm_kasprintf() is not NULL in mchp_ipc_get_cluster_aggr_irq(), returning -ENOMEM if the function failed.

Assisted-by gkh_clanker_t1000
CC Jassi Brar <[email protected]>
---
 drivers/mailbox/mailbox-mchp-ipc-sbi.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/mailbox/mailbox-mchp-ipc-sbi.c +2 -0
@@ -378,6 +378,8 @@ static int mchp_ipc_get_cluster_aggr_irq
 	for_each_online_cpu(cpuid) {
 		hartid = cpuid_to_hartid_map(cpuid);
 		irq_name = devm_kasprintf(ipc->dev, GFP_KERNEL, "hart-%lu", hartid);
+		if (!irq_name)
+			return -ENOMEM;
 		ret = platform_get_irq_byname_optional(pdev, irq_name);
 		if (ret <= 0)
 			continue;
author maintainer
#52 lore raw

PATCH 52/101 dmaengine: fsl-edma: Add error handling for devm_kasprintf

In reply to Greg Kroah-Hartman

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

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

From: Griffin Kroah-Hartman <[email protected]>

commit bf1af4dfdc017dfe989c0dbcf0e608dc95f1d2cb upstream.

Add error handling statement to fls_edma3_irq_init() for the devm_kasprintf call.

---
 drivers/dma/fsl-edma-main.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/dma/fsl-edma-main.c +2 -0
@@ -414,6 +414,8 @@ static int fsl_edma3_irq_init(struct pla
 
 		errirq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s-err",
 					     dev_name(&pdev->dev));
+		if (!errirq_name)
+			return -ENOMEM;
 
 		ret = devm_request_irq(&pdev->dev, fsl_edma->errirq, fsl_edma3_err_handler_shared,
 				       0, errirq_name, fsl_edma);
author maintainer
#53 lore raw

PATCH 53/101 nvmet-auth: zero the AUTH_RECEIVE response buffer

In reply to Greg Kroah-Hartman

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

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

From: Bryam Vargas <[email protected]>

commit 3ddcfb013322aa37eaa7a0d344b73079c38dfa21 upstream.

nvmet_execute_auth_receive() allocates the response buffer with kmalloc() sized by the host-supplied AUTH_RECEIVE allocation length, but the DH-HMAC-CHAP builders write only a fixed-size message into it. The full allocation length is then copied to the wire by nvmet_copy_to_sgl(), so a remote initiator receives the bytes past the built message -- up to nearly a page of uninitialized slab -- during the pre-authentication handshake.

Allocate the buffer with kzalloc() so the unwritten tail is zeroed before it is sent; conforming responses are unaffected.

Fixes db1312dd9548 ("nvmet: implement basic In-Band Authentication")
---
 drivers/nvme/target/fabrics-cmd-auth.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/nvme/target/fabrics-cmd-auth.c +1 -1
@@ -557,7 +557,7 @@ void nvmet_execute_auth_receive(struct n
 		return;
 	}
 
-	d = kmalloc(al, GFP_KERNEL);
+	d = kzalloc(al, GFP_KERNEL);
 	if (!d) {
 		status = NVME_SC_INTERNAL;
 		goto done;
author maintainer
#54 lore raw

PATCH 54/101 nvmet-fc: fix invalid free in LS IOD error path

In reply to Greg Kroah-Hartman

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

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

From: Jiang HongHui <[email protected]>

commit ba98d6796d12258e837ece065d2ecb59d76ce4ff upstream.

nvmet_fc_alloc_ls_iodlist() advances iod while initializing the LS IOD array. If an rqstbuf allocation or response buffer DMA mapping fails, the unwind loop decrements iod past the start of the array. The final kfree(iod) therefore frees an address before the allocated object.

This can be reproduced with nvme-fcloop and failslab by setting fail-nth to 6 before creating a target port. KASAN reports:

  BUG: KASAN: invalid-free in nvmet_fc_register_targetport
  Free of addr ffff88816cf8ff48 by task nvmet_fail_nth/9552

Free the original allocation base stored in tgtport->iod instead. With this fix applied, the same sysfs write with fail-nth=6 returns -ENOMEM without any KASAN report.

Fixes c53432030d86 ("nvme-fabrics: Add target support for FC transport")
Assisted-by Codex:gpt-5
---
 drivers/nvme/target/fc.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/nvme/target/fc.c +1 -1
@@ -566,7 +566,7 @@ out_fail:
 		list_del(&iod->ls_rcv_list);
 	}
 
-	kfree(iod);
+	kfree(tgtport->iod);
 
 	return -EFAULT;
 }
author maintainer
#55 lore raw

PATCH 55/101 nvmet-tcp: bound SGL data length before allocating command buffers

In reply to Greg Kroah-Hartman

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

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

From: Ibrahim Hashimov <[email protected]>

commit 4a3f00262a044e8e15064b1a6860968bf0500bf4 upstream.

nvmet_tcp_map_data() reads the host-controlled 32-bit sgl->length and, for the in-capsule offset descriptor (type 0x01), checks it against port->inline_data_size before use. Any other SGL descriptor type -- including the non-inline transport SGL data-block descriptor (type (NVME_TRANSPORT_SGL_DATA_DESC << 4) | NVME_SGL_FMT_TRANSPORT_A, the type a real host uses for out-of-capsule writes) skips that check entirely and falls straight through to:

	cmd->req.sg = sgl_alloc(len, GFP_KERNEL, &cmd->req.sg_cnt);

with len taken directly from the wire, unbounded up to 4 GiB.

nvmet_req_init() only parses the command and never inspects sgl->length, and nvmet_check_transfer_len() -- the only other place transfer_len is validated -- runs later, from req->execute(), after the allocation has already happened. For a write command the target responds with an R2T and parks the command waiting for the host to send the data; if the host (or an unauthenticated peer that simply never follows up) never does, the sgl_alloc() buffer stays resident for the life of the command. NVMe/TCP has no mandatory authentication in the default configuration, so any peer able to reach the target portal and complete a Fabrics connect can drive this with a single crafted command, repeatable across queues and connections for amplification. This is unbounded kernel memory allocation triggered by a remote, effectively unauthenticated peer.

Validate len against the same NVMET_TCP_MAXH2CDATA ceiling this file already uses to bound per-PDU H2C data, for every SGL descriptor type, before doing any allocation. This closes the gap for the non-inline descriptor while leaving the existing, tighter inline_data_size check in place for the in-capsule case.

Runtime-verified on a v6.19 KASAN stand: with this bound in place, a crafted write command carrying an oversized non-inline SGL length is rejected before sgl_alloc() runs, where the same request previously drove an unbounded ~256 MiB kernel allocation (up to 4 GiB) that stayed resident pending an R2T the host never satisfies.

Fixes 872d26a391da ("nvmet-tcp: add NVMe over TCP target driver")
Assisted-by AuditCode-AI:2026.07
---
 drivers/nvme/target/tcp.c |   13 +++++++++++++
 1 file changed, 13 insertions(+)
drivers/nvme/target/tcp.c +13 -0
@@ -422,6 +422,19 @@ static int nvmet_tcp_map_data(struct nvm
 	if (!len)
 		return 0;
 
+	/*
+	 * inline_data_size only bounds the in-capsule (type 0x01) SGL
+	 * descriptor below. A non-inline transport SGL data-block
+	 * descriptor skips that check entirely and would otherwise reach
+	 * sgl_alloc() with an attacker-controlled len of up to 4 GiB,
+	 * pinning that much kernel memory for a command that may never
+	 * complete. Bound every descriptor type here, before allocating
+	 * anything, using the same ceiling this file already applies to
+	 * per-PDU H2C data.
+	 */
+	if (len > NVMET_TCP_MAXH2CDATA)
+		return NVME_SC_SGL_INVALID_DATA | NVME_STATUS_DNR;
+
 	if (sgl->type == ((NVME_SGL_FMT_DATA_DESC << 4) |
 			  NVME_SGL_FMT_OFFSET)) {
 		if (!nvme_is_write(cmd->req.cmd))
author maintainer
#56 lore raw

PATCH 56/101 nvmet-tcp: Do not WARN on remotely-controlled oversized SGL allocations

In reply to Greg Kroah-Hartman

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

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

From: Greg Kroah-Hartman <[email protected]>

commit 737a3b535247226f6e1a7988fd9d6e63e7d6fc71 upstream.

When fuzzing the nvme target code, I tripped a kernel warning in nvmet_tcp_map_data() because the length passed into the allocator is controlled by the remote initiator.

A remote initiator that sends a command with an SGL claiming a huge number, can create a scatterlist and iovec allocation of over 1 million entries, which causes the backing kmalloc call to exceed MAX_PAGE_ORDER and then the page allocator will trip on a WARN_ON_ONCE_GFP() message:

  WARNING: mm/page_alloc.c:5280 __alloc_frozen_pages_noprof
  Workqueue: nvmet_tcp_wq nvmet_tcp_io_work
  ...
  sgl_alloc_order
  nvmet_tcp_map_data
  nvmet_tcp_try_recv_pdu

As it's never good to trip a kernel warning remotely due to many systems having panic-on-warn enabled, let's silence it by just add GFP_NOWARN to the allocation flags.

---
 drivers/nvme/target/tcp.c |    6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/nvme/target/tcp.c +4 -2
@@ -446,13 +446,15 @@ static int nvmet_tcp_map_data(struct nvm
 	}
 	cmd->req.transfer_len += len;
 
-	cmd->req.sg = sgl_alloc(len, GFP_KERNEL, &cmd->req.sg_cnt);
+	cmd->req.sg = sgl_alloc(len, GFP_KERNEL | __GFP_NOWARN,
+				&cmd->req.sg_cnt);
 	if (!cmd->req.sg)
 		return NVME_SC_INTERNAL;
 	cmd->cur_sg = cmd->req.sg;
 
 	if (nvmet_tcp_has_data_in(cmd)) {
-		cmd->iov = kmalloc_objs(*cmd->iov, cmd->req.sg_cnt);
+		cmd->iov = kmalloc_objs(*cmd->iov, cmd->req.sg_cnt,
+					GFP_KERNEL | __GFP_NOWARN);
 		if (!cmd->iov)
 			goto err;
 	}
author maintainer
#57 lore raw

PATCH 57/101 nvmet: pci-epf: fix use-after-free in nvmet_pci_epf_exec_iod_work()

In reply to Greg Kroah-Hartman

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

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

From: Shin'ichiro Kawasaki <[email protected]>

commit c9e9bb757971485b4e8414b1744507af186d72c9 upstream.

nvmet_pci_epf_exec_iod_work() submits an I/O command with req->execute() and then waits for the command to complete and transfers the data back to the host. This wait is not needed for commands that do not transfer data from the device to the host. To decide whether that wait is needed, it reads iod->data_len and iod->dma_dir after calling req->execute().

However, once req->execute() is called, the command may complete asynchronously on another CPU. For commands that do not require a device-to-host data transfer, nvmet_pci_epf_queue_response() calls nvmet_pci_epf_complete_iod() directly, which can free the iod before it reads iod->data_len and iod->dma_dir, resulting in the KFENCE use-after- free:

 BUG: KFENCE: use-after-free read in nvmet_pci_epf_exec_iod_work+0x288/0x798 [nvmet_pci_epf]

Use-after-free read at 0x00000000fdfa6d03 (in kfence-#63):

  nvmet_pci_epf_exec_iod_work+0x288/0x798 [nvmet_pci_epf]
  process_one_work+0x15c/0x4f0
  worker_thread+0x18c/0x30c
  kthread+0x130/0x140
  ret_from_fork+0x10/0x20

kfence-#63: 0x00000000e3de0e71-0x00000000c938ad62, size=712, cache=kmalloc-1k

allocated by task 10 on cpu 0 at 73.995480s (0.005122s ago):

  mempool_kmalloc+0x1c/0x28
  mempool_alloc_noprof+0x40/0x9c
  nvmet_pci_epf_poll_sqs_work+0xd4/0x344 [nvmet_pci_epf]
  process_one_work+0x15c/0x4f0
  worker_thread+0x18c/0x30c
  kthread+0x130/0x140
  ret_from_fork+0x10/0x20

freed by task 131 on cpu 3 at 73.995521s (0.008385s ago):

  mempool_kfree+0x10/0x20
  mempool_free+0x44/0x64
  nvmet_pci_epf_free_iod+0x88/0x98 [nvmet_pci_epf]
  nvmet_pci_epf_cq_work+0xfc/0x280 [nvmet_pci_epf]
  process_one_work+0x15c/0x4f0
  worker_thread+0x18c/0x30c
  kthread+0x130/0x140
  ret_from_fork+0x10/0x20

Fix this by referring to iod->data_len and iod->dma_dir before calling req->execute(). The remaining iod accesses such as iod->status are only reached on the device-to-host read path. In this case, nvmet_pci_epf_queue_response() signals iod->done instead of freeing the iod, so the iod stays valid.

Fixes 0faa0fe6f90e ("nvmet: New NVMe PCI endpoint function target driver")
---
 drivers/nvme/target/pci-epf.c |    9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)
drivers/nvme/target/pci-epf.c +6 -3
@@ -1594,6 +1594,7 @@ static void nvmet_pci_epf_exec_iod_work(
 	struct nvmet_pci_epf_iod *iod =
 		container_of(work, struct nvmet_pci_epf_iod, work);
 	struct nvmet_req *req = &iod->req;
+	bool no_wait;
 	int ret;
 
 	if (!iod->ctrl->link_up) {
@@ -1638,14 +1639,16 @@ static void nvmet_pci_epf_exec_iod_work(
 		}
 	}
 
-	req->execute(req);
-
 	/*
 	 * If we do not have data to transfer after the command execution
 	 * finishes, nvmet_pci_epf_queue_response() will complete the command
 	 * directly. No need to wait for the completion in this case.
 	 */
-	if (!iod->data_len || iod->dma_dir != DMA_TO_DEVICE)
+	no_wait = !iod->data_len || iod->dma_dir != DMA_TO_DEVICE;
+
+	req->execute(req);
+
+	if (no_wait)
 		return;
 
 	wait_for_completion(&iod->done);
author maintainer
#58 lore raw

PATCH 58/101 nvmet: pci-epf: put CQ ref on create_cq mapping failure

In reply to Greg Kroah-Hartman

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

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

From: Yifei Gao <[email protected]>

commit 659ae9d02cb5d72c76f74fff7441eb8fb64d8f5c upstream.

nvmet_pci_epf_create_cq() calls nvmet_cq_create(), which takes a reference on the controller and installs the completion queue. If the subsequent PCI address-space mapping fails or returns a too-small partial mapping, the function jumps to err_internal / err_unmap_queue without calling nvmet_cq_put(). The matching put in nvmet_pci_epf_delete_cq() is gated on NVMET_PCI_EPF_Q_LIVE, which is only set after the mapping succeeds, so teardown never releases these references. A remote PCI host that drives Create IO CQ commands with a failing PRP1/pci_addr therefore leaks the CQ and a controller reference on each attempt.

Drop the CQ reference on the mapping-failure paths. The err_internal and err_unmap_queue labels are only reachable after nvmet_cq_create() has succeeded, so this pairs the create/put correctly.

Fixes 0faa0fe6f90e ("nvmet: New NVMe PCI endpoint function target driver")
Assisted-by Claude:claude-opus-4-8
---
 drivers/nvme/target/pci-epf.c |    1 +
 1 file changed, 1 insertion(+)
drivers/nvme/target/pci-epf.c +1 -0
@@ -1339,6 +1339,7 @@ err_unmap_queue:
 	nvmet_pci_epf_mem_unmap(ctrl->nvme_epf, &cq->pci_map);
 err_internal:
 	status = NVME_SC_INTERNAL | NVME_STATUS_DNR;
+	nvmet_cq_put(&cq->nvme_cq);
 err:
 	if (test_and_clear_bit(NVMET_PCI_EPF_Q_IRQ_ENABLED, &cq->flags))
 		nvmet_pci_epf_remove_irq_vector(ctrl, cq->vector);
author maintainer
#59 lore raw

PATCH 59/101 fbdev: Wrap user-invoked calls to fb_set_var() in helper

In reply to Greg Kroah-Hartman

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

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

From: Thomas Zimmermann <[email protected]>

[ Upstream commit 6f611e5e5f3327cf2e2daabe6ee5acac58cc784e ]

Handle fbcon during display updates in fb_set_var_from_user(). Check with fbcon if the mode change is possible, update hardware state and finally update fbcon. Update all callers.

Only the FBIOPUT_VSCREENINFO ioctl currently does all steps. Other mode-changes callers in sysfs and driver code are missing fbcon-related steps.

With the new helper, ps3fb and sh_mobile_lcdcfb no longer maintain fbcon state themselves.

Stable-dep-of: 061db6b7a910 ("fbdev: serialize mode sysfs access with lock_fb_info()")

---
 drivers/video/fbdev/core/fb_chrdev.c   |    6 +-----
 drivers/video/fbdev/core/fbcon.c       |    2 --
 drivers/video/fbdev/core/fbmem.c       |   13 +++++++++++++
 drivers/video/fbdev/core/fbsysfs.c     |    4 +---
 drivers/video/fbdev/ps3fb.c            |    5 +----
 drivers/video/fbdev/sh_mobile_lcdcfb.c |    5 +----
 include/linux/fb.h                     |    2 ++
 7 files changed, 19 insertions(+), 18 deletions(-)
drivers/video/fbdev/core/fb_chrdev.c +1 -5
@@ -85,11 +85,7 @@ static long do_fb_ioctl(struct fb_info *
 		var.activate &= ~FB_ACTIVATE_KD_TEXT;
 		console_lock();
 		lock_fb_info(info);
-		ret = fbcon_modechange_possible(info, &var);
-		if (!ret)
-			ret = fb_set_var(info, &var);
-		if (!ret)
-			fbcon_update_vcs(info, var.activate & FB_ACTIVATE_ALL);
+		ret = fb_set_var_from_user(info, &var);
 		unlock_fb_info(info);
 		console_unlock();
 		if (!ret && copy_to_user(argp, &var, sizeof(var)))
drivers/video/fbdev/core/fbcon.c +0 -2
@@ -2706,7 +2706,6 @@ void fbcon_update_vcs(struct fb_info *in
 	else
 		fbcon_modechanged(info);
 }
-EXPORT_SYMBOL(fbcon_update_vcs);
 
 /* let fbcon check if it supports a new screen resolution */
 int fbcon_modechange_possible(struct fb_info *info, struct fb_var_screeninfo *var)
@@ -2734,7 +2733,6 @@ int fbcon_modechange_possible(struct fb_
 
 	return 0;
 }
-EXPORT_SYMBOL_GPL(fbcon_modechange_possible);
 
 int fbcon_mode_deleted(struct fb_info *info,
 		       struct fb_videomode *mode)
drivers/video/fbdev/core/fbmem.c +13 -0
@@ -349,6 +349,19 @@ fb_set_var(struct fb_info *info, struct
 }
 EXPORT_SYMBOL(fb_set_var);
 
+int fb_set_var_from_user(struct fb_info *info, struct fb_var_screeninfo *var)
+{
+	int ret = fbcon_modechange_possible(info, var);
+
+	if (!ret)
+		ret = fb_set_var(info, var);
+	if (!ret)
+		fbcon_update_vcs(info, var->activate & FB_ACTIVATE_ALL);
+
+	return ret;
+}
+EXPORT_SYMBOL(fb_set_var_from_user);
+
 static void fb_lcd_notify_blank(struct fb_info *info)
 {
 	int power;
drivers/video/fbdev/core/fbsysfs.c +1 -3
@@ -20,9 +20,7 @@ static int activate(struct fb_info *fb_i
 	var->activate |= FB_ACTIVATE_FORCE;
 	console_lock();
 	lock_fb_info(fb_info);
-	err = fb_set_var(fb_info, var);
-	if (!err)
-		fbcon_update_vcs(fb_info, var->activate & FB_ACTIVATE_ALL);
+	err = fb_set_var_from_user(fb_info, var);
 	unlock_fb_info(fb_info);
 	console_unlock();
 	if (err)
drivers/video/fbdev/ps3fb.c +1 -4
@@ -29,7 +29,6 @@
 #include <linux/freezer.h>
 #include <linux/uaccess.h>
 #include <linux/fb.h>
-#include <linux/fbcon.h>
 #include <linux/init.h>
 
 #include <asm/cell-regs.h>
@@ -830,9 +829,7 @@ static int ps3fb_ioctl(struct fb_info *i
 				/* Force, in case only special bits changed */
 				var.activate |= FB_ACTIVATE_FORCE;
 				par->new_mode_id = val;
-				retval = fb_set_var(info, &var);
-				if (!retval)
-					fbcon_update_vcs(info, var.activate & FB_ACTIVATE_ALL);
+				retval = fb_set_var_from_user(info, &var);
 				console_unlock();
 			}
 			break;
drivers/video/fbdev/sh_mobile_lcdcfb.c +1 -4
@@ -15,7 +15,6 @@
 #include <linux/ctype.h>
 #include <linux/dma-mapping.h>
 #include <linux/delay.h>
-#include <linux/fbcon.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/ioctl.h>
@@ -1768,11 +1767,9 @@ static void sh_mobile_fb_reconfig(struct
 	var.height = ch->display.height;
 	var.activate = FB_ACTIVATE_NOW;
 
-	if (fb_set_var(info, &var) < 0)
+	if (fb_set_var_from_user(info, &var) < 0)
 		/* Couldn't reconfigure, hopefully, can continue as before */
 		return;
-
-	fbcon_update_vcs(info, true);
 }
 
 /*
include/linux/fb.h +2 -0
@@ -533,6 +533,8 @@ extern int fb_set_var(struct fb_info *in
 extern int fb_pan_display(struct fb_info *info, struct fb_var_screeninfo *var);
 extern int fb_blank(struct fb_info *info, int blank);
 
+int fb_set_var_from_user(struct fb_info *info, struct fb_var_screeninfo *var);
+
 /*
  * Helpers for framebuffers in I/O memory
  */
author maintainer
#60 lore raw

PATCH 60/101 fbdev: serialize mode sysfs access with lock_fb_info()

In reply to Greg Kroah-Hartman

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

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

From: Melbin K Mathew <[email protected]>

[ Upstream commit 061db6b7a910b8378f3b2df64f8c0a3ddc6e85f2 ]

show_mode(), show_modes(), and store_mode() access fb_info->modelist and fb_info->mode without holding lock_fb_info(). store_modes() takes lock_fb_info() while replacing the modelist and freeing the old one.

A concurrent reader or writer can load a pointer to an old modelist entry before store_modes() frees it, then dereference freed memory or store a stale freed pointer in fb_info->mode.

Take lock_fb_info() in show_mode(), show_modes(), and store_mode() to serialize with store_modes(). In show_mode(), copy the mode to the stack and format after dropping the lock. In store_mode(), split activate() into a _locked variant to avoid double-locking, and hold the locks for the modelist walk, mode conversion, activation, and fb_info->mode assignment together.

---
 drivers/video/fbdev/core/fbsysfs.c |   46 ++++++++++++++++++++++++++++++-------
 1 file changed, 38 insertions(+), 8 deletions(-)
drivers/video/fbdev/core/fbsysfs.c +38 -8
@@ -13,19 +13,24 @@
 #include "fb_internal.h"
 #include "fbcon.h"
 
+static int activate_locked(struct fb_info *fb_info,
+			    struct fb_var_screeninfo *var)
+{
+	var->activate |= FB_ACTIVATE_FORCE;
+	return fb_set_var_from_user(fb_info, var);
+}
+
 static int activate(struct fb_info *fb_info, struct fb_var_screeninfo *var)
 {
 	int err;
 
-	var->activate |= FB_ACTIVATE_FORCE;
 	console_lock();
 	lock_fb_info(fb_info);
-	err = fb_set_var_from_user(fb_info, var);
+	err = activate_locked(fb_info, var);
 	unlock_fb_info(fb_info);
 	console_unlock();
-	if (err)
-		return err;
-	return 0;
+
+	return err;
 }
 
 static int mode_string(char *buf, size_t size, unsigned int offset,
@@ -66,6 +71,9 @@ static ssize_t store_mode(struct device
 
 	memset(&var, 0, sizeof(var));
 
+	console_lock();
+	lock_fb_info(fb_info);
+
 	list_for_each_entry(modelist, &fb_info->modelist, list) {
 		mode = &modelist->mode;
 		i = mode_string(mstr, sizeof(mstr), 0, mode);
@@ -73,12 +81,22 @@ static ssize_t store_mode(struct device
 
 			var = fb_info->var;
 			fb_videomode_to_var(&var, mode);
-			if ((err = activate(fb_info, &var)))
+			err = activate_locked(fb_info, &var);
+			if (err) {
+				unlock_fb_info(fb_info);
+				console_unlock();
 				return err;
+			}
 			fb_info->mode = mode;
+			unlock_fb_info(fb_info);
+			console_unlock();
 			return count;
 		}
 	}
+
+	unlock_fb_info(fb_info);
+	console_unlock();
+
 	return -EINVAL;
 }
 
@@ -86,11 +104,20 @@ static ssize_t show_mode(struct device *
 			 char *buf)
 {
 	struct fb_info *fb_info = dev_get_drvdata(device);
+	struct fb_videomode mode;
+	bool have_mode = false;
+
+	lock_fb_info(fb_info);
+	if (fb_info->mode) {
+		mode = *fb_info->mode;
+		have_mode = true;
+	}
+	unlock_fb_info(fb_info);
 
-	if (!fb_info->mode)
+	if (!have_mode)
 		return 0;
 
-	return mode_string(buf, PAGE_SIZE, 0, fb_info->mode);
+	return mode_string(buf, PAGE_SIZE, 0, &mode);
 }
 
 static ssize_t store_modes(struct device *device,
@@ -138,12 +165,15 @@ static ssize_t show_modes(struct device
 	const struct fb_videomode *mode;
 
 	i = 0;
+	lock_fb_info(fb_info);
 	list_for_each_entry(modelist, &fb_info->modelist, list) {
 		mode = &modelist->mode;
 		i += mode_string(buf, PAGE_SIZE, i, mode);
 		if (i >= PAGE_SIZE - 1)
 			break;
 	}
+	unlock_fb_info(fb_info);
+
 	return i;
 }
 
author maintainer
#61 lore raw

PATCH 61/101 drm/amdgpu: fix recursive ww_mutex acquire in amdgpu_devcoredump_format

In reply to Greg Kroah-Hartman

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

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

From: Mikhail Gavrilov <[email protected]>

[ Upstream commit 7152b248dc3c8d5fa8629e99ed5655dd41b51562 ]

When dumping IB contents from a hung job, amdgpu_devcoredump_format() acquired the VM root PD's reservation via amdgpu_vm_lock_by_pasid() and then, for each IB, called amdgpu_bo_reserve() on the BO backing the IB. Both reservations are reservation_ww_class_mutex objects and neither used a ww_acquire_ctx, which trips lockdep:

WARNING: possible recursive locking detected
--------------------------------------------
kworker/u128:0 is trying to acquire lock:
ffff88838b16e1f0 (reservation_ww_class_mutex){+.+.}-{4:4},

    at: amdgpu_devcoredump_format+0x1594/0x23f0 [amdgpu]

but task is already holding lock:
ffff8882f82681f0 (reservation_ww_class_mutex){+.+.}-{4:4},

    at: amdgpu_devcoredump_format+0x1594/0x23f0 [amdgpu]
   Possible unsafe locking scenario:
         CPU0
         ----
    lock(reservation_ww_class_mutex);
    lock(reservation_ww_class_mutex);

*** DEADLOCK ***
May be due to missing lock nesting notation

  Workqueue: events_unbound amdgpu_devcoredump_deferred_work [amdgpu]
  Call Trace:
   __ww_mutex_lock.constprop.0
   ww_mutex_lock
   amdgpu_bo_reserve
   amdgpu_devcoredump_format+0x1594 [amdgpu]
   amdgpu_devcoredump_deferred_work+0xea [amdgpu]

The two reservations are on different BOs in the captured trace, so the splat is a lockdep-correctness warning, not an observed deadlock. It becomes a real self-deadlock whenever the IB BO shares its dma_resv with the root PD (the always-valid case, see amdgpu_vm_is_bo_always_valid()):
amdgpu_bo_reserve(abo) re-acquires the same ww_mutex without a ticket and blocks forever. With amdgpu.gpu_recovery=0 the timeout handler refires every ~2 s and each invocation produces this splat, drowning the kernel ring buffer.

Now that amdgpu_vm_lock_by_pasid() takes a drm_exec context, move the IB dumping into a separate helper that locks the root PD and every IB BO together in a single drm_exec ticket. DRM_EXEC_IGNORE_DUPLICATES handles IB BOs that share a dma_resv (e.g. always-valid BOs, or two IBs backed by the same BO). Every lock is now a top-level acquire under one ww_acquire_ctx, so the recursive ww_mutex condition is gone, and the per-IB amdgpu_bo_reserve()/amdgpu_bo_unref() dance -- including a BO refcount leak on the amdgpu_bo_reserve() failure path -- is removed.

Fixes 7b15fc2d1f1a ("drm/amdgpu: dump job ibs in the devcoredump")
(cherry picked from commit d6bf4242731219ee08ce54c365631e395486651e)

Stable-dep-of: e40ff9840fa8 ("drm/amdgpu: Allocate coredump ring buffers per ring")

---
 drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c |  215 +++++++++++++----------
 drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c           |   91 ++++++---
 drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h           |    2 
 3 files changed, 184 insertions(+), 124 deletions(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c +126 -89
@@ -24,6 +24,7 @@
 
 #include <generated/utsrelease.h>
 #include <linux/devcoredump.h>
+#include <drm/drm_exec.h>
 #include "amdgpu_dev_coredump.h"
 #include "atom.h"
 
@@ -207,23 +208,137 @@ static void amdgpu_devcoredump_fw_info(s
 	}
 }
 
+static void
+amdgpu_devcoredump_print_ibs(struct drm_printer *p,
+			     struct amdgpu_coredump_info *coredump,
+			     bool sizing_pass)
+{
+	struct amdgpu_device *adev = coredump->adev;
+	struct amdgpu_bo_va_mapping *mapping;
+	struct amdgpu_bo *abo;
+	struct drm_exec exec;
+	struct amdgpu_vm *vm;
+	u32 *ib_content;
+	u64 va_start, offset;
+	u8 *kptr;
+	u32 off;
+	int r;
+
+	/*
+	 * On the sizing pass there is no VM to look up and no BO to lock; the
+	 * size estimate doesn't depend on whether the IB BOs are reachable.
+	 * Just emit the per-IB headers (the content is not written anywhere).
+	 */
+	if (sizing_pass) {
+		for (int i = 0; i < coredump->num_ibs; i++) {
+			drm_printf(p, "\nIB #%d 0x%llx %d dw\n", i,
+				   coredump->ibs[i].gpu_addr,
+				   coredump->ibs[i].ib_size_dw);
+		}
+		return;
+	}
+
+	/*
+	 * Lock the VM root PD and every IB BO together in a single drm_exec
+	 * ticket. Reserving the IB BOs one by one while the root PD is held
+	 * would be a recursive reservation_ww_class_mutex acquire without a
+	 * ww_acquire_ctx, which trips lockdep and self-deadlocks for IB BOs
+	 * that share their dma_resv with the root PD (always-valid BOs).
+	 */
+	drm_exec_init(&exec, DRM_EXEC_IGNORE_DUPLICATES, 1 + coredump->num_ibs);
+	drm_exec_until_all_locked(&exec) {
+		vm = amdgpu_vm_lock_by_pasid(adev, coredump->pasid, &exec);
+		if (!vm)
+			goto unlock;
+
+		for (int i = 0; i < coredump->num_ibs; i++) {
+			u64 pfn = (coredump->ibs[i].gpu_addr &
+				   AMDGPU_GMC_HOLE_MASK) / AMDGPU_GPU_PAGE_SIZE;
+
+			mapping = amdgpu_vm_bo_lookup_mapping(vm, pfn);
+			if (!mapping)
+				continue;
+
+			abo = mapping->bo_va->base.bo;
+			r = drm_exec_lock_obj(&exec, &abo->tbo.base);
+			drm_exec_retry_on_contention(&exec);
+			if (r)
+				goto unlock;
+		}
+	}
+
+	for (int i = 0; i < coredump->num_ibs; i++) {
+		bool emit_content = false;
+
+		ib_content = kvmalloc_array(coredump->ibs[i].ib_size_dw, 4,
+					    GFP_KERNEL);
+		if (!ib_content)
+			continue;
+
+		va_start = coredump->ibs[i].gpu_addr & AMDGPU_GMC_HOLE_MASK;
+		mapping = amdgpu_vm_bo_lookup_mapping(vm,
+						      va_start / AMDGPU_GPU_PAGE_SIZE);
+		if (!mapping)
+			goto output_ib_content;
+
+		abo = mapping->bo_va->base.bo;
+		offset = va_start - mapping->start * AMDGPU_GPU_PAGE_SIZE;
+
+		if (abo->flags & AMDGPU_GEM_CREATE_NO_CPU_ACCESS) {
+			struct amdgpu_res_cursor cursor;
+
+			off = 0;
+
+			if (abo->tbo.resource->mem_type != TTM_PL_VRAM)
+				goto output_ib_content;
+
+			amdgpu_res_first(abo->tbo.resource, offset,
+					 coredump->ibs[i].ib_size_dw * 4, &cursor);
+			while (cursor.remaining) {
+				amdgpu_device_mm_access(adev, cursor.start / 4,
+							&ib_content[off], cursor.size / 4,
+							false);
+				off += cursor.size;
+				amdgpu_res_next(&cursor, cursor.size);
+			}
+			emit_content = true;
+		} else {
+			r = ttm_bo_kmap(&abo->tbo, 0, PFN_UP(abo->tbo.base.size),
+					&abo->kmap);
+			if (r)
+				goto output_ib_content;
+
+			kptr = amdgpu_bo_kptr(abo);
+			kptr += offset;
+			memcpy(ib_content, kptr, coredump->ibs[i].ib_size_dw * 4);
+
+			amdgpu_bo_kunmap(abo);
+			emit_content = true;
+		}
+
+output_ib_content:
+		drm_printf(p, "\nIB #%d 0x%llx %d dw\n", i,
+			   coredump->ibs[i].gpu_addr, coredump->ibs[i].ib_size_dw);
+		if (emit_content) {
+			for (int j = 0; j < coredump->ibs[i].ib_size_dw; j++)
+				drm_printf(p, "0x%08x\n", ib_content[j]);
+		}
+		kvfree(ib_content);
+	}
+
+unlock:
+	drm_exec_fini(&exec);
+}
+
 static ssize_t
 amdgpu_devcoredump_format(char *buffer, size_t count, struct amdgpu_coredump_info *coredump)
 {
-	struct amdgpu_device *adev = coredump->adev;
 	struct drm_printer p;
 	struct drm_print_iterator iter;
 	struct amdgpu_vm_fault_info *fault_info;
-	struct amdgpu_bo_va_mapping *mapping;
 	struct amdgpu_ip_block *ip_block;
-	struct amdgpu_res_cursor cursor;
-	struct amdgpu_bo *abo, *root;
-	uint64_t va_start, offset;
 	struct amdgpu_ring *ring;
-	struct amdgpu_vm *vm;
-	u32 *ib_content;
-	uint8_t *kptr;
-	int ver, i, j, r;
+	int ver, i, j;
 	u32 ring_idx, off;
 	bool sizing_pass;
 
@@ -347,86 +462,8 @@ amdgpu_devcoredump_format(char *buffer,
 	else if (coredump->reset_vram_lost)
 		drm_printf(&p, "VRAM is lost due to GPU reset!\n");
 
-	if (coredump->num_ibs) {
-		/* Don't try to lookup the VM or map the BOs when calculating the
-		 * size required to store the devcoredump.
-		 */
-		if (sizing_pass)
-			vm = NULL;
-		else
-			vm = amdgpu_vm_lock_by_pasid(adev, &root, coredump->pasid);
-
-		for (int i = 0; i < coredump->num_ibs && (sizing_pass || vm); i++) {
-			ib_content = kvmalloc_array(coredump->ibs[i].ib_size_dw, 4,
-						    GFP_KERNEL);
-			if (!ib_content)
-				continue;
-
-			/* vm=NULL can only happen when 'sizing_pass' is true. Skip to the
-			 * drm_printf() calls (ib_content doesn't need to be initialized
-			 * as its content won't be written anywhere).
-			 */
-			if (!vm)
-				goto output_ib_content;
-
-			va_start = coredump->ibs[i].gpu_addr & AMDGPU_GMC_HOLE_MASK;
-			mapping = amdgpu_vm_bo_lookup_mapping(vm, va_start / AMDGPU_GPU_PAGE_SIZE);
-			if (!mapping)
-				goto free_ib_content;
-
-			offset = va_start - (mapping->start * AMDGPU_GPU_PAGE_SIZE);
-			abo = amdgpu_bo_ref(mapping->bo_va->base.bo);
-			r = amdgpu_bo_reserve(abo, false);
-			if (r)
-				goto free_ib_content;
-
-			if (abo->flags & AMDGPU_GEM_CREATE_NO_CPU_ACCESS) {
-				off = 0;
-
-				if (abo->tbo.resource->mem_type != TTM_PL_VRAM)
-					goto unreserve_abo;
-
-				amdgpu_res_first(abo->tbo.resource, offset,
-						 coredump->ibs[i].ib_size_dw * 4,
-						 &cursor);
-				while (cursor.remaining) {
-					amdgpu_device_mm_access(adev, cursor.start / 4,
-								&ib_content[off], cursor.size / 4,
-								false);
-					off += cursor.size;
-					amdgpu_res_next(&cursor, cursor.size);
-				}
-			} else {
-				r = ttm_bo_kmap(&abo->tbo, 0,
-						PFN_UP(abo->tbo.base.size),
-						&abo->kmap);
-				if (r)
-					goto unreserve_abo;
-
-				kptr = amdgpu_bo_kptr(abo);
-				kptr += offset;
-				memcpy(ib_content, kptr,
-				       coredump->ibs[i].ib_size_dw * 4);
-
-				amdgpu_bo_kunmap(abo);
-			}
-
-output_ib_content:
-			drm_printf(&p, "\nIB #%d 0x%llx %d dw\n",
-				   i, coredump->ibs[i].gpu_addr, coredump->ibs[i].ib_size_dw);
-			for (int j = 0; j < coredump->ibs[i].ib_size_dw; j++)
-				drm_printf(&p, "0x%08x\n", ib_content[j]);
-unreserve_abo:
-			if (vm)
-				amdgpu_bo_unreserve(abo);
-free_ib_content:
-			kvfree(ib_content);
-		}
-		if (vm) {
-			amdgpu_bo_unreserve(root);
-			amdgpu_bo_unref(&root);
-		}
-	}
+	if (coredump->num_ibs)
+		amdgpu_devcoredump_print_ibs(&p, coredump, sizing_pass);
 
 	return count - iter.remain;
 }
drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +57 -34

156 lines not on this page · raw message

drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h +1 -1
@@ -593,7 +593,7 @@ bool amdgpu_vm_handle_fault(struct amdgp
 			    bool write_fault);
 
 struct amdgpu_vm *amdgpu_vm_lock_by_pasid(struct amdgpu_device *adev,
-					  struct amdgpu_bo **root, u32 pasid);
+					  u32 pasid, struct drm_exec *exec);
 
 void amdgpu_vm_set_task_info(struct amdgpu_vm *vm);
 
author maintainer
#62 lore raw

PATCH 62/101 drm/amdgpu: Allocate coredump ring buffers per ring

In reply to Greg Kroah-Hartman

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

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

From: Lijo Lazar <[email protected]>

[ Upstream commit e40ff9840fa8a633d149f0242df10cae5e518062 ]

Allocate each ring buffer separately. A single allocation summing all ring sizes can exceed the page allocator's MAX_ORDER limit and fail; per-ring buffers stay small enough to satisfy. The existing allocation style doesn't capture any ring data if the huge allocation fails. Splitting into multiple allocations helps to capture as much data as possible for the core dump.

A failed ring is left with a NULL buffer and skipped when formatting.

Fixes eea85914d15b ("drm/amdgpu: save ring content before resetting the device")
Assisted-by Claude Code
(cherry picked from commit 3e8e92b7892a6377bef86106bfff1b98cf586aee)
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c |   50 +++++++++++------------
 drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.h |    3 -
 2 files changed, 27 insertions(+), 26 deletions(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c +26 -24
@@ -339,7 +339,7 @@ amdgpu_devcoredump_format(char *buffer,
 	struct amdgpu_ip_block *ip_block;
 	struct amdgpu_ring *ring;
 	int ver, i, j;
-	u32 ring_idx, off;
+	u32 ring_idx;
 	bool sizing_pass;
 
 	sizing_pass = buffer == NULL;
@@ -439,7 +439,6 @@ amdgpu_devcoredump_format(char *buffer,
 		for (i = 0; i < coredump->num_rings; i++) {
 			ring_idx = coredump->rings[i].ring_index;
 			ring = coredump->adev->rings[ring_idx];
-			off = coredump->rings[i].offset;
 
 			drm_printf(&p, "ring name: %s\n", ring->name);
 			drm_printf(&p, "Rptr: 0x%llx Wptr: 0x%llx RB mask: %x\n",
@@ -448,12 +447,18 @@ amdgpu_devcoredump_format(char *buffer,
 				   ring->buf_mask);
 			drm_printf(&p, "Ring size in dwords: %d\n",
 				ring->ring_size / 4);
+
+			if (!coredump->rings[i].ring_dw) {
+				drm_printf(&p, "Ring contents unavailable\n");
+				continue;
+			}
+
 			drm_printf(&p, "Ring contents\n");
 			drm_printf(&p, "Offset \t Value\n");
 
 			for (j = 0; j < ring->ring_size; j += 4)
 				drm_printf(&p, "0x%x \t 0x%x\n", j,
-					   coredump->rings_dw[off + j / 4]);
+					   coredump->rings[i].ring_dw[j / 4]);
 		}
 	}
 
@@ -494,10 +499,12 @@ amdgpu_devcoredump_read(char *buffer, lo
 static void amdgpu_devcoredump_free(void *data)
 {
 	struct amdgpu_coredump_info *coredump = data;
+	u32 i;
 
 	kvfree(coredump->formatted);
+	for (i = 0; i < coredump->num_rings; i++)
+		kvfree(coredump->rings[i].ring_dw);
 	kvfree(coredump->rings);
-	kvfree(coredump->rings_dw);
 	kvfree(data);
 }
 
@@ -539,9 +546,9 @@ void amdgpu_coredump(struct amdgpu_devic
 	struct amdgpu_coredump_info *coredump;
 	size_t size = sizeof(*coredump);
 	struct drm_sched_job *s_job;
-	u64 total_ring_size, ring_count;
+	u64 ring_count;
 	struct amdgpu_ring *ring;
-	int i, off, idx;
+	int i, idx;
 
 	/* No need to generate a new coredump if there's one in progress already. */
 	if (work_busy(&adev->coredump_work))
@@ -581,7 +588,6 @@ void amdgpu_coredump(struct amdgpu_devic
 
 	/* Dump ring content if memory allocation succeeds. */
 	ring_count = 0;
-	total_ring_size = 0;
 	for (i = 0; i < adev->num_rings; i++) {
 		ring = adev->rings[i];
 
@@ -590,38 +596,34 @@ void amdgpu_coredump(struct amdgpu_devic
 		    coredump->ring != ring)
 			continue;
 
-		total_ring_size += ring->ring_size;
 		ring_count++;
 	}
-	if (ring_count) {
-		coredump->rings_dw = kvzalloc(total_ring_size, GFP_NOWAIT);
+	if (ring_count)
 		coredump->rings = kvcalloc(ring_count,
 					   sizeof(struct amdgpu_coredump_ring),
 					   GFP_NOWAIT);
-	}
-	if (coredump->rings && coredump->rings_dw) {
-		for (i = 0, off = 0, idx = 0; i < adev->num_rings && idx < ring_count; i++) {
+	if (coredump->rings) {
+		for (i = 0, idx = 0; i < adev->num_rings && idx < ring_count; i++) {
+			struct amdgpu_coredump_ring *cdump_ring;
+
 			ring = adev->rings[i];
 
 			if (atomic_read(&ring->fence_drv.last_seq) == ring->fence_drv.sync_seq &&
 			    coredump->ring != ring)
 				continue;
 
-			coredump->rings[idx].ring_index = ring->idx;
-			coredump->rings[idx].rptr = amdgpu_ring_get_rptr(ring);
-			coredump->rings[idx].wptr = amdgpu_ring_get_wptr(ring);
-			coredump->rings[idx].offset = off;
+			cdump_ring = &coredump->rings[idx];
 
-			memcpy(&coredump->rings_dw[off], ring->ring, ring->ring_size);
-			off += ring->ring_size / 4;
+			cdump_ring->ring_dw = kvzalloc(ring->ring_size, GFP_NOWAIT);
+			if (cdump_ring->ring_dw)
+				memcpy(cdump_ring->ring_dw, ring->ring, ring->ring_size);
+
+			cdump_ring->ring_index = ring->idx;
+			cdump_ring->rptr = amdgpu_ring_get_rptr(ring);
+			cdump_ring->wptr = amdgpu_ring_get_wptr(ring);
 			idx++;
 		}
 		coredump->num_rings = idx;
-	} else {
-		kvfree(coredump->rings_dw);
-		kvfree(coredump->rings);
-		coredump->rings_dw = NULL;
-		coredump->rings = NULL;
 	}
 
 	coredump->adev = adev;
drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.h +1 -2
@@ -34,8 +34,8 @@
 struct amdgpu_coredump_ring {
 	u64				rptr;
 	u64				wptr;
+	u32				*ring_dw;
 	u32				ring_index;
-	u32				offset;
 };
 
 struct amdgpu_coredump_ib_info {
@@ -53,7 +53,6 @@ struct amdgpu_coredump_info {
 	struct amdgpu_ring              *ring;
 
 	struct amdgpu_coredump_ring	*rings;
-	u32				*rings_dw;
 	u32				num_rings;
 
 	/* Readable form of coredevdump, generate once to speed up
author maintainer
#63 lore raw

PATCH 63/101 mptcp: pm: rename add_entry structure to add_addr

In reply to Greg Kroah-Hartman

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

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

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

[ Upstream commit 350d76dd6e79468ac85767f2d236299a135572df ]

Using only the 'add' prefix is confusing: does it refer to a generic added entry or address, or specifically to ADD_ADDRs. Using add_addr removes this confusion.

Stable-dep-of: efc33b5102ff ("mptcp: pm: fix memory leak from alloc-during-teardown race")

---
 net/mptcp/pm.c       |   22 +++++++++++-----------
 net/mptcp/protocol.h |    2 +-
 2 files changed, 12 insertions(+), 12 deletions(-)
net/mptcp/pm.c +11 -11
@@ -12,7 +12,7 @@
 
 #define ADD_ADDR_RETRANS_MAX	3
 
-struct mptcp_pm_add_entry {
+struct mptcp_pm_add_addr {
 	struct list_head	list;
 	struct mptcp_addr_info	addr;
 	u8			retrans_times;
@@ -133,11 +133,11 @@ bool mptcp_lookup_subflow_by_saddr(const
 	return false;
 }
 
-static struct mptcp_pm_add_entry *
+static struct mptcp_pm_add_addr *
 mptcp_lookup_anno_list_by_saddr(const struct mptcp_sock *msk,
 				const struct mptcp_addr_info *addr)
 {
-	struct mptcp_pm_add_entry *entry;
+	struct mptcp_pm_add_addr *entry;
 
 	lockdep_assert_held(&msk->pm.lock);
 
@@ -152,7 +152,7 @@ mptcp_lookup_anno_list_by_saddr(const st
 bool mptcp_remove_anno_list_by_saddr(struct mptcp_sock *msk,
 				     const struct mptcp_addr_info *addr)
 {
-	struct mptcp_pm_add_entry *entry;
+	struct mptcp_pm_add_addr *entry;
 	bool ret;
 
 	entry = mptcp_pm_del_add_timer(msk, addr, false);
@@ -164,7 +164,7 @@ bool mptcp_remove_anno_list_by_saddr(str
 
 bool mptcp_pm_sport_in_anno_list(struct mptcp_sock *msk, const struct sock *sk)
 {
-	struct mptcp_pm_add_entry *entry;
+	struct mptcp_pm_add_addr *entry;
 	struct mptcp_addr_info saddr;
 	bool ret = false;
 
@@ -340,8 +340,8 @@ static unsigned int mptcp_adjust_add_add
 
 static void mptcp_pm_add_timer(struct timer_list *timer)
 {
-	struct mptcp_pm_add_entry *entry = timer_container_of(entry, timer,
-							      add_timer);
+	struct mptcp_pm_add_addr *entry = timer_container_of(entry, timer,
+							     add_timer);
 	struct mptcp_sock *msk = entry->sock;
 	struct sock *sk = (struct sock *)msk;
 	unsigned int timeout = 0;
@@ -399,12 +399,12 @@ out:
 	sock_put(sk);
 }
 
-struct mptcp_pm_add_entry *
+struct mptcp_pm_add_addr *
 mptcp_pm_del_add_timer(struct mptcp_sock *msk,
 		       const struct mptcp_addr_info *addr, bool check_id)
 {
-	struct mptcp_pm_add_entry *entry;
 	struct sock *sk = (struct sock *)msk;
+	struct mptcp_pm_add_addr *entry;
 	bool stop_timer = false;
 
 	rcu_read_lock();
@@ -436,7 +436,7 @@ mptcp_pm_del_add_timer(struct mptcp_sock
 bool mptcp_pm_alloc_anno_list(struct mptcp_sock *msk,
 			      const struct mptcp_addr_info *addr)
 {
-	struct mptcp_pm_add_entry *add_entry = NULL;
+	struct mptcp_pm_add_addr *add_entry = NULL;
 	struct sock *sk = (struct sock *)msk;
 	unsigned int timeout;
 
@@ -473,7 +473,7 @@ reset_timer:
 
 static void mptcp_pm_free_anno_list(struct mptcp_sock *msk)
 {
-	struct mptcp_pm_add_entry *entry, *tmp;
+	struct mptcp_pm_add_addr *entry, *tmp;
 	struct sock *sk = (struct sock *)msk;
 	LIST_HEAD(free_list);
 
net/mptcp/protocol.h +1 -1
@@ -1133,7 +1133,7 @@ int mptcp_pm_mp_prio_send_ack(struct mpt
 bool mptcp_pm_alloc_anno_list(struct mptcp_sock *msk,
 			      const struct mptcp_addr_info *addr);
 bool mptcp_pm_sport_in_anno_list(struct mptcp_sock *msk, const struct sock *sk);
-struct mptcp_pm_add_entry *
+struct mptcp_pm_add_addr *
 mptcp_pm_del_add_timer(struct mptcp_sock *msk,
 		       const struct mptcp_addr_info *addr, bool check_id);
 bool mptcp_lookup_subflow_by_saddr(const struct list_head *list,
author maintainer
#64 lore raw

PATCH 64/101 mptcp: pm: uniform announced addresses helpers

In reply to Greg Kroah-Hartman

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

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

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

[ Upstream commit 7d4dacc8ccca8e40fcb5f2ece9ea3e502da1bd3f ]

Similar to the previous commit, only using the 'add' or 'anno' prefixes is confusing -- generally associated to the action of adding something, or the Latin name for "year" -- and lack of uniformity.

This has been causing issues in the past, e.g. del_add_timer seemed to suggest the goal is to delete a previously added timer.

Instead, use the mptcp_pm_announced_ prefix.

While at it, slightly improves some helpers:

- mptcp_lookup_anno_list_by_saddr: no need to specify what is used to do the lookup: mptcp_pm_announced_lookup.

- mptcp_pm_sport_in_anno_list: it doesn't just compare the port, but the whole address linked to the sublow: mptcp_pm_announced_has_ssk.

- mptcp_pm_alloc_anno_list: it allocates one item of the list, not a whole list: mptcp_pm_announced_alloc.

Stable-dep-of: efc33b5102ff ("mptcp: pm: fix memory leak from alloc-during-teardown race")

---
 net/mptcp/options.c      |    2 +-
 net/mptcp/pm.c           |   35 +++++++++++++++++------------------
 net/mptcp/pm_kernel.c    |   10 +++++-----
 net/mptcp/pm_userspace.c |    4 ++--
 net/mptcp/protocol.h     |   12 ++++++------
 net/mptcp/subflow.c      |    4 ++--
 6 files changed, 33 insertions(+), 34 deletions(-)
net/mptcp/options.c +1 -1
@@ -1261,7 +1261,7 @@ bool mptcp_incoming_options(struct sock
 				MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_ADDADDR);
 			} else {
 				mptcp_pm_add_addr_echoed(msk, &mp_opt.addr);
-				mptcp_pm_del_add_timer(msk, &mp_opt.addr, true);
+				mptcp_pm_announced_del_timer(msk, &mp_opt.addr, true);
 				MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_ECHOADD);
 			}
 
net/mptcp/pm.c +17 -18
@@ -134,8 +134,8 @@ bool mptcp_lookup_subflow_by_saddr(const
 }
 
 static struct mptcp_pm_add_addr *
-mptcp_lookup_anno_list_by_saddr(const struct mptcp_sock *msk,
-				const struct mptcp_addr_info *addr)
+mptcp_pm_announced_lookup(const struct mptcp_sock *msk,
+			  const struct mptcp_addr_info *addr)
 {
 	struct mptcp_pm_add_addr *entry;
 
@@ -149,26 +149,26 @@ mptcp_lookup_anno_list_by_saddr(const st
 	return NULL;
 }
 
-bool mptcp_remove_anno_list_by_saddr(struct mptcp_sock *msk,
-				     const struct mptcp_addr_info *addr)
+bool mptcp_pm_announced_remove(struct mptcp_sock *msk,
+			       const struct mptcp_addr_info *addr)
 {
 	struct mptcp_pm_add_addr *entry;
 	bool ret;
 
-	entry = mptcp_pm_del_add_timer(msk, addr, false);
+	entry = mptcp_pm_announced_del_timer(msk, addr, false);
 	ret = entry;
 	kfree_rcu(entry, rcu);
 
 	return ret;
 }
 
-bool mptcp_pm_sport_in_anno_list(struct mptcp_sock *msk, const struct sock *sk)
+bool mptcp_pm_announced_has_ssk(struct mptcp_sock *msk, const struct sock *ssk)
 {
 	struct mptcp_pm_add_addr *entry;
 	struct mptcp_addr_info saddr;
 	bool ret = false;
 
-	mptcp_local_address((struct sock_common *)sk, &saddr);
+	mptcp_local_address((struct sock_common *)ssk, &saddr);
 
 	spin_lock_bh(&msk->pm.lock);
 	list_for_each_entry(entry, &msk->pm.anno_list, list) {
@@ -365,7 +365,7 @@ static void mptcp_pm_add_timer(struct ti
 
 	spin_lock_bh(&msk->pm.lock);
 
-	/* The cancel path (mptcp_pm_del_add_timer()) can race with this
+	/* The cancel path (mptcp_pm_announced_del_timer()) can race with this
 	 * callback. Once cancel updates retrans_times to MAX, suppress further
 	 * retransmissions here. If this callback acquires pm.lock first, one
 	 * final transmit attempt is still possible.
@@ -400,8 +400,8 @@ out:
 }
 
 struct mptcp_pm_add_addr *
-mptcp_pm_del_add_timer(struct mptcp_sock *msk,
-		       const struct mptcp_addr_info *addr, bool check_id)
+mptcp_pm_announced_del_timer(struct mptcp_sock *msk,
+			     const struct mptcp_addr_info *addr, bool check_id)
 {
 	struct sock *sk = (struct sock *)msk;
 	struct mptcp_pm_add_addr *entry;
@@ -410,7 +410,7 @@ mptcp_pm_del_add_timer(struct mptcp_sock
 	rcu_read_lock();
 
 	spin_lock_bh(&msk->pm.lock);
-	entry = mptcp_lookup_anno_list_by_saddr(msk, addr);
+	entry = mptcp_pm_announced_lookup(msk, addr);
 	if (entry && (!check_id || entry->addr.id == addr->id)) {
 		entry->retrans_times = ADD_ADDR_RETRANS_MAX;
 		stop_timer = true;
@@ -433,7 +433,7 @@ mptcp_pm_del_add_timer(struct mptcp_sock
 	return entry;
 }
 
-bool mptcp_pm_alloc_anno_list(struct mptcp_sock *msk,
+bool mptcp_pm_announced_alloc(struct mptcp_sock *msk,
 			      const struct mptcp_addr_info *addr)
 {
 	struct mptcp_pm_add_addr *add_entry = NULL;
@@ -442,8 +442,7 @@ bool mptcp_pm_alloc_anno_list(struct mpt
 
 	lockdep_assert_held(&msk->pm.lock);
 
-	add_entry = mptcp_lookup_anno_list_by_saddr(msk, addr);
-
+	add_entry = mptcp_pm_announced_lookup(msk, addr);
 	if (add_entry) {
 		if (WARN_ON_ONCE(mptcp_pm_is_kernel(msk)))
 			return false;
@@ -471,7 +470,7 @@ reset_timer:
 	return true;
 }
 
-static void mptcp_pm_free_anno_list(struct mptcp_sock *msk)
+static void mptcp_pm_free_announced_list(struct mptcp_sock *msk)
 {
 	struct mptcp_pm_add_addr *entry, *tmp;
 	struct sock *sk = (struct sock *)msk;
@@ -740,7 +739,7 @@ void mptcp_pm_add_addr_echoed(struct mpt
 
 	spin_lock_bh(&pm->lock);
 
-	if (mptcp_lookup_anno_list_by_saddr(msk, addr) && READ_ONCE(pm->work_pending))
+	if (mptcp_pm_announced_lookup(msk, addr) && READ_ONCE(pm->work_pending))
 		mptcp_pm_schedule_work(msk, MPTCP_PM_SUBFLOW_ESTABLISHED);
 
 	spin_unlock_bh(&pm->lock);
@@ -949,7 +948,7 @@ out_unlock:
 	 * let the PM state machine progress.
 	 */
 	if (skip_add_addr) {
-		mptcp_pm_del_add_timer(msk, addr, true);
+		mptcp_pm_announced_del_timer(msk, addr, true);
 		mptcp_pm_subflow_established(msk);
 	}
 	return ret;
@@ -1104,7 +1103,7 @@ void mptcp_pm_worker(struct mptcp_sock *
 
 void mptcp_pm_destroy(struct mptcp_sock *msk)
 {
-	mptcp_pm_free_anno_list(msk);
+	mptcp_pm_free_announced_list(msk);
 
 	if (mptcp_pm_is_userspace(msk))
 		mptcp_userspace_pm_free_local_addr_list(msk);
net/mptcp/pm_kernel.c +5 -5
@@ -370,7 +370,7 @@ static void mptcp_pm_create_subflow_or_s
 		/* If the alloc fails, we are on memory pressure, not worth
 		 * continuing, and trying to create subflows.
 		 */
-		if (!mptcp_pm_alloc_anno_list(msk, &local.addr))
+		if (!mptcp_pm_announced_alloc(msk, &local.addr))
 			return;
 
 		__clear_bit(endp_id, msk->pm.id_avail_bitmap);
@@ -1042,7 +1042,7 @@ out_free:
 	return ret;
 }
 
-static void mptcp_pm_remove_anno_addr(struct mptcp_sock *msk,
+static void mptcp_pm_remove_announced(struct mptcp_sock *msk,
 				      const struct mptcp_addr_info *addr,
 				      bool force)
 {
@@ -1051,7 +1051,7 @@ static void mptcp_pm_remove_anno_addr(st
 
 	list.ids[list.nr++] = mptcp_endp_get_local_id(msk, addr);
 
-	announced = mptcp_remove_anno_list_by_saddr(msk, addr);
+	announced = mptcp_pm_announced_remove(msk, addr);
 	if (announced || force) {
 		spin_lock_bh(&msk->pm.lock);
 		if (announced)
@@ -1088,7 +1088,7 @@ static int mptcp_nl_remove_subflow_and_s
 
 		lock_sock(sk);
 		remove_subflow = mptcp_lookup_subflow_by_saddr(&msk->conn_list, addr);
-		mptcp_pm_remove_anno_addr(msk, addr, remove_subflow &&
+		mptcp_pm_remove_announced(msk, addr, remove_subflow &&
 					  !(entry->flags & MPTCP_PM_ADDR_FLAG_IMPLICIT));
 
 		list.ids[0] = mptcp_endp_get_local_id(msk, addr);
@@ -1223,7 +1223,7 @@ static void mptcp_pm_flush_addrs_and_sub
 			slist.ids[slist.nr++] = mptcp_endp_get_local_id(msk, &entry->addr);
 
 		if (alist.nr < MPTCP_RM_IDS_MAX &&
-		    mptcp_remove_anno_list_by_saddr(msk, &entry->addr))
+		    mptcp_pm_announced_remove(msk, &entry->addr))
 			alist.ids[alist.nr++] = mptcp_endp_get_local_id(msk, &entry->addr);
 	}
 
net/mptcp/pm_userspace.c +2 -2
@@ -231,7 +231,7 @@ int mptcp_pm_nl_announce_doit(struct sk_
 	lock_sock(sk);
 	spin_lock_bh(&msk->pm.lock);
 
-	if (mptcp_pm_alloc_anno_list(msk, &addr_val.addr)) {
+	if (mptcp_pm_announced_alloc(msk, &addr_val.addr)) {
 		msk->pm.add_addr_signaled++;
 		mptcp_pm_announce_addr(msk, &addr_val.addr, false);
 		mptcp_pm_addr_send_ack(msk);
@@ -284,7 +284,7 @@ void mptcp_pm_remove_addr_entry(struct m
 	int anno_nr = 0;
 
 	/* only delete if either announced or matching a subflow */
-	if (mptcp_remove_anno_list_by_saddr(msk, &entry->addr))
+	if (mptcp_pm_announced_remove(msk, &entry->addr))
 		anno_nr++;
 	else if (!mptcp_lookup_subflow_by_saddr(&msk->conn_list, &entry->addr))
 		return;
net/mptcp/protocol.h +6 -6
@@ -1130,16 +1130,16 @@ int mptcp_pm_mp_prio_send_ack(struct mpt
 			      struct mptcp_addr_info *addr,
 			      struct mptcp_addr_info *rem,
 			      u8 bkup);
-bool mptcp_pm_alloc_anno_list(struct mptcp_sock *msk,
+bool mptcp_pm_announced_alloc(struct mptcp_sock *msk,
 			      const struct mptcp_addr_info *addr);
-bool mptcp_pm_sport_in_anno_list(struct mptcp_sock *msk, const struct sock *sk);
 struct mptcp_pm_add_addr *
-mptcp_pm_del_add_timer(struct mptcp_sock *msk,
-		       const struct mptcp_addr_info *addr, bool check_id);
+mptcp_pm_announced_del_timer(struct mptcp_sock *msk,
+			     const struct mptcp_addr_info *addr, bool check_id);
+bool mptcp_pm_announced_remove(struct mptcp_sock *msk,
+			       const struct mptcp_addr_info *addr);
+bool mptcp_pm_announced_has_ssk(struct mptcp_sock *msk, const struct sock *ssk);
 bool mptcp_lookup_subflow_by_saddr(const struct list_head *list,
 				   const struct mptcp_addr_info *saddr);
-bool mptcp_remove_anno_list_by_saddr(struct mptcp_sock *msk,
-				     const struct mptcp_addr_info *addr);
 int mptcp_pm_nl_set_flags(struct mptcp_pm_addr_entry *local,
 			  struct genl_info *info);
 int mptcp_userspace_pm_set_flags(struct mptcp_pm_addr_entry *local,
net/mptcp/subflow.c +2 -2
@@ -235,7 +235,7 @@ again:
 			pr_debug("syn inet_sport=%d %d\n",
 				 ntohs(inet_sk(sk_listener)->inet_sport),
 				 ntohs(inet_sk((struct sock *)subflow_req->msk)->inet_sport));
-			if (!mptcp_pm_sport_in_anno_list(subflow_req->msk, sk_listener)) {
+			if (!mptcp_pm_announced_has_ssk(subflow_req->msk, sk_listener)) {
 				SUBFLOW_REQ_INC_STATS(req, MPTCP_MIB_MISMATCHPORTSYNRX);
 				subflow_add_reset_reason(skb, MPTCP_RST_EPROHIBIT);
 				return -EPERM;
@@ -926,7 +926,7 @@ create_child:
 				pr_debug("ack inet_sport=%d %d\n",
 					 ntohs(inet_sk(sk)->inet_sport),
 					 ntohs(inet_sk((struct sock *)owner)->inet_sport));
-				if (!mptcp_pm_sport_in_anno_list(owner, sk)) {
+				if (!mptcp_pm_announced_has_ssk(owner, sk)) {
 					SUBFLOW_REQ_INC_STATS(req, MPTCP_MIB_MISMATCHPORTACKRX);
 					subflow_add_reset_reason(skb, MPTCP_RST_EPROHIBIT);
 					goto dispose_child;
author maintainer
#65 lore raw

PATCH 65/101 mptcp: pm: fix memory leak from alloc-during-teardown race

In reply to Greg Kroah-Hartman

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

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

From: Shardul Bankar <[email protected]>

[ Upstream commit efc33b5102ff859bacd390a5f30112d8e0c084c0 ]

mptcp_pm_destroy() empties msk->pm.anno_list and msk->pm.userspace_pm_local_addr_list under msk->pm.lock during socket teardown, dropping the lock between the two.

A concurrent userspace PM genl ANNOUNCE on the same msk holds a sock reference via mptcp_token_get_sock() and, in mptcp_pm_nl_announce_doit(), calls mptcp_userspace_pm_append_new_local_addr() and mptcp_pm_announced_alloc(). Both take msk->pm.lock briefly to add to their respective lists. Because the genl handler holds a sock reference, mptcp_pm_destroy() may run on the same msk via mptcp_disconnect(), which invokes mptcp_destroy_common() without dropping the sock refcount, before the handler completes.

If the lock acquisitions interleave such that mptcp_pm_destroy() empties a list first, the later alloc adds its entry to a list head that nothing else iterates for this msk, and the entry leaks. kmemleak reports both mptcp_pm_add_addr objects (from mptcp_pm_announced_alloc()) and mptcp_pm_addr_entry objects (from mptcp_userspace_pm_append_new_local_addr()) under sustained concurrent ANNOUNCE + close load against the userspace PM.

Add an MPTCP_PM_DESTROYING bit in msk->pm.status, set by mptcp_pm_destroy() under pm.lock before the lists are emptied and checked under pm.lock by the alloc paths. Either the alloc takes pm.lock first, in which case its entry is on the list when mptcp_pm_destroy() frees it; or mptcp_pm_destroy() takes pm.lock first, in which case the later alloc observes the bit and refuses.

Found by an MPTCP protocol-flow harness extending BRF (arXiv:2305.08782).

---
 net/mptcp/pm.c           |   13 +++++++++++--
 net/mptcp/pm_userspace.c |    4 ++++
 net/mptcp/protocol.h     |    7 ++++---
 3 files changed, 19 insertions(+), 5 deletions(-)
net/mptcp/pm.c +11 -2
@@ -442,6 +442,9 @@ bool mptcp_pm_announced_alloc(struct mpt
 
 	lockdep_assert_held(&msk->pm.lock);
 
+	if (msk->pm.status & BIT(MPTCP_PM_DESTROYING))
+		return false;
+
 	add_entry = mptcp_pm_announced_lookup(msk, addr);
 	if (add_entry) {
 		if (WARN_ON_ONCE(mptcp_pm_is_kernel(msk)))
@@ -1103,10 +1106,16 @@ void mptcp_pm_worker(struct mptcp_sock *
 
 void mptcp_pm_destroy(struct mptcp_sock *msk)
 {
+	spin_lock_bh(&msk->pm.lock);
+	msk->pm.status |= BIT(MPTCP_PM_DESTROYING);
+	spin_unlock_bh(&msk->pm.lock);
+
 	mptcp_pm_free_announced_list(msk);
 
-	if (mptcp_pm_is_userspace(msk))
-		mptcp_userspace_pm_free_local_addr_list(msk);
+	/* Free the userspace local address list unconditionally: the socket
+	 * can be reused (mptcp_disconnect()) and re-selected to a different PM
+	 */
+	mptcp_userspace_pm_free_local_addr_list(msk);
 }
 
 void mptcp_pm_data_reset(struct mptcp_sock *msk)
net/mptcp/pm_userspace.c +4 -0
@@ -54,6 +54,10 @@ static int mptcp_userspace_pm_append_new
 	bitmap_zero(id_bitmap, MPTCP_PM_MAX_ADDR_ID + 1);
 
 	spin_lock_bh(&msk->pm.lock);
+	if (msk->pm.status & BIT(MPTCP_PM_DESTROYING)) {
+		ret = -EINVAL;
+		goto append_err;
+	}
 	mptcp_for_each_userspace_pm_addr(msk, e) {
 		addr_match = mptcp_addresses_equal(&e->addr, &entry->addr, true);
 		if (addr_match && entry->addr.id == 0 && needs_id)
net/mptcp/protocol.h +4 -3
@@ -190,9 +190,10 @@ enum mptcp_pm_status {
 	MPTCP_PM_ESTABLISHED,
 	MPTCP_PM_SUBFLOW_ESTABLISHED,
 	MPTCP_PM_ALREADY_ESTABLISHED,	/* persistent status, set after ESTABLISHED event */
-	MPTCP_PM_MPC_ENDPOINT_ACCOUNTED /* persistent status, set after MPC local address is
-					 * accounted int id_avail_bitmap
-					 */
+	MPTCP_PM_MPC_ENDPOINT_ACCOUNTED, /* persistent status, set after MPC local address is
+					  * accounted int id_avail_bitmap
+					  */
+	MPTCP_PM_DESTROYING,		/* To fence out PM list allocs */
 };
 
 enum mptcp_pm_type {
author maintainer
#66 lore raw

PATCH 66/101 selinux: use u16 for security classes

In reply to Greg Kroah-Hartman

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

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

From: Christian Göttsche <[email protected]>

[ Upstream commit fa79a596848fe38c55ccab8832ac35dac07fb00c ]

Security class identifiers are limited to 2^16, thus use the appropriate type u16 consistently.

Stable-dep-of: b98a8ac50775 ("selinux: require a class's permission values to cover its permission count")

---
 security/selinux/ss/policydb.c |    5 +++--
 security/selinux/ss/policydb.h |   10 +++++-----
 security/selinux/ss/services.c |    2 +-
 3 files changed, 9 insertions(+), 8 deletions(-)
security/selinux/ss/policydb.c +3 -2
@@ -950,7 +950,7 @@ int policydb_load_isids(struct policydb
 	return 0;
 }
 
-int policydb_class_isvalid(struct policydb *p, unsigned int class)
+int policydb_class_isvalid(struct policydb *p, u16 class)
 {
 	if (!class || class > p->p_classes.nprim)
 		return 0;
@@ -2039,7 +2039,8 @@ static int filename_trans_read_helper(st
 	struct filename_trans_key *ft = NULL;
 	struct filename_trans_datum **dst, *datum, *first = NULL;
 	char *name = NULL;
-	u32 len, ttype, tclass, ndatum, i;
+	u32 len, ttype, ndatum, i;
+	u16 tclass;
 	__le32 buf[3];
 	int rc;
 
security/selinux/ss/policydb.h +5 -5
@@ -48,7 +48,7 @@ struct common_datum {
 
 /* Class attributes */
 struct class_datum {
-	u32 value; /* class value */
+	u16 value; /* class value */
 	char *comkey; /* common name */
 	struct common_datum *comdatum; /* common datum */
 	struct symtab permissions; /* class-specific permission symbol table */
@@ -82,7 +82,7 @@ struct role_datum {
 struct role_trans_key {
 	u32 role; /* current role */
 	u32 type; /* program executable type, or new object type */
-	u32 tclass; /* process class, or new object class */
+	u16 tclass; /* process class, or new object class */
 };
 
 struct role_trans_datum {
@@ -139,7 +139,7 @@ struct cat_datum {
 struct range_trans {
 	u32 source_type;
 	u32 target_type;
-	u32 target_class;
+	u16 target_class;
 };
 
 /* Boolean data type */
@@ -195,7 +195,7 @@ struct ocontext {
 		} ibendport;
 	} u;
 	union {
-		u32 sclass; /* security class for genfs */
+		u16 sclass; /* security class for genfs */
 		u32 behavior; /* labeling behavior for fs_use */
 	} v;
 	struct context context[2]; /* security context(s) */
@@ -322,7 +322,7 @@ struct policy_file {
 extern void policydb_destroy(struct policydb *p);
 extern int policydb_load_isids(struct policydb *p, struct sidtab *s);
 extern int policydb_context_isvalid(struct policydb *p, struct context *c);
-extern int policydb_class_isvalid(struct policydb *p, unsigned int class);
+extern int policydb_class_isvalid(struct policydb *p, u16 class);
 extern int policydb_type_isvalid(struct policydb *p, unsigned int type);
 extern int policydb_role_isvalid(struct policydb *p, unsigned int role);
 extern int policydb_read(struct policydb *p, struct policy_file *fp);
security/selinux/ss/services.c +1 -1
@@ -3290,7 +3290,7 @@ static int get_classes_callback(void *k,
 {
 	struct class_datum *datum = d;
 	char *name = k, **classes = args;
-	u32 value = datum->value - 1;
+	u16 value = datum->value - 1;
 
 	classes[value] = kstrdup(name, GFP_ATOMIC);
 	if (!classes[value])
author maintainer
#67 lore raw

PATCH 67/101 selinux: more strict policy parsing

In reply to Greg Kroah-Hartman

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

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

From: Christian Göttsche <[email protected]>

[ Upstream commit 18fa21f10d008a0fc22565109c7d38f304295912 ]

Be more strict during parsing of policies and reject invalid values.

Add some error messages in the case of policy parse failures, to enhance debugging, either on a malformed policy or a too strict check.

[PM: fixed checkpatch.pl warnings, style problems]

Stable-dep-of: b98a8ac50775 ("selinux: require a class's permission values to cover its permission count")

---
 security/selinux/include/security.h |    1 
 security/selinux/ss/avtab.c         |   35 +++++-
 security/selinux/ss/avtab.h         |   13 ++
 security/selinux/ss/conditional.c   |   18 +--
 security/selinux/ss/constraint.h    |    1 
 security/selinux/ss/policydb.c      |  196 +++++++++++++++++++++++++++++-------
 security/selinux/ss/policydb.h      |   23 +++-
 security/selinux/ss/services.c      |    6 -
 8 files changed, 233 insertions(+), 60 deletions(-)
security/selinux/include/security.h +1 -0
@@ -236,6 +236,7 @@ int security_read_policy(void **data, si
 int security_read_state_kernel(void **data, size_t *len);
 int security_policycap_supported(unsigned int req_cap);
 
+/* Maximum supported number of permissions per class */
 #define SEL_VEC_MAX 32
 struct av_decision {
 	u32 allowed;
security/selinux/ss/avtab.c +29 -6
@@ -316,7 +316,7 @@ int avtab_read_item(struct avtab *a, str
 	struct avtab_extended_perms xperms;
 	__le32 buf32[ARRAY_SIZE(xperms.perms.p)];
 	int rc;
-	unsigned int set, vers = pol->policyvers;
+	unsigned int vers = pol->policyvers;
 
 	memset(&key, 0, sizeof(struct avtab_key));
 	memset(&datum, 0, sizeof(struct avtab_datum));
@@ -327,9 +327,12 @@ int avtab_read_item(struct avtab *a, str
 			pr_err("SELinux: avtab: truncated entry\n");
 			return rc;
 		}
+		/* Read five or more items: source type, target type,
+		 * target class, AV type, and at least one datum.
+		 */
 		items2 = le32_to_cpu(buf32[0]);
-		if (items2 > ARRAY_SIZE(buf32)) {
-			pr_err("SELinux: avtab: entry overflow\n");
+		if (items2 < 5 || items2 > ARRAY_SIZE(buf32)) {
+			pr_err("SELinux: avtab: invalid item count\n");
 			return -EINVAL;
 		}
 		rc = next_entry(buf32, fp, sizeof(u32) * items2);
@@ -358,6 +361,13 @@ int avtab_read_item(struct avtab *a, str
 			return -EINVAL;
 		}
 
+		if (!policydb_type_isvalid(pol, key.source_type) ||
+		    !policydb_type_isvalid(pol, key.target_type) ||
+		    !policydb_class_isvalid(pol, key.target_class)) {
+			pr_err("SELinux: avtab: invalid type or class\n");
+			return -EINVAL;
+		}
+
 		val = le32_to_cpu(buf32[items++]);
 		enabled = (val & AVTAB_ENABLED_OLD) ? AVTAB_ENABLED : 0;
 
@@ -376,6 +386,11 @@ int avtab_read_item(struct avtab *a, str
 
 		for (i = 0; i < ARRAY_SIZE(spec_order); i++) {
 			if (val & spec_order[i]) {
+				if (items >= items2) {
+					pr_err("SELinux: avtab: entry has too many items (%d/%d)\n",
+					       items + 1, items2);
+					return -EINVAL;
+				}
 				key.specified = spec_order[i] | enabled;
 				datum.u.data = le32_to_cpu(buf32[items++]);
 				rc = insertf(a, &key, &datum, p);
@@ -411,9 +426,13 @@ int avtab_read_item(struct avtab *a, str
 		return -EINVAL;
 	}
 
-	set = hweight16(key.specified & (AVTAB_XPERMS | AVTAB_TYPE | AVTAB_AV));
-	if (!set || set > 1) {
-		pr_err("SELinux:  avtab:  more than one specifier\n");
+	if (hweight16(key.specified & ~AVTAB_ENABLED) != 1) {
+		pr_err("SELinux:  avtab:  not exactly one specifier\n");
+		return -EINVAL;
+	}
+
+	if (key.specified & ~AVTAB_SPECIFIER_MASK) {
+		pr_err("SELinux:  avtab:  invalid specifier\n");
 		return -EINVAL;
 	}
 
@@ -438,6 +457,10 @@ int avtab_read_item(struct avtab *a, str
 			pr_err("SELinux: avtab: truncated entry\n");
 			return rc;
 		}
+		if (!avtab_is_valid_xperm_specified(xperms.specified))
+			pr_warn_once_policyload(pol,
+						"SELinux: avtab: unsupported xperm specifier %#x\n",
+						xperms.specified);
 		rc = next_entry(&xperms.driver, fp, sizeof(u8));
 		if (rc) {
 			pr_err("SELinux: avtab: truncated entry\n");
security/selinux/ss/avtab.h +13 -0
@@ -44,6 +44,7 @@ struct avtab_key {
 	 AVTAB_XPERMS_DONTAUDIT)
 #define AVTAB_ENABLED_OLD 0x80000000 /* reserved for used in cond_avtab */
 #define AVTAB_ENABLED	  0x8000 /* reserved for used in cond_avtab */
+#define AVTAB_SPECIFIER_MASK (AVTAB_AV | AVTAB_TYPE | AVTAB_XPERMS | AVTAB_ENABLED)
 	u16 specified; /* what field is specified */
 };
 
@@ -68,6 +69,18 @@ struct avtab_extended_perms {
 	struct extended_perms_data perms;
 };
 
+static inline bool avtab_is_valid_xperm_specified(u8 specified)
+{
+	switch (specified) {
+	case AVTAB_XPERMS_IOCTLFUNCTION:
+	case AVTAB_XPERMS_IOCTLDRIVER:
+	case AVTAB_XPERMS_NLMSG:
+		return true;
+	default:
+		return false;
+	}
+}
+
 struct avtab_datum {
 	union {
 		u32 data; /* access vector or type value */
security/selinux/ss/conditional.c +7 -11
@@ -199,19 +199,12 @@ int cond_index_bool(void *key, void *dat
 	return 0;
 }
 
-static int bool_isvalid(struct cond_bool_datum *b)
-{
-	if (!(b->state == 0 || b->state == 1))
-		return 0;
-	return 1;
-}
-
 int cond_read_bool(struct policydb *p, struct symtab *s, struct policy_file *fp)
 {
 	char *key = NULL;
 	struct cond_bool_datum *booldatum;
 	__le32 buf[3];
-	u32 len;
+	u32 len, val;
 	int rc;
 
 	booldatum = kzalloc_obj(*booldatum);
@@ -223,11 +216,12 @@ int cond_read_bool(struct policydb *p, s
 		goto err;
 
 	booldatum->value = le32_to_cpu(buf[0]);
-	booldatum->state = le32_to_cpu(buf[1]);
+	val = le32_to_cpu(buf[1]);
 
 	rc = -EINVAL;
-	if (!bool_isvalid(booldatum))
+	if (!val_is_boolean(val))
 		goto err;
+	booldatum->state = (int)val;
 
 	len = le32_to_cpu(buf[2]);
 
@@ -241,6 +235,7 @@ int cond_read_bool(struct policydb *p, s
 
 	return 0;
 err:
+	pr_err("SELinux: conditional: failed to read boolean\n");
 	cond_destroy_bool(key, booldatum, NULL);
 	return rc;
 }
@@ -362,7 +357,8 @@ static int expr_node_isvalid(struct poli
 		return 0;
 	}
 
-	if (expr->boolean > p->p_bools.nprim) {
+	if (expr->expr_type == COND_BOOL &&
+	    (expr->boolean == 0 || expr->boolean > p->p_bools.nprim)) {
 		pr_err("SELinux: conditional expressions uses unknown bool.\n");
 		return 0;
 	}
security/selinux/ss/constraint.h +1 -0
@@ -50,6 +50,7 @@ struct constraint_expr {
 	u32 op; /* operator */
 
 	struct ebitmap names; /* names */
+	/* internally unused, only forwarded via policydb_write() */
 	struct type_set *type_names;
 
 	struct constraint_expr *next; /* next expression */
security/selinux/ss/policydb.c +161 -35

402 lines not on this page · raw message

security/selinux/ss/policydb.h +19 -4
@@ -74,7 +74,7 @@ struct class_datum {
 /* Role attributes */
 struct role_datum {
 	u32 value; /* internal role value */
-	u32 bounds; /* boundary of role */
+	u32 bounds; /* boundary of role, 0 for none */
 	struct ebitmap dominates; /* set of roles dominated by this role */
 	struct ebitmap types; /* set of authorized types for role */
 };
@@ -110,7 +110,8 @@ struct role_allow {
 /* Type attributes */
 struct type_datum {
 	u32 value; /* internal type value */
-	u32 bounds; /* boundary of type */
+	u32 bounds; /* boundary of type, 0 for none */
+	/* internally unused, only forwarded via policydb_write() */
 	unsigned char primary; /* primary name? */
 	unsigned char attribute; /* attribute ?*/
 };
@@ -118,7 +119,7 @@ struct type_datum {
 /* User attributes */
 struct user_datum {
 	u32 value; /* internal user value */
-	u32 bounds; /* bounds of user */
+	u32 bounds; /* bounds of user, 0 for none */
 	struct ebitmap roles; /* set of authorized roles for user */
 	struct mls_range range; /* MLS range (min - max) for user */
 	struct mls_level dfltlevel; /* default login MLS level for user */
@@ -195,7 +196,7 @@ struct ocontext {
 		} ibendport;
 	} u;
 	union {
-		u16 sclass; /* security class for genfs */
+		u16 sclass; /* security class for genfs (can be 0 for wildcard) */
 		u32 behavior; /* labeling behavior for fs_use */
 	} v;
 	struct context context[2]; /* security context(s) */
@@ -388,9 +389,23 @@ static inline char *sym_name(struct poli
 	return p->sym_val_to_name[sym_num][element_nr];
 }
 
+static inline bool val_is_boolean(u32 value)
+{
+	return value == 0 || value == 1;
+}
+
 extern int str_read(char **strp, gfp_t flags, struct policy_file *fp, u32 len);
 
 extern u16 string_to_security_class(struct policydb *p, const char *name);
 extern u32 string_to_av_perm(struct policydb *p, u16 tclass, const char *name);
 
+#define pr_warn_once_policyload(policy, fmt, ...)    \
+	do {                                         \
+		static const void *prev_policy__;    \
+		if (prev_policy__ != policy) {       \
+			pr_warn(fmt, ##__VA_ARGS__); \
+			prev_policy__ = policy;      \
+		}                                    \
+	} while (0)
+
 #endif /* _SS_POLICYDB_H_ */
security/selinux/ss/services.c +2 -4
@@ -446,8 +446,6 @@ static int dump_masked_av_helper(void *k
 	struct perm_datum *pdatum = d;
 	char **permission_names = args;
 
-	BUG_ON(pdatum->value < 1 || pdatum->value > 32);
-
 	permission_names[pdatum->value - 1] = (char *)k;
 
 	return 0;
@@ -466,7 +464,7 @@ static void security_dump_masked_av(stru
 	char *tclass_name;
 	char *scontext_name = NULL;
 	char *tcontext_name = NULL;
-	char *permission_names[32];
+	char *permission_names[SEL_VEC_MAX];
 	int index;
 	u32 length;
 	bool need_comma = false;
@@ -507,7 +505,7 @@ static void security_dump_masked_av(stru
 			 "scontext=%s tcontext=%s tclass=%s perms=",
 			 reason, scontext_name, tcontext_name, tclass_name);
 
-	for (index = 0; index < 32; index++) {
+	for (index = 0; index < SEL_VEC_MAX; index++) {
 		u32 mask = (1 << index);
 
 		if ((mask & permissions) == 0)
author maintainer
#68 lore raw

PATCH 68/101 selinux: require a classs permission values to cover its permission count

In reply to Greg Kroah-Hartman

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

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

From: Bryam Vargas <[email protected]>

[ Upstream commit b98a8ac50775540f3804397ed08f61ef9910bcab ]

security_get_permissions() sizes an array by the class's permissions.nprim and fills it at value - 1, from the inherited common's permission table and then the class's own. A value no permission defines leaves a NULL that sel_make_perm_files() passes to d_alloc_name(), an oops inside sel_write_load() that strands selinux_state.policy_mutex and leaves every later load in uninterruptible sleep; two permissions sharing a value overwrite the first kstrdup(). Bounding each value by nprim catches neither, and neither would a count: the symbol table is keyed on the permission name, so duplicates pass.

Track the values each permission table claims and require them to cover exactly what its count declares, rejecting a count no value can reach. Conforming policies are unaffected.

Fixes 55fcf09b3fe4 ("selinux: add support for querying object classes and permissions from the running policy")
---
 security/selinux/ss/policydb.c |   51 ++++++++++++++++++++++++++++++++++++-----
 1 file changed, 46 insertions(+), 5 deletions(-)
security/selinux/ss/policydb.c +46 -5
@@ -1138,7 +1138,18 @@ int str_read(char **strp, gfp_t flags, s
 	return 0;
 }
 
-static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *fp)
+/*
+ * Bitmap of the permission values a symtab has claimed.  Values are 1-based
+ * and bounded by SEL_VEC_MAX, the width of an access vector, so the whole set
+ * fits in a u32 and the callers reject an nprim past that width.
+ */
+static u32 perm_claimed_mask(u32 nprim)
+{
+	return nprim ? U32_MAX >> (SEL_VEC_MAX - nprim) : 0;
+}
+
+static int perm_read(struct policydb *p, struct symtab *s,
+		     struct policy_file *fp, u32 *claimed)
 {
 	char *key = NULL;
 	struct perm_datum *perdatum;
@@ -1170,6 +1181,10 @@ static int perm_read(struct policydb *p,
 	/* indexes an nprim-sized array in security_get_permissions() */
 	if (perdatum->value > s->nprim)
 		goto bad;
+	/* two permissions cannot share one slot of that array */
+	if (*claimed & (1U << (perdatum->value - 1)))
+		goto bad;
+	*claimed |= 1U << (perdatum->value - 1);
 
 	return 0;
 bad:
@@ -1182,7 +1197,7 @@ static int common_read(struct policydb *
 	char *key = NULL;
 	struct common_datum *comdatum;
 	__le32 buf[4];
-	u32 i, len, nel;
+	u32 i, len, nel, claimed = 0;
 	int rc;
 
 	comdatum = kzalloc_obj(*comdatum);
@@ -1204,17 +1219,28 @@ static int common_read(struct policydb *
 	if (rc)
 		goto bad;
 	comdatum->permissions.nprim = le32_to_cpu(buf[2]);
+	/* no permission value can reach a slot past SEL_VEC_MAX */
+	rc = -EINVAL;
+	if (comdatum->permissions.nprim > SEL_VEC_MAX)
+		goto bad;
 
 	rc = str_read(&key, GFP_KERNEL, fp, len);
 	if (rc)
 		goto bad;
 
 	for (i = 0; i < nel; i++) {
-		rc = perm_read(p, &comdatum->permissions, fp);
+		rc = perm_read(p, &comdatum->permissions, fp, &claimed);
 		if (rc)
 			goto bad;
 	}
 
+	rc = -EINVAL;
+	if (claimed != perm_claimed_mask(comdatum->permissions.nprim)) {
+		pr_err("SELinux:  common %s does not define every permission it declares\n",
+		       key);
+		goto bad;
+	}
+
 	hash_eval(&comdatum->permissions.table, "common_permissions", key);
 
 	rc = symtab_insert(s, key, comdatum);
@@ -1348,7 +1374,7 @@ static int class_read(struct policydb *p
 	char *key = NULL;
 	struct class_datum *cladatum;
 	__le32 buf[6];
-	u32 i, len, len2, ncons, nel, val;
+	u32 i, len, len2, ncons, nel, val, claimed = 0, inherited = 0;
 	int rc;
 
 	cladatum = kzalloc_obj(*cladatum);
@@ -1376,6 +1402,10 @@ static int class_read(struct policydb *p
 	if (rc)
 		goto bad;
 	cladatum->permissions.nprim = le32_to_cpu(buf[3]);
+	/* no permission value can reach a slot past SEL_VEC_MAX */
+	rc = -EINVAL;
+	if (cladatum->permissions.nprim > SEL_VEC_MAX)
+		goto bad;
 
 	ncons = le32_to_cpu(buf[5]);
 
@@ -1410,11 +1440,22 @@ static int class_read(struct policydb *p
 		}
 	}
 	for (i = 0; i < nel; i++) {
-		rc = perm_read(p, &cladatum->permissions, fp);
+		rc = perm_read(p, &cladatum->permissions, fp, &claimed);
 		if (rc)
 			goto bad;
 	}
 
+	/* the class's own permissions must claim the slots the common leaves */
+	if (cladatum->comdatum)
+		inherited = cladatum->comdatum->permissions.nprim;
+	rc = -EINVAL;
+	if (claimed != (perm_claimed_mask(cladatum->permissions.nprim) &
+			~perm_claimed_mask(inherited))) {
+		pr_err("SELinux:  class %s does not define every permission it declares\n",
+		       key);
+		goto bad;
+	}
+
 	hash_eval(&cladatum->permissions.table, "class_permissions", key);
 
 	rc = read_cons_helper(p, &cladatum->constraints, ncons, 0, fp);
author maintainer
#69 lore raw

PATCH 69/101 HID: magicmouse: fix battery reporting for Bluetooth Magic Trackpad USB-C

In reply to Greg Kroah-Hartman

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

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

From: Andrei Fed <[email protected]>

commit a1556b48efc157fdda07b52ecc56c7bd1e1786f0 upstream.

The Apple Magic Trackpad 2 (USB-C) reports a wildly wrong battery capacity over Bluetooth, for example a constant 4% for a pack that is actually at 74%.

The device's battery input report (0x90) is laid out as [report-id][status][charge]. hid-input's synchronous capacity query, hidinput_query_battery_capacity(), assumes the common [report-id][capacity] layout and returns buf[1], which for this device is the status byte rather than the charge (buf[2]).

magicmouse_fetch_battery(), which requests the battery report through hid_hw_request() so the reply is decoded via the report descriptor at the correct field offset, is gated to the USB models and never runs over Bluetooth. The device does not push battery reports on its own either, except a single one at connect time, which is delivered while probe holds driver_input_lock and is silently dropped. All userspace reads therefore go through the misparsing query, and the device is stuck reporting its status byte as the capacity.

Enabling the fetch for Bluetooth is not sufficient on its own: user space reacts to the power_supply registration immediately, so a query is typically already in flight when the fetch reply is parsed. hidinput_get_battery_property() stores the query result and marks the battery as queried without rechecking whether a report arrived while it was waiting, clobbering the just-reported correct value with the misparsed one.

Fix this by adding HID_BATTERY_QUIRK_AVOID_QUERY for the Bluetooth Magic Trackpad USB-C so the misparsing query path is never used, and by fetching the battery at the end of probe for this device. hidp has no asynchronous request() callback, so the fetch is serviced synchronously via __hid_request() while probe still holds driver_input_lock; call hid_device_io_start() first so the reply is processed instead of being discarded.

Tested with a Magic Trackpad USB-C (004c:0324) over Bluetooth on 6.18.37: the reported capacity now matches the device (verified against a raw GET_REPORT of report 0x90) and updates on reconnect.

Fixes 87a2f10395c8 ("HID: magicmouse: Apple Magic Trackpad 2 USB-C driver support")
---
 drivers/hid/hid-input.c      |    3 +++
 drivers/hid/hid-magicmouse.c |   19 ++++++++++++++++++-
 2 files changed, 21 insertions(+), 1 deletion(-)
drivers/hid/hid-input.c +3 -0
@@ -375,6 +375,9 @@ static const struct hid_device_id hid_ba
 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
 		USB_DEVICE_ID_APPLE_MAGICTRACKPAD),
 	  HID_BATTERY_QUIRK_IGNORE },
+	{ HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE,
+		USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC),
+	  HID_BATTERY_QUIRK_AVOID_QUERY },
 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM,
 		USB_DEVICE_ID_ELECOM_BM084),
 	  HID_BATTERY_QUIRK_IGNORE },
drivers/hid/hid-magicmouse.c +18 -1
@@ -828,6 +828,12 @@ static bool is_usb_magictrackpad2(__u32
 	       product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC;
 }
 
+static bool is_bt_magictrackpad2(__u32 vendor, __u32 product)
+{
+	return vendor == BT_VENDOR_ID_APPLE &&
+	       product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC;
+}
+
 static int magicmouse_fetch_battery(struct hid_device *hdev)
 {
 #ifdef CONFIG_HID_BATTERY_STRENGTH
@@ -838,7 +844,8 @@ static int magicmouse_fetch_battery(stru
 	bat = hid_get_battery(hdev);
 	if (!bat ||
 	    (!is_usb_magicmouse2(hdev->vendor, hdev->product) &&
-	     !is_usb_magictrackpad2(hdev->vendor, hdev->product)))
+	     !is_usb_magictrackpad2(hdev->vendor, hdev->product) &&
+	     !is_bt_magictrackpad2(hdev->vendor, hdev->product)))
 		return -1;
 
 	report_enum = &hdev->report_enum[bat->report_type];
@@ -971,6 +978,16 @@ static int magicmouse_probe(struct hid_d
 		schedule_delayed_work(&msc->work, msecs_to_jiffies(500));
 	}
 
+	/*
+	 * Query the Bluetooth Magic Trackpad USB-C battery as done for USB.
+	 * Start io first: probe holds driver_input_lock and the synchronous
+	 * GET_REPORT reply would otherwise be dropped.
+	 */
+	if (is_bt_magictrackpad2(id->vendor, id->product)) {
+		hid_device_io_start(hdev);
+		magicmouse_fetch_battery(hdev);
+	}
+
 	return 0;
 err_stop_hw:
 	if (is_usb_magicmouse2(id->vendor, id->product) ||
author maintainer
#70 lore raw

PATCH 70/101 HID: magicmouse: prevent unbounded recursion in magicmouse_raw_event()

In reply to Greg Kroah-Hartman

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

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

From: Jose Villaseñor Montfort <[email protected]>

commit db8d634128d2ba88d79c0b601e983ebe14bb0519 upstream.

magicmouse_raw_event() handles DOUBLE_REPORT_ID (0xf7) packets, which pack two touch reports into one, by splitting the packet and calling itself on each half. The only guard against runaway recursion is a "size < 1" check, which stops zero-sized calls but does not bound the recursion depth.

A malicious HID device that matches this driver can send a report starting with DOUBLE_REPORT_ID and filled with the sequence [0xf7, 0x00]. Each level consumes two bytes and recurses on the remainder, so an incoming report of up to HID_MAX_BUFFER_SIZE (16 KiB) drives roughly 8000 nested calls. That easily exhausts the 16 KiB kernel stack, leading to a stack overflow: a panic with CONFIG_VMAP_STACK, or memory corruption without it.

A double report only ever wraps two normal reports; it is never legitimately nested. Refuse to re-enter the DOUBLE_REPORT_ID case from a recursive call so the recursion depth is bounded to two, while all valid packets keep being parsed exactly as before.

---
 drivers/hid/hid-magicmouse.c |   25 ++++++++++++++++++++-----
 1 file changed, 20 insertions(+), 5 deletions(-)
drivers/hid/hid-magicmouse.c +20 -5
@@ -383,8 +383,8 @@ static void magicmouse_emit_touch(struct
 	}
 }
 
-static int magicmouse_raw_event(struct hid_device *hdev,
-		struct hid_report *report, u8 *data, int size)
+static int __magicmouse_raw_event(struct hid_device *hdev,
+		struct hid_report *report, u8 *data, int size, bool nested)
 {
 	struct magicmouse_sc *msc = hid_get_drvdata(hdev);
 	struct input_dev *input = msc->input;
@@ -495,6 +495,15 @@ static int magicmouse_raw_event(struct h
 		 * packet.
 		 */
 
+		/*
+		 * A double report only ever wraps two normal reports, so it is
+		 * never nested. Refuse to recurse a second time; otherwise a
+		 * malicious device could chain DOUBLE_REPORT_ID packets to drive
+		 * unbounded recursion and overflow the kernel stack.
+		 */
+		if (nested)
+			return 0;
+
 		/* Ensure that we have at least 2 elements (report type and size) */
 		if (size < 2)
 			return 0;
@@ -506,9 +515,9 @@ static int magicmouse_raw_event(struct h
 			return 0;
 		}
 
-		magicmouse_raw_event(hdev, report, data + 2, data[1]);
-		magicmouse_raw_event(hdev, report, data + 2 + data[1],
-			size - 2 - data[1]);
+		__magicmouse_raw_event(hdev, report, data + 2, data[1], true);
+		__magicmouse_raw_event(hdev, report, data + 2 + data[1],
+			size - 2 - data[1], true);
 		return 0;
 	default:
 		return 0;
@@ -534,6 +543,12 @@ static int magicmouse_raw_event(struct h
 	return 1;
 }
 
+static int magicmouse_raw_event(struct hid_device *hdev,
+		struct hid_report *report, u8 *data, int size)
+{
+	return __magicmouse_raw_event(hdev, report, data, size, false);
+}
+
 static int magicmouse_event(struct hid_device *hdev, struct hid_field *field,
 		struct hid_usage *usage, __s32 value)
 {
author maintainer
#71 lore raw

PATCH 71/101 HID: magicmouse: re-enable multitouch after reset-resume

In reply to Greg Kroah-Hartman

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

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

From: Christopher Kodama <[email protected]>

commit 4253fe22b137c4ee68f36b707fdb1b44b191edc4 upstream.

When the Apple Magic Trackpad 2 (USB) is reset across a power transition (e.g. resume from hibernation) it drops out of multitouch mode: it keeps sending report ID 0x02 on its HID_TYPE_USBMOUSE interface, but the packet shrinks from 21 to 8 bytes and the trackpad2 handler drops it (size < 12). Clicks still work but pointer motion is lost until the device is re-plugged or the driver reloaded.

Re-enable multitouch from .reset_resume via the workqueue. Only .reset_resume is needed; suspend-to-idle keeps the device powered and retains multitouch.

Fixes 87a2f10395c8 ("HID: magicmouse: Apple Magic Trackpad 2 USB-C driver support")
Assisted-by Claude-Code:claude-opus-4-8
---
 drivers/hid/hid-magicmouse.c |   19 +++++++++++++++++++
 1 file changed, 19 insertions(+)
drivers/hid/hid-magicmouse.c +19 -0
@@ -1027,6 +1027,22 @@ static void magicmouse_remove(struct hid
 	hid_hw_stop(hdev);
 }
 
+#ifdef CONFIG_PM
+static int magicmouse_reset_resume(struct hid_device *hdev)
+{
+	struct magicmouse_sc *msc = hid_get_drvdata(hdev);
+
+	/* The device drops out of multitouch mode on resume; re-send the
+	 * enable report.  Only the HID_TYPE_USBMOUSE interface accepts it, and
+	 * it must be deferred. Sending it inline here is too early.
+	 */
+	if (msc && hdev->type == HID_TYPE_USBMOUSE)
+		schedule_delayed_work(&msc->work, msecs_to_jiffies(500));
+
+	return 0;
+}
+#endif
+
 static const __u8 *magicmouse_report_fixup(struct hid_device *hdev, __u8 *rdesc,
 					   unsigned int *rsize)
 {
@@ -1090,6 +1106,9 @@ static struct hid_driver magicmouse_driv
 	.event = magicmouse_event,
 	.input_mapping = magicmouse_input_mapping,
 	.input_configured = magicmouse_input_configured,
+#ifdef CONFIG_PM
+	.reset_resume = magicmouse_reset_resume,
+#endif
 };
 module_hid_driver(magicmouse_driver);
 
author maintainer
#72 lore raw

PATCH 72/101 HID: magicmouse: do not keep a stale msc->input if no input is claimed

In reply to Greg Kroah-Hartman

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

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

From: Jose Villaseñor Montfort <[email protected]>

commit 0af3b89705688af01aa06025b84fa7a1e06ba6cc upstream.

magicmouse_input_mapping() caches the first hid_input's input_dev in msc->input while the report descriptor is parsed, and the rest of the driver treats a non-NULL msc->input as proof that an input device was registered.

That does not hold on the hid-input error path. If hidinput_connect() fails -- for instance because input_register_device() returns an error -- it unwinds through hidinput_disconnect(), which frees every input_dev it created, including the one cached in msc->input.

The failure does not abort the probe. hid_connect() only skips the claim:

	if ((connect_mask & HID_CONNECT_HIDINPUT) && !hidinput_connect(hdev,
				connect_mask & HID_CONNECT_HIDINPUT_FORCE))
		hdev->claimed |= HID_CLAIMED_INPUT;

and the "device has no listeners" bailout below it does not fire for this driver, which sets ->raw_event; on the USB Magic Mouse 2 / Magic Trackpad 2 paths hidraw and hiddev are claimed as well. hid_hw_start() therefore returns 0 and magicmouse_probe() continues with msc->input pointing at freed memory. Being non-NULL, it passes the "input not registered" check in probe and the NULL checks in ->raw_event and ->event, so the next input report dereferences freed memory.

Clear msc->input when the HID core did not claim an input device, so the existing NULL checks cover this case as well.

---
 drivers/hid/hid-magicmouse.c |   10 ++++++++++
 1 file changed, 10 insertions(+)
drivers/hid/hid-magicmouse.c +10 -0
@@ -922,6 +922,16 @@ static int magicmouse_probe(struct hid_d
 		return ret;
 	}
 
+	/*
+	 * When hidinput_connect() fails it frees every input device it
+	 * created, but that does not fail hid_hw_start(): the core simply
+	 * does not claim an input. msc->input, cached in ->input_mapping
+	 * while the report descriptor was parsed, would then be a dangling
+	 * pointer that passes every NULL check. Trust the core's claim.
+	 */
+	if (!(hdev->claimed & HID_CLAIMED_INPUT))
+		msc->input = NULL;
+
 	if (is_usb_magicmouse2(id->vendor, id->product) ||
 	    is_usb_magictrackpad2(id->vendor, id->product)) {
 		timer_setup(&msc->battery_timer, magicmouse_battery_timer_tick, 0);
author maintainer
#73 lore raw

PATCH 73/101 HID: core: fix OOB read of field->usage in hid_set_field()

In reply to Greg Kroah-Hartman

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

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

From: Baul Lee <[email protected]>

commit a13cdb19fcb223ed41bdab3bab42b98dba87e90b upstream.

hid_set_field() hands field->usage + offset to hid_dump_input() before the guard that bounds offset:

	hid_dump_input(field->report->device, field->usage + offset, value);

	if (offset >= field->report_count) {
		hid_err(...);
		return -1;
	}

Under CONFIG_DEBUG_FS hid_dump_input() dereferences that pointer, with buf = hid_resolv_usage(usage->hid, NULL). The usage[] array is allocated inline with the hid_field in hid_register_field() and holds field->maxusage entries, so an offset past it reads off the end of the kvzalloc()ed allocation and into a neighbouring object. Had the guard run first, offset < report_count <= maxusage would already have confined the pointer to the array.

A caller supplies such an offset today. picolcd_fb_send_tile() validates only report->maxfield before issuing hid_set_field(report->field[0], 11 + i, ...) for i = 0..31, so its offsets are fixed at 11..42 and are never checked against the bound field. When the device registers that field with fewer usages, the framebuffer deferred-io work drives the read on every tile. KASAN reports a 4-byte slab-out-of-bounds read in hid_dump_input() below hid_set_field(), and the same boot logs "offset (1) exceeds report_count (1)" from the guard that runs only afterwards.

Move the hid_dump_input() call below the guard. Because field->maxusage >= field->report_count, the guard then establishes that field->usage + offset lies inside the array before it is dereferenced, for every caller and without changing behaviour on the valid path.

Discovered by XBOW, triaged by Baul Lee <[email protected]>

---
 drivers/hid/hid-core.c |    5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/hid/hid-core.c +3 -2
@@ -1933,13 +1933,14 @@ int hid_set_field(struct hid_field *fiel
 
 	size = field->report_size;
 
-	hid_dump_input(field->report->device, field->usage + offset, value);
-
 	if (offset >= field->report_count) {
 		hid_err(field->report->device, "offset (%d) exceeds report_count (%d)\n",
 				offset, field->report_count);
 		return -1;
 	}
+
+	hid_dump_input(field->report->device, field->usage + offset, value);
+
 	if (field->logical_minimum < 0) {
 		if (value != snto32(s32ton(value, size), size)) {
 			hid_err(field->report->device, "value %d is out of range\n", value);
author maintainer
#74 lore raw

PATCH 74/101 HID: pidff: fix OOB write when hid->inputs is empty

In reply to Greg Kroah-Hartman

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

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

From: Baul Lee <[email protected]>

commit 67bb1074e3d2d12fa059a9cc707e89398a4e4704 upstream.

hid_pidff_init_with_quirks() derives its input_dev from

	list_entry(hid->inputs.next, struct hid_input, list)

without first checking that hid->inputs is non-empty. The list member of struct hid_input is at offset 0, so on an empty list list_entry() yields &hid->inputs itself and the following hidinput->input load reads an unrelated member of struct hid_device. dev is then a type-confused pointer, and force-feedback init writes through it: each set_bit(FF_*, dev->ffbit) stores 8 bytes at dev + 192, past the end of the object dev actually aliases, and input_ff_create() adds further writes of a heap pointer and two function pointers.

Until hid-universal-pidff the only caller was hid_pidff_init() from usbhid, which runs under HID_CLAIMED_INPUT and therefore always has at least one hid_input. universal_pidff_probe() starts the device with HID_CONNECT_DEFAULT & ~HID_CONNECT_FF and then calls hid_pidff_init_with_quirks() directly whenever the descriptor carries a PID usage page, bypassing that gate. A report descriptor whose only application collection is on HID_UP_PID leaves hid->inputs empty while hid_connect() still succeeds through the hidraw claim, so probe reaches the unguarded list_entry().

The write happens in the USB probe path, on the hotplug workqueue, so plugging in a malicious device is enough to trigger it; no attacker software and no logged-in user are required. KASAN reports an 8-byte out-of-bounds write in hid_pidff_init_with_quirks() reached from universal_pidff_probe().

Check for an empty list before deriving dev and return -ENODEV, as the other HID force-feedback drivers already do. universal_pidff_probe() propagates the error and unwinds.

Discovered by XBOW, triaged by Baul Lee <[email protected]>

Fixes f06bf8d94fff ("HID: Add hid-universal-pidff driver and supported device ids")
---
 drivers/hid/usbhid/hid-pidff.c |   13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)
drivers/hid/usbhid/hid-pidff.c +10 -3
@@ -1539,13 +1539,20 @@ static int pidff_check_autocenter(struct
 int hid_pidff_init_with_quirks(struct hid_device *hid, u32 initial_quirks)
 {
 	struct pidff_device *pidff;
-	struct hid_input *hidinput =
-		list_entry(hid->inputs.next, struct hid_input, list);
-	struct input_dev *dev = hidinput->input;
+	struct hid_input *hidinput;
+	struct input_dev *dev;
 	struct ff_device *ff;
 	int max_effects;
 	int error;
 
+	if (list_empty(&hid->inputs)) {
+		hid_err(hid, "no inputs found\n");
+		return -ENODEV;
+	}
+
+	hidinput = list_first_entry(&hid->inputs, struct hid_input, list);
+	dev = hidinput->input;
+
 	hid_dbg(hid, "starting pid init\n");
 
 	if (list_empty(&hid->report_enum[HID_OUTPUT_REPORT].report_list)) {
author maintainer
#75 lore raw

PATCH 75/101 net/ionic: avoid OOB TX partner lookup for hwstamp RXQ

In reply to Greg Kroah-Hartman

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

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

From: Anand Khoje <[email protected]>

commit d92255b405fb6f5acca408239ccd742e0a42c9cb upstream.

The dedicated hardware timestamp RX queue is allocated with q->index equal to lif->ionic->nrxqs_per_lif. The normal txqcqs array only contains the regular queue pairs, so using that index to set rxq->partner can read one entry past txqcqs[] and then write through the derived pointer.
Only link RX/TX partners for normal queue-pair indexes. Leave the hwstamp RX queue unpaired, and make the XDP_TX path abort cleanly if an RX queue has no TX partner.

---
 drivers/net/ethernet/pensando/ionic/ionic_lif.c  |   17 +++++++++++++++--
 drivers/net/ethernet/pensando/ionic/ionic_txrx.c |    7 ++++++-
 2 files changed, 21 insertions(+), 3 deletions(-)
drivers/net/ethernet/pensando/ionic/ionic_lif.c +15 -2
@@ -918,8 +918,21 @@ static int ionic_lif_rxq_init(struct ion
 	};
 	int err;
 
-	q->partner = &lif->txqcqs[q->index]->q;
-	q->partner->partner = q;
+	q->partner = NULL;
+
+	/* Only normal RX queues have matching TX queue partners. */
+	if (q->index < lif->nxqs) {
+		if (!lif->txqcqs ||
+		    q->index >= lif->ionic->ntxqs_per_lif ||
+		    !lif->txqcqs[q->index]) {
+			dev_err(dev, "missing TX queue partner for RX queue %u\n",
+				q->index);
+			return -ENXIO;
+		}
+
+		q->partner = &lif->txqcqs[q->index]->q;
+		q->partner->partner = q;
+	}
 
 	if (!lif->xdp_prog ||
 	    (lif->xdp_prog->aux && lif->xdp_prog->aux->xdp_has_frags))
drivers/net/ethernet/pensando/ionic/ionic_txrx.c +6 -1
@@ -545,13 +545,18 @@ static bool ionic_run_xdp(struct ionic_r
 		break;
 
 	case XDP_TX:
+		txq = rxq->partner;
+		if (unlikely(!txq)) {
+			err = -EIO;
+			break;
+		}
+
 		xdpf = xdp_convert_buff_to_frame(&xdp_buf);
 		if (!xdpf) {
 			err = -ENOSPC;
 			break;
 		}
 
-		txq = rxq->partner;
 		nq = netdev_get_tx_queue(netdev, txq->index);
 		__netif_tx_lock(nq, smp_processor_id());
 		txq_trans_cond_update(nq);
author maintainer
#76 lore raw

PATCH 76/101 futex/pi: Reject cross-mm private futex owners

In reply to Greg Kroah-Hartman

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

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

From: Kyle Zeng <[email protected]>

commit 59b3732f95dda1fbd2234514d35f4fb6b5bb6d85 upstream.

A private futex key borrows the waiter's mm without taking an mm_users reference. Nevertheless, attach_to_pi_owner() currently accepts an owner from a different address space and copies the private key into the owner's PI state.

When that owner exits, exit_pi_state_list() uses the saved key to find the hash bucket and acquires a reference to the waiter's private hash. If the last user of the waiter's mm exits concurrently, futex_hash_free() frees the hash while the owner still uses its bucket and reference.

Prevent this by validating in attach_to_pi_owner() that, for private futexes, the owner mm and waiter mm are the same. Perform the check with the owner's pi_lock held and after validating owner::futex::state to serialize against a concurrent PI-state exit cleanup.

[ tglx: Amended comment ]

Fixes 80367ad01d93 ("futex: Add basic infrastructure for local task local hash")
Assisted-by Codex:gpt-5.6-sol
---
 kernel/futex/core.c  |    9 ---------
 kernel/futex/futex.h |    9 +++++++++
 kernel/futex/pi.c    |   20 ++++++++++++++++++++
 3 files changed, 29 insertions(+), 9 deletions(-)

---

kernel/futex/core.c +0 -9
@@ -133,15 +133,6 @@ static bool futex_ref_is_dead(struct fut
 
 enum { FR_PERCPU = 0, FR_ATOMIC };
 
-static inline bool futex_key_is_private(union futex_key *key)
-{
-	/*
-	 * Relies on get_futex_key() to set either bit for shared
-	 * futexes -- see comment with union futex_key.
-	 */
-	return !(key->both.offset & (FUT_OFF_INODE | FUT_OFF_MMSHARED));
-}
-
 static bool futex_private_hash_get(struct futex_private_hash *fph)
 {
 	return futex_ref_get(fph);
kernel/futex/futex.h +9 -0
@@ -126,6 +126,15 @@ static inline bool should_fail_futex(boo
 }
 #endif
 
+static inline bool futex_key_is_private(union futex_key *key)
+{
+	/*
+	 * Relies on get_futex_key() to set either bit for shared
+	 * futexes -- see comment with union futex_key.
+	 */
+	return !(key->both.offset & (FUT_OFF_INODE | FUT_OFF_MMSHARED));
+}
+
 /*
  * Hash buckets are shared by all the futex_keys that hash to the same
  * location.  Each key may have multiple futex_q structures, one for each task
kernel/futex/pi.c +20 -0
@@ -465,6 +465,26 @@ static int attach_to_pi_owner(u32 __user
 		return ret;
 	}
 
+	/*
+	 * If the owner is about to exit() or exec() and tries to modify
+	 * p::futex::exit_state it is serialized against this code by
+	 * p::pi_lock.
+	 */
+	if (IS_ENABLED(CONFIG_MMU) && futex_key_is_private(key)) {
+		/*
+		 * A private futex key holds a pointer to the waiter's mm
+		 * without holding a reference on it. So it must not be attached
+		 * to an owner in a different address space. Otherwise that
+		 * owner's exit cleanup could access the private hash after the
+		 * key's mm is freed.
+		 */
+		if (unlikely(p->mm != key->private.mm)) {
+			raw_spin_unlock_irq(&p->pi_lock);
+			put_task_struct(p);
+			return -EPERM;
+		}
+	}
+
 	__attach_to_pi_owner(p, key, ps);
 	raw_spin_unlock_irq(&p->pi_lock);
 
author maintainer
#77 lore raw

PATCH 77/101 futex: Sanitize and document task_struct::futex::state transitions

In reply to Greg Kroah-Hartman

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

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

From: Thomas Gleixner <[email protected]>

commit f9ece060cc43eae8a1f148737d193ba0d07b8f88 upstream.

The futex state is used to prevent a waiter from attaching to the lock owner while the owner runs the futex cleanup in exit() or exec().

Only the state transition from FUTEX_STATE_OK to FUTEX_STATE_EXITING must be done with the task's pi_lock held, the transition away from FUTEX_STATE_EXITING has no serialization requirements on the writer side, but it's completely non obvious why. It's magically protected by exit_pi_state(), which operates under tsk::pi_lock, as that's the state which has to be correct when the waiter observes the new state.

OTOH, taking the pi_lock in futex_cleanup_end() is not a performance issue because at that point the lock should be uncontended in the vast majority of cases.

Aside of that the handling of FUTEX_STATE_EXITING in attach_to_pi_owner() and handle_exit_race() is confusing at best.

Protect the store in futex_cleanup_end() with tsk::pi_lock, handle FUTEX_STATE_EXITING in attach_to_pi_owner() explicitly and document how this is supposed to work.

---
 kernel/futex/core.c |    8 +--
 kernel/futex/pi.c   |  105 ++++++++++++++++++++++++++++++++++------------------
 2 files changed, 73 insertions(+), 40 deletions(-)

---

kernel/futex/core.c +3 -5
@@ -1501,11 +1501,9 @@ static void futex_cleanup_begin(struct t
 static void futex_cleanup_end(struct task_struct *tsk, int state)
 	__releases(&tsk->futex_exit_mutex)
 {
-	/*
-	 * Lockless store. The only side effect is that an observer might
-	 * take another loop until it becomes visible.
-	 */
-	tsk->futex_state = state;
+	scoped_guard(raw_spinlock_irq, &tsk->pi_lock)
+		tsk->futex_state = state;
+
 	/*
 	 * Drop the exit protection. This unblocks waiters which observed
 	 * FUTEX_STATE_EXITING to reevaluate the state.
kernel/futex/pi.c +70 -35

143 lines not on this page · raw message

author maintainer
#78 lore raw

PATCH 78/101 futex/pi: Plug private futex exec() race

In reply to Greg Kroah-Hartman

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

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

From: Thomas Gleixner <[email protected]>

commit c5f0bc9fd1cec4a00400cc727fcde03e0fde17cc upstream.

The check for private futexes whether the waiter's mm, which is stored in the futex_key and copied into the pi_state, is the same as the owner's mm is not sufficient for exec(). exec() has a gap where the mm check fails to give the correct answer:

exec()
...

    exec_release_mm()
      futex_exec_release()
        tsk::futex::exit_state = EXITING;
        cleanup_robust_list();
1)      tsk::futex::exit_state = OK;
    ...
    old_mm = tsk::mm;

2) tsk::mm = ->mm;

Between #1 and #2 the check for the mm is wrong as that mm is about to be swapped out and eventually freed.

Plug this gap by:

1) Setting tsk::futex::exit_state to FUTEX_STATE_DEAD in futex_exec_release()

2) Setting tsk::futex::exit_state to FUTEX_STATE_OK after the mm has been switched.

Quoted 1 line
Quoted 1 line

From a futex point of view the task is dead after it finished the robust

list cleanup up to the point where it sets the state to OK again.

Fixes 80367ad01d93 ("futex: Add basic infrastructure for local task local hash")
---
 fs/exec.c             |    8 +++++--
 include/linux/futex.h |    2 +
 kernel/futex/core.c   |   54 ++++++++++++++++++++++++++++++++++----------------
 kernel/futex/pi.c     |   22 ++++++++++++++------
 4 files changed, 61 insertions(+), 25 deletions(-)

---

fs/exec.c +6 -2
@@ -30,6 +30,7 @@
 #include <linux/mm.h>
 #include <linux/stat.h>
 #include <linux/fcntl.h>
+#include <linux/futex.h>
 #include <linux/swap.h>
 #include <linux/string.h>
 #include <linux/init.h>
@@ -843,6 +844,7 @@ static int exec_mmap(struct mm_struct *m
 	/* Notify parent that we're no longer interested in the old VM */
 	tsk = current;
 	old_mm = current->mm;
+	/* Clean up futexes and release the mm */
 	exec_mm_release(tsk, old_mm);
 
 	ret = down_write_killable(&tsk->signal->exec_update_lock);
@@ -891,9 +893,11 @@ static int exec_mmap(struct mm_struct *m
 		setmax_mm_hiwater_rss(&tsk->signal->maxrss, old_mm);
 		mm_update_next_owner(old_mm);
 		mmput(old_mm);
-		return 0;
+	} else {
+		mmdrop_lazy_tlb(active_mm);
 	}
-	mmdrop_lazy_tlb(active_mm);
+
+	futex_exec_done(tsk);
 	return 0;
 }
 
include/linux/futex.h +2 -0
@@ -77,6 +77,7 @@ static inline void futex_init_task(struc
 void futex_exit_recursive(struct task_struct *tsk);
 void futex_exit_release(struct task_struct *tsk);
 void futex_exec_release(struct task_struct *tsk);
+void futex_exec_done(struct task_struct *tsk);
 
 long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,
 	      u32 __user *uaddr2, u32 val2, u32 val3);
@@ -98,6 +99,7 @@ static inline void futex_init_task(struc
 static inline void futex_exit_recursive(struct task_struct *tsk) { }
 static inline void futex_exit_release(struct task_struct *tsk) { }
 static inline void futex_exec_release(struct task_struct *tsk) { }
+static inline void futex_exec_done(struct task_struct *tsk) { }
 static inline long do_futex(u32 __user *uaddr, int op, u32 val,
 			    ktime_t *timeout, u32 __user *uaddr2,
 			    u32 val2, u32 val3)
kernel/futex/core.c +37 -17

77 lines not on this page · raw message

kernel/futex/pi.c +16 -6
@@ -200,15 +200,25 @@ void put_pi_state(struct futex_pi_state
  *
  * - FUTEX_STATE_OK when the task is alive and waiters can be attached
  *
- * - FUTEX_STATE_EXITING when the task cleans up the robust list and pi
+ * - FUTEX_STATE_EXITING when the task cleans up the robust list and PI
  *   state. Concurrent waiters cannot attach anymore and have to wait until the
- *   cleanup is finished to re-evaluate the potential changes of robust list and
- *   pi state cleanups.
+ *   cleanup is finished to re-evaluate the potential changes caused by the
+ *   robust list and PI state cleanups.
  *
- * - FUTEX_STATE_DEAD when the task has cleaned up the robust list and
- *   is about to fully exit.
+ * - FUTEX_STATE_DEAD when the task has cleaned up the robust list. This state
+ *   is set independent of exit() or exec(). In the exit() case the task is
+ *   gone. In the exec() case this ensures that nothing can attach to the task
+ *   after cleaning up the robust list and PI state before it has switched to
+ *   the new mm. From a futex point of view the task is dead until it sets the
+ *   state to FUTEX_STATE_OK again after switching to the new mm.
  *
- * exec() switches back to FUTEX_STATE_OK after the cleanup.
+ * The valid state transitions for exit():
+ *
+ *   FUTEX_STATE_OK -> FUTEX_STATE_EXITING -> FUTEX_STATE_DEAD
+ *
+ * The valid state transitions for exec():
+ *
+ *   FUTEX_STATE_OK -> FUTEX_STATE_EXITING -> FUTEX_STATE_DEAD -> FUTEX_STATE_OK
  *
  * The state has two related locks:
  *
author maintainer
#79 lore raw

PATCH 79/101 futex: Fix race in futex_pivot_pending() during private hash resize

In reply to Greg Kroah-Hartman

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

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

From: Yao Kai <[email protected]>

commit 8e7ff730dd96519a333d1570edf1c3fabb6d3629 upstream.

A task performing a custom private hash resize can remain blocked in uninterruptible sleep indefinitely. The hung-task detector reports:

  INFO: task futex-resizer:314 blocked for more than 10 seconds.
  task:futex-resizer state:D stack:14824 pid:314 tgid:312 ppid:311

  Call Trace:
   __schedule+0x521/0xf30
   schedule+0x22/0xa0
   futex_hash_allocate+0x3db/0x490
   __do_sys_prctl+0x6f5/0xbd0
   do_syscall_64+0xf9/0x530
   entry_SYSCALL_64_after_hwframe+0x77/0x7f

  Kernel panic - not syncing: hung_task: blocked tasks

futex_pivot_pending() allows the resize request to continue when either no replacement hash is pending (hash_new == NULL) or the current hash reference count has reached zero.

After the final-reference wake, another futex task can complete the pivot between the two observations:

T1 T2

futex_hash_allocate()

    wait_var_event(mm, ...)
      futex_pivot_pending(mm)
        hash_new != NULL
                                      futex_hash()
                                        futex_ref_get(old) -> false
                                        futex_pivot_hash(mm)
                                          hash_new = NULL
                                          __futex_pivot_hash(mm, new)
                                            rcu_assign_pointer(hash, new)
        fph = rcu_dereference(hash) /* new */
        futex_ref_is_dead(fph) -> false
      schedule()

The pivot changes the state from hash_new != NULL with a dead current hash to hash_new == NULL with a live current hash. Because futex_pivot_pending() reads hash_new and hash without serialization, the resize task can observe hash_new in the pre-pivot state and hash in the post-pivot state, causing futex_pivot_pending() to return false even though the pivot has completed. The task then goes to sleep after the wakeup has already been consumed.

Serialize state reads in futex_pivot_pending() using futex_mm_phash::lock. This guarantees that futex_pivot_pending() observes hash_new and hash atomically, eliminating the race condition.

---
 kernel/futex/core.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

---

kernel/futex/core.c +2 -2
@@ -1786,12 +1786,12 @@ static bool futex_pivot_pending(struct m
 {
 	struct futex_private_hash *fph;
 
-	guard(rcu)();
+	guard(mutex)(&mm->futex_hash_lock);
 
 	if (!mm->futex_phash_new)
 		return true;
 
-	fph = rcu_dereference(mm->futex_phash);
+	fph = rcu_dereference_raw(mm->futex_phash);
 	return futex_ref_is_dead(fph);
 }
 
author maintainer
#80 lore raw

PATCH 80/101 futex: Fix race on the initial mm->futex.phash.ref allocation

In reply to Greg Kroah-Hartman

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

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

From: Hyunwoo Kim <[email protected]>

commit bde0238083647381d4747355c5a19115a3422b96 upstream.

futex_hash_allocate() allocates mm->futex.phash.ref without any locking. Commit d9b05321e21e ("futex: Move futex_hash_free() back to __mmput()") moved the allocation here and assumed that the process has just a single thread at this point.

Commit ee9dce44362b ("futex: Drop CLONE_THREAD requirement for private default hash alloc") widened need_futex_hash_allocate_default() to cover any CLONE_VM clone, but left out vfork because the parent is suspended and cannot race.

That no longer holds once vfork is nested. If a vfork child calls vfork again and is then killed with SIGKILL, the parent is released from its vfork wait and runs concurrently with the grandchild in the same mm. Neither of them went through futex_hash_allocate_default().

When both call prctl(PR_FUTEX_HASH, PR_FUTEX_HASH_SET_SLOTS) at the same time, each one sees mm->futex.phash.ref as NULL and stores its own percpu counter. Only the last store survives. The counter stored first is no longer reachable from the mm, so the references on it are not seen by __futex_ref_atomic_end(). A private hash that still has references is then considered dead and freed, and a task that still holds one of its buckets writes into freed memory in futex_q_lock().

Store the counter once with cmpxchg() and let the loser free_percpu() its own. The initial reference has to be taken before the store, otherwise another task can install a private hash while the counter is still 0.

---
 kernel/futex/core.c |   16 ++++++++++------
 1 file changed, 10 insertions(+), 6 deletions(-)

---

kernel/futex/core.c +10 -6
@@ -1842,14 +1842,18 @@ static int futex_hash_allocate(unsigned
 	}
 
 	if (!mm->futex_ref) {
+		unsigned int __percpu *ref = alloc_percpu(unsigned int);
+
+		if (!ref)
+			return -ENOMEM;
+
 		/*
-		 * This will always be allocated by the first thread and
-		 * therefore requires no locking.
+		 * Tasks sharing the mm can run this concurrently, so take the
+		 * initial reference before publishing the counter.
 		 */
-		mm->futex_ref = alloc_percpu(unsigned int);
-		if (!mm->futex_ref)
-			return -ENOMEM;
-		this_cpu_inc(*mm->futex_ref); /* 0 -> 1 */
+		this_cpu_inc(*ref); /* 0 -> 1 */
+		if (cmpxchg(&mm->futex_ref, NULL, ref))
+			free_percpu(ref);
 	}
 
 	fph = kvzalloc(struct_size(fph, queues, hash_slots),
author maintainer
#81 lore raw

PATCH 81/101 futex: Fix might_sleep() warning in futex_pivot_pending()

In reply to Greg Kroah-Hartman

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

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

From: Peter Zijlstra <[email protected]>

commit d8aa5dd97944a72d4a9e3cc79bb80fcac7d6e829 upstream.

A younger me put a WARN in might_sleep() to warn about nested sleep loops. This younger me also build a wait-loop variant that can deal with it. This wait-loop variant doesn't have all the fancy wrappers, since it isn't used much. It also lacks wait-bit support.

Add the wait-bit support and use it to fix the nested wait issue.

---
 include/linux/wait.h     |    1 +
 include/linux/wait_bit.h |    1 +
 kernel/futex/core.c      |   27 ++++++++++++++++++++++++++-
 kernel/sched/wait.c      |   15 +++++++++++++++
 kernel/sched/wait_bit.c  |   14 +++++++++++---
 5 files changed, 54 insertions(+), 4 deletions(-)

---

include/linux/wait.h +1 -0
@@ -1228,6 +1228,7 @@ long prepare_to_wait_event(struct wait_q
 void finish_wait(struct wait_queue_head *wq_head, struct wait_queue_entry *wq_entry);
 long wait_woken(struct wait_queue_entry *wq_entry, unsigned mode, long timeout);
 int woken_wake_function(struct wait_queue_entry *wq_entry, unsigned mode, int sync, void *key);
+int woken_wake_bit_function(struct wait_queue_entry *wq_entry, unsigned mode, int sync, void *key);
 int autoremove_wake_function(struct wait_queue_entry *wq_entry, unsigned mode, int sync, void *key);
 
 #define DEFINE_WAIT_FUNC(name, function)					\
include/linux/wait_bit.h +1 -0
@@ -32,6 +32,7 @@ int out_of_line_wait_on_bit_timeout(unsi
 int out_of_line_wait_on_bit_lock(unsigned long *word, int, wait_bit_action_f *action, unsigned int mode);
 struct wait_queue_head *bit_waitqueue(unsigned long *word, int bit);
 extern void __init wait_bit_init(void);
+extern struct wait_bit_key *__var_wake_key(struct wait_queue_entry *wq_entry, void *arg);
 
 int wake_bit_function(struct wait_queue_entry *wq_entry, unsigned mode, int sync, void *key);
 
kernel/futex/core.c +26 -1
@@ -44,6 +44,7 @@
 #include <linux/prctl.h>
 #include <linux/mempolicy.h>
 #include <linux/mmap_lock.h>
+#include <linux/wait_bit.h>
 
 #include "futex.h"
 #include "../locking/rtmutex_common.h"
@@ -1869,11 +1870,35 @@ static int futex_hash_allocate(unsigned
 		futex_hash_bucket_init(&fph->queues[i], fph);
 
 	if (custom) {
+		struct wait_bit_queue_entry __wbq_entry;
+		struct wait_queue_head *__wq_head;
+
 		/*
 		 * Only let prctl() wait / retry; don't unduly delay clone().
 		 */
 again:
-		wait_var_event(mm, futex_pivot_pending(mm));
+		__wq_head = __var_waitqueue(mm);
+		init_wait_var_entry(&__wbq_entry, mm, 0);
+		__wbq_entry.wq_entry.func = woken_wake_bit_function;
+		add_wait_queue(__wq_head, &__wbq_entry.wq_entry);
+
+		/*
+		 * add_wait_queue()		futex_ref_put()
+		 * MB (this)			MB (implied)
+		 * futex_pivot_pending()	wake_up_var()
+		 *                                waitqueue_active()
+		 *
+		 * Notably, it must not be possible to see
+		 * !futex_pivot_pending() && !waitqueue_active().
+		 */
+		smp_mb();
+
+		while (!futex_pivot_pending(mm) &&
+		       wait_woken(&__wbq_entry.wq_entry, TASK_UNINTERRUPTIBLE,
+				  MAX_SCHEDULE_TIMEOUT))
+			/* empty */;
+
+		remove_wait_queue(__wq_head, &__wbq_entry.wq_entry);
 	}
 
 	scoped_guard(mutex, &mm->futex_hash_lock) {
kernel/sched/wait.c +15 -0
@@ -5,6 +5,7 @@
  * (C) 2004 Nadia Yvette Chambers, Oracle
  */
 #include "sched.h"
+#include <linux/wait_bit.h>
 
 void __init_waitqueue_head(struct wait_queue_head *wq_head, const char *name, struct lock_class_key *key)
 {
@@ -463,3 +464,17 @@ int woken_wake_function(struct wait_queu
 	return default_wake_function(wq_entry, mode, sync, key);
 }
 EXPORT_SYMBOL(woken_wake_function);
+
+int woken_wake_bit_function(struct wait_queue_entry *wq_entry, unsigned mode, int sync, void *arg)
+{
+	struct wait_bit_key *key = __var_wake_key(wq_entry, arg);
+	if (!key)
+		return 0;
+
+	/* Pairs with the smp_store_mb() in wait_woken(). */
+	smp_mb(); /* C */
+	wq_entry->flags |= WQ_FLAG_WOKEN;
+
+	return default_wake_function(wq_entry, mode, sync, key);
+}
+EXPORT_SYMBOL(woken_wake_bit_function);
kernel/sched/wait_bit.c +11 -3
@@ -167,9 +167,7 @@ wait_queue_head_t *__var_waitqueue(void
 }
 EXPORT_SYMBOL(__var_waitqueue);
 
-static int
-var_wake_function(struct wait_queue_entry *wq_entry, unsigned int mode,
-		  int sync, void *arg)
+struct wait_bit_key *__var_wake_key(struct wait_queue_entry *wq_entry, void *arg)
 {
 	struct wait_bit_key *key = arg;
 	struct wait_bit_queue_entry *wbq_entry =
@@ -177,6 +175,16 @@ var_wake_function(struct wait_queue_entr
 
 	if (wbq_entry->key.flags != key->flags ||
 	    wbq_entry->key.bit_nr != key->bit_nr)
+		return NULL;
+
+	return key;
+}
+
+static int var_wake_function(struct wait_queue_entry *wq_entry, unsigned int mode,
+			     int sync, void *arg)
+{
+	struct wait_bit_key *key = __var_wake_key(wq_entry, arg);
+	if (!key)
 		return 0;
 
 	return autoremove_wake_function(wq_entry, mode, sync, key);
author maintainer
#82 lore raw

PATCH 82/101 HID: asus: fix missing hid_is_usb() check

In reply to Greg Kroah-Hartman

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

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

From: Jann Horn <[email protected]>

commit 02bf61dfb44f17ec187d1da1a82495951bbd12df upstream.

to_usb_interface() can only be used on a hid_device whose parent is really USB; uhid can create devices that identify as being on BUS_USB, but don't actually have a USB parent.
Fix the use of to_usb_interface() without a hid_is_usb() check.

I have verified that it is currently possible to trigger a kernel splat due to this bug in an ASAN build, and that this commit fixes the issue.

Fixes 00e005c952f7 ("hid-asus: check ROG Ally MCU version and warn")
---
 drivers/hid/hid-asus.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/hid/hid-asus.c +1 -1
@@ -753,7 +753,7 @@ static int asus_kbd_register_leds(struct
 			return ret;
 	}
 
-	if (drvdata->quirks & QUIRK_ROG_ALLY_XPAD) {
+	if ((drvdata->quirks & QUIRK_ROG_ALLY_XPAD) && hid_is_usb(hdev)) {
 		intf = to_usb_interface(hdev->dev.parent);
 		udev = interface_to_usbdev(intf);
 		validate_mcu_fw_version(hdev,
author maintainer
#83 lore raw

PATCH 83/101 HID: huawei: fix missing hid_is_usb() check

In reply to Greg Kroah-Hartman

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

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

From: Jann Horn <[email protected]>

commit 4cdb6b4b34d7823254f6e1b22faf56c96ac57fb9 upstream.

to_usb_interface() can only be used on a hid_device whose parent is really USB; uhid can create devices that identify as being on BUS_USB, but don't actually have a USB parent.
Fix the use of to_usb_interface() without a hid_is_usb() check.

I have verified that it is currently possible to trigger a kernel splat due to this bug in an ASAN build, and that this commit fixes the issue.

Fixes e93faaca84b7 ("HID: huawei: fix CD30 keyboard report descriptor issue")
---
 drivers/hid/hid-huawei.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/hid/hid-huawei.c +3 -2
index 6a616bf21b38..ee3fc6f68475 100644
@@ -44,11 +44,12 @@ static const __u8 huawei_cd30_kbd_rdesc_fixed[] = {
 static const __u8 *huawei_report_fixup(struct hid_device *hdev, __u8 *rdesc,
 				  unsigned int *rsize)
 {
-	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+	struct usb_interface *intf = hid_is_usb(hdev) ?
+			to_usb_interface(hdev->dev.parent) : NULL;
 
 	switch (hdev->product) {
 	case USB_DEVICE_ID_HUAWEI_CD30KBD:
-		if (intf->cur_altsetting->desc.bInterfaceNumber == 1) {
+		if (!intf || intf->cur_altsetting->desc.bInterfaceNumber == 1) {
 			if (*rsize != sizeof(huawei_cd30_kbd_rdesc_fixed) ||
 				memcmp(huawei_cd30_kbd_rdesc_fixed, rdesc,
 					sizeof(huawei_cd30_kbd_rdesc_fixed)) != 0) {
Signature · 1 line
2.55.0
author maintainer
#84 lore raw

PATCH 84/101 HID: nintendo: fix out-of-bounds read in joycon_ctlr_read_handler()

In reply to Greg Kroah-Hartman

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

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

From: Ibrahim Hashimov <[email protected]>

commit 27b376b945c0aac46fcdfcc950b14a85b874b557 upstream.

joycon_ctlr_read_handler() casts an incoming HID input report to struct joycon_input_report and parses it, guarding the cast only with a 12-byte length check:

	if (size >= 12) /* make sure it contains the input report */
		joycon_parse_report(ctlr, (struct joycon_input_report *)data);

struct joycon_input_report is 49 bytes: a 13-byte header followed by a union whose IMU arm is 36 bytes. For an IMU report joycon_parse_report() -> joycon_parse_imu_report() walks that union (struct offsets 13..48), so a report of exactly 12 bytes with data[0] == JC_INPUT_IMU_DATA passes the guard yet is read up to 37 bytes past its declared length. The over-read bytes are decoded into accelerometer/gyroscope values and forwarded to userspace through the "(IMU)" input device, leaking driver-internal memory. data[0] and size are fully controlled by a malicious or spoofed Joy-Con/Pro Controller.

Receive buffers are sized to the maximum report length, so this is an over-read within the allocation rather than a slab OOB, but the decoded bytes still reach userspace.

The sibling subcmd path in joycon_ctlr_handle_event() already bounds the same cast correctly:

	if (size < sizeof(struct joycon_input_report) ||
	    data[0] != JC_INPUT_SUBCMD_REPLY)
		break;

Use the same sizeof(struct joycon_input_report) bound here.

Fixes 2af16c1f846b ("HID: nintendo: add nintendo switch controller driver")
Assisted-by AuditCode-AI:2026.07
---
 drivers/hid/hid-nintendo.c |    7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)
drivers/hid/hid-nintendo.c +6 -1
@@ -2559,7 +2559,12 @@ static int joycon_ctlr_read_handler(stru
 {
 	if (data[0] == JC_INPUT_SUBCMD_REPLY || data[0] == JC_INPUT_IMU_DATA ||
 	    data[0] == JC_INPUT_MCU_DATA) {
-		if (size >= 12) /* make sure it contains the input report */
+		/*
+		 * The whole struct is cast and parsed below, including the
+		 * IMU/subcmd union, not just the 12-byte partial header this
+		 * used to check for.
+		 */
+		if (size >= sizeof(struct joycon_input_report))
 			joycon_parse_report(ctlr,
 					    (struct joycon_input_report *)data);
 	}
author maintainer
#85 lore raw

PATCH 85/101 HID: nintendo: register input device after capabilities are set

In reply to Greg Kroah-Hartman

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

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

From: Jiangshan Yi <[email protected]>

commit d723bc1fe2e72b9252234e94c11af644ec477bf7 upstream.

input_register_device() exposes the device to userspace immediately. In joycon_input_create() it was called before joycon_config_rumble() configures the FF_RUMBLE capability and the memless force-feedback device, so a concurrent EVIOCSFF could dereference a NULL dev->ff.

Registering early also means the initial udev event lacks button and axis information, which can make input managers ignore the device.

Move input_register_device() to the end of joycon_input_create(), after all capabilities, the IMU input device and the force-feedback callbacks have been configured.

---
 drivers/hid/hid-nintendo.c |    8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/hid/hid-nintendo.c +4 -4
@@ -2138,10 +2138,6 @@ static int joycon_input_create(struct jo
 	ctlr->input->phys = hdev->phys;
 	input_set_drvdata(ctlr->input, ctlr);
 
-	ret = input_register_device(ctlr->input);
-	if (ret)
-		return ret;
-
 	if (joycon_type_is_right_joycon(ctlr)) {
 		joycon_config_right_stick(ctlr->input);
 		joycon_config_buttons(ctlr->input, right_joycon_button_mappings);
@@ -2181,6 +2177,10 @@ static int joycon_input_create(struct jo
 	if (joycon_has_rumble(ctlr))
 		joycon_config_rumble(ctlr);
 
+	ret = input_register_device(ctlr->input);
+	if (ret)
+		return ret;
+
 	return 0;
 }
 
author maintainer
#86 lore raw

PATCH 86/101 HID: nintendo: stop device IO before hid_hw_stop on probe failure

In reply to Greg Kroah-Hartman

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

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

From: Jiangshan Yi <[email protected]>

commit 1f74d3bff6fe04a64e02ab3661d2e0d554565aa6 upstream.

nintendo_hid_probe() calls hid_device_io_start() before joycon_init() and joycon_leds_create(). If either fails, the error path jumps to err_close which calls hid_hw_close()/hid_hw_stop() without first calling hid_device_io_stop().

hid_hw_stop() does not stop device IO, so hid_input_report() may still run and access driver data that is being torn down, resulting in a use-after-free.

Add an err_io_stop label that calls hid_device_io_stop() before hid_hw_close(), and point the two post-io_start error paths at it.

Fixes 2af16c1f846b ("HID: nintendo: add nintendo switch controller driver")
---
 drivers/hid/hid-nintendo.c |    7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
drivers/hid/hid-nintendo.c +4 -3
@@ -2693,14 +2693,14 @@ static int nintendo_hid_probe(struct hid
 	ret = joycon_init(hdev);
 	if (ret) {
 		hid_err(hdev, "Failed to initialize controller; ret=%d\n", ret);
-		goto err_close;
+		goto err_io_stop;
 	}
 
 	/* Initialize the leds */
 	ret = joycon_leds_create(ctlr);
 	if (ret) {
 		hid_err(hdev, "Failed to create leds; ret=%d\n", ret);
-		goto err_close;
+		goto err_io_stop;
 	}
 
 	/* Initialize the battery power supply */
@@ -2723,7 +2723,8 @@ static int nintendo_hid_probe(struct hid
 
 err_ida:
 	ida_free(&nintendo_player_id_allocator, ctlr->player_id);
-err_close:
+err_io_stop:
+	hid_device_io_stop(hdev);
 	hid_hw_close(hdev);
 err_stop:
 	hid_hw_stop(hdev);
author maintainer
#87 lore raw

PATCH 87/101 HID: rapoo: fix missing hid_is_usb() check

In reply to Greg Kroah-Hartman

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

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

From: Jann Horn <[email protected]>

commit b57af2448268c30c25509a832b6ff6dc27176b28 upstream.

to_usb_interface() can only be used on a hid_device whose parent is really USB; uhid can create devices that identify as being on BUS_USB, but don't actually have a USB parent.
Fix the use of to_usb_interface() without a hid_is_usb() check.

Add a dependency on USB_HID for hid_is_usb(), as other HID drivers do; the alternative would be to provide a simple stub implementation on !USB_HID builds.

I have verified that it is currently possible to trigger a kernel splat due to this bug in an ASAN build, and that this commit fixes the issue.

Fixes b3b1c68fb726 ("HID: rapoo: Add support for side buttons on RAPOO 0x2015 mouse")
---
 drivers/hid/Kconfig     |    1 +
 drivers/hid/hid-rapoo.c |    2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)
drivers/hid/Kconfig +1 -0
@@ -1026,6 +1026,7 @@ config HID_PXRC
 
 config HID_RAPOO
 	tristate "Rapoo non-fully HID-compliant devices"
+	depends on USB_HID
 	help
 	Support for Rapoo devices that are not fully compliant with the
 	HID standard.
drivers/hid/hid-rapoo.c +1 -1
@@ -36,7 +36,7 @@ static int rapoo_probe(struct hid_device
 		return ret;
 	}
 
-	if (hdev->bus == BUS_USB) {
+	if (hid_is_usb(hdev)) {
 		struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
 
 		if (intf->cur_altsetting->desc.bInterfaceNumber != 1)
author maintainer
#88 lore raw

PATCH 88/101 HID: core: fix number/pointer type confusion on long items

In reply to Greg Kroah-Hartman

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

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

From: Jann Horn <[email protected]>

commit 28abce951343fcec26e397610868efa4e1395c3f upstream.

When fetch_item() is called by hid_scan_report() on an item with HID_ITEM_TAG_LONG, it stores a pointer to the item data in item->data.longdata instead of storing a value directly in item->data.{u8/u16/u32}.

When item_udata() or item_sdata() encounters such an item, it incorrectly assumes that the item is in short format, and therefore returns the lower part of a kernel pointer reinterpreted as a number.

When a HID device is connected whose descriptor contains a HID_GLOBAL_ITEM_TAG_REPORT_SIZE encoded in long format with size=4, this causes the lower half of a kernel pointer to be printed into dmesg as a number, like this:

    hid (null): invalid report_size 107953555

To fix it, let item_udata() and item_sdata() verify that the item is in short format.

Note that this bug only affects hid_scan_report(), while the main parsing pass hid_parse_collections() will always bail out when encountering a long item.

Sidenote: There are currently no users of data.longdata; maybe we should just remove any parsing of long-format descriptors as a follow-up.

Fixes 3dc8fc083dbf ("HID: Use hid_parser for pre-scanning the report descriptors")
---
 drivers/hid/hid-core.c |    6 ++++++
 1 file changed, 6 insertions(+)
drivers/hid/hid-core.c +6 -0
@@ -379,6 +379,9 @@ static int hid_add_field(struct hid_pars
 
 static u32 item_udata(struct hid_item *item)
 {
+	if (item->format != HID_ITEM_FORMAT_SHORT)
+		return 0;
+
 	switch (item->size) {
 	case 1: return item->data.u8;
 	case 2: return item->data.u16;
@@ -389,6 +392,9 @@ static u32 item_udata(struct hid_item *i
 
 static s32 item_sdata(struct hid_item *item)
 {
+	if (item->format != HID_ITEM_FORMAT_SHORT)
+		return 0;
+
 	switch (item->size) {
 	case 1: return item->data.s8;
 	case 2: return item->data.s16;
author maintainer
#89 lore raw

PATCH 89/101 HID: ft260: fix stack-use-after-return write in I2C read race

In reply to Greg Kroah-Hartman

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

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

From: Raman Varabets <[email protected]>

commit bf3e39df3a397fd82967a31d17c4e02c7feab221 upstream.

ft260_i2c_read() points dev->read_buf at a caller-supplied buffer (often an on-stack variable), arms a completion and waits up to five seconds for the device to return the data. The HID input callback ft260_raw_event() runs in the input/IRQ path, independent of the dev->lock mutex held by the read path, and copies the device-supplied payload into dev->read_buf after a plain NULL check.

These two paths share read_buf, read_idx and read_len with no serialization. If the device delays its response until the read times out, ft260_i2c_read() resets the controller, clears read_buf and returns, unwinding the stack frame the buffer lived in. A response that arrives at that moment lets ft260_raw_event() pass the NULL check and then memcpy() the device-controlled payload into the now-freed stack location, a bounded but attacker-influenced stack-use-after-return write triggerable by malicious or malfunctioning hardware.

Add a dedicated spinlock that serializes every access to read_buf, read_idx and read_len. ft260_raw_event() now holds it across the NULL check, the memcpy and the index update, while the read path takes it when arming and when clearing the buffer, so the teardown can no longer slip between the check and the copy.

Fixes 6a82582d9fa4 ("HID: ft260: add usb hid to i2c host bridge driver")
---
 drivers/hid/hid-ft260.c |   25 ++++++++++++++++++++++++-
 1 file changed, 24 insertions(+), 1 deletion(-)
drivers/hid/hid-ft260.c +24 -1
@@ -240,6 +240,8 @@ struct ft260_device {
 	struct mutex lock;
 	u8 write_buf[FT260_REPORT_MAX_LENGTH];
 	unsigned long need_wakeup_at;
+	/* Protects read_buf, read_idx and read_len against ft260_raw_event() */
+	spinlock_t read_lock;
 	u8 *read_buf;
 	u16 read_idx;
 	u16 read_len;
@@ -501,6 +503,7 @@ static int ft260_i2c_read(struct ft260_d
 	int timeout, ret = 0;
 	struct ft260_i2c_read_request_report rep;
 	struct hid_device *hdev = dev->hdev;
+	unsigned long irqflags;
 	u8 bus_busy = 0;
 
 	if ((flag & FT260_FLAG_START_REPEATED) == FT260_FLAG_START_REPEATED)
@@ -526,9 +529,11 @@ static int ft260_i2c_read(struct ft260_d
 
 		reinit_completion(&dev->wait);
 
+		spin_lock_irqsave(&dev->read_lock, irqflags);
 		dev->read_idx = 0;
 		dev->read_buf = data;
 		dev->read_len = rd_len;
+		spin_unlock_irqrestore(&dev->read_lock, irqflags);
 
 		ret = ft260_hid_output_report(hdev, (u8 *)&rep, sizeof(rep));
 		if (ret < 0) {
@@ -543,7 +548,9 @@ static int ft260_i2c_read(struct ft260_d
 			goto ft260_i2c_read_exit;
 		}
 
+		spin_lock_irqsave(&dev->read_lock, irqflags);
 		dev->read_buf = NULL;
+		spin_unlock_irqrestore(&dev->read_lock, irqflags);
 
 		if (flag & FT260_FLAG_STOP)
 			bus_busy = FT260_I2C_STATUS_BUS_BUSY;
@@ -562,7 +569,9 @@ static int ft260_i2c_read(struct ft260_d
 	} while (len > 0);
 
 ft260_i2c_read_exit:
+	spin_lock_irqsave(&dev->read_lock, irqflags);
 	dev->read_buf = NULL;
+	spin_unlock_irqrestore(&dev->read_lock, irqflags);
 	return ret;
 }
 
@@ -1018,6 +1027,7 @@ static int ft260_probe(struct hid_device
 		 "FT260 usb-i2c bridge");
 
 	mutex_init(&dev->lock);
+	spin_lock_init(&dev->read_lock);
 	init_completion(&dev->wait);
 
 	ret = ft260_xfer_status(dev, FT260_I2C_STATUS_BUS_BUSY);
@@ -1067,6 +1077,7 @@ static int ft260_raw_event(struct hid_de
 {
 	struct ft260_device *dev = hid_get_drvdata(hdev);
 	struct ft260_i2c_input_report *xfer = (void *)data;
+	unsigned long irqflags;
 
 	if (size < offsetof(struct ft260_i2c_input_report, data)) {
 		hid_err(hdev, "short report %d\n", size);
@@ -1075,6 +1086,8 @@ static int ft260_raw_event(struct hid_de
 
 	if (xfer->report >= FT260_I2C_REPORT_MIN &&
 	    xfer->report <= FT260_I2C_REPORT_MAX) {
+		bool complete_read;
+
 		ft260_dbg("i2c resp: rep %#02x len %d size %d\n",
 			  xfer->report, xfer->length, size);
 
@@ -1085,8 +1098,15 @@ static int ft260_raw_event(struct hid_de
 			return -1;
 		}
 
+		/*
+		 * Hold read_lock so a timed-out ft260_i2c_read() cannot
+		 * clear read_buf between the NULL check and the memcpy.
+		 */
+		spin_lock_irqsave(&dev->read_lock, irqflags);
+
 		if ((dev->read_buf == NULL) ||
 		    (xfer->length > dev->read_len - dev->read_idx)) {
+			spin_unlock_irqrestore(&dev->read_lock, irqflags);
 			hid_err(hdev, "unexpected report %#02x, length %d\n",
 				xfer->report, xfer->length);
 			return -1;
@@ -1095,8 +1115,11 @@ static int ft260_raw_event(struct hid_de
 		memcpy(&dev->read_buf[dev->read_idx], &xfer->data,
 		       xfer->length);
 		dev->read_idx += xfer->length;
+		complete_read = dev->read_idx == dev->read_len;
+
+		spin_unlock_irqrestore(&dev->read_lock, irqflags);
 
-		if (dev->read_idx == dev->read_len)
+		if (complete_read)
 			complete(&dev->wait);
 
 	} else {
author maintainer
#90 lore raw

PATCH 90/101 HID: sensor: custom: Fix use-after-free in enable_sensor

In reply to Greg Kroah-Hartman

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

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

From: Haoxiang Li <[email protected]>

commit ad8fb82b04422f49530d2aa2753cc81d1c60102c upstream.

enable_sensor_store() can call set_power_report_state(), which dereferences sensor_inst->power_state and sensor_inst->report_state. These pointers refer to entries in sensor_inst->fields.

Create the field attributes before exposing the enable_sensor sysfs attribute, so enable_sensor cannot be accessed before the state it depends on has been initialized.

On remove, delete enable_sensor before freeing the field attributes, so a concurrent sysfs write cannot dereference freed memory through power_state or report_state.

---
 drivers/hid/hid-sensor-custom.c |   17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)
drivers/hid/hid-sensor-custom.c +9 -8
@@ -1005,26 +1005,26 @@ static int hid_sensor_custom_probe(struc
 		return ret;
 	}
 
-	ret = sysfs_create_group(&sensor_inst->pdev->dev.kobj,
-				 &enable_sensor_attr_group);
+	ret = hid_sensor_custom_add_attributes(sensor_inst);
 	if (ret)
 		goto err_remove_callback;
 
-	ret = hid_sensor_custom_add_attributes(sensor_inst);
+	ret = sysfs_create_group(&sensor_inst->pdev->dev.kobj,
+				 &enable_sensor_attr_group);
 	if (ret)
-		goto err_remove_group;
+		goto err_remove_attributes;
 
 	ret = hid_sensor_custom_dev_if_add(sensor_inst);
 	if (ret)
-		goto err_remove_attributes;
+		goto err_remove_group;
 
 	return 0;
 
-err_remove_attributes:
-	hid_sensor_custom_remove_attributes(sensor_inst);
 err_remove_group:
 	sysfs_remove_group(&sensor_inst->pdev->dev.kobj,
 			   &enable_sensor_attr_group);
+err_remove_attributes:
+	hid_sensor_custom_remove_attributes(sensor_inst);
 err_remove_callback:
 	sensor_hub_remove_callback(hsdev, hsdev->usage);
 
@@ -1042,9 +1042,10 @@ static void hid_sensor_custom_remove(str
 	}
 
 	hid_sensor_custom_dev_if_remove(sensor_inst);
-	hid_sensor_custom_remove_attributes(sensor_inst);
+	/* Remove enable_sensor first as it uses fields via power_state/report_state. */
 	sysfs_remove_group(&sensor_inst->pdev->dev.kobj,
 			   &enable_sensor_attr_group);
+	hid_sensor_custom_remove_attributes(sensor_inst);
 	sensor_hub_remove_callback(hsdev, hsdev->usage);
 }
 
author maintainer
#91 lore raw

PATCH 91/101 HID: uclogic: fix use-after-free of inrange_timer on remove

In reply to Greg Kroah-Hartman

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

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

From: Ibrahim Hashimov <[email protected]>

commit 506fd50a9027340f0e9dcc587d10ccb03312dba6 upstream.

uclogic_remove() cancels the pen in-range timer and then stops the device:

	timer_delete_sync(&drvdata->inrange_timer);
	hid_hw_stop(hdev);

timer_delete_sync() only guarantees the timer is idle at that instant. uclogic_raw_event_pen() keeps delivering pen reports until hid_hw_stop() stops the transport several lines later, and every report with pen->inrange == UCLOGIC_PARAMS_PEN_INRANGE_NONE re-arms the timer:

	mod_timer(&drvdata->inrange_timer, jiffies + msecs_to_jiffies(100));

A report landing between the timer_delete_sync() call and the transport teardown in hid_hw_stop() re-arms inrange_timer after it was cancelled. uclogic_remove() then returns and the devm drvdata is freed, while hid_hw_stop() has already freed the input device drvdata->pen_input points at, so when the timer fires ~100 ms later uclogic_inrange_timeout() dereferences freed memory -- a use-after-free in timer-softirq context.

Swapping the two calls is not a fix: stopping the device first frees drvdata->pen_input via hidinput_disconnect() while the timer may still be pending, so a timer already armed before removal fires on the freed input device in the window before timer_delete_sync() runs.

Use timer_shutdown_sync() before hid_hw_stop() instead. It cancels the timer, waits for a running callback while pen_input is still valid, and prevents any further re-arming -- a later mod_timer() from an in-flight report is silently ignored -- so the timer is provably dead before hid_hw_stop() frees the inputs. This is the ordering the timer core documents for this "timer re-armed from another path" teardown case.

Fixes 01309e29eb95 ("HID: uclogic: Support in-range reporting emulation")
Assisted-by AuditCode-AI:2026.07
---
 drivers/hid/hid-uclogic-core.c |   12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)
drivers/hid/hid-uclogic-core.c +11 -1
@@ -548,7 +548,17 @@ static void uclogic_remove(struct hid_de
 {
 	struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
 
-	timer_delete_sync(&drvdata->inrange_timer);
+	/*
+	 * Shut the in-range timer down before stopping the device.
+	 * uclogic_raw_event_pen() re-arms inrange_timer on every pen report
+	 * and keeps running until hid_hw_stop() stops the transport, so a
+	 * plain timer_delete_sync() here can be undone by a report landing in
+	 * the window before hid_hw_stop().  timer_shutdown_sync() cancels the
+	 * timer and makes any later re-arm a no-op, so it is provably dead
+	 * before hid_hw_stop() frees the input device drvdata->pen_input
+	 * points at.
+	 */
+	timer_shutdown_sync(&drvdata->inrange_timer);
 	hid_hw_stop(hdev);
 	kfree(drvdata->desc_ptr);
 	uclogic_params_cleanup(&drvdata->params);
author maintainer
#92 lore raw

PATCH 92/101 HID: hyperv: validate initial device info bounds

In reply to Greg Kroah-Hartman

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

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

From: Michael Bommarito <[email protected]>

commit 934b7778aa7b7c8f6bb073d2a73ba3674885bae0 upstream.

The Hyper-V synthetic HID host supplies SYNTH_HID_INITIAL_DEVICE_INFO messages that contain a HID descriptor followed by the report descriptor bytes. mousevsc_on_receive_device_info() trusts bLength and wDescriptorLength without checking that the received packet contains both byte ranges.

A malformed host or backend message can therefore make the guest read past the received VMBus packet while copying the report descriptor. Pass the received initial-device-info size into the parser and reject descriptor lengths that exceed the packet.

Impact: A malicious Hyper-V host or backend can crash a guest by sending a short initial device-info message with an oversized HID report descriptor length.

Fixes b95f5bcb811e ("HID: Move the hid-hyperv driver out of staging")
Assisted-by Codex:gpt-5-5-xhigh
---
 drivers/hid/hid-hyperv.c |   27 ++++++++++++++++++++++++---
 1 file changed, 24 insertions(+), 3 deletions(-)
drivers/hid/hid-hyperv.c +24 -3
@@ -171,18 +171,32 @@ static void mousevsc_free_device(struct
 }
 
 static void mousevsc_on_receive_device_info(struct mousevsc_dev *input_device,
-				struct synthhid_device_info *device_info)
+					    struct synthhid_device_info *device_info,
+					    u32 device_info_size)
 {
 	int ret = 0;
 	struct hid_descriptor *desc;
 	struct mousevsc_prt_msg ack;
+	size_t desc_offset;
+	size_t desc_size;
 
 	input_device->dev_info_status = -ENOMEM;
 
+	if (device_info_size < sizeof(*device_info)) {
+		input_device->dev_info_status = -EINVAL;
+		goto cleanup;
+	}
+
 	input_device->hid_dev_info = device_info->hid_dev_info;
 	desc = &device_info->hid_descriptor;
+	desc_offset = offsetof(struct synthhid_device_info, hid_descriptor);
+	desc_size = device_info_size - desc_offset;
 	if (desc->bLength == 0)
 		goto cleanup;
+	if (desc->bLength < sizeof(*desc) || desc->bLength > desc_size) {
+		input_device->dev_info_status = -EINVAL;
+		goto cleanup;
+	}
 
 	/* The pointer is not NULL when we resume from hibernation */
 	kfree(input_device->hid_desc);
@@ -197,6 +211,10 @@ static void mousevsc_on_receive_device_i
 		input_device->dev_info_status = -EINVAL;
 		goto cleanup;
 	}
+	if (input_device->report_desc_size > desc_size - desc->bLength) {
+		input_device->dev_info_status = -EINVAL;
+		goto cleanup;
+	}
 
 	/* The pointer is not NULL when we resume from hibernation */
 	kfree(input_device->report_desc);
@@ -273,14 +291,17 @@ static void mousevsc_on_receive(struct h
 		break;
 
 	case SYNTH_HID_INITIAL_DEVICE_INFO:
-		WARN_ON(pipe_msg->size < sizeof(struct hv_input_dev_info));
+		if (WARN_ON_ONCE(pipe_msg->size <
+				 sizeof(struct synthhid_device_info)))
+			break;
 
 		/*
 		 * Parse out the device info into device attr,
 		 * hid desc and report desc
 		 */
 		mousevsc_on_receive_device_info(input_dev,
-			(struct synthhid_device_info *)pipe_msg->data);
+						(struct synthhid_device_info *)pipe_msg->data,
+						pipe_msg->size);
 		break;
 	case SYNTH_HID_INPUT_REPORT:
 		input_report =
author maintainer
#93 lore raw

PATCH 93/101 HID: input: read battery capacity from its actual report offset

In reply to Greg Kroah-Hartman

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

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

From: Jose Villaseñor Montfort <[email protected]>

commit d07644524b6511b622ee7b0e2e68c9ee43d522a4 upstream.

hidinput_query_battery_capacity() assumes the state-of-charge value is the first byte following the report ID (buf[1]) and ignores where the battery field actually sits within the report.

An Apple Magic Trackpad 2 precedes the AbsoluteStateOfCharge byte with a byte of status flags in its battery reports, so this query returns the flags byte instead of the charge level.

The device happens to make that easy to observe, because it exposes the same cell twice: its report descriptor declares AbsoluteStateOfCharge in two reports (0x90 and 0x9b), so hidinput_setup_battery() registers two power supplies. Only the first one is refreshed by hid-magicmouse -- it uses hid_get_battery(), which returns the first battery of the list -- and that refresh goes through the report event path, which parses the field correctly. Nothing ever reports the second one, so every read of its capacity takes the query path above. On a USB-C Magic Trackpad over USB, on an unpatched 7.1.5:

  hid-<serial>-battery-144 = 100%  (Charging)      <- report event path
  hid-<serial>-battery-155 =   3%  (Discharging)   <- query path

Both are the same physical battery. A raw HIDIOCGINPUT of the two reports at that same moment:

report 0x90 -> [90 03 64]
report 0x9b -> [9b 03 64 64 00 00 10 00 00 00 00 00 00 00]

                     ^flags ^SoC = 0x64 = 100%

The device answers correctly in both cases; only the offset the kernel reads the capacity from is wrong. 0x03 is the flags byte (present, charging), reported as "3%".

Bluetooth takes the same query path for its capacity, where the trackpad reported a bogus near-constant ~4% -- 0b100, the FullyCharged flag -- regardless of the real charge.

Store the battery field's offset within the report at setup time and use it when querying, so the capacity is read from its real position. The report event path already parses the field correctly through the HID core; only the explicit GET_REPORT query was wrong.

Devices whose capacity field is the first field in the report have a report_offset of 0 and are unaffected (buf[1 + 0] == buf[1]).

Fixes 581c4484769e ("HID: input: map digitizer battery usage")
---
 drivers/hid/hid-input.c |   17 +++++++++++++----
 include/linux/hid.h     |    2 ++
 2 files changed, 15 insertions(+), 4 deletions(-)
drivers/hid/hid-input.c +13 -4
@@ -433,17 +433,25 @@ static int hidinput_scale_battery_capaci
 static int hidinput_query_battery_capacity(struct hid_battery *bat)
 {
 	int ret;
+	/*
+	 * The capacity field may not be the first field in the report: some
+	 * devices (e.g. the Apple Magic Trackpad 2 over Bluetooth) precede it
+	 * with status flags. Read it from its actual byte offset in the report
+	 * (report_offset is in bits; the leading byte is the report id).
+	 */
+	int offset = 1 + bat->report_offset / 8;
+	int len = offset + 1;
 
-	u8 *buf __free(kfree) = kmalloc(4, GFP_KERNEL);
+	u8 *buf __free(kfree) = kmalloc(max(len, 4), GFP_KERNEL);
 	if (!buf)
 		return -ENOMEM;
 
-	ret = hid_hw_raw_request(bat->dev, bat->report_id, buf, 4,
+	ret = hid_hw_raw_request(bat->dev, bat->report_id, buf, max(len, 4),
 				 bat->report_type, HID_REQ_GET_REPORT);
-	if (ret < 2)
+	if (ret < len)
 		return -ENODATA;
 
-	return hidinput_scale_battery_capacity(bat, buf[1]);
+	return hidinput_scale_battery_capacity(bat, buf[offset]);
 }
 
 static int hidinput_get_battery_property(struct power_supply *psy,
@@ -594,6 +602,7 @@ static int hidinput_setup_battery(struct
 	bat->max = max;
 	bat->report_type = report_type;
 	bat->report_id = field->report->id;
+	bat->report_offset = field->report_offset;
 	bat->charge_status = POWER_SUPPLY_STATUS_DISCHARGING;
 	bat->status = HID_BATTERY_UNKNOWN;
 
include/linux/hid.h +2 -0
@@ -642,6 +642,7 @@ enum hid_battery_status {
  * @max: maximum battery value from HID descriptor
  * @report_type: HID report type (input/feature)
  * @report_id: HID report ID for this battery
+ * @report_offset: bit offset of the capacity field within its report
  * @charge_status: current charging status
  * @status: battery reporting status
  * @capacity: current battery capacity (0-100)
@@ -657,6 +658,7 @@ struct hid_battery {
 	__s32 max;
 	__s32 report_type;
 	__s32 report_id;
+	__s32 report_offset;
 	__s32 charge_status;
 	enum hid_battery_status status;
 	__s32 capacity;
author maintainer
#94 lore raw

PATCH 94/101 drm/xe: Dont hand out the flat CCS storage as usable VRAM

In reply to Greg Kroah-Hartman

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

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

From: Linus Torvalds <[email protected]>

commit 818bebeb63dd6bf5f4e07e145f6cdbace520a34c upstream.

get_flat_ccs_offset() reads the base of the flat CCS storage from the hardware, scales it by the number of enabled L3 nodes, and rounds the result up to 128K. Everything below that offset is then handed to the VRAM allocator as usable memory.

Rounding a limit that means "usable memory ends here" upwards publishes whatever lies between the real base and the rounded one as free memory, and that memory belongs to the compression hardware. The scaled value has no reason to be 128K aligned, and on a Battlemage G21 with 16 GiB it is not:

	flat CCS base: raw 0x3fafff800, rounded 0x3fb000000

so the last 2 KiB of page 0x3fafff000 is CCS storage, in the allocator's pool. Whatever is allocated there gets that tail overwritten by the compression hardware, which needs no page-table entry, no buffer object and no GPU submission to do it, and does it before userspace exists.

On this machine a Mesa VM's level-3 page table landed on that page on every cold boot. It lost the entry covering the compositor's batch-buffer heap, so the compositor's first submission faulted fetching its batch and gdm restarted it forever: a black screen on an otherwise working machine. Restarting gdm cleared it because the next VM's page tables were allocated somewhere else.

Round down instead, to the page size the allocator works in. On this machine that excludes exactly one page.

Reading the reserved page afterwards shows what had been writing it:

	[369] 0xcccc000000000000
	[371] 0xcc77000000000000
	[373] 0xcccc000000000000
	[375] 0xcc77000000000000

compression metadata, two bytes per sixteen, sitting where the driver used to hand out memory.

The assertion that should have caught this compares the offset against GSMBASE - ccs_size for equality. That value is 128K aligned, so it agrees with the rounded-up offset precisely when the base is not aligned - the check cannot fail in the case it exists to catch, and is compiled out unless CONFIG_DRM_XE_DEBUG is set. Replace it with one that can fail: CCS storage must not run into GSM.

[ And this was a debug session from hell, enormously helped by an AI
doing much of the grunt-work.

I'd like to call it my tireless helper, but the AI several times stated flat out that this was impossible and unsolvable and that we should just write a report about it.

I suspect those things have been trained by people who may not be quite as stubborn as I am.

But while the AI was ready to give up several times, it did keep adding debug code and analyzing it faithfully when I pushed. So credit where credit is due and I let the AI write the commit message above.

This is basically a one-liner fixing a bogus "round_up()" to a "round_down()", but there were 24 patches adding more and more debug information to this, and 18 kernel boot to finally narrow it down to
this. - Linus ]

Fixes 37173392741c ("drm/xe/vram: fix ccs offset calculation")
---
 drivers/gpu/drm/xe/xe_vram.c |   23 ++++++++++++++++++-----
 1 file changed, 18 insertions(+), 5 deletions(-)
drivers/gpu/drm/xe/xe_vram.c +18 -5
@@ -88,12 +88,25 @@ static int get_flat_ccs_offset(struct xe
 		offset = offset_hi << 32; /* HW view bits 39:32 */
 		offset |= offset_lo << 6; /* HW view bits 31:6 */
 		offset *= num_enabled; /* convert to SW view */
-		offset = round_up(offset, SZ_128K); /* SW must round up to nearest 128K */
 
-		/* We don't expect any holes */
-		xe_assert_msg(xe, offset == (xe_mmio_read64_2x32(&gt_to_tile(gt)->mmio, GSMBASE) -
-					     ccs_size),
-			      "Hole between CCS and GSM.\n");
+		/*
+		 * Everything below this offset is handed to the VRAM
+		 * allocator, so it has to be the *first* address the
+		 * compression hardware owns, rounded down.  Rounding it up
+		 * publishes CCS storage as free memory.
+		 */
+		offset = round_down(offset, SZ_4K);
+
+		/*
+		 * CCS storage must not run into GSM.  The old check compared
+		 * the offset against GSMBASE - ccs_size for equality, which
+		 * could not fail: that value is 128K aligned, so it agreed
+		 * with the rounded-up offset even when the base was not 128K
+		 * aligned - exactly the case this fixes.
+		 */
+		xe_assert_msg(xe, offset + ccs_size <=
+			      xe_mmio_read64_2x32(&gt_to_tile(gt)->mmio, GSMBASE),
+			      "CCS overlaps GSM.\n");
 	} else {
 		reg = xe_gt_mcr_unicast_read_any(gt, XEHP_FLAT_CCS_BASE_ADDR);
 		offset = (u64)REG_FIELD_GET(XEHP_FLAT_CCS_PTR, reg) * SZ_64K;
author maintainer
#95 lore raw

PATCH 95/101 Bluetooth: hci_event: fix LE list UAF on reset

In reply to Greg Kroah-Hartman

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

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

From: Chengfeng Ye <[email protected]>

commit 33af47e847fe4a28b109673affb5874015d54f5a upstream.

hci_cc_reset() clears the LE accept and resolving lists without taking hdev->lock. Other command-complete handlers serialize updates to these lists with that lock, and the debugfs readers hold it while walking them.

This permits the reset completion and a debugfs read to interleave as follows:

hci_rx_work debugfs reader
----------- --------------
lock hdev->lock
fetch current entry
list_del(entry)
kfree(entry)
read entry fields

The reader then dereferences a freed list entry and may follow its stale next pointer.

KASAN reported:

  BUG: KASAN: slab-use-after-free in white_list_show+0x15f/0x180
  Read of size 1 at addr ffff8881015dab16 by task poc/95

  Call Trace:
   white_list_show+0x15f/0x180
   seq_read_iter+0x3ff/0x1190
   seq_read+0x267/0x3d0
   vfs_read+0x177/0xa20
   ksys_read+0xf7/0x1c0

  Allocated by task 91:
   hci_bdaddr_list_add+0x1a6/0x3a0
   hci_cc_le_add_to_accept_list+0xab/0x140
   hci_cmd_complete_evt+0x26c/0x9a0
   hci_event_packet+0x454/0xb20
   hci_rx_work+0x293/0x730

  Freed by task 90:
   kfree+0x131/0x3c0
   hci_bdaddr_list_clear+0xd8/0x160
   hci_cc_reset+0x28a/0x370
   hci_cmd_complete_evt+0x26c/0x9a0
   hci_event_packet+0x454/0xb20
   hci_rx_work+0x293/0x730

Take hdev->lock around both list clears. This matches the existing mutation and traversal locking convention.

Fixes a4d5504d5c39 ("Bluetooth: Clear LE white list when resetting controller")
Fixes cfdb0c2d095a ("Bluetooth: Store Resolv list size")
---
 net/bluetooth/hci_event.c |    2 ++
 1 file changed, 2 insertions(+)
net/bluetooth/hci_event.c +2 -0
@@ -297,8 +297,10 @@ static u8 hci_cc_reset(struct hci_dev *h
 
 	hdev->ssp_debug_mode = 0;
 
+	hci_dev_lock(hdev);
 	hci_bdaddr_list_clear(&hdev->le_accept_list);
 	hci_bdaddr_list_clear(&hdev->le_resolv_list);
+	hci_dev_unlock(hdev);
 
 	return rp->status;
 }
author maintainer
#96 lore raw

PATCH 96/101 Bluetooth: hci_event: validate LE Set CIG Parameters response

In reply to Greg Kroah-Hartman

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

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

From: Laxman Acharya Padhya <[email protected]>

commit 0acd4eeb4b225b9bebbf9ef96cc10cdd79b94899 upstream.

The Command Complete dispatch validates only the fixed part of the LE Set CIG Parameters response. After that part is pulled from the skb, hci_cc_le_set_cig_params() trusts num_handles and reads each entry in the trailing handle array.

Matching num_handles against the command's num_cis does not guarantee that the response contains the advertised handles. A truncated response from a malfunctioning controller can therefore make the handler read beyond the skb data.

Validate that the remaining skb data contains all advertised handles. Include this in the existing response validation so malformed responses also follow the established CIG failure handling.

Fixes 26afbd826ee3 ("Bluetooth: Add initial implementation of CIS connections")
---
 net/bluetooth/hci_event.c |    6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
net/bluetooth/hci_event.c +4 -2
@@ -3832,8 +3832,10 @@ static u8 hci_cc_le_set_cig_params(struc
 	bt_dev_dbg(hdev, "status 0x%2.2x", rp->status);
 
 	cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_CIG_PARAMS);
-	if (!rp->status && (!cp || rp->num_handles != cp->num_cis ||
-			    rp->cig_id != cp->cig_id)) {
+	if (!rp->status &&
+	    (!cp || rp->num_handles != cp->num_cis ||
+	     rp->cig_id != cp->cig_id ||
+	     skb->len < array_size(rp->num_handles, sizeof(*rp->handle)))) {
 		bt_dev_err(hdev, "unexpected Set CIG Parameters response data");
 		status = HCI_ERROR_UNSPECIFIED;
 	}
author maintainer
#97 lore raw

PATCH 97/101 Bluetooth: hci_sync: Fix accept list UAF during suspend

In reply to Greg Kroah-Hartman

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

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

From: Chengfeng Ye <[email protected]>

commit f57b399c4fa1501b2d5451f52d861ece86bcf3db upstream.

hci_update_event_filter_sync() walks hdev->accept_list while sending a synchronous HCI command for each remote-wakeup device. The suspend path holds hdev->req_lock, but accept-list updates are serialized by hdev->lock. Consequently, remove_device() can free the current list entry during the controller wait.

The following interleaving causes the use-after-free:

hci_update_event_filter_sync() remove_device()
fetch accept-list entry
hci_set_event_filter_sync()

    wait for controller response    hci_dev_lock()
                                    list_del()
                                    kfree()
                                    hci_dev_unlock()

read the freed list.next

KASAN reported:

  BUG: KASAN: slab-use-after-free in hci_suspend_sync+0x835/0x910
  Read of size 8 at addr ffff88810bec8440 by task kworker/0:1/10
  Workqueue: events vhci_suspend_work
  Call Trace:
   hci_suspend_sync+0x835/0x910
   hci_suspend_dev+0x182/0x450
   process_one_work+0x661/0x1090
   worker_thread+0x45b/0xd10

  Allocated by task 86:
   hci_bdaddr_list_add_with_flags+0x1a8/0x400
   add_device+0x381/0x820
   hci_sock_sendmsg+0x1033/0x1ea0

  Freed by task 91:
   kfree+0x131/0x3c0
   remove_device+0x429/0xb70
   hci_sock_sendmsg+0x1033/0x1ea0

Snapshot the remote-wakeup addresses under hdev->lock. Release the lock before sending HCI commands. Clear the controller event filter before building the snapshot, and skip allocation and the second list traversal when there are no matching entries. This preserves the original filter and scan-state updates without retaining an accept-list node across a controller wait.

---
 net/bluetooth/hci_sync.c |   50 ++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 39 insertions(+), 11 deletions(-)
net/bluetooth/hci_sync.c +39 -11
@@ -6291,6 +6291,8 @@ static int hci_pause_discovery_sync(stru
 static int hci_update_event_filter_sync(struct hci_dev *hdev)
 {
 	struct bdaddr_list_with_flags *b;
+	bdaddr_t *accept_list;
+	size_t i, num_entries = 0;
 	u8 scan = SCAN_DISABLED;
 	bool scanning = test_bit(HCI_PSCAN, &hdev->flags);
 	int err;
@@ -6307,23 +6309,49 @@ static int hci_update_event_filter_sync(
 	/* Always clear event filter when starting */
 	hci_clear_event_filter_sync(hdev);
 
-	list_for_each_entry(b, &hdev->accept_list, list) {
-		if (!(b->flags & HCI_CONN_FLAG_REMOTE_WAKEUP))
-			continue;
-
-		bt_dev_dbg(hdev, "Adding event filters for %pMR", &b->bdaddr);
-
-		err =  hci_set_event_filter_sync(hdev, HCI_FLT_CONN_SETUP,
-						 HCI_CONN_SETUP_ALLOW_BDADDR,
-						 &b->bdaddr,
-						 HCI_CONN_SETUP_AUTO_ON);
+	hci_dev_lock(hdev);
+
+	list_for_each_entry(b, &hdev->accept_list, list)
+		if (b->flags & HCI_CONN_FLAG_REMOTE_WAKEUP)
+			num_entries++;
+
+	if (!num_entries) {
+		hci_dev_unlock(hdev);
+		goto update_scan;
+	}
+
+	accept_list = kmalloc_array(num_entries, sizeof(*accept_list),
+				    GFP_KERNEL);
+	if (!accept_list) {
+		hci_dev_unlock(hdev);
+		return -ENOMEM;
+	}
+
+	i = 0;
+	list_for_each_entry(b, &hdev->accept_list, list)
+		if (b->flags & HCI_CONN_FLAG_REMOTE_WAKEUP)
+			bacpy(&accept_list[i++], &b->bdaddr);
+
+	hci_dev_unlock(hdev);
+
+	for (i = 0; i < num_entries; i++) {
+		bt_dev_dbg(hdev, "Adding event filters for %pMR",
+			   &accept_list[i]);
+
+		err = hci_set_event_filter_sync(hdev, HCI_FLT_CONN_SETUP,
+						HCI_CONN_SETUP_ALLOW_BDADDR,
+					 &accept_list[i],
+					 HCI_CONN_SETUP_AUTO_ON);
 		if (err)
 			bt_dev_err(hdev, "Failed to set event filter for %pMR",
-				   &b->bdaddr);
+				   &accept_list[i]);
 		else
 			scan = SCAN_PAGE;
 	}
 
+	kfree(accept_list);
+
+update_scan:
 	if (scan && !scanning)
 		hci_write_scan_enable_sync(hdev, scan);
 	else if (!scan && scanning)
author maintainer
#98 lore raw

PATCH 98/101 Bluetooth: ISO: do not force BT_LISTEN after a failed BIG sync

In reply to Greg Kroah-Hartman

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

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

From: Ali Ahmet Memis <[email protected]>

commit 9838a80096ba472d5e03057136a112631aabae6e upstream.

iso_sock_recvmsg() handles the deferred setup of a broadcast sink by dropping the socket lock, calling iso_conn_big_sync() and taking the lock again:

	release_sock(sk);
	iso_conn_big_sync(sk);
	lock_sock(sk);

	sk->sk_state = BT_LISTEN;

The state is written unconditionally, but iso_conn_big_sync() returns void and has paths that do nothing at all: hci_get_route() may fail, and after re-acquiring the socket lock the connection may already be gone, in which case it bails out without ever issuing an LE BIG Create Sync.

While the lock is dropped the connection can be torn down, for example when the controller reports HCI_EV_LE_PA_SYNC_LOST:

	hci_le_pa_sync_lost_evt()
	  hci_disconn_cfm() -> iso_disconn_cfm() -> iso_conn_del()
	    iso_chan_del()
	      iso_pi(sk)->conn = NULL
	      sk->sk_state = BT_CLOSED
	      sock_set_flag(sk, SOCK_ZAPPED)

iso_conn_big_sync() then finds conn == NULL and returns, but the caller still overwrites the BT_CLOSED that iso_chan_del() has just set. The socket ends up marked BT_LISTEN with no connection, so recvmsg() reports success for a setup that never happened and a later accept() waits for BIS connections that can never arrive instead of failing.

A concurrent shutdown() reaches the same write by another route:
__iso_sock_close() takes the BT_CONNECT2 PA sync path to iso_sock_disconn(), which sets BT_DISCONN but leaves conn and conn->hcon in place, so iso_conn_big_sync() succeeds and BT_LISTEN is written over BT_DISCONN. Both the BT_CONNECT2 and the BT_CONNECTED case write the state the same way.

Let iso_conn_big_sync() report whether the BIG sync was started, and only move the socket to BT_LISTEN when it was and when the state has not changed while the lock was dropped, mirroring what the BT_CONNECT case of the same switch already does with iso_connect_cis(). Both conditions are needed, the error alone does not cover the shutdown() race.

This corrupts the socket state machine only, it is not a memory safety issue. KASAN and lockdep stayed quiet in all of the runs below.

Reproduced with an emulated controller over /dev/vhci on a KASAN + PROVE_LOCKING kernel. A PA sync broadcast sink socket is driven to BT_CONNECT2 and recvmsg() on it is raced against teardown, with a debug delay inside the lock-dropped section to widen the window:

- HCI_EV_LE_PA_SYNC_LOST injected: 64 of 64 rounds left the socket in BT_LISTEN with the connection gone, recvmsg() returned 0 and accept() on that fd returned EAGAIN, which iso_sock_accept() can only do while the socket is BT_LISTEN. With this patch, 0 of 64, recvmsg() returns an error and accept() returns EBADFD.

- shutdown() instead of a controller event: 24 of 32 rounds wedged in BT_LISTEN, 0 of 32 with this patch. With only the error check in place and a short window, one round still wedged while recvmsg() returned 0, which is the case the state re-check covers.

An unraced control round behaves the same before and after: recvmsg() returns 0, the socket reaches BT_LISTEN and an LE BIG Create Sync is issued.

Fixes 7a17308c1788 ("Bluetooth: iso: Fix circular lock in iso_conn_big_sync")
---
 net/bluetooth/iso.c |   30 ++++++++++++++++++++++--------
 1 file changed, 22 insertions(+), 8 deletions(-)
net/bluetooth/iso.c +22 -8
@@ -1657,9 +1657,9 @@ static void iso_conn_defer_accept(struct
 	hci_send_cmd(hdev, HCI_OP_LE_ACCEPT_CIS, sizeof(cp), &cp);
 }
 
-static void iso_conn_big_sync(struct sock *sk)
+static int iso_conn_big_sync(struct sock *sk)
 {
-	int err;
+	int err = 0;
 	struct hci_dev *hdev;
 	struct iso_conn *conn;
 	bdaddr_t src, dst;
@@ -1674,7 +1674,7 @@ static void iso_conn_big_sync(struct soc
 	hdev = hci_get_route(&dst, &src, src_type);
 
 	if (!hdev)
-		return;
+		return -EHOSTUNREACH;
 
 	/* hci_le_big_create_sync requires hdev lock to be held, since
 	 * it enqueues the HCI LE BIG Create Sync command via
@@ -1690,8 +1690,10 @@ static void iso_conn_big_sync(struct soc
 	 * both before dereferencing conn->hcon.
 	 */
 	conn = iso_pi(sk)->conn;
-	if (!conn || !conn->hcon)
+	if (!conn || !conn->hcon) {
+		err = -ENOTCONN;
 		goto unlock;
+	}
 
 	if (!test_and_set_bit(BT_SK_BIG_SYNC, &iso_pi(sk)->flags)) {
 		err = hci_conn_big_create_sync(hdev, conn->hcon,
@@ -1707,6 +1709,8 @@ unlock:
 	release_sock(sk);
 	hci_dev_unlock(hdev);
 	hci_dev_put(hdev);
+
+	return err;
 }
 
 static int iso_sock_recvmsg(struct socket *sock, struct msghdr *msg,
@@ -1731,10 +1735,19 @@ static int iso_sock_recvmsg(struct socke
 		case BT_CONNECT2:
 			if (test_bit(BT_SK_PA_SYNC, &pi->flags)) {
 				release_sock(sk);
-				iso_conn_big_sync(sk);
+				err = iso_conn_big_sync(sk);
 				lock_sock(sk);
 
-				sk->sk_state = BT_LISTEN;
+				/* The socket lock was dropped, so the
+				 * connection may have been torn down
+				 * meanwhile and iso_chan_del() may have
+				 * already moved the socket to BT_CLOSED.
+				 * Only move on to BT_LISTEN if the BIG sync
+				 * was actually started and nothing else has
+				 * changed the state.
+				 */
+				if (!err && sk->sk_state == BT_CONNECT2)
+					sk->sk_state = BT_LISTEN;
 			} else {
 				iso_conn_defer_accept(pi->conn->hcon);
 				sk->sk_state = BT_CONFIG;
@@ -1745,10 +1758,11 @@ static int iso_sock_recvmsg(struct socke
 		case BT_CONNECTED:
 			if (test_bit(BT_SK_PA_SYNC, &iso_pi(sk)->flags)) {
 				release_sock(sk);
-				iso_conn_big_sync(sk);
+				err = iso_conn_big_sync(sk);
 				lock_sock(sk);
 
-				sk->sk_state = BT_LISTEN;
+				if (!err && sk->sk_state == BT_CONNECTED)
+					sk->sk_state = BT_LISTEN;
 				early_ret = true;
 			}
 
author maintainer
#99 lore raw

PATCH 99/101 Bluetooth: ISO: zero the sockaddr before returning it in getname

In reply to Greg Kroah-Hartman

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

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

From: Ali Ahmet Memis <[email protected]>

commit 884cf2cc957da7ac178a0e6c6c69ddfec0481cc8 upstream.

iso_sock_getname() fills a struct sockaddr_iso in place and returns its size without clearing it first, so bytes it does not write are copied to user space from the kernel stack. The getsockname(2) and getpeername(2) paths both run through do_getsockname(), which hands getname() an uninitialized sockaddr_storage on the stack and copies back up to the number of bytes getname() returns, so the driver has to initialize every byte it accounts for.

Two ranges are left uninitialized:

- struct sockaddr_iso is 10 bytes but only 9 are written (family, iso_bdaddr, iso_bdaddr_type), leaking the trailing pad byte on every call.

- for a broadcast peer (BIS_LINK or PA_LINK) the returned length grows by sizeof(struct sockaddr_iso_bc), but only bc_sid, bc_num_bis and bc_bis are filled; bc_bdaddr and bc_bdaddr_type, the first 7 bytes of that structure, are never written.

An unprivileged process can open a BTPROTO_ISO socket and reach the pad leak with getsockname(); the broadcast leak needs an established BIS/PA connection. l2cap and rfcomm already memset their sockaddr in getname for the same reason; do the same here.

Fixes ccf74f2390d6 ("Bluetooth: Add BTPROTO_ISO socket type")
Fixes 0a766a0affb5 ("Bluetooth: ISO: Fix getpeername not returning sockaddr_iso_bc fields")
---
 net/bluetooth/iso.c |    2 ++
 1 file changed, 2 insertions(+)
net/bluetooth/iso.c +2 -0
@@ -1535,6 +1535,7 @@ static int iso_sock_getname(struct socke
 
 	lock_sock(sk);
 
+	memset(sa, 0, sizeof(struct sockaddr_iso));
 	addr->sa_family = AF_BLUETOOTH;
 
 	if (peer) {
@@ -1545,6 +1546,7 @@ static int iso_sock_getname(struct socke
 		sa->iso_bdaddr_type = iso_pi(sk)->dst_type;
 
 		if (hcon && (hcon->type == BIS_LINK || hcon->type == PA_LINK)) {
+			memset(sa->iso_bc, 0, sizeof(struct sockaddr_iso_bc));
 			sa->iso_bc->bc_sid = iso_pi(sk)->bc_sid;
 			sa->iso_bc->bc_num_bis = iso_pi(sk)->bc_num_bis;
 			memcpy(sa->iso_bc->bc_bis, iso_pi(sk)->bc_bis,
author maintainer
#100 lore raw

PATCH 100/101 Bluetooth: MGMT: reject HCI_CMD_SYNC params_len above 255

In reply to Greg Kroah-Hartman

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

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

From: Ali Ahmet Memis <[email protected]>

commit 5d95286b6d6e8f1d304da7522bfa6860fc017e48 upstream.

mgmt_hci_cmd_sync() checks that the message length agrees with params_len but puts no upper bound on it. params_len is __le16 while the parameter length in the HCI command header is a u8:

	struct hci_command_hdr {
		__le16	opcode;
		__u8	plen;
	} __packed;

hci_cmd_sync_alloc() assigns one to the other:

	hdr->plen = plen;

	if (plen)
		skb_put_data(skb, param, plen);

so a params_len of 256 leaves plen at 0 while all 256 bytes are still appended. The frame handed to the driver then declares no parameters and carries 256 of them. On a length framed transport such as H:4 the controller takes the trailing bytes as the start of the next packet.

The mgmt socket MTU is HCI_MAX_FRAME_SIZE, so params_len can reach about 1KB this way. Commit 03f1700b9b4d ("Bluetooth: MGMT: reject malformed HCI_CMD_SYNC commands") only made params_len agree with the message length, a value that fits the message but not the header field is still accepted.

Reject params_len that does not fit the header field.

Fixes 827af4787e74 ("Bluetooth: MGMT: Add initial implementation of MGMT_OP_HCI_CMD_SYNC")
---
 net/bluetooth/mgmt.c |    8 ++++++++
 1 file changed, 8 insertions(+)
net/bluetooth/mgmt.c +8 -0
@@ -2671,6 +2671,14 @@ static int mgmt_hci_cmd_sync(struct sock
 		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_HCI_CMD_SYNC,
 				       MGMT_STATUS_INVALID_PARAMS);
 
+	/* The HCI command header carries the parameter length in a u8, a
+	 * larger value would be truncated there while the parameters are
+	 * still appended to the frame in full.
+	 */
+	if (le16_to_cpu(cp->params_len) > U8_MAX)
+		return mgmt_cmd_status(sk, hdev->id, MGMT_OP_HCI_CMD_SYNC,
+				       MGMT_STATUS_INVALID_PARAMS);
+
 	hci_dev_lock(hdev);
 	cmd = mgmt_pending_new(sk, MGMT_OP_HCI_CMD_SYNC, hdev, data, len);
 	if (!cmd)
author maintainer
#101 lore raw

PATCH 101/101 Bluetooth: hci_aml: validate firmware segment lengths

In reply to Greg Kroah-Hartman

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

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

From: Laxman Acharya Padhya <[email protected]>

commit 2bf6b9baca9372ea51b6d0f2820dc9bf29a83ef4 upstream.

aml_download_firmware() reads two lengths from the firmware header and uses them to build pointers before checking that the header and segment data are present. A truncated or inconsistent firmware image can make the driver read past firmware->data while constructing TCI commands.

Reject images shorter than the header and ensure that the ICCM and DCCM ranges fit within the loaded firmware before downloading either segment.

Fixes 37bac77e4649 ("Bluetooth: hci_uart: Add support for Amlogic HCI UART")
---
 drivers/bluetooth/hci_aml.c |   18 ++++++++++++++++--
 1 file changed, 16 insertions(+), 2 deletions(-)
drivers/bluetooth/hci_aml.c +16 -2
@@ -247,7 +247,7 @@ static int aml_download_firmware(struct
 	struct hci_uart *hu = hci_get_drvdata(hdev);
 	struct aml_serdev *amldev = serdev_device_get_drvdata(hu->serdev);
 	const struct firmware *firmware = NULL;
-	struct aml_fw_len *fw_len = NULL;
+	const struct aml_fw_len *fw_len = NULL;
 	u8 *iccm_start = NULL, *dccm_start = NULL;
 	u32 iccm_len, dccm_len;
 	u32 value = 0;
@@ -281,7 +281,21 @@ static int aml_download_firmware(struct
 		goto exit;
 	}
 
-	fw_len = (struct aml_fw_len *)firmware->data;
+	if (firmware->size < sizeof(*fw_len)) {
+		bt_dev_err(hdev, "Firmware is too small for its header");
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	fw_len = (const struct aml_fw_len *)firmware->data;
+	if (fw_len->iccm_len < amldev->aml_dev_data->iccm_offset ||
+	    fw_len->iccm_len > firmware->size - sizeof(*fw_len) ||
+	    fw_len->dccm_len > firmware->size - sizeof(*fw_len) -
+			fw_len->iccm_len) {
+		bt_dev_err(hdev, "Invalid firmware segment lengths");
+		ret = -EINVAL;
+		goto exit;
+	}
 
 	/* Download ICCM */
 	iccm_start = (u8 *)(firmware->data) + sizeof(struct aml_fw_len)
Tested-by on the v1 series
#102 lore raw
In reply to Greg Kroah-Hartman

Hi!

Quoted 4 lines

This is the start of the stable review cycle for the 7.1.11 release. There are 101 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.

CIP testing did not find any problems here:

https://gitlab.com/cip-project/cip-testing/linux-stable-rc-ci/-/tree/linux-7.1.y

Best regards,
Pavel

Tested-by on the v1 series
#103 lore raw
In reply to Greg Kroah-Hartman
Quoted 9 lines · Greg Kroah-Hartman

This is the start of the stable review cycle for the 7.1.11 release. There are 101 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 Thu, 27 Aug 2026 13:25:02 +0000. Anything received after that time might be too late.

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

Quoted 8 lines
	https://www.kernel.org/pub/linux/kernel/v7.x/stable-review/patch-7.1.11-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-7.1.y
and the diffstat can be found below.

thanks,

greg k-h

Tested rc1 against the Fedora build system (aarch64, ppc64le, s390x, x86_64), and boot tested x86_64. No regressions noted.

Tested-by on the v1 series
#104 lore raw
In reply to Greg Kroah-Hartman
Quoted 9 lines · Greg Kroah-Hartman

This is the start of the stable review cycle for the 7.1.11 release. There are 101 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 Thu, 27 Aug 2026 13:25:02 +0000. Anything received after that time might be too late.

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

Quoted 8 lines
	https://www.kernel.org/pub/linux/kernel/v7.x/stable-review/patch-7.1.11-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-7.1.y
and the diffstat can be found below.

thanks,

greg k-h

On ARCH_BRCMSTB using 32-bit and 64-bit ARM kernels, build tested on BMIPS_GENERIC:

Signature · 1 line
Florian
Tested-by on the v1 series
#105 lore raw
In reply to Greg Kroah-Hartman
Quoted 9 lines · Greg Kroah-Hartman

This is the start of the stable review cycle for the 7.1.11 release. There are 101 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 Thu, 27 Aug 2026 13:25:02 +0000. Anything received after that time might be too late.

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

Quoted 9 lines
	https://www.kernel.org/pub/linux/kernel/v7.x/stable-review/patch-7.1.11-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-7.1.y
and the diffstat can be found below.

thanks,

greg k-h

Compiled and booted on my test system. No dmesg regressions.

thanks,
-- Shuah

Tested-by on the v1 series
#106 lore raw
In reply to Greg Kroah-Hartman
Quoted 9 lines · Greg Kroah-Hartman

This is the start of the stable review cycle for the 7.1.11 release. There are 101 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 Thu, 27 Aug 2026 13:25:02 +0000. Anything received after that time might be too late.

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

Quoted 8 lines
	https://www.kernel.org/pub/linux/kernel/v7.x/stable-review/patch-7.1.11-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-7.1.y
and the diffstat can be found below.

thanks,

greg k-h

Built and booted successfully on RISC-V RV64 (HiFive Unmatched).

Tested-by on the v1 series
#108 lore raw
In reply to Greg Kroah-Hartman

Hi Greg

Quoted 10 lines · Greg Kroah-Hartman

This is the start of the stable review cycle for the 7.1.11 release. There are 101 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 Thu, 27 Aug 2026 13:25:02 +0000. Anything received after that time might be too late.

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

Quoted 9 lines
        https://www.kernel.org/pub/linux/kernel/v7.x/stable-review/patch-7.1.11-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-7.1.y
and the diffstat can be found below.

thanks,

greg k-h

Linux version 7.1.11-rc1 tested.

Build successfully completed.
Boot successfully completed.
No dmesg regressions.
Video output normal.
Sound output normal.

Lenovo ThinkPad X1 Carbon Gen10(Intel i7-1260P(x86_64) arch linux)

[    0.000000] Linux version 7.1.11-rc1rv-g822f846a60fc

(takeshi@ThinkPadX1Gen10J0764) (gcc (GCC) 16.2.1 20260810, GNU ld (GNU Binutils) 2.47) #1 SMP PREEMPT_DYNAMIC Wed Aug 26 20:10:08 JST 2026

Thanks

Tested-by on the v1 series
#109 lore raw
In reply to Greg Kroah-Hartman
Quoted 8 lines · On Tue, 25 Aug 2026 15:24:38 +0200 Greg Kroah-Hartman…

This is the start of the stable review cycle for the 7.1.11 release. There are 101 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 Thu, 27 Aug 2026 13:25:02 +0000. Anything received after that time might be too late.

Boot-tested under QEMU for Rust x86_64, arm64 and riscv64; built-tested for loongarch64 and arm32:

In loongarch64, I am seeing:

    drivers/gpu/drm/amd/amdgpu/amdgpu_vm.o: warning: objtool: amdgpu_vm_handle_fault+0x140: sibling call from callable instruction with modified stack frame
Cc Alex Deucher <[email protected]>
Cc Christian König <[email protected]>
Cc Huacai Chen <[email protected]>
Cc WANG Xuerui <[email protected]>

Thanks!

Cheers,
Miguel

Tested-by on the v1 series
#110 lore raw
In reply to Greg Kroah-Hartman

Am 25.08.2026 um 15:24 schrieb Greg Kroah-Hartman:

Quoted 4 lines

This is the start of the stable review cycle for the 7.1.11 release. There are 101 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.

Builds, boots and works on my 2-socket Ivy Bridge Xeon E5-2697 v2 server. No dmesg oddities or regressions found.

Beste Grüße,
Peter Schneider

Signature · 8 lines
Climb the mountain not to plant your flag, but to embrace the challenge,
enjoy the air and behold the view. Climb it so you can see the world,
not so the world can see you.                    -- David McCullough Jr.

OpenPGP:  0xA3828BD796CCE11A8CADE8866E3A92C92C3FF244
Download: https://www.peters-netzplatz.de/download/pschneider1968_pub.asc
https://keys.mailvelope.com/pks/lookup?op=get&[email protected]
https://keys.mailvelope.com/pks/lookup?op=get&[email protected]
Tested-by on the v1 series
#111 lore raw
In reply to Greg Kroah-Hartman
Quoted 4 lines · Greg Kroah-Hartman

This is the start of the stable review cycle for the 7.1.11 release. There are 101 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.

Build and boot tested with QEMU for x86_64, i386, arm64, and riscv across multiple configurations, and boots on AMD Ryzen 7 5800H. No regressions observed.

Tested-by on the v1 series
#112 lore raw
In reply to Greg Kroah-Hartman
Quoted 9 lines · Greg Kroah-Hartman

This is the start of the stable review cycle for the 7.1.11 release. There are 101 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 Thu, 27 Aug 2026 13:25:02 +0000. Anything received after that time might be too late.

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

Quoted 8 lines
	https://www.kernel.org/pub/linux/kernel/v7.x/stable-review/patch-7.1.11-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-7.1.y
and the diffstat can be found below.

thanks,

greg k-h

Tested on an amd64 laptop (Lenovo ThinkPad T14 Gen 1). Working well, no regressions observed.

Signature · 1 line
-Barry K. Nathan  <[email protected]>
Tested-by on the v1 series
#114 lore raw
In reply to Greg Kroah-Hartman
Quoted 15 lines · Greg Kroah-Hartman

This is the start of the stable review cycle for the 7.1.11 release. There are 101 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 Thu, 27 Aug 2026 13:25:02 +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/v7.x/stable-review/patch-7.1.11-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-7.1.y
and the diffstat can be found below.

Quoted 5 lines

thanks,

greg k-h

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

Build and Boot Report 7.1.11-rc1

I built and tested Linux kernel version 7.1.11-rc1 using the default configurations
on both x86_64 and arm64 architectures in a virtualized environment.

The kernel compiled successfully on both architectures and booted without issues.
I did not observe any regressions or new warnings in dmesg during boot.

Kernel version: 7.1.11-rc1
Configurations tested: x86_64_defconfig, defconfig
Architectures tested: x86_64, arm64
Kernel source: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.git
Commit: 822f846a60fc16f7c944c77d54276d8932b2c4e0

Best regards,
Dileep Malepu.