Skip to contentSkip to conversation

7.2.4-rc1 review

PATCH In review Greg Kroah-Hartman 15 participants 731 messages Sep 4 – Sep 18 Page 3 of 6

Subsystems (2)
lore
Share & tools
lore
author maintainer
#228 lore raw

PATCH 228/713 btrfs: do not overwrite NODATASUM flag when removing NODATACOW flag

In reply to Greg Kroah-Hartman

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

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

From: Qu Wenruo <[email protected]>

commit 15f7c86215e8d5f14b24127fa88af6c79363d50e upstream.

[TEST FAILURE]
The test case generic/628 will fail if MOUNT_OPTIONS is set to "-o nodatasum":

  FSTYP         -- btrfs
  PLATFORM      -- Linux/x86_64 btrfs-vm 7.1.0-rc4-custom+ #383 SMP PREEMPT_DYNAMIC Sat May 30 07:35:42 ACST 2026
  MKFS_OPTIONS  -- -O bgt -K /dev/mapper/test-scratch1

MOUNT_OPTIONS -- -o nodatasum /dev/mapper/test-scratch1 /mnt/scratch

# generic/628 1s ... - output mismatch (see /home/adam/xfstests/results//generic/628.out.bad)

#     --- tests/generic/628.out	2022-05-11 11:25:30.816666664 +0930
#     +++ /home/adam/xfstests/results//generic/628.out.bad	2026-06-08 18:56:49.878542927 +0930
#     @@ -8,8 +8,9 @@
#      310f146ce52077fcd3308dcbe7632bb2  SCRATCH_MNT/a
#      310f146ce52077fcd3308dcbe7632bb2  SCRATCH_MNT/d
#      test reflink flag not set iflag
#     +XFS_IOC_CLONE: Invalid argument
#      310f146ce52077fcd3308dcbe7632bb2  SCRATCH_MNT/a
#     -310f146ce52077fcd3308dcbe7632bb2  SCRATCH_MNT/b
#     +d41d8cd98f00b204e9800998ecf8427e  SCRATCH_MNT/b

# ...

[CAUSE]
The direct cause is that after "chattr +S", the btrfs inode will lose its NODATASUM flag inherited from the mount option. E.g.:

  # mkfs.btrfs -f $dev
  # mount $dev $mnt -o nodatasum
  # touch $mnt/foobar
  # sync
  # btrfs ins dump-tree -t 5 $dev | grep "(257 INODE_ITEM 0) itemoff" -A 3
         item 4 key (257 INODE_ITEM 0) itemoff 15879 itemsize 160
         	generation 9 transid 9 size 0 nbytes 0
         	block group 0 mode 100644 links 1 uid 0 gid 0 rdev 0
         	sequence 1 flags 0x1(NODATASUM)
         	                     ^^^^^^^^^ Proper NODATASUM flag
  # chattr +S $mnt/foobar
  # sync
  # btrfs ins dump-tree -t 5 $dev | grep "(257 INODE_ITEM 0) itemoff" -A 3
  	item 4 key (257 INODE_ITEM 0) itemoff 15879 itemsize 160
         	generation 9 transid 10 size 0 nbytes 0
         	block group 0 mode 100644 links 1 uid 0 gid 0 rdev 0
         	sequence 2 flags 0x20(SYNC)
         	                      ^^^^ Only the new SYNC flag

This makes the inode drop the old NODATASUM flag, while the new reflink destination will still inherit the NODATASUM flag. The mismatching NODATASUM flags will cause the reflink to fail.

The root cause is that, inside btrfs_fileattr_set() if no FS_NOCOW_FL is set, we remove both NODATASUM and NODATACOW flag.

However we should not touch NODATASUM flag, as data COW doesn't require checksum. Only NODATACOW implies NODATASUM, but DATACOW doesn't imply DATASUM.

The deeper problems are:

- Fileattr API is too binary
It either clears or sets a flag, there is no "do not change" option. So that why "chattr +S" implies "chattr -C", and is forcing us to change NODATACOW along with NODATASUM flag.

- No way to change NODATASUM through fileattr API
In fact NODATASUM can only be modified through mount option.

The deeper problems are much harder to attack.

[FIX]
Remove NODATACOW flag when FS_NOCOW_FL is not set, but only remove NODATASUM if "nodatasum" mount option is not set.

This allows the existing "chattr +C" then "chattr -C" to remove both NODATACOW and NODATASUM flags on a default mount.

But for a mount with "nodatasum" option, the NODATASUM inode flag will persist through either "chattr +C" and "chattr -C".

Fixes 7e97b8daf634 ("btrfs: allow setting NOCOW for a zero sized file via ioctl")
---
 fs/btrfs/ioctl.c |   23 +++++++++++++++--------
 1 file changed, 15 insertions(+), 8 deletions(-)
fs/btrfs/ioctl.c +15 -8
@@ -356,14 +356,21 @@ int btrfs_fileattr_set(struct mnt_idmap
 			inode_flags |= BTRFS_INODE_NODATACOW;
 		}
 	} else {
-		/*
-		 * Revert back under same assumptions as above
-		 */
-		if (S_ISREG(inode->vfs_inode.i_mode)) {
-			if (inode->vfs_inode.i_size == 0)
-				inode_flags &= ~(BTRFS_INODE_NODATACOW |
-						 BTRFS_INODE_NODATASUM);
-		} else {
+		/* We can only change NODATACOW for zero-sized regular file. */
+		if (S_ISREG(inode->vfs_inode.i_mode) && (inode->vfs_inode.i_size == 0)) {
+			inode_flags &= ~BTRFS_INODE_NODATACOW;
+			/*
+			 * There is currently no way to change NODATASUM flag
+			 * through fileattr API.  If we unconditionally keep the
+			 * current NODATASUM flag, chattr +C then chattr -C will
+			 * keep the NODATASUM flag, and no way to remove that
+			 * flag.
+			 *
+			 * So respect the current mount option for NODATASUM flag.
+			 */
+			if (!btrfs_test_opt(fs_info, NODATASUM))
+				inode_flags &= ~BTRFS_INODE_NODATASUM;
+		} else if (!S_ISREG(inode->vfs_inode.i_mode)) {
 			inode_flags &= ~BTRFS_INODE_NODATACOW;
 		}
 	}
author maintainer
#229 lore raw

PATCH 229/713 audit: avoid dropping live tree ref on fsnotify rule autoremove

In reply to Greg Kroah-Hartman

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

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

From: Jérémy Jean <[email protected]>

commit 783f0f0974c156aca630f4ffff248671082a098d upstream.

audit_del_rule() is used for both netlink deletion templates and internal fsnotify autoremove. The former passes a parsed template which owns a temporary tree reference; the latter passes the installed entry itself.

The unconditional audit_put_tree() at the end of audit_del_rule() assumes the template case. For mixed AUDIT_DIR plus AUDIT_EXE rules, an fsnotify autoremove event therefore drops the installed rule's live tree reference. Repeating this across rules sharing the same tree can free the tree while another rule still references it, and a later autoremove dereferences the freed pathname while comparing rules.

Move the temporary-tree put to audit_rule_change(), the caller that owns deletion templates. Keep it in the AUDIT_DEL_RULE cleanup so both successful deletion and -ENOENT still release the parser-owned tree.

Fixes 34d99af52ad4 ("audit: implement audit by executable")
Assisted-by Codex:gpt-5
[PM: dropped unnecessary comment for line length reasons]
---
 kernel/auditfilter.c |    6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)
kernel/auditfilter.c +2 -4
@@ -1023,7 +1023,6 @@ static inline int audit_add_rule(struct
 int audit_del_rule(struct audit_entry *entry)
 {
 	struct audit_entry  *e;
-	struct audit_tree *tree = entry->rule.tree;
 	struct list_head *list;
 	int ret = 0;
 #ifdef CONFIG_AUDITSYSCALL
@@ -1071,9 +1070,6 @@ int audit_del_rule(struct audit_entry *e
 out:
 	mutex_unlock(&audit_filter_mutex);
 
-	if (tree)
-		audit_put_tree(tree);	/* that's the temporary one */
-
 	return ret;
 }
 
@@ -1158,6 +1154,8 @@ int audit_rule_change(int type, int seq,
 	}
 
 	if (err || type == AUDIT_DEL_RULE) {
+		if (type == AUDIT_DEL_RULE && entry->rule.tree)
+			audit_put_tree(entry->rule.tree);
 		if (entry->rule.exe)
 			audit_remove_mark(entry->rule.exe);
 		audit_free_rule(entry);
author maintainer
#230 lore raw

PATCH 230/713 cifs: add cifs_resize_file_locked() to guard fscache_resize_cookie() under i_rwsem

In reply to Greg Kroah-Hartman

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

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

From: Frank Sorenson <[email protected]>

commit 32a7af68df7361fe7cf153cf36124d04b94aec00 upstream.

cifs_setsize() calls fscache_resize_cookie() without holding i_rwsem. When the fscache cookie is active (FSCACHE_COOKIE_IS_CACHING is set), fscache_resize_cookie() performs a real resize that requires i_rwsem held exclusively. If another file descriptor has the same inode open, fscache_use_cookie() was already called from that cifs_open(), making the cookie active. In that case, calling cifs_setsize() from cifs_do_truncate() (invoked from cifs_open() without i_rwsem) races against concurrent fscache I/O.

Strip fscache_resize_cookie() from cifs_setsize(), making it a pure size/page-cache helper. Add cifs_resize_file_locked() for callers that already hold i_rwsem: it calls netfs_resize_file() and cifs_setsize(), then temporarily activates the cookie with fscache_use_cookie() to perform the resize under the lock, then deactivates it with cifs_fscache_unuse_inode_cookie(). Using fscache_use_cookie() before the resize ensures correctness whether or not another fd already holds the cookie active.

Switch cifs_file_set_size(), smb2_duplicate_extents(), and both size- extension branches of smb3_simple_falloc() to the new wrapper; those paths already hold i_rwsem via VFS setattr, lock_two_nondirectories(), or cifs_fallocate() respectively. cifs_do_truncate() continues to call cifs_setsize() followed by cifs_invalidate_cache(), since it runs without i_rwsem.

Fixes fa724e235cfd ("cifs: add fscache_resize_cookie() to cifs_setsize()")
Cc David Howells <[email protected]>
Cc Paulo Alcantara <[email protected]>
---
 fs/smb/client/cifsfs.h  |    1 +
 fs/smb/client/inode.c   |   24 +++++++++++++++++++-----
 fs/smb/client/smb2ops.c |    9 +++------
 3 files changed, 23 insertions(+), 11 deletions(-)
fs/smb/client/cifsfs.h +1 -0
@@ -147,6 +147,7 @@ ssize_t cifs_file_copychunk_range(unsign
 
 long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg);
 void cifs_setsize(struct inode *inode, loff_t offset);
+void cifs_resize_file_locked(struct inode *inode, loff_t offset);
 
 struct fs_context;
 struct smb3_fs_context;
fs/smb/client/inode.c +19 -5
@@ -3059,7 +3059,23 @@ void cifs_setsize(struct inode *inode, l
 	inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
 	truncate_pagecache(inode, offset);
 	netfs_wait_for_outstanding_io(inode);
-	fscache_resize_cookie(cifs_inode_cookie(inode), offset);
+}
+
+void cifs_resize_file_locked(struct inode *inode, loff_t offset)
+{
+	struct fscache_cookie *cookie = cifs_inode_cookie(inode);
+
+	lockdep_assert_held_write(&inode->i_rwsem);
+
+	netfs_resize_file(netfs_inode(inode), offset, true);
+	cifs_setsize(inode, offset);
+
+	if (!cookie)
+		return;
+
+	fscache_use_cookie(cookie, true);
+	fscache_resize_cookie(cookie, offset);
+	cifs_fscache_unuse_inode_cookie(inode, true);
 }
 
 int cifs_file_set_size(const unsigned int xid, struct dentry *dentry,
@@ -3125,10 +3141,8 @@ int cifs_file_set_size(const unsigned in
 	cifs_put_tlink(tlink);
 
 set_size_out:
-	if (rc == 0) {
-		netfs_resize_file(&cifsInode->netfs, size, true);
-		cifs_setsize(inode, size);
-	}
+	if (rc == 0)
+		cifs_resize_file_locked(inode, size);
 
 	return rc;
 }
fs/smb/client/smb2ops.c +3 -6
@@ -2222,8 +2222,7 @@ smb2_duplicate_extents(const unsigned in
 		rc = smb2_set_file_size(xid, tcon, trgtfile, dest_off + len, false);
 		if (rc)
 			goto duplicate_extents_out;
-		netfs_resize_file(netfs_inode(inode), dest_off + len, true);
-		cifs_setsize(inode, dest_off + len);
+		cifs_resize_file_locked(inode, dest_off + len);
 	}
 	rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid,
 			trgtfile->fid.volatile_fid,
@@ -3776,8 +3775,7 @@ static long smb3_simple_falloc(struct fi
 			}
 
 			new_eof = off + len;
-			netfs_resize_file(&cifsi->netfs, new_eof, true);
-			cifs_setsize(inode, new_eof);
+			cifs_resize_file_locked(inode, new_eof);
 
 			qrc = SMB2_query_info(xid, tcon,
 					      cfile->fid.persistent_fid,
@@ -3825,8 +3823,7 @@ static long smb3_simple_falloc(struct fi
 		if (rc)
 			goto out;
 
-		netfs_resize_file(&cifsi->netfs, new_eof, true);
-		cifs_setsize(inode, new_eof);
+		cifs_resize_file_locked(inode, new_eof);
 
 		qrc = SMB2_query_info(xid, tcon,
 				      cfile->fid.persistent_fid,
author maintainer
#231 lore raw

PATCH 231/713 cifs: call pagecache_isize_extended() in cifs_setsize() when extending

In reply to Greg Kroah-Hartman

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

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

From: Frank Sorenson <[email protected]>

commit c510edb9734af1c274d18f4f31a471a166bbc7e8 upstream.

cifs_setsize() calls truncate_pagecache() but skips pagecache_isize_extended() on extension. truncate_setsize() shows the correct pattern:

  i_size_write(inode, newsize);
  if (newsize > oldsize)
      pagecache_isize_extended(inode, oldsize, newsize);
  truncate_pagecache(inode, newsize);

pagecache_isize_extended() zeroes the tail of the page straddling old EOF. Without it, dirty bytes in that region can be written back to the server, exposing stale data in the newly extended range.

---
 fs/smb/client/inode.c |    2 ++
 1 file changed, 2 insertions(+)
fs/smb/client/inode.c +2 -0
@@ -3057,6 +3057,8 @@ void cifs_setsize(struct inode *inode, l
 		inode->i_blocks = blocks;
 	spin_unlock(&inode->i_lock);
 	inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
+	if (offset > old_size)
+		pagecache_isize_extended(inode, old_size, offset);
 	truncate_pagecache(inode, offset);
 	netfs_wait_for_outstanding_io(inode);
 }
author maintainer
#232 lore raw

PATCH 232/713 cifs: clear tcon after cifsFileInfo_put() in cifs_file_set_size()

In reply to Greg Kroah-Hartman

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

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

From: Frank Sorenson <[email protected]>

commit b96db32fed8dfb2478d7c208f89bf383beed1535 upstream.

When the else branch of cifs_file_set_size() finds a writable file handle via find_writable_file(), it borrows tcon and server from the handle's tlink, attempts the handle-based set_file_size() RPC, and then releases the handle with cifsFileInfo_put().

If set_file_size() fails, execution falls through to the path-based fallback, which reuses the borrowed tcon and server under the "if (tcon == NULL)" guard. Since tcon is not NULL at that point, the guard is skipped. If cifsFileInfo_put() dropped the last reference on a tlink that was already removed from the tlink tree (TCON_LINK_IN_TREE cleared, as happens during reconnection or session teardown), cifs_put_tlink() will have freed tcon; the subsequent set_path_size() call is then a use-after-free.

Setting tcon = NULL after cifsFileInfo_put() causes the existing guard to take the cifs_sb_tlink() path, which acquires a fresh reference for the path-based operation or fails cleanly if the session is gone.

Fixes 110fee6b9bb5 ("smb: client: fix missing timestamp updates with O_TRUNC")
Cc Paulo Alcantara <[email protected]>
---
 fs/smb/client/inode.c |    1 +
 1 file changed, 1 insertion(+)
fs/smb/client/inode.c +1 -0
@@ -3118,6 +3118,7 @@ int cifs_file_set_size(const unsigned in
 							size, false);
 			cifs_dbg(FYI, "%s: set_file_size: rc = %d\n", __func__, rc);
 			cifsFileInfo_put(open_file);
+			tcon = NULL;
 		}
 	}
 
author maintainer
#233 lore raw

PATCH 233/713 cifs: fix loff_t underflow in cifs_remap_file_range() when len == 0

In reply to Greg Kroah-Hartman

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

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

From: Frank Sorenson <[email protected]>

commit 6c322f5cf7476ded7a9a20f7be72462065a03c68 upstream.

With len == 0 (clone to EOF), the effective length is computed as:

    len = src_inode->i_size - off;

If off > i_size, this is a negative loff_t, corrupting the ByteCount in the FSCTL_DUPLICATE_EXTENTS_TO_FILE request and inverting the range in filemap_write_and_wait_range(). The existing off >= i_size check fires only after the ioctl has already been sent.

Snapshot i_size_read() once for both the bounds check and the length calculation, eliminating the TOCTOU and 32-bit torn-read risk. Reject off > src_size with -EINVAL. Treat off == src_size as a no-op, consistent with __generic_remap_file_range_prep().

Fixes 04b38d601239 ("vfs: pull btrfs clone API to vfs layer")
---
 fs/smb/client/cifsfs.c |   15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)
fs/smb/client/cifsfs.c +13 -2
@@ -1413,8 +1413,19 @@ static loff_t cifs_remap_file_range(stru
 	 */
 	lock_two_nondirectories(target_inode, src_inode);
 
-	if (len == 0)
-		len = src_inode->i_size - off;
+	if (len == 0) {
+		loff_t src_size = i_size_read(src_inode);
+
+		if (off > src_size) {
+			rc = -EINVAL;
+			goto unlock;
+		}
+		len = src_size - off;
+		if (!len) {
+			rc = 0;
+			goto unlock;
+		}
+	}
 
 	cifs_dbg(FYI, "clone range\n");
 
author maintainer
#234 lore raw

PATCH 234/713 cifs: use cifs_invalidate_cache() in cifs_do_truncate() for O_TRUNC

In reply to Greg Kroah-Hartman

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

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

From: Frank Sorenson <[email protected]>

commit 364b183230586a62660a7280c1eb20138338eeb5 upstream.

cifs_do_truncate() is invoked from cifs_open() without i_rwsem, so it cannot use cifs_resize_file_locked() to perform a proper fscache cookie resize. Instead, add cifs_invalidate_cache() after cifs_setsize().

cifs_invalidate_cache() calls fscache_invalidate(), which works without holding i_rwsem: it unconditionally increments inval_counter and sets FSCACHE_COOKIE_NO_DATA_TO_READ, ensuring that stale cached data is not served once the cookie is later activated by fscache_use_cookie(). Truncation to zero leaves no valid cached data, making invalidation the correct semantic here.

Fixes fa724e235cfd ("cifs: add fscache_resize_cookie() to cifs_setsize()")
Cc David Howells <[email protected]>
Cc Paulo Alcantara <[email protected]>
---
 fs/smb/client/file.c |    1 +
 1 file changed, 1 insertion(+)
fs/smb/client/file.c +1 -0
@@ -1016,6 +1016,7 @@ static int cifs_do_truncate(const unsign
 		if (!rc) {
 			netfs_resize_file(&cinode->netfs, 0, true);
 			cifs_setsize(inode, 0);
+			cifs_invalidate_cache(inode, 0);
 		}
 	}
 	if (cfile)
author maintainer
#235 lore raw

PATCH 235/713 smb: client: clear setuid/setgid bit on write with cifsacl/modefromsid/posix extensions

In reply to Greg Kroah-Hartman

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

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

From: Jiangshan Yi <[email protected]>

commit b8e5dc4f95e5484159b343903f302eb6d783f2e6 upstream.

When a file has the setuid or setgid bit set and is written to, the VFS strips those bits and issues a setattr with ATTR_KILL_SUID/ATTR_KILL_SGID together with an ATTR_MODE carrying the already-cleared mode.

Both cifs_setattr_unix() and cifs_setattr_nounix() unconditionally dropped ATTR_MODE in that case:

	/* skip mode change if it's just for clearing setuid/setgid */
	if (attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID))
		attrs->ia_valid &= ~ATTR_MODE;

This is fine for the default mount, where the mode is only emulated via the DOS read-only attribute and cannot represent the setuid/setgid bits anyway. However, with the "cifsacl" or "modefromsid" mount options the mode is stored on the server through an ACL (id_mode_to_cifs_acl()), with the SMB3.1.1 POSIX extensions the mode is sent to the server directly, and with the SMB1 Unix extensions (cifs_setattr_unix) the mode is sent via CIFSSMBUnixSetPathInfo(). In all those cases dropping ATTR_MODE means the cleared mode is never pushed to the server, so the setuid/setgid bit survives the write.

This is a security issue: on local filesystems the setuid bit is stripped when a file is written, but over these cifs.ko mounts the bit persists on the server, potentially allowing an unexpected privilege escalation on subsequent execution.

Fix this in two places:

1. cifs_setattr_nounix(): only take the "skip mode change" shortcut when the mode is emulated via the DOS read-only attribute (i.e. neither cifsacl/modefromsid nor the SMB3.1.1 POSIX extensions are in effect), so that the cleared mode is propagated to the server in the ACL / POSIX cases.

2. cifs_setattr_unix(): this function is only called when Unix extensions are in effect, so the mode is always stored on the server. Remove the shortcut entirely so that the cleared mode is always pushed.

Fixes d32c4f2626ac ("CIFS: ignore mode change if it's just for clearing setuid/setgid bits")
---
 fs/smb/client/inode.c |   29 ++++++++++++++++++++++++-----
 1 file changed, 24 insertions(+), 5 deletions(-)
fs/smb/client/inode.c +24 -5
@@ -3231,9 +3231,13 @@ cifs_setattr_unix(struct dentry *direntr
 		attrs->ia_valid &= ~(ATTR_CTIME | ATTR_MTIME);
 	}
 
-	/* skip mode change if it's just for clearing setuid/setgid */
-	if (attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID))
-		attrs->ia_valid &= ~ATTR_MODE;
+	/*
+	 * This function is only called when Unix extensions are in effect,
+	 * so the mode is always sent to and stored on the server.  Do not
+	 * skip the mode change when clearing setuid/setgid bits: dropping
+	 * ATTR_MODE here would leave those bits set on the server after a
+	 * write, which is a security issue.
+	 */
 
 	args = kmalloc_obj(*args);
 	if (args == NULL) {
@@ -3442,8 +3446,23 @@ cifs_setattr_nounix(struct dentry *diren
 		attrs->ia_valid &= ~(ATTR_UID | ATTR_GID);
 	}
 
-	/* skip mode change if it's just for clearing setuid/setgid */
-	if (attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID))
+	/*
+	 * Skip the mode change if it is only being done to clear the
+	 * setuid/setgid bits *and* the mode is emulated via the DOS
+	 * read-only attribute (the default, non-ACL case), which cannot
+	 * represent the setuid/setgid bits anyway.
+	 *
+	 * When the mode is instead stored on the server - i.e. with the
+	 * cifsacl or modefromsid mount options (via an ACL) or with the
+	 * SMB3.1.1 POSIX extensions - the cleared mode must be pushed to
+	 * the server.  Dropping ATTR_MODE here would leave the setuid/
+	 * setgid bit set on the server after a write, which is a security
+	 * issue (the bits are not stripped as they are on local
+	 * filesystems).
+	 */
+	if ((attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID)) &&
+	    !((sbflags & (CIFS_MOUNT_CIFS_ACL | CIFS_MOUNT_MODE_FROM_SID)) ||
+	      cifs_sb_master_tcon(cifs_sb)->posix_extensions))
 		attrs->ia_valid &= ~ATTR_MODE;
 
 	if (attrs->ia_valid & ATTR_MODE) {
author maintainer
#236 lore raw

PATCH 236/713 smb: client: fix UAF and buffer leak in cifs_check_trans2() for malformed secondary T2

In reply to Greg Kroah-Hartman

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

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

From: Frank Sorenson <[email protected]>

commit 730d0bb19507b9e19c2fe5343109ac618e2fbce5 upstream.

When a valid primary TRANSACT2 response has been received (mid->resp_buf set, mid->multiRsp true) and a subsequent secondary response causes cifs_check_trans2() to return false -- either because the SMB header is invalid (malformed != 0) or because check2ndT2() rejects the PDU -- handle_mid() overwrites mid->resp_buf with the new buffer (leaking the primary buffer) and, because mid->multiRsp is set, skips the server->smallbuf/bigbuf NULL-out. When the user thread frees mid->resp_buf, server->smallbuf or server->bigbuf is left dangling; the demux thread reuses it for the next packet, resulting in a use-after-free.

Combine both early-exit conditions and, when mid->multiRsp is already set, abort the pending transaction inline: set multiEnd, call dequeue_mid() with malformed=true, and return true so handle_mid() exits without touching mid->resp_buf or the server buffer pointers.

Fixes 316cf94a910f ("CIFS: Move trans2 processing to ops struct")
Cc [email protected] # cifs_check_trans2() is in smb1ops.c on kernels < 7.0
---
 fs/smb/client/smb1transport.c |   14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)
fs/smb/client/smb1transport.c +11 -3
@@ -430,10 +430,18 @@ bool
 cifs_check_trans2(struct mid_q_entry *mid, struct TCP_Server_Info *server,
 		  char *buf, int malformed)
 {
-	if (malformed)
-		return false;
-	if (check2ndT2(buf) <= 0)
+	if (malformed || check2ndT2(buf) <= 0) {
+		/* mid->multiRsp blocks the server buf detach in handle_mid();
+		 * returning false here would leak resp_buf and leave a dangling
+		 * server->smallbuf/bigbuf after the user thread frees resp_buf.
+		 */
+		if (mid->multiRsp) {
+			mid->multiEnd = true;
+			dequeue_mid(server, mid, true);
+			return true;
+		}
 		return false;
+	}
 	mid->multiRsp = true;
 	if (mid->resp_buf) {
 		/* merge response - fix up 1st*/
author maintainer
#237 lore raw

PATCH 237/713 smb: client: clear ce->tgthint in free_tgts()

In reply to Greg Kroah-Hartman

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

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

From: Fredric Cover <[email protected]>

commit b1b741cf8e7ce1b91d937e23decd3d3358748700 upstream.

When free_tgts() frees all structures in ce->tlist, ce->tgthint is left pointing to one of the freed cache_dfs_tgt structures.

If ce->tgthint is not reset before it is used later, it results in a use-after-free.

Set ce->tgthint to NULL in free_tgts() after the elements are freed to reflect that no elements remain.

Fixes 54be1f6c1c37 ("cifs: Add DFS cache routines")
Cc [email protected] # depends on: smb: client: harden DFS cache against invalid target hints
---
 fs/smb/client/dfs_cache.c |    2 ++
 1 file changed, 2 insertions(+)
fs/smb/client/dfs_cache.c +2 -0
@@ -122,6 +122,8 @@ static inline void free_tgts(struct cach
 		kfree(t->name);
 		kfree(t);
 	}
+
+	WRITE_ONCE(ce->tgthint, NULL);
 }
 
 static inline void flush_cache_ent(struct cache_entry *ce)
author maintainer
#238 lore raw

PATCH 238/713 ksmbd: only rebind the reopened files own oplock on durable reconnect

In reply to Greg Kroah-Hartman

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

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

From: Aldo Ariel Panzardo <[email protected]>

commit 3f220a0a62e6b9b391c9d1f0e6580b05173cc7f7 upstream.

ksmbd_reopen_durable_fd() walks the inode's m_op_list and rebinds every detached oplock to the reconnecting session:

	list_for_each_entry_rcu(op, &ci->m_op_list, op_entry,
				lockdep_is_held(&ci->m_lock)) {
		if (op->conn)
			continue;
		op->conn = ksmbd_conn_get(fp->conn);
		op->sess = work->sess;
	}

The only key is op->conn == NULL, which every detached durable handle on that inode matches, not just the one owned by fp. When two sessions hold durable handles on the same file and both disconnect, reconnecting one of them adopts the other session's oplock: op->sess is overwritten with the reconnecting session without taking a reference on it, while op->conn pins the connection.

The sibling teardown path, session_fd_check(), keys on the identity of the connection being torn down (op->conn == conn) rather than on shared state, and so does not have this problem.

Once the adopting session is destroyed, ksmbd_session_destroy() frees it while the foreign oplock still points at it. The reader in ksmbd_close_fd_app_instance_id() validates only opinfo->conn, which is still live thanks to the reference taken above, and then dereferences the stale session:

	if (!opinfo->conn) {
		up_read(&fp->f_ci->m_lock);
		goto out;
	}

	ft = &opinfo->sess->file_table;
	write_lock(&ft->lock);
  BUG: KASAN: slab-use-after-free in _raw_write_lock+0x74/0xd0
  Write of size 4 at addr ffff88810a970528 by task kworker/0:0/9
  Workqueue: ksmbd-io handle_ksmbd_work
  Call Trace:
   _raw_write_lock+0x74/0xd0
   ksmbd_close_fd_app_instance_id+0x183/0x410
   smb2_open+0x1346/0x4430
   handle_ksmbd_work+0x2bb/0x7b0

Reached from an authenticated session against a share with the default durable-handle and oplock configuration: two sessions open the same file with a durable-v2 handle and an RH lease under distinct AppInstanceIds, both log off, one reconnects with DH2C, and a later durable-v2 create carrying the other AppInstanceId walks into the freed session.

Constrain the loop to the oplock owned by the file being reopened.

Fixes f363a0fb134a ("ksmbd: fix app-instance durable supersede session UAF")
---
 fs/smb/server/vfs_cache.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/smb/server/vfs_cache.c +1 -1
@@ -1722,7 +1722,7 @@ int ksmbd_reopen_durable_fd(struct ksmbd
 	down_write(&ci->m_lock);
 	list_for_each_entry_rcu(op, &ci->m_op_list, op_entry,
 				lockdep_is_held(&ci->m_lock)) {
-		if (op->conn)
+		if (op->conn || op->o_fp != fp)
 			continue;
 		op->conn = ksmbd_conn_get(fp->conn);
 		op->sess = work->sess;
author maintainer
#239 lore raw

PATCH 239/713 smb: client: fix ALIGN() overflow in symlink_data() error context loop

In reply to Greg Kroah-Hartman

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

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

From: Frank Sorenson <[email protected]>

commit 62656b024efc21c3230eade1a847f25871c3d2bb upstream.

The check added by commit 7d9a7f1f96cd ("smb/client: fix possible infinite loop and oob read in symlink_data()") compared the post-ALIGN length against the remaining buffer, but ALIGN() itself can overflow:
for ErrorDataLength near UINT32_MAX (e.g. 0xFFFFFFF9), ALIGN(x, 8) wraps to 0, so the subsequent bounds check passes, and the loop advances by zero bytes leaving 'p' pointing into stale data.

Fix by checking the raw ErrorDataLength against the remaining space before applying ALIGN(), then checking again after. Since raw_len is bounded by the buffer, raw_len + 7 cannot overflow, so the second check is an exact post-alignment bounds guard.

Fixes 76894f3e2f71 ("cifs: improve symlink handling for smb2+")
---
 fs/smb/client/smb2file.c |    5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
fs/smb/client/smb2file.c +4 -1
@@ -61,7 +61,10 @@ static struct smb2_symlink_err_rsp *syml
 			cifs_dbg(FYI, "%s: skipping unhandled error context: 0x%x\n",
 				 __func__, le32_to_cpu(p->ErrorId));
 
-			len = ALIGN(le32_to_cpu(p->ErrorDataLength), 8);
+			len = le32_to_cpu(p->ErrorDataLength);
+			if (len > end - ((u8 *)p + sizeof(*p)))
+				return ERR_PTR(-EINVAL);
+			len = ALIGN(len, 8);
 			if (len > end - ((u8 *)p + sizeof(*p)))
 				return ERR_PTR(-EINVAL);
 
author maintainer
#240 lore raw

PATCH 240/713 smb: client: fix copy-paste error in WSL EA length accounting for $LXDEV

In reply to Greg Kroah-Hartman

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

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

From: Frank Sorenson <[email protected]>

commit 5d14030b46af1a958fd104b020fbb93631c98822 upstream.

The LXDEV block in cifs_query_path_info() uses SMB2_WSL_XATTR_MODE_SIZE
(4) instead of SMB2_WSL_XATTR_DEV_SIZE (8), undercounting eas_len by 4 bytes per $LXDEV EA.

eas_len is used only as a zero/non-zero presence flag so there is no current functional impact, but the value is incorrect and misleading.

Fixes 97db41604555 ("smb: client: parse uid, gid, mode and dev from WSL reparse points")
Cc Paulo Alcantara <[email protected]>
---
 fs/smb/client/smb1ops.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/smb/client/smb1ops.c +1 -1
@@ -717,7 +717,7 @@ static int cifs_query_path_info(const un
 			ea->ea_value_length = cpu_to_le16(SMB2_WSL_XATTR_DEV_SIZE);
 			memcpy(&ea->ea_data[0], SMB2_WSL_XATTR_DEV, SMB2_WSL_XATTR_NAME_LEN + 1);
 			data->wsl.eas_len += ALIGN(sizeof(*ea) + SMB2_WSL_XATTR_NAME_LEN + 1 +
-						   SMB2_WSL_XATTR_MODE_SIZE, 4);
+						   SMB2_WSL_XATTR_DEV_SIZE, 4);
 			rc = 0;
 		} else if (rc >= 0) {
 			/* It is an error if EA $LXDEV has wrong size. */
author maintainer
#241 lore raw

PATCH 241/713 smb: client: fix OOB read/write from unvalidated DataOffset in coalesce_t2()

In reply to Greg Kroah-Hartman

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

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

From: Frank Sorenson <[email protected]>

commit 6343c1da561962688f203362d80d6a3bfa39fa1b upstream.

coalesce_t2() computes data pointers directly from server-supplied DataOffset fields with no validation against buffer bounds:

  data_area_of_tgt = (char *)&pSMBt->hdr.Protocol +
                     get_unaligned_le16(&pSMBt->t2_rsp.DataOffset);
  data_area_of_src = (char *)&pSMBs->hdr.Protocol +
                     get_unaligned_le16(&pSMBs->t2_rsp.DataOffset);
  data_area_of_tgt += total_in_tgt;
  ...
  memcpy(data_area_of_tgt, data_area_of_src, total_in_src);

A small DataOffset can push a pointer below the actual byte area, overwriting header fields; a large one can push it past the buffer end, causing out-of-bounds heap reads (source) or writes (target). The BCC overflow guard does not prevent this: BCC reflects how much data is present, while DataOffset controls where in the buffer it starts.

The "validate target area" comment present since the function was first written in 2005 was a placeholder that was never implemented.

Add lower- and upper-bound checks for both data pointers before the memcpy, and before any target header fields are modified.

Fixes e4eb295d38b5 ("[PATCH] cifs: Handle multiple response transact2 part 1 of 2")
---
 fs/smb/client/smb1transport.c |   21 ++++++++++++++++++++-
 1 file changed, 20 insertions(+), 1 deletion(-)
fs/smb/client/smb1transport.c +20 -1
@@ -375,12 +375,31 @@ coalesce_t2(char *second_buf, struct smb
 	data_area_of_tgt = (char *)&pSMBt->hdr.Protocol +
 				get_unaligned_le16(&pSMBt->t2_rsp.DataOffset);
 
-	/* validate target area */
 	data_area_of_src = (char *)&pSMBs->hdr.Protocol +
 				get_unaligned_le16(&pSMBs->t2_rsp.DataOffset);
 
 	data_area_of_tgt += total_in_tgt;
 
+	/*
+	 * DataOffset fields are server-supplied and not validated against
+	 * buffer bounds; check both data pointers before mutating the
+	 * target header.
+	 */
+	if (data_area_of_tgt < (char *)target_hdr +
+				sizeof(struct smb_t2_rsp) + sizeof(__le16) ||
+	    data_area_of_tgt + total_in_src >
+	    (char *)target_hdr + CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
+		cifs_dbg(VFS, "%s: target data area out of bounds\n", __func__);
+		return -EPROTO;
+	}
+	if (data_area_of_src < second_buf +
+				sizeof(struct smb_t2_rsp) + sizeof(__le16) ||
+	    data_area_of_src + total_in_src >
+	    second_buf + smbCalcSize((struct smb_hdr *)second_buf)) {
+		cifs_dbg(VFS, "%s: secondary data area out of bounds\n", __func__);
+		return -EPROTO;
+	}
+
 	total_in_tgt += total_in_src;
 	/* is the result too big for the field? */
 	if (total_in_tgt > USHRT_MAX) {
author maintainer
#242 lore raw

PATCH 242/713 smb: client: fix use-before-check of ReparseDataLength in reparse_buf_ptr()

In reply to Greg Kroah-Hartman

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

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

From: Frank Sorenson <[email protected]>

commit 05f78e6cf34ea3a285053bd5999e08e8ac298bd5 upstream.

reparse_buf_ptr() reads buf->ReparseDataLength before checking that count covers the full fixed header:

    buf = (struct reparse_data_buffer *)((u8 *)io + off);
    len = sizeof(*buf);                          /* 8 bytes */
    rdlen = le16_to_cpu(buf->ReparseDataLength); /* offset 4, 2 bytes */

    if (count < len || count < rdlen + len)      /* check comes after */

struct reparse_data_buffer has ReparseDataLength at offset 4. If a server returns OutputCount < 6, the read at offset 4-5 reaches past the end of the received data. The off+count bounds against iov_len were already validated, but that does not protect against count being smaller than sizeof(*buf).

Split the check: verify count >= sizeof(*buf) before reading ReparseDataLength, then verify count covers the data region.

Fixes a158bb66b137 ("smb: client: optimise reparse point querying")
---
 fs/smb/client/smb2inode.c |    6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
fs/smb/client/smb2inode.c +4 -2
@@ -40,9 +40,11 @@ static struct reparse_data_buffer *repar
 
 	buf = (struct reparse_data_buffer *)((u8 *)io + off);
 	len = sizeof(*buf);
-	rdlen = le16_to_cpu(buf->ReparseDataLength);
+	if (count < len)
+		return ERR_PTR(smb_EIO2(smb_eio_trace_reparse_rdlen, count, 0));
 
-	if (count < len || count < rdlen + len)
+	rdlen = le16_to_cpu(buf->ReparseDataLength);
+	if (count < rdlen + len)
 		return ERR_PTR(smb_EIO2(smb_eio_trace_reparse_rdlen, count, rdlen));
 	return buf;
 }
author maintainer
#243 lore raw

PATCH 243/713 smb: client: harden DFS cache against invalid target hints

In reply to Greg Kroah-Hartman

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

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

From: Fredric Cover <[email protected]>

commit bf86c08123c6ab8c61cc0be1dad7540db93738ff upstream.

Currently, get_tgt_name() returns ERR_PTR(-ENOENT) when ce->tgthint is NULL, and dfs_cache_noreq_update_tgthint() assumes ce->tgthint is always valid.

In preparation for clearing ce->tgthint in free_tgts(), harden callers of get_tgt_name() against ERR_PTR results and harden dfs_cache_noreq_update_tgthint() against NULL pointer dereferences.

---
 fs/smb/client/dfs_cache.c |   31 ++++++++++++++++++++++++-------
 1 file changed, 24 insertions(+), 7 deletions(-)
fs/smb/client/dfs_cache.c +24 -7
@@ -871,13 +871,22 @@ int dfs_cache_find(const unsigned int xi
 		goto out_free_path;
 	}
 
-	if (ref)
-		rc = setup_referral(path, ce, ref, get_tgt_name(ce));
-	else
+	if (ref) {
+		char *target = get_tgt_name(ce);
+
+		if (IS_ERR(target)) {
+			rc = PTR_ERR(target);
+			goto out_unlock;
+		}
+		rc = setup_referral(path, ce, ref, target);
+	} else {
 		rc = 0;
+	}
+
 	if (!rc && tgt_list)
 		rc = get_targets(ce, tgt_list);
 
+out_unlock:
 	up_read(&htable_rw_lock);
 
 out_free_path:
@@ -917,10 +926,17 @@ int dfs_cache_noreq_find(const char *pat
 		goto out_unlock;
 	}
 
-	if (ref)
-		rc = setup_referral(path, ce, ref, get_tgt_name(ce));
-	else
+	if (ref) {
+		char *target = get_tgt_name(ce);
+
+		if (IS_ERR(target)) {
+			rc = PTR_ERR(target);
+			goto out_unlock;
+		}
+		rc = setup_referral(path, ce, ref, target);
+	} else {
 		rc = 0;
+	}
 	if (!rc && tgt_list)
 		rc = get_targets(ce, tgt_list);
 
@@ -961,7 +977,8 @@ void dfs_cache_noreq_update_tgthint(cons
 
 	t = READ_ONCE(ce->tgthint);
 
-	if (unlikely(!strcasecmp(it->it_name, t->name)))
+	/* Check 't' in case ce->tgthint was cleared by free_tgts() */
+	if (t && unlikely(!strcasecmp(it->it_name, t->name)))
 		goto out_unlock;
 
 	list_for_each_entry(t, &ce->tlist, list) {
author maintainer
#244 lore raw

PATCH 244/713 smb: client: reject a tree connect response whose byte count is too small

In reply to Greg Kroah-Hartman

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

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

From: Bryam Vargas <[email protected]>

commit 65deb18359341141d37dc86fc7853511be3c87a7 upstream.

CIFSTCon() bounds its strnlen() over the byte area with the server's ByteCount minus two, which for ByteCount 0 or 1 goes negative as an int and converts to a huge size_t. The later subtraction wraps the __u16 bytes_left, and that is what bounds cifs_strndup_from_utf16(): a bound of up to 65535 against a ~16 KB cifs_req_poolp object runs off the end of the slab object, and the bytes reach userspace through tcon->nativeFileSystem in /proc/fs/cifs/DebugData.

Reject a byte area too small for what the parser consumes. Two bytes is the least it can consume, and no conformant response carries fewer. The new trace point is the 129th smb_eio_trace entry, which __mode(byte) cannot represent, so the attribute goes with it.

Fixes cc20c031bb06 ("cifs: convert CIFSTCon to use new unicode helper functions")
---
 fs/smb/client/cifssmb.c |    6 ++++++
 fs/smb/client/trace.h   |    3 ++-
 2 files changed, 8 insertions(+), 1 deletion(-)
fs/smb/client/cifssmb.c +6 -0
@@ -615,6 +615,11 @@ CIFSTCon(const unsigned int xid, struct
 		tcon->tid = smb_buffer_response->Tid;
 		bcc_ptr = pByteArea(smb_buffer_response);
 		bytes_left = get_bcc(smb_buffer_response);
+		if (bytes_left < 2) {
+			rc = smb_EIO2(smb_eio_trace_tcon_bcc_too_small,
+				      bytes_left, 2);
+			goto out;
+		}
 		length = strnlen(bcc_ptr, bytes_left - 2);
 		if (smb_buffer->Flags2 & SMBFLG2_UNICODE)
 			is_unicode = true;
@@ -670,6 +675,7 @@ CIFSTCon(const unsigned int xid, struct
 			reset_cifs_unix_caps(xid, tcon, NULL, NULL);
 		}
 	}
+out:
 	cifs_buf_release(smb_buffer);
 	return rc;
 }
fs/smb/client/trace.h +2 -1
@@ -133,6 +133,7 @@
 	EM(smb_eio_trace_sym_slash,			"sym_slash") \
 	EM(smb_eio_trace_sym_target_len,		"sym_target_len") \
 	EM(smb_eio_trace_symlink_file_size,		"symlink_file_size") \
+	EM(smb_eio_trace_tcon_bcc_too_small,		"tcon_bcc_too_small") \
 	EM(smb_eio_trace_tdis_in_reconnect,		"tdis_in_reconnect") \
 	EM(smb_eio_trace_tx_chained_async,		"tx_chained_async") \
 	EM(smb_eio_trace_tx_compress_failed,		"tx_compress_failed") \
@@ -213,7 +214,7 @@
 #define EM(a, b) a,
 #define E_(a, b) a
 
-enum smb_eio_trace		{ smb_eio_traces } __mode(byte);
+enum smb_eio_trace		{ smb_eio_traces };
 enum smb3_rw_credits_trace	{ smb3_rw_credits_traces } __mode(byte);
 enum smb3_tcon_ref_trace	{ smb3_tcon_ref_traces } __mode(byte);
 
author maintainer
#245 lore raw

PATCH 245/713 smb: client: restore the data_offset bound in is_valid_oplock_break()

In reply to Greg Kroah-Hartman

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

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

From: Bryam Vargas <[email protected]>

commit ba22f575de9deeae4ae0859ca4315a7698226237 upstream.

Commit 83bfbd0bb902 ("cifs: Remove the RFC1002 header from smb_hdr") changed the quantity this bound is measured against. It used to be srv->total_read minus the 4-byte RFC1002 preamble that total_read then included, so it was the SMB message length. The same commit stopped counting the preamble, and the mechanical substitution to srv->total_read - srv->pdu_size left an expression that is identically
zero: standard_receive3() reads MID_HEADER_SIZE() bytes and then exactly pdu_length - MID_HEADER_SIZE() more, adding both to total_read.

len is therefore 0, the subtraction below it wraps, and no __u32 DataOffset can exceed the result, so the check from commit 097f5863b1a0 ("cifs: read overflow in is_valid_oplock_break()") no longer rejects anything. Use total_read, which is now the message length on its own.

Fixes 83bfbd0bb902 ("cifs: Remove the RFC1002 header from smb_hdr")
---
 fs/smb/client/smb1misc.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
fs/smb/client/smb1misc.c +2 -1
index ba56023010d8..cdfbbff24b72 100644
@@ -80,7 +80,8 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
 			(struct smb_com_transaction_change_notify_rsp *)buf;
 		struct file_notify_information *pnotify;
 		__u32 data_offset = 0;
-		size_t len = srv->total_read - srv->pdu_size;
+		/* total_read excludes the RFC1002 preamble */
+		size_t len = srv->total_read;
 
 		if (get_bcc(buf) > sizeof(struct file_notify_information)) {
 			data_offset = le32_to_cpu(pSMBr->DataOffset);
Signature · 1 line
2.55.0
author maintainer
#246 lore raw

PATCH 246/713 HID: apple: preserve keyboard backlight across T2 resume

In reply to Greg Kroah-Hartman

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

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

From: Andre Eikmeyer <[email protected]>

commit 2b5d1495bd101f3d9c13caf3923754f48ee7371a upstream.

The T2 virtual USB host controller re-enumerates the internal keyboard after system resume. The butterfly keyboard backlight currently uses LED_CORE_SUSPENDRESUME, so the LED core sends a blocking request to the old HID device while it is disappearing. That request fails with -ENODEV and the newly probed device starts with its backlight off.

To fix this, we cache the requested brightness when the old HID device is removed and restore it when the replacement is probed. We let re-enumeration handle restoration instead of issuing a request through the stale device.

Fixes 1f95a6cd5ad7 ("HID: apple: ensure the keyboard backlight is off if suspending")
---
 drivers/hid/hid-apple.c |   18 ++++++++++++++++--
 1 file changed, 16 insertions(+), 2 deletions(-)
drivers/hid/hid-apple.c +16 -2
@@ -91,6 +91,9 @@ struct apple_sc_backlight {
 	struct hid_device *hdev;
 };
 
+/* T2 VHCI re-enumerates the internal keyboard across system resume. */
+static int apple_backlight_resume_brightness = -1;
+
 struct apple_backlight_config_report {
 	u8 report_id;
 	u8 version;
@@ -825,6 +828,7 @@ static int apple_backlight_led_set(struc
 static int apple_backlight_init(struct hid_device *hdev)
 {
 	int ret;
+	int brightness;
 	struct apple_sc *asc = hid_get_drvdata(hdev);
 	struct apple_backlight_config_report *rep;
 
@@ -860,13 +864,20 @@ static int apple_backlight_init(struct h
 	asc->backlight->cdev.name = "apple::kbd_backlight";
 	asc->backlight->cdev.max_brightness = rep->backlight_on_max;
 	asc->backlight->cdev.brightness_set_blocking = apple_backlight_led_set;
-	asc->backlight->cdev.flags = LED_CORE_SUSPENDRESUME;
+	/* VHCI re-enumeration restores the cached brightness in the next probe. */
+
+	brightness = READ_ONCE(apple_backlight_resume_brightness);
+	if (brightness < 0)
+		brightness = LED_OFF;
+	else
+		brightness = min_t(int, brightness, rep->backlight_on_max);
 
-	ret = apple_backlight_set(hdev, 0, 0);
+	ret = apple_backlight_set(hdev, brightness, 0);
 	if (ret < 0) {
 		hid_err(hdev, "backlight set request failed: %d\n", ret);
 		goto cleanup_and_exit;
 	}
+	asc->backlight->cdev.brightness = brightness;
 
 	ret = devm_led_classdev_register(&hdev->dev, &asc->backlight->cdev);
 
@@ -999,6 +1010,9 @@ static void apple_remove(struct hid_devi
 
 	if (asc->quirks & APPLE_RDESC_BATTERY)
 		timer_delete_sync(&asc->battery_timer);
+	if (asc->backlight)
+		WRITE_ONCE(apple_backlight_resume_brightness,
+			   asc->backlight->cdev.brightness);
 
 	hid_hw_stop(hdev);
 }
author maintainer
#247 lore raw

PATCH 247/713 HID: corsair-void: Check size of status and firmware events before reading them

In reply to Greg Kroah-Hartman

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

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

From: Stuart Hayhurst <[email protected]>

commit 08d8814521885e67b1bdf6a3036ee264e3e58377 upstream.

Malformed status and firmware events could cause an out-of-bounds read since the size wasn't being checked. Check the size and warn on unexpected values to avoid this.

Fixes 6ea2a6fd3872 ("HID: corsair-void: Add Corsair Void headset family driver")
---
 drivers/hid/hid-corsair-void.c |   17 +++++++++++++++++
 1 file changed, 17 insertions(+)
drivers/hid/hid-corsair-void.c +17 -0
@@ -92,6 +92,9 @@
 #define CORSAIR_VOID_STATUS_REPORT_ID		0x64
 #define CORSAIR_VOID_FIRMWARE_REPORT_ID		0x66
 
+#define CORSAIR_VOID_STATUS_REPORT_SIZE		5
+#define CORSAIR_VOID_FIRMWARE_REPORT_SIZE	5
+
 #define CORSAIR_VOID_USB_SIDETONE_REQUEST	0x1
 #define CORSAIR_VOID_USB_SIDETONE_REQUEST_TYPE	0x21
 #define CORSAIR_VOID_USB_SIDETONE_VALUE		0x200
@@ -742,6 +745,13 @@ static int corsair_void_raw_event(struct
 
 	/* Description of packets are documented at the top of this file */
 	if (hid_report->id == CORSAIR_VOID_STATUS_REPORT_ID) {
+		if (size < CORSAIR_VOID_STATUS_REPORT_SIZE) {
+			hid_warn_ratelimited(hid_dev,
+			                     "unexpected status report of size %d",
+			                     size);
+			return 1;
+		}
+
 		drvdata->mic_up = FIELD_GET(CORSAIR_VOID_MIC_MASK, data[2]);
 		drvdata->connected = (data[3] == CORSAIR_VOID_WIRELESS_CONNECTED) ||
 				     drvdata->is_wired;
@@ -750,6 +760,13 @@ static int corsair_void_raw_event(struct
 					      FIELD_GET(CORSAIR_VOID_CAPACITY_MASK, data[2]),
 					      data[3], data[4]);
 	} else if (hid_report->id == CORSAIR_VOID_FIRMWARE_REPORT_ID) {
+		if (size < CORSAIR_VOID_FIRMWARE_REPORT_SIZE) {
+			hid_warn_ratelimited(hid_dev,
+			                     "unexpected firmware report of size %d",
+			                     size);
+			return 1;
+		}
+
 		drvdata->fw_receiver_major = data[1];
 		drvdata->fw_receiver_minor = data[2];
 		drvdata->fw_headset_major = data[3];
author maintainer
#248 lore raw

PATCH 248/713 HID: picolcd: clamp eeprom debugfs read to bytes actually received

In reply to Greg Kroah-Hartman

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

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

From: Ibrahim Hashimov <[email protected]>

commit e9c667395ac1f8024f623250b32bae4c7af9caa0 upstream.

picolcd_debug_eeprom_read() trusts resp->raw_data[2] -- a length byte supplied by the device in its REPORT_EE_DATA reply -- clamped only to the caller's read() count:

	ret = resp->raw_data[2];
	if (ret > s)
		ret = s;
	if (copy_to_user(u, resp->raw_data+3, ret))

It never checks resp->raw_size, the number of bytes picolcd_raw_event() actually copied into the 64-byte raw_data[] of the kmalloc'd struct picolcd_pending. A device (or a spoofed picoLCD) returning a length byte of 0xff, read with a count >= 255, makes copy_to_user() read past raw_data[] into adjacent slab memory and return it to userspace through the debugfs "eeprom" file:

	BUG: KASAN: slab-out-of-bounds in _copy_to_user
	Read of size 255 ... picolcd_debug_eeprom_read+0x214/0x2f0 [hid_picolcd]

The debug-dump path in the same file already validates the device length byte against the received size before trusting it; this read does not. The file is created S_IRUSR (root-only) and a crafted device is needed, so it is neither unprivileged- nor remotely-triggerable.

Clamp the copy length to resp->raw_size - 3 (the payload actually received, minus the 3-byte header), floored at 0 for short replies.

Fixes 9bbf2b98ba11 ("HID: add experimental access to PicoLCD device's EEPROM and FLASH")
Assisted-by AuditCode-AI:2026.07
---
 drivers/hid/hid-picolcd_debugfs.c |    9 +++++++++
 1 file changed, 9 insertions(+)
drivers/hid/hid-picolcd_debugfs.c +9 -0
@@ -99,6 +99,15 @@ static ssize_t picolcd_debug_eeprom_read
 		ret = resp->raw_data[2];
 		if (ret > s)
 			ret = s;
+		/*
+		 * raw_data[2] is a device-supplied length; also clamp it to
+		 * what picolcd_raw_event() actually stored (raw_size), or a
+		 * hostile device overruns the raw_data[] buffer.
+		 */
+		if (ret > resp->raw_size - 3)
+			ret = resp->raw_size - 3;
+		if (ret < 0)
+			ret = 0;
 		if (copy_to_user(u, resp->raw_data+3, ret))
 			ret = -EFAULT;
 		else
author maintainer
#249 lore raw

PATCH 249/713 HID: roccat: free buffered reports when destroying device

In reply to Greg Kroah-Hartman

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

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

From: Xu Rao <[email protected]>

commit bbff0ccbff360a5498075525005f6a913239a3d7 upstream.

roccat_report_event() duplicates each report with kmemdup() and stores the allocation in a circular-buffer slot. The allocation is released only when that slot is reused.

The device destruction paths free struct roccat_device without releasing reports still stored in cbuf[]. This makes those allocations unreachable and leaks up to ROCCAT_CBUF_SIZE report buffers per device.

Add a small destructor that frees every buffered report before freeing the device, and use it in both paths that can destroy a registered device.

Fixes 206f5f2fcb5f ("HID: roccat: propagate special events of roccat hardware to userspace")
---
 drivers/hid/hid-roccat.c |   13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)
drivers/hid/hid-roccat.c +11 -2
@@ -70,6 +70,15 @@ static struct roccat_device *devices[ROC
 /* protects modifications of devices array */
 static DEFINE_MUTEX(devices_lock);
 
+static void roccat_free_device(struct roccat_device *device)
+{
+	int i;
+
+	for (i = 0; i < ROCCAT_CBUF_SIZE; i++)
+		kfree(device->cbuf[i].value);
+	kfree(device);
+}
+
 static ssize_t roccat_read(struct file *file, char __user *buffer,
 		size_t count, loff_t *ppos)
 {
@@ -226,7 +235,7 @@ static int roccat_release(struct inode *
 			hid_hw_power(device->hid, PM_HINT_NORMAL);
 			hid_hw_close(device->hid);
 		} else {
-			kfree(device);
+			roccat_free_device(device);
 		}
 	}
 
@@ -374,7 +383,7 @@ void roccat_disconnect(int minor)
 		hid_hw_close(device->hid);
 		wake_up_interruptible(&device->wait);
 	} else {
-		kfree(device);
+		roccat_free_device(device);
 	}
 }
 EXPORT_SYMBOL_GPL(roccat_disconnect);
author maintainer
#250 lore raw

PATCH 250/713 HID: sensor: custom: Fix field sysfs group cleanup on failure

In reply to Greg Kroah-Hartman

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

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

From: Haoxiang Li <[email protected]>

commit 3789d0802ddb4b3be04062caf4bfadd23496e9a7 upstream.

hid_sensor_custom_add_attributes() creates one sysfs group for each custom sensor field. If sysfs_create_group() fails after some groups have already been created, the function returns the error without removing the previously created groups.

Add a local unwind path to remove the groups that were already created. With enable_sensor exposed only after the field attributes are ready, this path can free sensor_inst->fields without leaving enable_sensor able to access pointers into that array.

Fixes 4a7de0519df5 ("HID: sensor: Custom and Generic sensor support")
---
 drivers/hid/hid-sensor-custom.c |    9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)
drivers/hid/hid-sensor-custom.c +8 -1
@@ -609,7 +609,7 @@ static int hid_sensor_custom_add_attribu
 					 &sensor_inst->fields[i].
 					 hid_custom_attribute_group);
 		if (ret)
-			break;
+			goto err_remove_groups;
 
 		/* For power or report field store indexes */
 		if (sensor_inst->fields[i].attribute.attrib_id ==
@@ -621,6 +621,13 @@ static int hid_sensor_custom_add_attribu
 	}
 
 	return ret;
+
+err_remove_groups:
+	while (--i >= 0)
+		sysfs_remove_group(&sensor_inst->pdev->dev.kobj,
+				   &sensor_inst->fields[i].hid_custom_attribute_group);
+	kfree(sensor_inst->fields);
+	return ret;
 }
 
 static void hid_sensor_custom_remove_attributes(struct hid_sensor_custom *
author maintainer
#251 lore raw

PATCH 251/713 HID: sony: fix UAF of ghl_poke_timer / ghl_urb at driver unbind

In reply to Greg Kroah-Hartman

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

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

From: Doruk Tan Ozturk <[email protected]>

commit a26705bd2e2728833e7a538ce91e58a5eeff496a upstream.

For GHL (Guitar Hero Live) dongles, sony_probe() arms a periodic timer:
ghl_magic_poke() (the timer callback) submits sc->ghl_urb, and the URB completion ghl_magic_poke_cb() re-arms the timer with mod_timer().

sony_remove() drained the timer with timer_delete_sync() and then freed the URB with usb_free_urb():

	timer_delete_sync(&sc->ghl_poke_timer);
	usb_free_urb(sc->ghl_urb);

timer_delete_sync() does not block re-arming, and while the URB is in flight the timer is not pending, so the sync delete is a no-op. A URB completion that runs after the delete re-arms the timer, and usb_free_urb() only drops a reference -- it does not kill an in-flight URB. sc is allocated with devm_kzalloc() and freed once sony_remove() returns, so the re-armed ghl_poke_timer (embedded in sc) then fires on freed memory, a use-after-free from timer softirq. This is a disconnect/rmmod race.

Poison the URB first, then shut the timer down, before freeing the URB. usb_poison_urb() kills any in-flight URB and permanently rejects further submissions, so a poke timer that is still pending cannot re-submit the URB from ghl_magic_poke() in the window before timer_shutdown_sync() runs. usb_kill_urb() would not suffice: it only cancels the in-flight URB and leaves it submittable once it returns, so the pending timer could re-submit it and put a fresh URB in flight over the freed sc. timer_shutdown_sync() then drains any last callback and blocks re-arming. The probe error path is unaffected: it is only reached before the timer is armed.

Reproduced under KASAN on next-20260710 via dummy_hcd + raw-gadget emulation of the GHL PS4 dongle (VID 0x1430 / PID 0x07bb): hid-sony binds and arms the poke timer, the poke URB is held in flight, the driver is unbound (freeing sc), then the URB is released. The completion re-arms the timer on the freed sc, and the re-armed timer fires ~8 s later:

  BUG: KASAN: slab-use-after-free in ghl_magic_poke+0x98/0xb0
  Read of size 8 at addr ffff88810b02fd50 by task swapper/0/0
   ghl_magic_poke+0x98/0xb0
   call_timer_fn+0x35/0x2b0
   __run_timers+0x69c/0x9a0
   run_timer_softirq+0x173/0x2a0
  Allocated by task 169:  sony_probe
  Freed by task 338:      devres_release_group <- hid_device_remove (sony_remove)

Found by 0sec (https://0sec.ai) using automated source analysis.

Fixes cc894ac55360 ("HID: sony: support for ghlive ps3/wii u dongles")
Assisted-by 0sec:multi-model
---
 drivers/hid/hid-sony.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/hid/hid-sony.c +3 -1
@@ -2424,7 +2424,9 @@ static void sony_remove(struct hid_devic
 	struct sony_sc *sc = hid_get_drvdata(hdev);
 
 	if (sc->quirks & (GHL_GUITAR_PS3WIIU | GHL_GUITAR_PS4)) {
-		timer_delete_sync(&sc->ghl_poke_timer);
+		/* poison, not kill: a pending timer must not re-submit during teardown */
+		usb_poison_urb(sc->ghl_urb);
+		timer_shutdown_sync(&sc->ghl_poke_timer);
 		usb_free_urb(sc->ghl_urb);
 	}
 
author maintainer
#252 lore raw

PATCH 252/713 HID: universal-pidff: stop the device when force-feedback init fails

In reply to Greg Kroah-Hartman

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

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

From: Baul Lee <[email protected]>

commit ce08c5555cabcd444d8b77fa69a7cb68bb05f611 upstream.

universal_pidff_probe() starts the device with hid_hw_start() and then, if force-feedback initialisation fails, returns the error through a label that only does "return error". The device is left started.

The HID core does not unwind on the driver's behalf. __hid_device_probe() releases the devres group, closes the report and clears hdev->driver:

	if (ret) {
		devres_release_group(&hdev->dev, hdev->devres_group_id);
		hid_close_report(hdev);
		hdev->driver = NULL;
	}

The hidraw character device that hid_hw_start() registered through hid_connect() is allocated with kzalloc() and added with cdev_device_add(), so it is not devres-managed and survives that. With hdev->driver NULL, hid_device_remove() skips hid_hw_stop() as well, because it only unwinds while a driver is still attached. The registration therefore outlives the device on both paths.

Opening the surviving /dev/hidrawX writes into freed memory. KASAN reports a use-after-free write from hidraw_open() -> hid_hw_open() -> the transport's open callback, which takes a spinlock inside the freed object. A descriptor that carries a PID usage page and no input reports is enough:
hidraw claims the device so hid_hw_start() succeeds, while hid->inputs stays empty so force-feedback init fails. The other failure returns in hid_pidff_init_with_quirks() - no output reports, an allocation failure, pidff_init_fields(), pidff_check_autocenter(), an unusable effect count, input_ff_create() - all reach the same label.

Stop the device on that path. hid-dr.c and hid-emsff.c, which start the device with the same HID_CONNECT_DEFAULT & ~HID_CONNECT_FF mask, already do this. The two earlier gotos must keep returning without hid_hw_stop(), since neither has a started device, so give the path that fails after the start its own label.

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

Fixes f06bf8d94fff ("HID: Add hid-universal-pidff driver and supported device ids")
---
 drivers/hid/hid-universal-pidff.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/hid/hid-universal-pidff.c +3 -1
@@ -104,12 +104,14 @@ static int universal_pidff_probe(struct
 	error = init_function(hdev, id->driver_data);
 	if (error) {
 		hid_warn(hdev, "Error initialising force feedback\n");
-		goto err;
+		goto err_stop;
 	}
 
 	hid_info(hdev, "Universal pidff driver loaded successfully!");
 
 	return 0;
+err_stop:
+	hid_hw_stop(hdev);
 err:
 	return error;
 }
author maintainer
#253 lore raw

PATCH 253/713 HID: mcp2221: stop device IO before hid_hw_stop

In reply to Greg Kroah-Hartman

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

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

From: Jiangshan Yi <[email protected]>

commit dca151633c0fde90935311c60e7cfc064aa56134 upstream.

Quiesce device IO at the start of the devm cleanup callback mcp2221_hid_unregister() so that incoming HID reports cannot race with hardware teardown during probe failure or device removal, addressing a potential use-after-free.

Guard the call to hid_device_io_stop() with io_started. On normal removal hid_device_remove() has already cleared io_started before the devres group is released, so an unconditional call would otherwise hit the !io_started path and emit a spurious "io already stopped" warning on every removal. The guard preserves the probe-failure balancing, where io_started is still set after hid_device_io_start(), while staying silent on the normal removal path.

Fixes d4b50ac06ea6 ("HID: mcp2221: Allow IO to start during probe")
---
 drivers/hid/hid-mcp2221.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/hid/hid-mcp2221.c +2 -0
@@ -1049,6 +1049,8 @@ static void mcp2221_hid_unregister(void
 {
 	struct hid_device *hdev = ptr;
 
+	if (hdev->io_started)
+		hid_device_io_stop(hdev);
 	hid_hw_close(hdev);
 	hid_hw_stop(hdev);
 }
author maintainer
#254 lore raw

PATCH 254/713 HID: mcp2221: clear rxbuf after I2C/SMBus transfer completes

In reply to Greg Kroah-Hartman

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

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

From: Jiangshan Yi <[email protected]>

commit db2333f88729c8aae062cb171ed058725ff5c901 upstream.

mcp_i2c_smbus_read() stores the caller-supplied buffer pointer in mcp->rxbuf for the duration of a transfer but never clears it when the transfer finishes or times out. Once the caller frees or reuses the buffer, mcp->rxbuf becomes a dangling pointer. A delayed or spurious MCP2221_I2C_GET_DATA report can then drive mcp2221_raw_event() to memcpy device data into the freed memory, causing a write use-after-free.

Route all return paths through a single exit point that clears mcp->rxbuf and mcp->rxbuf_size, so that the existing !mcp->rxbuf guard in the raw_event handler can reject any report arriving after the transfer has ended.

Fixes 67a95c21463d ("HID: mcp2221: add usb to i2c-smbus host bridge")
---
 drivers/hid/hid-mcp2221.c |    8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/hid/hid-mcp2221.c +6 -2
@@ -343,7 +343,7 @@ static int mcp_i2c_smbus_read(struct mcp
 
 	ret = mcp_send_data_req_status(mcp, mcp->txbuf, 4);
 	if (ret)
-		return ret;
+		goto out;
 
 	mcp->rxbuf_idx = 0;
 
@@ -365,7 +365,7 @@ static int mcp_i2c_smbus_read(struct mcp
 			} else {
 				usleep_range(980, 1000);
 				mcp_cancel_last_cmd(mcp);
-				return ret;
+				goto out;
 			}
 		} else {
 			retries = 0;
@@ -375,6 +375,10 @@ static int mcp_i2c_smbus_read(struct mcp
 	usleep_range(980, 1000);
 	ret = mcp_chk_last_cmd_status_free_bus(mcp);
 
+out:
+	mcp->rxbuf = NULL;
+	mcp->rxbuf_size = 0;
+
 	return ret;
 }
 
author maintainer
#255 lore raw

PATCH 255/713 HID: mcp2221: validate report size in mcp2221_raw_event()

In reply to Greg Kroah-Hartman

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

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

From: Jiangshan Yi <[email protected]>

commit 2c9a6998c19503626c57a2267bf279e204113079 upstream.

mcp2221_raw_event() never validates the size of incoming HID reports. In the MCP2221_I2C_GET_DATA path it trusts the device-supplied data[3] as the copy length without checking that 4 + data[3] bytes actually exist in the received report. A malicious or misbehaving USB device can send a short report with a large data[3], causing the memcpy to read past the valid report data in the HID transfer buffer and leak uninitialized kernel memory back to userspace through the I2C/SMBus read path.

Add a minimum size check at entry and validate that the source range fits within the received report before the copy.

Fixes 67a95c21463d ("HID: mcp2221: add usb to i2c-smbus host bridge")
---
 drivers/hid/hid-mcp2221.c |    7 +++++++
 1 file changed, 7 insertions(+)
drivers/hid/hid-mcp2221.c +7 -0
@@ -865,6 +865,9 @@ static int mcp2221_raw_event(struct hid_
 	u8 *buf;
 	struct mcp2221 *mcp = hid_get_drvdata(hdev);
 
+	if (size < 4)
+		return 0;
+
 	switch (data[0]) {
 
 	case MCP2221_I2C_WR_DATA:
@@ -930,6 +933,10 @@ static int mcp2221_raw_event(struct hid_
 					mcp->status = -EINVAL;
 					break;
 				}
+				if (4 + data[3] > size) {
+					mcp->status = -EINVAL;
+					break;
+				}
 				buf = mcp->rxbuf;
 				memcpy(&buf[mcp->rxbuf_idx], &data[4], data[3]);
 				mcp->rxbuf_idx = mcp->rxbuf_idx + data[3];
author maintainer
#256 lore raw

PATCH 256/713 HID: intel-thc-hid: intel-quickspi: validate report size before copy

In reply to Greg Kroah-Hartman

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

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

From: HyeongJun An <[email protected]>

commit a59cf84441f9a17323c89452cec2bf16724c48a9 upstream.

write_cmd_to_txdma() builds an output report in qsdev->report_buf, a heap buffer allocated in quickspi_alloc_report_buf() to the device-descriptor derived max_report_len (a few hundred bytes for a touch controller). It copies the caller-supplied report into that buffer:

    memcpy(write_buf->content, report_buf, report_buf_len);

The HID core caps a report at HID_MAX_BUFFER_SIZE (16384) by default, and quickspi_hid_ll_driver does not set max_buffer_size, so the length reaches the driver unbounded. A hidraw SET_REPORT/SET_FEATURE ioctl carrying a report larger than max_report_len therefore overflows report_buf with attacker-controlled length and content.

Record the report_buf allocation size and reject reports that do not fit before copying, matching the equivalent guard in the intel-quicki2c sibling (quicki2c_init_write_buf()) and the hid-goodix-spi fix.

write_cmd_to_txdma() writes the output report header ahead of the content in the same buffer, so size the allocation to cover the header as well. That keeps the added bound from rejecting a maximum-sized report.

Fixes 9d8d51735a3a ("HID: intel-thc-hid: intel-quickspi: Add HIDSPI protocol implementation")
Assisted-by Claude:claude-opus-4-8
---
 drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c      |    9 ++++++++-
 drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h      |    1 +
 drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c |    3 +++
 3 files changed, 12 insertions(+), 1 deletion(-)
drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c +8 -1
@@ -555,7 +555,14 @@ static int quickspi_alloc_report_buf(str
 	max_report_len = max(le16_to_cpu(qsdev->dev_desc.max_output_len),
 			     le16_to_cpu(qsdev->dev_desc.max_input_len));
 
-	qsdev->report_buf = devm_kzalloc(qsdev->dev, max_report_len, GFP_KERNEL);
+	/*
+	 * write_cmd_to_txdma() writes the output report header ahead of the
+	 * content in this buffer, so it has to hold both.
+	 */
+	qsdev->report_buf_size = HIDSPI_OUTPUT_REPORT_SIZE(max_report_len);
+
+	qsdev->report_buf = devm_kzalloc(qsdev->dev, qsdev->report_buf_size,
+					 GFP_KERNEL);
 	if (!qsdev->report_buf)
 		return -ENOMEM;
 
drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h +1 -0
@@ -157,6 +157,7 @@ struct quickspi_device {
 	u8 *report_descriptor;
 	u8 *input_buf;
 	u8 *report_buf;
+	u32 report_buf_size;
 	u32 report_len;
 
 	wait_queue_head_t reset_ack_wq;
drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c +3 -0
@@ -30,6 +30,9 @@ static int write_cmd_to_txdma(struct qui
 
 	write_buf = (struct output_report *)qsdev->report_buf;
 
+	if (HIDSPI_OUTPUT_REPORT_SIZE(report_buf_len) > qsdev->report_buf_size)
+		return -EINVAL;
+
 	write_buf->output_hdr.report_type = report_type;
 	write_buf->output_hdr.content_len = cpu_to_le16(report_buf_len);
 	write_buf->output_hdr.content_id = report_id;
author maintainer
#257 lore raw

PATCH 257/713 HID: intel-thc-hid: intel-quickspi: bound GET_REPORT response to the caller buffer

In reply to Greg Kroah-Hartman

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

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

From: HyeongJun An <[email protected]>

commit 035ec4a71cb8020a927c123bbe75c2f88d614986 upstream.

quickspi_hid_raw_request() receives the caller's buffer length in len, but quickspi_get_report() never sees it and copies the whole device-supplied response into buf regardless:

    memcpy(buf, qsdev->report_buf, qsdev->report_len);

qsdev->report_len comes from the input report the touch controller returns, while buf is sized to whatever the caller asked hidraw for through HIDIOCGFEATURE or HIDIOCGINPUT. A response larger than that overflows buf with device-controlled content.

The intel-quicki2c sibling already passes the caller length down to quicki2c_get_report() and validates the response against it before the copy. Do the same here.

Fixes 4138f21115ae ("HID: intel-thc-hid: intel-quickspi: Complete THC QuickSPI driver")
Assisted-by Claude:claude-opus-5
---
 drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c      |    2 -
 drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c |   16 ++++++++---
 drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.h |    2 -
 3 files changed, 15 insertions(+), 5 deletions(-)
drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c +1 -1
@@ -61,7 +61,7 @@ static int quickspi_hid_raw_request(stru
 
 	switch (reqtype) {
 	case HID_REQ_GET_REPORT:
-		ret = quickspi_get_report(qsdev, rtype, reportnum, buf);
+		ret = quickspi_get_report(qsdev, rtype, reportnum, buf, len);
 		break;
 	case HID_REQ_SET_REPORT:
 		ret = quickspi_set_report(qsdev, rtype, reportnum, buf, len);
drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c +13 -3
@@ -345,10 +345,12 @@ int reset_tic(struct quickspi_device *qs
 }
 
 int quickspi_get_report(struct quickspi_device *qsdev,
-			u8 report_type, unsigned int report_id, void *buf)
+			u8 report_type, unsigned int report_id, void *buf,
+			u32 buf_len)
 {
 	int rep_type;
 	int ret;
+	u32 report_len;
 
 	if (report_type == HID_INPUT_REPORT) {
 		rep_type = GET_INPUT_REPORT;
@@ -375,9 +377,17 @@ int quickspi_get_report(struct quickspi_
 	}
 	qsdev->get_report_cmpl = false;
 
-	memcpy(buf, qsdev->report_buf, qsdev->report_len);
+	/* quickspi_handle_input_data() updates this from IRQ context. */
+	report_len = READ_ONCE(qsdev->report_len);
+	if (report_len > buf_len) {
+		dev_err_once(qsdev->dev, "Get report response too big, %u vs %u\n",
+			     report_len, buf_len);
+		return -EINVAL;
+	}
+
+	memcpy(buf, qsdev->report_buf, report_len);
 
-	return qsdev->report_len;
+	return report_len;
 }
 
 int quickspi_set_report(struct quickspi_device *qsdev,
drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.h +1 -1
@@ -12,7 +12,7 @@ struct quickspi_device;
 
 void quickspi_handle_input_data(struct quickspi_device *qsdev, u32 buf_len);
 int quickspi_get_report(struct quickspi_device *qsdev, u8 report_type,
-			unsigned int report_id, void *buf);
+			unsigned int report_id, void *buf, u32 buf_len);
 int quickspi_set_report(struct quickspi_device *qsdev, u8 report_type,
 			unsigned int report_id, void *buf, u32 buf_len);
 int quickspi_get_report_descriptor(struct quickspi_device *qsdev);
author maintainer
#258 lore raw

PATCH 258/713 HID: intel-thc-hid: intel-quicki2c: fix autosuspend cleanup during teardown

In reply to Greg Kroah-Hartman

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

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

From: Guangshuo Li <[email protected]>

commit 42a941e39432ef6766402ef68f1389dcfec4ee37 upstream.

quicki2c_probe() calls pm_runtime_use_autosuspend(), but quicki2c_remove() does not call the matching pm_runtime_dont_use_autosuspend() during teardown.

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

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

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

This issue was found by manual code inspection.

Fixes 5f420e8215c6 ("HID: intel-thc-hid: intel-quicki2c: Add PM implementation")
---
 drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c |    1 +
 1 file changed, 1 insertion(+)
drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c +1 -0
@@ -774,6 +774,7 @@ static void quicki2c_remove(struct pci_d
 	quicki2c_hid_remove(qcdev);
 	quicki2c_dma_deinit(qcdev);
 
+	pm_runtime_dont_use_autosuspend(qcdev->dev);
 	pm_runtime_get_noresume(qcdev->dev);
 
 	quicki2c_dev_deinit(qcdev);
author maintainer
#259 lore raw

PATCH 259/713 HID: intel-thc-hid: intel-quickspi: fix autosuspend cleanup during teardown

In reply to Greg Kroah-Hartman

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

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

From: Guangshuo Li <[email protected]>

commit 05dffa55fd6dbed4bf2421fae5accc2b857e668d upstream.

quickspi_probe() calls pm_runtime_use_autosuspend(), but quickspi_remove() does not call the matching pm_runtime_dont_use_autosuspend() during teardown.

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

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

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

This issue was found by manual code inspection.

Fixes 6912aaf3fd24 ("HID: intel-thc-hid: intel-quickspi: Add PM implementation")
---
 drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c |    1 +
 1 file changed, 1 insertion(+)
drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c +1 -0
@@ -720,6 +720,7 @@ static void quickspi_remove(struct pci_d
 	quickspi_hid_remove(qsdev);
 	quickspi_dma_deinit(qsdev);
 
+	pm_runtime_dont_use_autosuspend(qsdev->dev);
 	pm_runtime_get_noresume(qsdev->dev);
 
 	quickspi_dev_deinit(qsdev);
author maintainer
#260 lore raw

PATCH 260/713 eventfs: Initialize ei->children and ei->list in init_ei()

In reply to Greg Kroah-Hartman

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

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

From: Deepanshu Kartikey <[email protected]>

commit 1704aaaf5d22bc765c168402350d191e24e245bc upstream.

eventfs_create_dir() allocates the eventfs_inode and initializes it with init_ei(). But this does not initialize the eventfs_inode list_heads. If the eventfs_create_dir() fails due to memory pressure, it will call free_ei() before it initialized the lists, and that checks to make sure the eventfs_inode has no children. But because the list wasn't initialized, it will give a false warning.

Fix it by moving the list initialization into init_ei().

---
 fs/tracefs/event_inode.c |    7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)
fs/tracefs/event_inode.c +2 -5
@@ -438,6 +438,8 @@ static inline struct eventfs_inode *init
 	if (!ei->name)
 		return NULL;
 	kref_init(&ei->kref);
+	INIT_LIST_HEAD(&ei->children);
+	INIT_LIST_HEAD(&ei->list);
 	return ei;
 }
 
@@ -725,8 +727,6 @@ struct eventfs_inode *eventfs_create_dir
 	ei->entries = entries;
 	ei->nr_entries = size;
 	ei->data = data;
-	INIT_LIST_HEAD(&ei->children);
-	INIT_LIST_HEAD(&ei->list);
 
 	scoped_guard(mutex, &eventfs_mutex) {
 		if (!parent->is_freed)
@@ -798,9 +798,6 @@ struct eventfs_inode *eventfs_create_eve
 	ei->attr.uid = uid;
 	ei->attr.gid = gid;
 
-	INIT_LIST_HEAD(&ei->children);
-	INIT_LIST_HEAD(&ei->list);
-
 	ti = get_tracefs(inode);
 	ti->flags |= TRACEFS_EVENT_INODE;
 	ti->private = ei;
author maintainer
#261 lore raw

PATCH 261/713 fs/ntfs3: validate dirty page table on log replay

In reply to Greg Kroah-Hartman

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

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

From: Xiang Mei <[email protected]>

commit 006cb7713dec10368e699abc4367e5faa334c9a5 upstream.

Each DIR_PAGE_ENTRY ends in a page_lcns[] array whose length is the on-disk lcns_follow field. check_rstbl() validates the table bookkeeping but never checks that this array fits in the entry, so a crafted lcns_follow lets the v0->v1 conversion memmove and later replay passes run off the entry.

Add check_dp_table() to reject, right after check_rstbl(), any entry larger than its size claims via struct_size() (the same expression used to allocate these entries, so the check is overflow-safe by construction). All consumers can then trust lcns_follow as the real capacity. This covers every page_lcns[] access whose index is bounded by the entry itself (the conversion memmove, the HotFix store via find_dp(), and the self-bounded scan loops). Accesses whose index comes from the log record need a separate bound and are handled in a follow-up patch.

Fixes b46acd6a6a62 ("fs/ntfs3: Add NTFS journal")
Assisted-by Claude:claude-opus-4-8
---
 fs/ntfs3/fslog.c |   19 +++++++++++++++++++
 1 file changed, 19 insertions(+)
fs/ntfs3/fslog.c +19 -0
@@ -789,6 +789,20 @@ static bool check_rstbl(const struct RES
 	return true;
 }
 
+static bool check_dp_table(const struct RESTART_TABLE *dptbl)
+{
+	u32 rsize = le16_to_cpu(dptbl->size);
+	struct DIR_PAGE_ENTRY *dp = NULL;
+
+	while ((dp = enum_rstbl((struct RESTART_TABLE *)dptbl, dp))) {
+		if (struct_size(dp, page_lcns, le32_to_cpu(dp->lcns_follow)) >
+		    rsize)
+			return false;
+	}
+
+	return true;
+}
+
 /*
  * free_rsttbl_idx - Free a previously allocated index a Restart Table.
  */
@@ -4294,6 +4308,11 @@ check_dirty_page_table:
 		err = -EINVAL;
 		goto out;
 	}
+
+	if (!check_dp_table(rt)) {
+		err = -EINVAL;
+		goto out;
+	}
 
 	dptbl = kmemdup(rt, t32, GFP_NOFS);
 	if (!dptbl) {
author maintainer
#262 lore raw

PATCH 262/713 fs/ntfs3: fix info-leak on partial LZNT decompress in ni_read_frame()

In reply to Greg Kroah-Hartman

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

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

From: Samuel Page <[email protected]>

commit 35d1ea92c7d946e2ebdbe36cdb2c969c8704bebd upstream.

ni_read_frame() decompresses an LZNT $DATA frame into the vmapped target pages and then trusts decompress_lznt()'s return value:

  unc_size = decompress_lznt(frame_ondisk, ondisk_size, frame_mem,
                             frame_size);
  if ((ssize_t)unc_size < 0)        err = unc_size;
  else if (!unc_size || unc_size > frame_size)  err = -EINVAL;

decompress_lznt() stops as soon as the compressed stream is exhausted (e.g. a zero chunk header) and returns the number of bytes it actually wrote, which may be far less than frame_size. The bytes between unc_size and frame_size are never written. The only memset() that follows zeroes the region beyond i_valid; when the frame lies entirely within the file's valid size that memset() does not run, so the gap retains whatever was in the just-vmapped pages. All pages are then marked uptodate and returned to userspace, disclosing uninitialized (recently-freed) kernel page memory. A crafted compressed file whose stream decompresses to only a few bytes leaks the remainder of every frame on a plain read(2), which is enough to recover kernel pointers and defeat KASLR.

Zero the [unc_size, frame_size) tail immediately after a successful LZNT decompress so the remainder reads back as zero.

Fixes 4342306f0f0d ("fs/ntfs3: Add file operations and implementation")
Assisted-by Bynario AI
---
 fs/ntfs3/frecord.c |    9 +++++++++
 1 file changed, 9 insertions(+)
fs/ntfs3/frecord.c +9 -0
@@ -2443,6 +2443,15 @@ int ni_read_frame(struct ntfs_inode *ni,
 			err = unc_size;
 		else if (!unc_size || unc_size > frame_size)
 			err = -EINVAL;
+		else if (unc_size < frame_size) {
+			/*
+			 * Partial decompress: zero the [unc_size, frame_size)
+			 * tail.  decompress_lznt() leaves it untouched, so
+			 * without this the freshly vmapped pages would expose
+			 * uninitialized kernel memory to userspace.
+			 */
+			memset(frame_mem + unc_size, 0, frame_size - unc_size);
+		}
 	}
 	if (!err && valid_size < frame_vbo + frame_size) {
 		size_t ok = valid_size - frame_vbo;
author maintainer
#263 lore raw

PATCH 263/713 fs/ntfs3: bound page_lcns[] index by the log record

In reply to Greg Kroah-Hartman

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

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

From: Konstantin Komarov <[email protected]>

commit 6f7b9dbdc1b7520206abce0049bdd143eb536e75 upstream.

The copy_lcns loop and the redo shorten loop index page_lcns[] at j + i, where i runs up to the log record's lcns_follow. That count is checked only against the record's own length, not the target entry, so check_dp_table() (which validates the entry's lcns_follow) does not cover it: the copy_lcns entry may even be freshly allocated after that check, and find_dp() bounds j but not i. A crafted record thus overflows page_lcns[] of an otherwise valid entry.

Add dp_range_ok() and reject, before each loop, any record whose run does not fit the entry. These are the only two page_lcns[] accesses indexed by the record rather than the entry, so together with the entry validation every access is now bounded.

Fixes b46acd6a6a62 ("fs/ntfs3: Add NTFS journal")
Assisted-by Claude:claude-opus-4-8
[[email protected]: original patch contained changes to the problem already handled, applied partly]
---
 fs/ntfs3/fslog.c |   15 +++++++++++++++
 1 file changed, 15 insertions(+)
fs/ntfs3/fslog.c +15 -0
@@ -648,6 +648,14 @@ static inline void *enum_rstbl(struct RE
 }
 
 /*
+ * dp_range_ok - true if [j, j + count) fits in a page_lcns[cap] array.
+ */
+static inline bool dp_range_ok(size_t j, u32 count, u32 cap)
+{
+	return j < cap && count <= cap - j;
+}
+
+/*
  * find_dp - Search for a @vcn in Dirty Page Table.
  */
 static inline struct DIR_PAGE_ENTRY *find_dp(struct RESTART_TABLE *dptbl,
@@ -5106,6 +5114,13 @@ find_dirty_page:
 	/* Shorten length by any Lcns which were deleted. */
 	saved_len = dlen;
 
+	if (!dp_range_ok(le64_to_cpu(lrh->target_vcn) - le64_to_cpu(dp->vcn),
+			 le16_to_cpu(lrh->lcns_follow),
+			 le32_to_cpu(dp->lcns_follow))) {
+		err = -EINVAL;
+		goto out;
+	}
+
 	for (i = le16_to_cpu(lrh->lcns_follow); i; i--) {
 		size_t j;
 		u32 alen, voff;
author maintainer
#264 lore raw

PATCH 264/713 ntfs: bound the free-cluster bitmap scan to the volume

In reply to Greg Kroah-Hartman

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

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

From: Bryam Vargas <[email protected]>

commit 19cac7902a8ab748e15f98ddaafcf5f8882be21d upstream.

vol->lcn_empty_bits_per_page is sized from vol->nr_clusters at mount, but ntfs_cluster_alloc() bounds its scan of that array by the size of $Bitmap. Those are independent on-disk quantities and the mount-time check only rejects a $Bitmap that is too small, so an image whose $Bitmap covers more clusters than the volume has lets the scan index past the array. A run whose LCN lies in that gap takes the allocator straight there, since the caller passes the file's own last LCN as its locality hint. KASAN reports a slab out-of-bounds read when a file on such a volume is extended.

Clamp the scan to what that array covers, mirroring the max_index calculation the mount-time scan already uses, and reject a decoded LCN at or beyond nr_clusters in the mapping pairs decoder. Conforming volumes are unaffected.

Fixes 11ccc9107dc4 ("ntfs: update runlist handling and cluster allocator")
---
 fs/ntfs/lcnalloc.c | 7 ++++++-
 fs/ntfs/runlist.c  | 7 +++++++
 2 files changed, 13 insertions(+), 1 deletion(-)
fs/ntfs/lcnalloc.c +6 -1
index 835a041023a2..aa2e017a4384 100644
@@ -298,7 +298,12 @@ struct runlist_element *ntfs_cluster_alloc(struct ntfs_volume *vol, const s64 st
 	clusters = count;
 	rlpos = rlsize = 0;
 	mapping = lcnbmp_vi->i_mapping;
-	i_size = i_size_read(lcnbmp_vi);
+	/*
+	 * lcn_empty_bits_per_page is sized from nr_clusters, but $Bitmap can
+	 * cover more clusters than that; bound the scan by the array.
+	 */
+	i_size = min_t(s64, i_size_read(lcnbmp_vi),
+		       ((s64)vol->nr_clusters + 7) >> 3);
 	while (1) {
 		ntfs_debug("Start of outer while loop: done_zones 0x%x, search_zone %i, pass %i, zone_start 0x%llx, zone_end 0x%llx, bmp_initial_pos 0x%llx, bmp_pos 0x%llx, rlpos %i, rlsize %i.",
 				done_zones, search_zone, pass,
fs/ntfs/runlist.c +7 -0
index 8e0fd400e7f7..17eb275a21ff 100644
@@ -884,6 +884,13 @@ struct runlist_element *ntfs_mapping_pairs_decompress(const struct ntfs_volume *
 					ntfs_error(vol->sb, "lcn == -1");
 			}
 #endif
+			/* Check lcn is within the volume. */
+			if (unlikely(lcn >= (s64)vol->nr_clusters)) {
+				ntfs_error(vol->sb,
+						"LCN >= nr_clusters in mapping pairs array.");
+				goto err_out;
+			}
+
 			/* Check lcn is not below -1. */
 			if (unlikely(lcn < -1)) {
 				ntfs_error(vol->sb, "Invalid s64 < -1 in mapping pairs array.");
Signature · 1 line
2.55.0
author maintainer
#265 lore raw

PATCH 265/713 ntfs: reject invalid empty mapping pairs

In reply to Greg Kroah-Hartman

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

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

From: Hyunchul Lee <[email protected]>

commit 766062a82e1ce4087c7dc077224144dfd34b3661 upstream.

Reject an attribute with empty mapping pairs if it has inconsistent highest VCN and size.

---
 fs/ntfs/runlist.c |   19 +++++++++++++++++++
 1 file changed, 19 insertions(+)
fs/ntfs/runlist.c +19 -0
@@ -772,6 +772,25 @@ struct runlist_element *ntfs_mapping_pai
 		return ERR_PTR(-EIO);
 	}
 
+	/*
+	 * An empty mapping-pairs array is valid only for a zero-length
+	 * attribute.
+	 */
+	if (!*buf &&
+	    (vcn ||
+	     le64_to_cpu(attr->data.non_resident.highest_vcn) !=
+		     (u64)(vcn - 1) ||
+	     le64_to_cpu(attr->data.non_resident.allocated_size) ||
+	     le64_to_cpu(attr->data.non_resident.data_size) ||
+	     le64_to_cpu(attr->data.non_resident.initialized_size))) {
+		ntfs_error(vol->sb, "Invalid empty mapping pairs array.");
+		return ERR_PTR(-EIO);
+	}
+	if (!vcn && !*buf && old_runlist && old_runlist->rl) {
+		*new_rl_count = old_runlist->count;
+		return old_runlist->rl;
+	}
+
 	/* Current position in runlist array. */
 	rlpos = 0;
 	/* Allocate first page and set current runlist size to one page. */
author maintainer
#266 lore raw

PATCH 266/713 ntfs: reject invalid MFT LCNs from boot sector

In reply to Greg Kroah-Hartman

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

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

From: Hyunchul Lee <[email protected]>

commit cc9d09fef78410bcd37ac05168cbd5f6dd75d3d2 upstream.

The NTFS boot sector stores the MFT and MFTMirr locations as unsigned 64-bit LCNs, but parse_ntfs_boot_sector() decoded them into an s64.

A crafted high-bit value could therefore become negative and pass the existing upper-bound check. The invalid value then propagated into the MFT zone allocator and could result in an out-of-bounds access to lcn_empty_bits_per_page.

---
 fs/ntfs/super.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)
fs/ntfs/super.c +6 -6
index 8abe7bee4c0d..cd8fa2c13370 100644
@@ -645,7 +645,7 @@ static bool parse_ntfs_boot_sector(struct ntfs_volume *vol,
 {
 	unsigned int sectors_per_cluster, sectors_per_cluster_bits, nr_hidden_sects;
 	int clusters_per_mft_record, clusters_per_index_record;
-	s64 ll;
+	u64 ll;
 
 	vol->sector_size = le16_to_cpu(b->bpb.bytes_per_sector);
 	vol->sector_size_bits = ffs(vol->sector_size) - 1;
@@ -755,23 +755,23 @@ static bool parse_ntfs_boot_sector(struct ntfs_volume *vol,
 	 * the same as it is much faster on 32-bit CPUs.
 	 */
 	ll = le64_to_cpu(b->number_of_sectors) >> sectors_per_cluster_bits;
-	if ((u64)ll >= 1ULL << 32) {
+	if (ll >= 1ULL << 32) {
 		ntfs_error(vol->sb, "Cannot handle 64-bit clusters.");
 		return false;
 	}
 	vol->nr_clusters = ll;
 	ntfs_debug("vol->nr_clusters = 0x%llx", vol->nr_clusters);
 	ll = le64_to_cpu(b->mft_lcn);
-	if (ll >= vol->nr_clusters) {
-		ntfs_error(vol->sb, "MFT LCN (%lli, 0x%llx) is beyond end of volume.  Weird.",
+	if (ll >= (u64)vol->nr_clusters) {
+		ntfs_error(vol->sb, "MFT LCN (%llu, 0x%llx) is beyond end of volume.  Weird.",
 				ll, ll);
 		return false;
 	}
 	vol->mft_lcn = ll;
 	ntfs_debug("vol->mft_lcn = 0x%llx", vol->mft_lcn);
 	ll = le64_to_cpu(b->mftmirr_lcn);
-	if (ll >= vol->nr_clusters) {
-		ntfs_error(vol->sb, "MFTMirr LCN (%lli, 0x%llx) is beyond end of volume.  Weird.",
+	if (ll >= (u64)vol->nr_clusters) {
+		ntfs_error(vol->sb, "MFTMirr LCN (%llu, 0x%llx) is beyond end of volume.  Weird.",
 				ll, ll);
 		return false;
 	}
Signature · 1 line
2.55.0
author maintainer
#267 lore raw

PATCH 267/713 ntfs: validate non-resident attribute offsets

In reply to Greg Kroah-Hartman

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

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

From: Hongling Zeng <[email protected]>

commit a83e82b0ec3ae523028e24813f23f18b43e8fc1c upstream.

ntfs_attr_update_meta() shifts the attribute name when converting between non-sparse and sparse attributes. Converting to sparse also adds the compressed_size field before the name and mapping pairs, requiring eight additional bytes in the attribute record.

However, the validator does not check that name_offset is within safe boundaries for these operations or that the additional space is available. A malicious MFT record could set name_offset such that:

1. The name is positioned at the very end of a non-sparse attribute. Converting to sparse would shift the name forward by 8 bytes, writing beyond the attribute boundary.

2. The name overlaps with the mapping pairs, causing corruption during conversion.

Add validation to ensure:
- For named attributes, name_offset is within valid bounds
- Name does not extend beyond the attribute or overlap with mapping pairs
- For non-sparse, non-compressed attributes, eight bytes are available after mapping_pairs_offset for the compressed_size field

The space check also covers unnamed attributes, for which name_offset = 0 is valid and no name range needs to be checked.

Fixes 7e2a1c554bc4 ("ntfs: Fix min_len for compressed/sparse attributes in ntfs_non_resident_attr_value_is_valid()")
---
 fs/ntfs/attrib.c |   24 +++++++++++++++++++++++-
 1 file changed, 23 insertions(+), 1 deletion(-)
fs/ntfs/attrib.c +23 -1
@@ -693,6 +693,8 @@ static bool ntfs_non_resident_attr_value
 	u32 attr_len;
 	u32 min_len;
 	u16 mp_offset;
+	u16 name_offset;
+	u32 name_end;
 
 	attr_len = le32_to_cpu(a->length);
 	min_len = offsetof(struct attr_record, data.non_resident.initialized_size) +
@@ -701,7 +703,27 @@ static bool ntfs_non_resident_attr_value
 		return false;
 
 	mp_offset = le16_to_cpu(a->data.non_resident.mapping_pairs_offset);
-	return mp_offset >= min_len && mp_offset <= attr_len;
+	if (mp_offset < min_len || mp_offset > attr_len)
+		return false;
+
+	if (a->name_length) {
+		name_offset = le16_to_cpu(a->name_offset);
+
+		if (name_offset < min_len || name_offset >= attr_len)
+			return false;
+
+		name_end = name_offset + a->name_length * sizeof(__le16);
+		if (name_end > attr_len || name_end > mp_offset)
+			return false;
+	}
+
+	/* Ensure there's room for the compressed_size field if needed. */
+	if (!(a->flags & (ATTR_IS_SPARSE | ATTR_COMPRESSION_MASK)) &&
+	    attr_len - mp_offset <
+			sizeof(a->data.non_resident.compressed_size))
+		return false;
+
+	return true;
 }
 
 static bool ntfs_attr_value_is_valid(struct ntfs_volume *vol,
author maintainer
#268 lore raw

PATCH 268/713 ntfs: verify run length exceeding volume boundary

In reply to Greg Kroah-Hartman

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

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

From: Hongling Zeng <[email protected]>

commit fea9e4488f384c0ef1c0e3d96b565127c1b98447 upstream.

The mapping pairs decoder validates that the starting LCN is within the volume but does not check if the run extends beyond the volume boundary.

A malformed NTFS image with a crafted mapping pairs array could cause the kernel to access memory beyond the volume boundary, potentially leading to memory corruption and privilege escalation.

Add validation to ensure lcn + length stays within nr_clusters.

Fixes b4be3a47f8ba4 ("ntfs: bound the free-cluster bitmap scan to the volume")
---
 fs/ntfs/runlist.c |   22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)
fs/ntfs/runlist.c +22 -0
@@ -916,6 +916,28 @@ struct runlist_element *ntfs_mapping_pai
 				goto err_out;
 			}
 
+			if (lcn >= 0) {
+				s64 run_end;
+
+				/*
+				 * Ensure that the run stays within the volume.
+				 * A valid starting LCN is not sufficient because
+				 * the run length comes from disk.
+				 */
+				if (unlikely(check_add_overflow(lcn,
+						rl[rlpos].length,
+						&run_end))) {
+					ntfs_error(vol->sb,
+							"Run length overflow in mapping pairs array.");
+					goto err_out;
+				}
+				if (unlikely(run_end > (s64)vol->nr_clusters)) {
+					ntfs_error(vol->sb,
+							"Run extends beyond volume boundary.");
+					goto err_out;
+				}
+			}
+
 			/* chkdsk accepts zero-sized runs only for holes */
 			if ((lcn != -1) && !rl[rlpos].length) {
 				ntfs_error(vol->sb,
author maintainer
#269 lore raw

PATCH 269/713 eCryptfs: bound the packet-length peek to the user buffer

In reply to Greg Kroah-Hartman

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

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

From: Pengpeng Hou <[email protected]>

commit 95540462e630edbc8504e9537d16453d6942d143 upstream.

ecryptfs_miscdev_write() accepts the minimum one-byte packet-length encoding, but always copies the maximum two-byte encoding from userspace before parsing it. A six-byte message therefore reads one byte beyond the submitted user buffer.

Zero-initialize the peek buffer and copy only the packet-length bytes present. The existing exact packet-size check still rejects truncated two-byte encodings after the parser determines their encoded length.

Fixes 8bf2debd5f7b ("eCryptfs: introduce device handle for userspace daemon communications")
---
 fs/ecryptfs/miscdev.c |    5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
fs/ecryptfs/miscdev.c +3 -2
@@ -360,7 +360,7 @@ ecryptfs_miscdev_write(struct file *file
 	u32 seq;
 	size_t packet_size, packet_size_length;
 	char *data;
-	unsigned char packet_size_peek[ECRYPTFS_MAX_PKT_LEN_SIZE];
+	unsigned char packet_size_peek[ECRYPTFS_MAX_PKT_LEN_SIZE] = { };
 	ssize_t rc;
 
 	if (count == 0) {
@@ -376,7 +376,8 @@ ecryptfs_miscdev_write(struct file *file
 	}
 
 	if (copy_from_user(packet_size_peek, &buf[PKT_LEN_OFFSET],
-			   sizeof(packet_size_peek))) {
+			   min_t(size_t, count - PKT_LEN_OFFSET,
+				 sizeof(packet_size_peek)))) {
 		printk(KERN_WARNING "%s: Error while inspecting packet size\n",
 		       __func__);
 		return -EFAULT;
author maintainer
#270 lore raw

PATCH 270/713 ecryptfs: fix tag 11 packet exact-fit size check

In reply to Greg Kroah-Hartman

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

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

From: Yichong Chen <[email protected]>

commit 8b2ec0f56f55477f547d332526c9ae2a8fabc0a5 upstream.

parse_tag_11_packet() rejects a packet when the already-consumed tag and length bytes plus the packet body exceed the caller supplied maximum packet size. The check currently adds one extra byte, even though *packet_size already includes the tag byte before the length is parsed.

Remove the extra byte so a tag 11 packet that exactly fits the available buffer is accepted while oversized packets are still rejected.

Fixes 237fead61998 ("[PATCH] ecryptfs: fs/Makefile and fs/Kconfig")
---
 fs/ecryptfs/keystore.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/ecryptfs/keystore.c +1 -1
@@ -1537,7 +1537,7 @@ parse_tag_11_packet(unsigned char *data,
 	}
 	(*packet_size) += length_size;
 	(*tag_11_contents_size) = (body_size - 14);
-	if (unlikely((*packet_size) + body_size + 1 > max_packet_size)) {
+	if (unlikely((*packet_size) + body_size > max_packet_size)) {
 		printk(KERN_ERR "Packet size exceeds max\n");
 		rc = -EINVAL;
 		goto out;
author maintainer
#271 lore raw

PATCH 271/713 ecryptfs: hold msg ctx list lock when cleaning daemon queue

In reply to Greg Kroah-Hartman

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

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

From: Yichong Chen <[email protected]>

commit 779972513c2fa8c7938e54976f686091dafff22f upstream.

ecryptfs_exorcise_daemon() drops queued messages from a dying daemon without holding ecryptfs_msg_ctx_lists_mux, but ecryptfs_msg_ctx_alloc_to_free() requires that lock.

Take the list lock while moving the queued contexts back to the free list to avoid racing with other global msg ctx list users.

Fixes f66e883eb618 ("eCryptfs: integrate eCryptfs device handle into the module.")
---
 fs/ecryptfs/messaging.c |    2 ++
 1 file changed, 2 insertions(+)
fs/ecryptfs/messaging.c +2 -0
@@ -166,6 +166,7 @@ int ecryptfs_exorcise_daemon(struct ecry
 		mutex_unlock(&daemon->mux);
 		goto out;
 	}
+	mutex_lock(&ecryptfs_msg_ctx_lists_mux);
 	list_for_each_entry_safe(msg_ctx, msg_ctx_tmp,
 				 &daemon->msg_ctx_out_queue, daemon_out_list) {
 		list_del(&msg_ctx->daemon_out_list);
@@ -174,6 +175,7 @@ int ecryptfs_exorcise_daemon(struct ecry
 		       "the out queue of a dying daemon\n", __func__);
 		ecryptfs_msg_ctx_alloc_to_free(msg_ctx);
 	}
+	mutex_unlock(&ecryptfs_msg_ctx_lists_mux);
 	hlist_del(&daemon->euid_chain);
 	mutex_unlock(&daemon->mux);
 	kfree_sensitive(daemon);
author maintainer
#272 lore raw

PATCH 272/713 ecryptfs: pass packet set buffer size to parser

In reply to Greg Kroah-Hartman

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

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

From: Yichong Chen <[email protected]>

commit 2602b79c5b3e2f6fce12e38a670f8e3fda4e46a2 upstream.

ecryptfs_parse_packet_set() receives a pointer into the file header, but it calculates the remaining packet buffer size from PAGE_SIZE - 8. For version 1 headers the packet set starts later in the header, so this can overstate the available buffer.

Pass the actual packet set buffer length from the caller and calculate per-packet limits from the remaining bytes in that buffer. Recompute the remaining length after consuming a tag 3 packet before parsing the following tag 11 packet.

Fixes 237fead61998 ("[PATCH] ecryptfs: fs/Makefile and fs/Kconfig")
---
 fs/ecryptfs/crypto.c          |    2 +-
 fs/ecryptfs/ecryptfs_kernel.h |    3 ++-
 fs/ecryptfs/keystore.c        |   23 ++++++++++++++++++++---
 3 files changed, 23 insertions(+), 5 deletions(-)
fs/ecryptfs/crypto.c +1 -1
@@ -1197,7 +1197,7 @@ static int ecryptfs_read_headers_virt(ch
 	} else
 		set_default_header_data(crypt_stat);
 	rc = ecryptfs_parse_packet_set(crypt_stat, (page_virt + offset),
-				       ecryptfs_dentry);
+				       PAGE_SIZE - offset, ecryptfs_dentry);
 out:
 	return rc;
 }
fs/ecryptfs/ecryptfs_kernel.h +2 -1
@@ -580,7 +580,8 @@ int ecryptfs_generate_key_packet_set(cha
 				     size_t *len, size_t max);
 int
 ecryptfs_parse_packet_set(struct ecryptfs_crypt_stat *crypt_stat,
-			  unsigned char *src, struct dentry *ecryptfs_dentry);
+			  unsigned char *src, size_t src_size,
+			  struct dentry *ecryptfs_dentry);
 int ecryptfs_truncate(struct dentry *dentry, loff_t new_length);
 ssize_t
 ecryptfs_getxattr_lower(struct dentry *lower_dentry, struct inode *lower_inode,
fs/ecryptfs/keystore.c +20 -3
@@ -1704,6 +1704,7 @@ out:
  * ecryptfs_parse_packet_set
  * @crypt_stat: The cryptographic context
  * @src: Virtual address of region of memory containing the packets
+ * @src_size: Size of the packet set buffer
  * @ecryptfs_dentry: The eCryptfs dentry associated with the packet set
  *
  * Get crypt_stat to have the file's session key if the requisite key
@@ -1714,7 +1715,7 @@ out:
  * conditions.
  */
 int ecryptfs_parse_packet_set(struct ecryptfs_crypt_stat *crypt_stat,
-			      unsigned char *src,
+			      unsigned char *src, size_t src_size,
 			      struct dentry *ecryptfs_dentry)
 {
 	size_t i = 0;
@@ -1736,7 +1737,11 @@ int ecryptfs_parse_packet_set(struct ecr
 	 * added the our &auth_tok_list */
 	next_packet_is_auth_tok_packet = 1;
 	while (next_packet_is_auth_tok_packet) {
-		size_t max_packet_size = ((PAGE_SIZE - 8) - i);
+		size_t max_packet_size;
+
+		if (i >= src_size)
+			break;
+		max_packet_size = src_size - i;
 
 		switch (src[i]) {
 		case ECRYPTFS_TAG_3_PACKET_TYPE:
@@ -1751,12 +1756,16 @@ int ecryptfs_parse_packet_set(struct ecr
 				goto out_wipe_list;
 			}
 			i += packet_size;
+			if (i > src_size) {
+				rc = -EIO;
+				goto out_wipe_list;
+			}
 			rc = parse_tag_11_packet((unsigned char *)&src[i],
 						 sig_tmp_space,
 						 ECRYPTFS_SIG_SIZE,
 						 &tag_11_contents_size,
 						 &tag_11_packet_size,
-						 max_packet_size);
+						 src_size - i);
 			if (rc) {
 				ecryptfs_printk(KERN_ERR, "No valid "
 						"(ecryptfs-specific) literal "
@@ -1768,6 +1777,10 @@ int ecryptfs_parse_packet_set(struct ecr
 				goto out_wipe_list;
 			}
 			i += tag_11_packet_size;
+			if (i > src_size) {
+				rc = -EIO;
+				goto out_wipe_list;
+			}
 			if (ECRYPTFS_SIG_SIZE != tag_11_contents_size) {
 				ecryptfs_printk(KERN_ERR, "Expected "
 						"signature of size [%d]; "
@@ -1793,6 +1806,10 @@ int ecryptfs_parse_packet_set(struct ecr
 				goto out_wipe_list;
 			}
 			i += packet_size;
+			if (i > src_size) {
+				rc = -EIO;
+				goto out_wipe_list;
+			}
 			crypt_stat->flags |= ECRYPTFS_ENCRYPTED;
 			break;
 		case ECRYPTFS_TAG_11_PACKET_TYPE:
author maintainer
#273 lore raw

PATCH 273/713 ecryptfs: reject oversized encrypted_key_size in parse_tag_3_packet

In reply to Greg Kroah-Hartman

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

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

From: HanQuan <[email protected]>

commit 5babe9c177c364521e3e682b949c5a8c47f4a441 upstream.

parse_tag_3_packet() set encrypted_key_size from the Tag 3 packet body without bounding it against ECRYPTFS_MAX_KEY_BYTES (64). When encrypted_key_size > 64, decrypt_passphrase_encrypted_session_key() sets decrypted_key_size = encrypted_key_size and performs two out-of-bounds writes:

1. crypto_skcipher_decrypt() writes encrypted_key_size bytes into decrypted_key[64] via scatterlist, overflowing into the parent ecryptfs_auth_tok struct.
2. memcpy(crypt_stat->key, decrypted_key, decrypted_key_size) writes into crypt_stat->key[64], corrupting root_iv, keysig_list, and mutexes in ecryptfs_crypt_stat.

Only AES-192 (cipher code 0x08) enables this because it sets crypt_stat->key_size = 24 independently of encrypted_key_size, allowing crypto_skcipher_setkey() to succeed while encrypted_key_size exceeds ECRYPTFS_MAX_KEY_BYTES.

The PKI decryption path (parse_tag_65_packet) already validates decrypted_key_size <= ECRYPTFS_MAX_KEY_BYTES; the passphrase path omits this check.

Bound encrypted_key_size against ECRYPTFS_MAX_KEY_BYTES (64) rather than ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES (512). The 64-byte limit also protects the 512-byte encrypted_key[] buffer, so the former 512-byte check is removed as redundant.

Fixes 237fead61998 ("[PATCH] ecryptfs: fs/Makefile and fs/Kconfig")

[tyhicks: Adjust the code comment to refer to macros representing the
buffer sizes rather than mentioning the buffer size values since they may change in the future]

---
 fs/ecryptfs/keystore.c |   14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)
fs/ecryptfs/keystore.c +12 -2
@@ -1384,10 +1384,20 @@ parse_tag_3_packet(struct ecryptfs_crypt
 	}
 	(*new_auth_tok)->session_key.encrypted_key_size =
 		(body_size - (ECRYPTFS_SALT_SIZE + 5));
+	/*
+	 * Although encrypted_key_size is copied into the
+	 * encrypted_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES] buffer here,
+	 * it later bounds operations on a smaller buffer:
+	 * decrypt_passphrase_encrypted_session_key() sets decrypted_key_size =
+	 * encrypted_key_size and decrypts into
+	 * decrypted_key[ECRYPTFS_MAX_KEY_BYTES], then memcpy's into
+	 * crypt_stat->key[ECRYPTFS_MAX_KEY_BYTES]. Limit to
+	 * ECRYPTFS_MAX_KEY_BYTES to protect those smaller buffers.
+	 */
 	if ((*new_auth_tok)->session_key.encrypted_key_size
-	    > ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES) {
+	    > ECRYPTFS_MAX_KEY_BYTES) {
 		printk(KERN_WARNING "Tag 3 packet contains key larger "
-		       "than ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES\n");
+		       "than ECRYPTFS_MAX_KEY_BYTES\n");
 		rc = -EINVAL;
 		goto out_free;
 	}
author maintainer
#274 lore raw

PATCH 274/713 ecryptfs: reject too-small tag 70 packets

In reply to Greg Kroah-Hartman

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

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

From: Yichong Chen <[email protected]>

commit e97bbe1b2bd82ec2ae37ad2e4965b4d3e78bbf7f upstream.

ecryptfs_parse_tag_70_packet() subtracts fixed metadata fields from the parsed packet body size to derive the encrypted filename size. A malformed packet with a body smaller than those fixed fields can underflow that size calculation.

Reject tag 70 packets before the subtraction unless the body contains the signature, cipher code, and at least one byte of encrypted filename data.

Fixes 9c79f34f7ee7 ("eCryptfs: Filename Encryption: Tag 70 packets")
---
 fs/ecryptfs/keystore.c |    6 ++++++
 1 file changed, 6 insertions(+)
fs/ecryptfs/keystore.c +6 -0
@@ -894,6 +894,12 @@ ecryptfs_parse_tag_70_packet(char **file
 		       "rc = [%d]\n", __func__, rc);
 		goto out;
 	}
+	if (s->parsed_tag_70_packet_size < (ECRYPTFS_SIG_SIZE + 2)) {
+		ecryptfs_printk(KERN_WARNING, "Invalid packet size [%zd]\n",
+				s->parsed_tag_70_packet_size);
+		rc = -EINVAL;
+		goto out;
+	}
 	s->block_aligned_filename_size = (s->parsed_tag_70_packet_size
 					  - ECRYPTFS_SIG_SIZE - 1);
 	if ((1 + s->packet_size_len + s->parsed_tag_70_packet_size)
author maintainer
#275 lore raw

PATCH 275/713 ecryptfs: release message context on send failure

In reply to Greg Kroah-Hartman

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

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

From: Yichong Chen <[email protected]>

commit 219644a3ad5518217b2d62cad6d2c36a2308c949 upstream.

ecryptfs_send_message_locked() moves a message context from the free list to the allocated list before sending the request to the userspace daemon.

If ecryptfs_send_miscdev() fails, the context is left on the allocated list and cannot be reused. Move it back to the free list on failure and clear the caller's pointer.

Fixes f66e883eb618 ("eCryptfs: integrate eCryptfs device handle into the module.")
---
 fs/ecryptfs/messaging.c |    9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)
fs/ecryptfs/messaging.c +8 -1
@@ -286,9 +286,16 @@ ecryptfs_send_message_locked(char *data,
 	mutex_unlock(&ecryptfs_msg_ctx_lists_mux);
 	rc = ecryptfs_send_miscdev(data, data_len, *msg_ctx, msg_type, 0,
 				   daemon);
-	if (rc)
+	if (rc) {
 		printk(KERN_ERR "%s: Error attempting to send message to "
 		       "userspace daemon; rc = [%d]\n", __func__, rc);
+		mutex_lock(&ecryptfs_msg_ctx_lists_mux);
+		mutex_lock(&(*msg_ctx)->mux);
+		ecryptfs_msg_ctx_alloc_to_free(*msg_ctx);
+		mutex_unlock(&(*msg_ctx)->mux);
+		mutex_unlock(&ecryptfs_msg_ctx_lists_mux);
+		*msg_ctx = NULL;
+	}
 out:
 	return rc;
 }
author maintainer
#276 lore raw

PATCH 276/713 ecryptfs: show filename encryption options

In reply to Greg Kroah-Hartman

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

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

From: Yichong Chen <[email protected]>

commit 496ec2d0852a02d2e631771b5c439130b9c7dce7 upstream.

ecryptfs_show_options() prints most user-visible mount options but omits the filename encryption cipher and key size.

Print ecryptfs_fn_cipher and ecryptfs_fn_key_bytes when filename encryption is enabled so that the displayed mount options reflect the active filename encryption settings.

Fixes 87c94c4df014 ("eCryptfs: Filename Encryption: mount option")
---
 fs/ecryptfs/super.c |    7 +++++++
 1 file changed, 7 insertions(+)
fs/ecryptfs/super.c +7 -0
@@ -150,6 +150,13 @@ static int ecryptfs_show_options(struct
 	if (mount_crypt_stat->global_default_cipher_key_size)
 		seq_printf(m, ",ecryptfs_key_bytes=%zd",
 			   mount_crypt_stat->global_default_cipher_key_size);
+	if (mount_crypt_stat->flags & ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES) {
+		seq_printf(m, ",ecryptfs_fn_cipher=%s",
+			   mount_crypt_stat->global_default_fn_cipher_name);
+		if (mount_crypt_stat->global_default_fn_cipher_key_bytes)
+			seq_printf(m, ",ecryptfs_fn_key_bytes=%zd",
+				   mount_crypt_stat->global_default_fn_cipher_key_bytes);
+	}
 	if (mount_crypt_stat->flags & ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED)
 		seq_printf(m, ",ecryptfs_passthrough");
 	if (mount_crypt_stat->flags & ECRYPTFS_XATTR_METADATA_ENABLED)
author maintainer
#277 lore raw

PATCH 277/713 efivarfs: Rate limit statfs() handler

In reply to Greg Kroah-Hartman

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

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

From: Ard Biesheuvel <[email protected]>

commit b2326338dc683e8c1067c0cbf7a47986c4190902 upstream.

Ravi reports that statfs() may be called by unprivileged users on the efivarfs mount point, which may result in a flood of calls to the QueryVariableInfo() runtime service. These calls are disproportionately costly on x86 systems where the variable store is backed by SMM, as each SMM entry requires a rendez-vous of all the CPUs.

So rate limit the calls to QueryVariableInfo() at twice per second, and return the most recently obtained value for calls that are elided.

Fixes d86ff3333cb1 ("efivarfs: expose used and total size")
---
 fs/efivarfs/super.c |   30 ++++++++++++++++++++++++------
 1 file changed, 24 insertions(+), 6 deletions(-)
fs/efivarfs/super.c +24 -6
@@ -89,12 +89,30 @@ static int efivarfs_statfs(struct dentry
 	/* Some UEFI firmware does not implement QueryVariableInfo() */
 	storage_space = remaining_space = 0;
 	if (efi_rt_services_supported(EFI_RT_SUPPORTED_QUERY_VARIABLE_INFO)) {
-		status = efivar_query_variable_info(attr, &storage_space,
-						    &remaining_space,
-						    &max_variable_size);
-		if (status != EFI_SUCCESS && status != EFI_UNSUPPORTED)
-			pr_warn_ratelimited("query_variable_info() failed: 0x%lx\n",
-					    status);
+		static DEFINE_RATELIMIT_STATE(_rs, 2 * HZ, 5);
+		static u64 storage, remaining;
+		static DEFINE_SPINLOCK(lock);
+
+		if (!__ratelimit(&_rs)) {
+			ratelimit_set_flags(&_rs, RATELIMIT_MSG_ON_RELEASE);
+
+			spin_lock(&lock);
+			storage_space = storage;
+			remaining_space = remaining;
+			spin_unlock(&lock);
+		} else {
+			status = efivar_query_variable_info(attr, &storage_space,
+							    &remaining_space,
+							    &max_variable_size);
+			if (status != EFI_SUCCESS && status != EFI_UNSUPPORTED)
+				pr_warn("query_variable_info() failed: 0x%lx\n",
+					status);
+
+			spin_lock(&lock);
+			storage = storage_space;
+			remaining = remaining_space;
+			spin_unlock(&lock);
+		}
 	}
 
 	/*
author maintainer
#278 lore raw

PATCH 278/713 entry: Fix seccomp bypass after ptrace with TSYNC

In reply to Greg Kroah-Hartman

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

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

From: Jinjie Ruan <[email protected]>

commit 4a3591287fb7f808e209b4974ed337f609a2006b upstream.

Sashiko review pointed out the following issue.

If a thread is stopped in syscall_trace_enter() for ptrace, another thread can install a seccomp filter with SECCOMP_FILTER_FLAG_TSYNC (e.g., via seccomp_attach_filter()). This will successfully set SYSCALL_WORK_SECCOMP on the stopped thread, but syscall_trace_enter() evaluates a cached 'work' variable sampled on entry. Consequently, the subsequent check for SYSCALL_WORK_SECCOMP misses the newly assigned flag, and the filter is silently bypassed.

This race condition could allow an unprivileged process to execute a prohibited system call (e.g., execve) that the newly installed filter was intended to block, especially since the tracer might have modified the system call number during the ptrace stop.

Fix this by re-reading the syscall_work flags after ptrace handling, so that any new SYSCALL_WORK_SECCOMP flag set by another thread via TSYNC during the ptrace stop is observed before the subsequent seccomp check.

---
 include/linux/entry-common.h |    3 +++
 1 file changed, 3 insertions(+)
include/linux/entry-common.h +3 -0
@@ -96,6 +96,9 @@ static __always_inline long syscall_trac
 		ret = arch_ptrace_report_syscall_entry(regs);
 		if (ret || (work & SYSCALL_WORK_SYSCALL_EMU))
 			return -1L;
+
+		/* ptrace might have changed work flags */
+		work = READ_ONCE(current_thread_info()->syscall_work);
 	}
 
 	/* Do seccomp after ptrace, to catch any tracer changes. */
author maintainer
#279 lore raw

PATCH 279/713 erofs: skip sufficiently large global buffers when resizing

In reply to Greg Kroah-Hartman

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

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

From: Nikhil Gurudasani <[email protected]>

commit a7d097cf01301c5da37927c8f26123d006f0fd8a upstream.

z_erofs_gbuf_nrpages is advanced only after every global buffer has been grown. If a resize fails after some buffers were enlarged, a retry revisits those enlarged buffers.

Retrying the same size then returns -ENOMEM because alloc_pages_bulk() has no pages to add and the unchanged return value is treated as a failure. Retrying an intermediate size allocates a temporary pointer array smaller than gbuf->nrpages and copies more existing pointers than the array can hold.

Skip buffers that already satisfy the request. Once all remaining buffers have caught up, advancing z_erofs_gbuf_nrpages again describes the guaranteed minimum size across the pool.

Fixes d6db47e571dc ("erofs: do not use pagepool in z_erofs_gbuf_growsize()")
---
 fs/erofs/zutil.c |    2 ++
 1 file changed, 2 insertions(+)
fs/erofs/zutil.c +2 -0
@@ -79,6 +79,8 @@ int z_erofs_gbuf_growsize(unsigned int n
 
 	for (i = 0; i < z_erofs_gbuf_count; ++i) {
 		gbuf = &z_erofs_gbufpool[i];
+		if (gbuf->nrpages >= nrpages)
+			continue;
 		tmp_pages = kzalloc_objs(*tmp_pages, nrpages);
 		if (!tmp_pages)
 			goto out;
author maintainer
#280 lore raw

PATCH 280/713 ext2: Fix lost inode updates for IS_SYNC inodes

In reply to Greg Kroah-Hartman

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

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

From: Jan Kara <[email protected]>

commit 356984d1a5c32e94810cbb6c8dc7d8ff2d4d919a upstream.

ext2_setsize() and ext2_xattr_set2() had a construct like:

	if (IS_SYNC(inode)) {
		sync_inode_metadata(inode, 1);
	} else {
		mark_inode_dirty(inode);
	}

which leads to lost inode updates for IS_SYNC inodes because sync_inode_metadata() does anything only if the inode is already dirty and hence inode updates may be simply lost. Fix the problem by unconditionally marking the inode dirty and *then* call sync_inode_metadata().

---
 fs/ext2/inode.c |    7 ++-----
 fs/ext2/xattr.c |    4 ++--
 2 files changed, 4 insertions(+), 7 deletions(-)
fs/ext2/inode.c +2 -5
@@ -1258,12 +1258,9 @@ static int ext2_setsize(struct inode *in
 	filemap_invalidate_unlock(inode->i_mapping);
 
 	inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
-	if (inode_needs_sync(inode)) {
-		mmb_sync(&EXT2_I(inode)->i_metadata_bhs);
+	mark_inode_dirty(inode);
+	if (inode_needs_sync(inode))
 		sync_inode_metadata(inode, 1);
-	} else {
-		mark_inode_dirty(inode);
-	}
 
 	return 0;
 }
fs/ext2/xattr.c +2 -2
@@ -777,6 +777,7 @@ ext2_xattr_set2(struct inode *inode, str
 	/* Update the inode. */
 	EXT2_I(inode)->i_file_acl = new_bh ? new_bh->b_blocknr : 0;
 	inode_set_ctime_current(inode);
+	mark_inode_dirty(inode);
 	if (IS_SYNC(inode)) {
 		error = sync_inode_metadata(inode, 1);
 		/* In case sync failed due to ENOSPC the inode was actually
@@ -789,8 +790,7 @@ ext2_xattr_set2(struct inode *inode, str
 			}
 			goto cleanup;
 		}
-	} else
-		mark_inode_dirty(inode);
+	}
 
 	error = 0;
 	if (old_bh && old_bh != new_bh) {
author maintainer
#281 lore raw

PATCH 281/713 fanotify: fix use-after-free of file range info

In reply to Greg Kroah-Hartman

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

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

From: Chengfeng Ye <[email protected]>

commit d7f1cf5be33ef0175a4e8ed8687aeb98fb00a851 upstream.

fsnotify_pre_content() builds its file_range on the triggering task's stack. fanotify_alloc_perm_event() saves a pointer to range.pos in the heap-allocated permission event so copy_range_info_to_user() can report the offset later.

The event reader can set the event state to FAN_EVENT_REPORTED and then sleep while preparing the file descriptor. If a signal interrupts the triggering task at that point, fanotify_get_response() changes the state to FAN_EVENT_CANCELED and returns. This unwinds the file_range stack frame while the reader still owns the event. The reader then dereferences pevent->ppos and copies the stale stack value to userspace.

KASAN reported:

  BUG: KASAN: use-after-free in fanotify_read+0x293e/0x2970
  Read of size 8 at addr ffff88811434fc50 by task fanotify_inotif/95
  Call Trace:
   fanotify_read+0x293e/0x2970
   vfs_read+0x177/0xa20
   ksys_read+0xf7/0x1c0
   do_syscall_64+0xf9/0x540
   entry_SYSCALL_64_after_hwframe+0x77/0x7f

Store the range position directly in the permission event and use FANOTIFY_NO_RANGE when range information is unavailable. The event remains alive until the reader finishes, so the reported offset no longer depends on the triggering task's stack.

Fixes 870499bc1d4d ("fanotify: report file range info with pre-content events")
---
 fs/notify/fanotify/fanotify.c      |    3 +--
 fs/notify/fanotify/fanotify.h      |    6 ++++--
 fs/notify/fanotify/fanotify_user.c |    5 +----
 3 files changed, 6 insertions(+), 8 deletions(-)
fs/notify/fanotify/fanotify.c +1 -2
@@ -600,8 +600,7 @@ static struct fanotify_event *fanotify_a
 	pevent->hdr.len = 0;
 	pevent->state = FAN_EVENT_INIT;
 	pevent->path = *path;
-	/* NULL ppos means no range info */
-	pevent->ppos = range ? &range->pos : NULL;
+	pevent->pos = range ? range->pos : FANOTIFY_NO_RANGE;
 	pevent->count = range ? range->count : 0;
 	path_get(path);
 
fs/notify/fanotify/fanotify.h +4 -2
@@ -428,6 +428,8 @@ FANOTIFY_ME(struct fanotify_event *event
 	return container_of(event, struct fanotify_mnt_event, fae);
 }
 
+#define FANOTIFY_NO_RANGE	((loff_t)-1)
+
 /*
  * Structure for permission fanotify events. It gets allocated and freed in
  * fanotify_handle_event() since we wait there for user response. When the
@@ -438,7 +440,7 @@ FANOTIFY_ME(struct fanotify_event *event
 struct fanotify_perm_event {
 	struct fanotify_event fae;
 	struct path path;
-	const loff_t *ppos;		/* optional file range info */
+	loff_t pos;			/* FANOTIFY_NO_RANGE if unavailable */
 	size_t count;
 	u32 response;			/* userspace answer to the event */
 	unsigned short state;		/* state of the event */
@@ -468,7 +470,7 @@ static inline bool fanotify_event_has_ac
 	if (!(event->mask & FANOTIFY_PRE_CONTENT_EVENTS))
 		return false;
 
-	return FANOTIFY_PERM(event)->ppos;
+	return FANOTIFY_PERM(event)->pos != FANOTIFY_NO_RANGE;
 }
 
 static inline struct fanotify_event *FANOTIFY_E(struct fsnotify_event *fse)
fs/notify/fanotify/fanotify_user.c +1 -4
@@ -675,12 +675,9 @@ static size_t copy_range_info_to_user(st
 	if (WARN_ON_ONCE(info_len > count))
 		return -EFAULT;
 
-	if (WARN_ON_ONCE(!pevent->ppos))
-		return -EINVAL;
-
 	info.hdr.info_type = FAN_EVENT_INFO_TYPE_RANGE;
 	info.hdr.len = info_len;
-	info.offset = *(pevent->ppos);
+	info.offset = pevent->pos;
 	info.count = pevent->count;
 
 	if (copy_to_user(buf, &info, info_len))
author maintainer
#282 lore raw

PATCH 282/713 fat: restore original value when fat_ent_write failed

In reply to Greg Kroah-Hartman

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

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

From: Yemu Lu <[email protected]>

commit 64d9183203eebe33de6188b70a8c1e91f52885db upstream.

fat_ent_write() may have committed the new link to the primary FAT but then failed on the mirror copy, leaving the chain pointing to new_dclus even though the caller will free it. Restore the original value to keep the chain consistent.

---
 fs/fat/misc.c |    4 ++++
 1 file changed, 4 insertions(+)
fs/fat/misc.c +4 -0
@@ -133,7 +133,11 @@ int fat_chain_add(struct inode *inode, i
 		ret = fat_ent_read(inode, &fatent, last);
 		if (ret >= 0) {
 			int wait = inode_needs_sync(inode);
+			int old = ret;
+
 			ret = fat_ent_write(inode, &fatent, new_dclus, wait);
+			if (ret < 0)
+				fat_ent_write(inode, &fatent, old, wait);
 			fatent_brelse(&fatent);
 		}
 		if (ret < 0)
author maintainer
#283 lore raw

PATCH 283/713 fbdev: omapfb: panel-dsi-cm: initialize lock before registering display

In reply to Greg Kroah-Hartman

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

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

From: Runyu Xiao <[email protected]>

commit f8e43fe0f22b7137ce456e6fe3581d3098174f74 upstream.

dsicm_probe() registers the display before initializing ddata->lock. Once omapdss_register_display() publishes the display, another consumer can reach a dsicm callback that takes this mutex while it is still uninitialized.

Initialize the mutex before registering the display so the published callbacks always see a valid lock.

Fixes f76ee892a99e ("omapfb: copy omapdss & displays for omapfb")
---
 drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c +2 -2
@@ -1150,14 +1150,14 @@ static int dsicm_probe(struct platform_d
 	dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE |
 		OMAP_DSS_DISPLAY_CAP_TEAR_ELIM;
 
+	mutex_init(&ddata->lock);
+
 	r = omapdss_register_display(dssdev);
 	if (r) {
 		dev_err(dev, "Failed to register panel\n");
 		goto err_reg;
 	}
 
-	mutex_init(&ddata->lock);
-
 	atomic_set(&ddata->do_update, 0);
 
 	ddata->reset_gpio = devm_gpiod_get(&pdev->dev, "reset", GPIOD_OUT_LOW);
author maintainer
#284 lore raw

PATCH 284/713 fbdev: pvr2fb: correct user pointer annotation and sentinel initializer

In reply to Greg Kroah-Hartman

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

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

From: Florian Fuchs <[email protected]>

commit 5dc2e70dd74b1f03e2e13bfb6922111d9e0adf90 upstream.

Add __user annotation to buf, as it is passed as a user pointer in pin_user_pages_fast(). Use an empty initializer for the sentinel board-table entry to avoid initializing a function pointer with an integer literal.

---
 drivers/video/fbdev/pvr2fb.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/video/fbdev/pvr2fb.c +2 -2
@@ -639,7 +639,7 @@ static irqreturn_t __maybe_unused pvr2fb
 }
 
 #ifdef CONFIG_PVR2_DMA
-static ssize_t pvr2fb_write(struct fb_info *info, const char *buf,
+static ssize_t pvr2fb_write(struct fb_info *info, const char __user *buf,
 			    size_t count, loff_t *ppos)
 {
 	unsigned long dst, start, end, len;
@@ -1077,7 +1077,7 @@ static struct pvr2_board {
 #ifdef CONFIG_PCI
 	{ pvr2fb_pci_init, pvr2fb_pci_exit, "PCI PVR2" },
 #endif
-	{ 0, },
+	{ },
 };
 
 static int __init pvr2fb_init(void)
author maintainer
#285 lore raw

PATCH 285/713 fbdev: ssd1307fb: defer I2C transfers from damage callbacks

In reply to Greg Kroah-Hartman

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

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

From: Hui Su <[email protected]>

commit 9ad709afdfa32509ed64938a6d9cd00db3cd54c2 upstream.

The fbdev damage callbacks may run from fbcon while printk has disabled preemption. They currently update the display synchronously, which enters the sleeping I2C transfer path from atomic context.

A complete report from an RK3566 system follows:

  [  258.129004] watchdog: watchdog0: watchdog did not stop!
  [  258.129067] BUG: scheduling while atomic: systemd/1/0x00000003
  [  258.129076] Modules linked in: algif_hash algif_skcipher af_alg bnep
  binfmt_misc lz4hc lz4 zram snd_soc_hdmi_codec brcmfmac_wcc hci_uart
  fb_ssd1306(C) fbtft(C) btqca btrtl btintel btsdio snd_soc_simple_card
  motorcomm pwm_fan snd_soc_simple_card_utils ssd130x_spi nls_iso8859_1
  ssd130x btbcm drm_shmem_helper display_connector brcmfmac ssd1307fb
  brcmutil bluetooth cfg80211 rfkill snd_soc_rockchip_i2s_tdm
  snd_soc_rk817 hantro_vpu snd_soc_core snd_compress snd_pcm_dmaengine
  v4l2_vp9 snd_pcm v4l2_h264 rockchip_rga snd_timer rk_crypto2
  spi_rockchip_sfc videobuf2_dma_contig snd sm3_generic v4l2_mem2mem
  videobuf2_dma_sg dwmac_rk sm3 soundcore videobuf2_memops videobuf2_v4l2
  stmmac_platform dw_hdmi_cec videodev videobuf2_common dw_hdmi_i2s_audio
  stmmac rk817_charger pcs_xpcs mc cpufreq_dt sch_fq_codel ip_tables
  x_tables autofs4
  [  258.129215] Preemption disabled at:
  [  258.129216] [<ffff80008012f96c>] vprintk_emit+0x11c/0x340
  [  258.129234] CPU: 0 PID: 1 Comm: systemd Tainted: G         C
  6.6.0-rc5-rockchip-rk356x #4
  [  258.129239] Hardware name: Rockchip RK3566 OPi 3B (DT)
  [  258.129243] Call trace:
  [  258.129245]  dump_backtrace+0xa0/0x128
  [  258.129252]  show_stack+0x20/0x38
  [  258.129256]  dump_stack_lvl+0x60/0xb0
  [  258.129265]  dump_stack+0x18/0x28
  [  258.129269]  __schedule_bug+0xa0/0xc8
  [  258.129274]  __schedule+0x9ac/0xd30
  [  258.129279]  schedule+0x60/0x100
  [  258.129282]  schedule_timeout+0x194/0x338
  [  258.129289]  rk3x_i2c_xfer_common.isra.0+0x384/0x498
  [  258.129296]  rk3x_i2c_xfer+0x20/0x60
  [  258.129300]  __i2c_transfer+0x194/0x648
  [  258.129308]  i2c_transfer+0x9c/0x130
  [  258.129313]  i2c_transfer_buffer_flags+0x64/0x98
  [  258.129318]  ssd1307fb_update_rect+0x42c/0x560 [ssd1307fb]
  [  258.129334]  ssd1307fb_defio_imageblit+0x34/0x50 [ssd1307fb]
  [  258.129343]  soft_cursor+0x13c/0x210
  [  258.129350]  bit_cursor+0x2dc/0x550
  [  258.129354]  fbcon_cursor+0xec/0x108
  [  258.129359]  hide_cursor+0x44/0xc8
  [  258.129365]  vt_console_print+0x398/0x3b0
  [  258.129370]  console_flush_all.isra.0+0x17c/0x410
  [  258.129377]  console_unlock+0x4c/0x100
  [  258.129382]  vprintk_emit+0x1c8/0x340
  [  258.129386]  vprintk_default+0x40/0x58
  [  258.129389]  vprintk+0xb8/0xd0
  [  258.129392]  _printk+0x68/0x98
  [  258.129398]  watchdog_release+0x170/0x230
  [  258.129404]  __fput+0xbc/0x288
  [  258.129409]  __fput_sync+0x58/0x70
  [  258.129413]  __arm64_sys_close+0x40/0x90
  [  258.129419]  invoke_syscall+0x4c/0x118
  [  258.129426]  el0_svc_common.constprop.0+0x48/0xf0
  [  258.129432]  do_el0_svc+0x24/0x38
  [  258.129437]  el0_svc+0x48/0x100
  [  258.129443]  el0t_64_sync_handler+0xc0/0xc8
  [  258.129448]  el0t_64_sync+0x190/0x198
  [  258.573087] ------------[ cut here ]------------
  [  258.573098] DEBUG_LOCKS_WARN_ON(val > preempt_count())
  [  258.573111] WARNING: CPU: 0 PID: 1 at kernel/sched/core.c:5871
  preempt_count_sub+0x9c/0x148
  [  258.573130] Modules linked in: algif_hash algif_skcipher af_alg bnep
  binfmt_misc lz4hc lz4 zram snd_soc_hdmi_codec brcmfmac_wcc hci_uart
  fb_ssd1306(C) fbtft(C) btqca btrtl btintel btsdio snd_soc_simple_card
  motorcomm pwm_fan snd_soc_simple_card_utils ssd130x_spi nls_iso8859_1
  ssd130x btbcm drm_shmem_helper display_connector brcmfmac ssd1307fb
  brcmutil bluetooth cfg80211 rfkill snd_soc_rockchip_i2s_tdm
  snd_soc_rk817 hantro_vpu snd_soc_core snd_compress snd_pcm_dmaengine
  v4l2_vp9 snd_pcm v4l2_h264 rockchip_rga snd_timer rk_crypto2
  spi_rockchip_sfc videobuf2_dma_contig snd sm3_generic v4l2_mem2mem
  videobuf2_dma_sg dwmac_rk sm3 soundcore videobuf2_memops videobuf2_v4l2
  stmmac_platform dw_hdmi_cec videodev videobuf2_common dw_hdmi_i2s_audio
  stmmac rk817_charger pcs_xpcs mc cpufreq_dt sch_fq_codel ip_tables
  x_tables autofs4
  [  258.573268] CPU: 0 PID: 1 Comm: systemd Tainted: G        WC
  6.6.0-rc5-rockchip-rk356x #4
  [  258.573274] Hardware name: Rockchip RK3566 OPi 3B (DT)
  ** 37 printk messages dropped **
  [  258.574064] Preemption disabled at:
  ** 42 printk messages dropped **
  [  259.190237] Preemption disabled at:

Track damage in the driver's private data under a spinlock and merge multiple updates into a bounding rectangle. Queue the existing deferred-I/O work immediately for damage reported by fbdev drawing and write helpers, so allocation and I2C transfers run from process context without adding the configured mmap refresh delay. Keep full-screen updates for dirty mmap pages, for which no precise rectangle is available.

Tested on an RK3566 board with a 128x64 OLED by running five rounds of 250 KERN_EMERG messages in total while issuing framebuffer writes every 15 ms. No atomic-sleep, preemption, or lockdep warning occurred. Kprobe tracing also confirmed that cursor-only damage remained an 8x16 partial update.

Fixes a2ed00da5047 ("drivers/video: add support for the Solomon SSD1307 OLED Controller")
---
 drivers/video/fbdev/ssd1307fb.c |   72 +++++++++++++++++++++++++++++++++++++---
 1 file changed, 67 insertions(+), 5 deletions(-)
drivers/video/fbdev/ssd1307fb.c +67 -5
@@ -14,6 +14,7 @@
 #include <linux/module.h>
 #include <linux/property.h>
 #include <linux/pwm.h>
+#include <linux/spinlock.h>
 #include <linux/uaccess.h>
 #include <linux/regulator/consumer.h>
 
@@ -72,6 +73,13 @@ struct ssd1307fb_par {
 	struct i2c_client *client;
 	u32 height;
 	struct fb_info *info;
+	/* Pending damage, with exclusive x2/y2, protected by damage_lock. */
+	spinlock_t damage_lock;
+	bool damage_pending;
+	u32 damage_x1;
+	u32 damage_x2;
+	u32 damage_y1;
+	u32 damage_y2;
 	u8 lookup_table[4];
 	u32 page_offset;
 	u32 col_offset;
@@ -302,19 +310,49 @@ static int ssd1307fb_blank(int blank_mod
 		return ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON);
 }
 
+static void ssd1307fb_schedule_damage(struct fb_info *info, u32 x, u32 y,
+				      u32 width, u32 height)
+{
+	struct ssd1307fb_par *par = info->par;
+	unsigned long flags;
+	u32 x2, y2;
+
+	if (!width || !height || x >= par->width || y >= par->height)
+		return;
+
+	x2 = x + min(width, par->width - x);
+	y2 = y + min(height, par->height - y);
+
+	spin_lock_irqsave(&par->damage_lock, flags);
+	if (par->damage_pending) {
+		par->damage_x1 = min(par->damage_x1, x);
+		par->damage_y1 = min(par->damage_y1, y);
+		par->damage_x2 = max(par->damage_x2, x2);
+		par->damage_y2 = max(par->damage_y2, y2);
+	} else {
+		par->damage_x1 = x;
+		par->damage_y1 = y;
+		par->damage_x2 = x2;
+		par->damage_y2 = y2;
+		par->damage_pending = true;
+	}
+	spin_unlock_irqrestore(&par->damage_lock, flags);
+
+	/* Advance an already-pending mmap update as well. */
+	mod_delayed_work(system_wq, &info->deferred_work, 0);
+}
+
 static void ssd1307fb_defio_damage_range(struct fb_info *info, off_t off, size_t len)
 {
 	struct ssd1307fb_par *par = info->par;
 
-	ssd1307fb_update_display(par);
+	ssd1307fb_schedule_damage(info, 0, 0, par->width, par->height);
 }
 
 static void ssd1307fb_defio_damage_area(struct fb_info *info, u32 x, u32 y,
 					u32 width, u32 height)
 {
-	struct ssd1307fb_par *par = info->par;
-
-	ssd1307fb_update_rect(par, x, y, width, height);
+	ssd1307fb_schedule_damage(info, x, y, width, height);
 }
 
 FB_GEN_DEFAULT_DEFERRED_SYSMEM_OPS(ssd1307fb,
@@ -329,7 +367,30 @@ static const struct fb_ops ssd1307fb_ops
 
 static void ssd1307fb_deferred_io(struct fb_info *info, struct list_head *pagereflist)
 {
-	ssd1307fb_update_display(info->par);
+	struct ssd1307fb_par *par = info->par;
+	unsigned long flags;
+	u32 x, y, width, height;
+
+	spin_lock_irqsave(&par->damage_lock, flags);
+	if (!list_empty(pagereflist)) {
+		x = 0;
+		y = 0;
+		width = par->width;
+		height = par->height;
+		par->damage_pending = false;
+	} else if (par->damage_pending) {
+		x = par->damage_x1;
+		y = par->damage_y1;
+		width = par->damage_x2 - par->damage_x1;
+		height = par->damage_y2 - par->damage_y1;
+		par->damage_pending = false;
+	} else {
+		spin_unlock_irqrestore(&par->damage_lock, flags);
+		return;
+	}
+	spin_unlock_irqrestore(&par->damage_lock, flags);
+
+	ssd1307fb_update_rect(par, x, y, width, height);
 }
 
 static int ssd1307fb_init(struct ssd1307fb_par *par)
@@ -601,6 +662,7 @@ static int ssd1307fb_probe(struct i2c_cl
 	par = info->par;
 	par->info = info;
 	par->client = client;
+	spin_lock_init(&par->damage_lock);
 
 	par->device_info = device_get_match_data(dev);
 
author maintainer
#286 lore raw

PATCH 286/713 fbdev: uvesafb: unregister connector callback on init failure

In reply to Greg Kroah-Hartman

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

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

From: Myeonghun Pak <[email protected]>

commit de8db23aa7c337e606fca9faf48b3ba72968597a upstream.

uvesafb_init() registers the v86d connector callback before registering the platform driver. If platform_driver_register() fails, the function returns the error directly and leaves the connector callback registered.

The later platform-device failure path already unregisters the callback. Add the same cleanup before the final return when platform-driver registration fails.

This issue was identified during our ongoing static-analysis research while reviewing kernel code.

Fixes 8bdb3a2d7df4 ("uvesafb: the driver core")
---
 drivers/video/fbdev/uvesafb.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/video/fbdev/uvesafb.c +2 -0
@@ -1907,6 +1907,8 @@ static int uvesafb_init(void)
 			err = 0;
 		}
 	}
+	if (err)
+		cn_del_callback(&uvesafb_cn_id);
 	return err;
 }
 
author maintainer
#287 lore raw

PATCH 287/713 fonts: fixup font.h kernel-doc warnings

In reply to Greg Kroah-Hartman

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

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

From: Randy Dunlap <[email protected]>

commit 048ad864d61feedc24129eeb976e973edcbc3aac upstream.

Use the typedef keyword when describing a typedef. Add the missing function return value for font_glyph_size().

Warning: include/linux/font.h:84 cannot understand function prototype:
'typedef const unsigned char font_data_t;'
Warning: include/linux/font.h:53 No description found for return value
of 'font_glyph_size'

---
 include/linux/font.h |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
include/linux/font.h +3 -1
@@ -49,6 +49,8 @@ static inline unsigned int font_glyph_pi
  * scanlines, which is usually the glyph's height in scanlines. Fonts
  * coming from user space can sometimes have a different vertical pitch
  * with empty scanlines between two adjacent glyphs.
+ *
+ * Returns: the number of bytes per glyph
  */
 static inline unsigned int font_glyph_size(unsigned int width, unsigned int vpitch)
 {
@@ -60,7 +62,7 @@ static inline unsigned int font_glyph_si
  */
 
 /**
- * font_data_t - Raw font data
+ * typedef font_data_t - Raw font data
  *
  * Values of type font_data_t store a pointer to raw font data. The format
  * is monochrome. Each bit sets a pixel of a stored glyph. Font data does
author maintainer
#288 lore raw

PATCH 288/713 forcedeth: fix off-by-one when saving/restoring non-PCI config space

In reply to Greg Kroah-Hartman

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

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

From: Marek Czernohous <[email protected]>

commit 9393f1d656a79693e0c123ff7bc7c5c0f708046d upstream.

nv_suspend() and nv_resume() walk the non-PCI configuration space with

	for (i = 0; i <= np->register_size/sizeof(u32); i++)

which runs one iteration too many. saved_config_space is declared as

	u32 saved_config_space[NV_PCI_REGSZ_MAX/4];

and NV_PCI_REGSZ_VER3 is equal to NV_PCI_REGSZ_MAX (0x604), so on a VER3 device register_size/sizeof(u32) is exactly the array length and the last iteration addresses one element past the end.

The element it lands on is np->name_rx[0..3]: saved_config_space[] is followed immediately by char name_rx[IFNAMSIZ + 3], and char needs no padding. Nothing observable is corrupted by that, because nv_request_irq() rewrites name_rx with sprintf() before it is ever passed to request_irq(). The bug is the out-of-bounds access itself, which UBSAN reports and which CONFIG_UBSAN_TRAP=y turns into a trap that aborts the running kernel code, plus an MMIO read and, on resume, an MMIO writel() to base + 0x604, one dword past the range the driver mapped:

	np->base = ioremap(addr, np->register_size);

VER1 and VER2 devices stay inside the array, but they too get the stray read and the stray write one dword past their own window.

Caught by UBSAN on an Apple Macmini3,1 (MCP79) during a deep S3 cycle. The splat below is trimmed: the build path in the file name, the CPU and taint lines, the Workqueue line, the "?" hint frames, and the frames below device_suspend are all cut. The kernel was tainted, with an out-of-tree nouveau and CPU_OUT_OF_SPEC; forcedeth itself was the stock module.

  UBSAN: array-index-out-of-bounds in drivers/net/ethernet/nvidia/forcedeth.c:6225:25
  index 385 is out of range for type 'u32 [385]'
  Call Trace:
   dump_stack_lvl+0x5d/0x80
   ubsan_epilogue+0x5/0x2b
   __ubsan_handle_out_of_bounds.cold+0x54/0x59
   __this_module+0xe398c/0xe9010 [forcedeth]
   pci_pm_suspend+0x80/0x170
   dpm_run_callback+0x51/0x160
   device_suspend+0x1a2/0x4a0
   ...

Both loops are hit. UBSAN reports each source location only once per module load (__ubsan_handle_out_of_bounds() calls suppress_report(), which does test_and_set_bit(REPORTED_BIT, ...) on the struct source_location), so the two splats land in the first S3 cycle after the module is loaded and later cycles are silent even though the access still runs off the end every time. In that first cycle line 6225 is reported from pci_pm_suspend and line 6240 from pci_pm_resume.

The same off-by-one was fixed in nv_get_regs() by commit ba9aa134287f ("forcedeth: fix buffer overflow") in 2012; these two loops were missed. The suspend and resume side was reported on LKML in September 2013 by Marc Weber, with the same analysis and the same one-character fix, but the patch was attached rather than sent inline and the thread ended there.

Use < instead of <=, which saves and restores exactly register_size bytes.

---
 drivers/net/ethernet/nvidia/forcedeth.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/ethernet/nvidia/forcedeth.c +2 -2
@@ -6221,7 +6221,7 @@ static int nv_suspend(struct device *dev
 	netif_device_detach(dev);
 
 	/* save non-pci configuration space */
-	for (i = 0; i <= np->register_size/sizeof(u32); i++)
+	for (i = 0; i < np->register_size/sizeof(u32); i++)
 		np->saved_config_space[i] = readl(base + i*sizeof(u32));
 
 	return 0;
@@ -6236,7 +6236,7 @@ static int nv_resume(struct device *devi
 	int i, rc = 0;
 
 	/* restore non-pci configuration space */
-	for (i = 0; i <= np->register_size/sizeof(u32); i++)
+	for (i = 0; i < np->register_size/sizeof(u32); i++)
 		writel(np->saved_config_space[i], base+i*sizeof(u32));
 
 	if (np->driver_data & DEV_NEED_MSI_FIX)
author maintainer
#289 lore raw

PATCH 289/713 fpga: stratix10-soc: Fix SVC mailbox handling during reconfiguration

In reply to Greg Kroah-Hartman

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

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

From: Tien Sung Ang <[email protected]>

commit c14a8b15c87b49efc3ef898cec8ac7c30336a080 upstream.

Fix incorrect stratix10_svc_done() usage during FPGA reconfiguration.

Do not call stratix10_svc_done() at the end of write_init() on success, so the SVC session remains active through write() and write_complete(). Call stratix10_svc_done() on failure in write_init() and write() so the shared SVC mailbox is released when reconfiguration aborts, allowing coexistence with other SVC clients such as soc64-hwmon.

---
 drivers/fpga/stratix10-soc.c |   21 ++++++++++++---------
 1 file changed, 12 insertions(+), 9 deletions(-)
drivers/fpga/stratix10-soc.c +12 -9
@@ -195,20 +195,18 @@ static int s10_ops_write_init(struct fpg
 	ret = s10_svc_send_msg(priv, COMMAND_RECONFIG,
 			       &ctype, sizeof(ctype));
 	if (ret < 0)
-		goto init_done;
+		goto init_error;
 
-	ret = wait_for_completion_timeout(
-		&priv->status_return_completion, S10_RECONFIG_TIMEOUT);
-	if (!ret) {
+	if (!wait_for_completion_timeout(&priv->status_return_completion,
+					 S10_RECONFIG_TIMEOUT)) {
 		dev_err(dev, "timeout waiting for RECONFIG_REQUEST\n");
 		ret = -ETIMEDOUT;
-		goto init_done;
+		goto init_error;
 	}
 
-	ret = 0;
 	if (!test_and_clear_bit(SVC_STATUS_OK, &priv->status)) {
 		ret = -ETIMEDOUT;
-		goto init_done;
+		goto init_error;
 	}
 
 	/* Allocate buffers from the service layer's pool. */
@@ -217,14 +215,16 @@ static int s10_ops_write_init(struct fpg
 		if (IS_ERR(kbuf)) {
 			s10_free_buffers(mgr);
 			ret = PTR_ERR(kbuf);
-			goto init_done;
+			goto init_error;
 		}
 
 		priv->svc_bufs[i].buf = kbuf;
 		priv->svc_bufs[i].lock = 0;
 	}
 
-init_done:
+	return 0;
+
+init_error:
 	stratix10_svc_done(priv->chan);
 	return ret;
 }
@@ -342,6 +342,9 @@ static int s10_ops_write(struct fpga_man
 	if (!s10_free_buffers(mgr))
 		dev_err(dev, "%s not all buffers were freed\n", __func__);
 
+	if (ret < 0)
+		stratix10_svc_done(priv->chan);
+
 	return ret;
 }
 
author maintainer
#290 lore raw

PATCH 290/713 fsnotify: Fix stale object mask after concurrent mark updates

In reply to Greg Kroah-Hartman

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

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

From: Youngjae Kwon <[email protected]>

commit e422777fdd4746de1109575c51e65038d4c5c1be upstream.

When a mark gets a new event bit, fanotify and inotify may avoid recalculating the object mask if the cached aggregate already contains that bit. This is racy with a recalculation triggered by a concurrent update to another mark on the same connector.

The concurrent scan can read the mark before the new bit is added, while the updater reads the old aggregate before that scan publishes its result. The updater then skips recalculation and the scan publishes a mask without the bit, leaving the object mask stale after both updates complete.

This can be reproduced with two fanotify groups watching the same inode:
one thread removes FAN_MODIFY from one existing mark while another thread adds FAN_MODIFY to the other mark. After both fanotify_mark() calls return, writes can fail to produce FAN_MODIFY for the group whose mark now contains the bit. This was reproduced on an unmodified v6.12.95 kernel. The equivalent inotify interleaving loses IN_MODIFY events.

For normal fanotify additions, recalculate whenever the raw mark mask changes. The normal mask is not cleared asynchronously, so an unchanged addition cannot introduce missing interest. Always recalculate ignore-mask updates because FS_MODIFY handling may clear the ignore mask without taking mark->lock, making snapshot comparisons unreliable.

Always recalculate after updating an existing inotify watch. Its replace path temporarily sets mark->mask to zero, so a concurrent scan can observe zero even when the old and final masks are equal. Assigning the replacement mask directly would avoid the transient zero, but existing-watch updates are infrequent, so unconditional recalculation is simpler.

Fixes 63c882a05416 ("inotify: reimplement inotify using fsnotify")
Fixes 912ee3946c5e ("fanotify: do not call fanotify_update_object_mask in fanotify_add_mark")
Cc [email protected] # needs adjustments for <= 7.0
---
 fs/notify/fanotify/fanotify_user.c |   12 +++++++-----
 fs/notify/inotify/inotify_user.c   |   15 +--------------
 2 files changed, 8 insertions(+), 19 deletions(-)
fs/notify/fanotify/fanotify_user.c +7 -5
@@ -1313,16 +1313,18 @@ static bool fanotify_mark_update_flags(s
 static bool fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark,
 				      __u32 mask, unsigned int fan_flags)
 {
+	__u32 old_mask;
 	bool recalc;
 
 	spin_lock(&fsn_mark->lock);
-	if (!(fan_flags & FANOTIFY_MARK_IGNORE_BITS))
+	if (!(fan_flags & FANOTIFY_MARK_IGNORE_BITS)) {
+		old_mask = fsn_mark->mask;
 		fsn_mark->mask |= mask;
-	else
+		recalc = old_mask != fsn_mark->mask;
+	} else {
 		fsn_mark->ignore_mask |= mask;
-
-	recalc = fsnotify_calc_mask(fsn_mark) &
-		~fsnotify_conn_mask(fsn_mark->connector);
+		recalc = true;
+	}
 
 	recalc |= fanotify_mark_update_flags(fsn_mark, fan_flags);
 	spin_unlock(&fsn_mark->lock);
fs/notify/inotify/inotify_user.c +1 -14
@@ -539,7 +539,6 @@ static int inotify_update_existing_watch
 {
 	struct fsnotify_mark *fsn_mark;
 	struct inotify_inode_mark *i_mark;
-	__u32 old_mask, new_mask;
 	int replace = !(arg & IN_MASK_ADD);
 	int create = (arg & IN_MASK_CREATE);
 	int ret;
@@ -555,27 +554,15 @@ static int inotify_update_existing_watch
 	i_mark = container_of(fsn_mark, struct inotify_inode_mark, fsn_mark);
 
 	spin_lock(&fsn_mark->lock);
-	old_mask = fsn_mark->mask;
 	if (replace) {
 		fsn_mark->mask = 0;
 		fsn_mark->flags &= ~INOTIFY_MARK_FLAGS;
 	}
 	fsn_mark->mask |= inotify_arg_to_mask(inode, arg);
 	fsn_mark->flags |= inotify_arg_to_flags(arg);
-	new_mask = fsn_mark->mask;
 	spin_unlock(&fsn_mark->lock);
 
-	if (old_mask != new_mask) {
-		/* more bits in old than in new? */
-		int dropped = (old_mask & ~new_mask);
-		/* more bits in this fsn_mark than the inode's mask? */
-		int do_inode = (new_mask & ~READ_ONCE(inode->i_fsnotify_mask));
-
-		/* update the inode with this new fsn_mark */
-		if (dropped || do_inode)
-			fsnotify_recalc_mask(fsn_mark->connector);
-
-	}
+	fsnotify_recalc_mask(fsn_mark->connector);
 
 	/* return the wd */
 	ret = i_mark->wd;
author maintainer
#291 lore raw

PATCH 291/713 hsi: omap_ssi_core: fix missing DMA mask setup for SSI controller device

In reply to Greg Kroah-Hartman

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

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

From: Ivaylo Dimitrov <[email protected]>

commit e81250ec6b69248b00d38c523dc6a13efaf38aab upstream.

The OMAP SSI driver uses a synthetic HSI controller device allocated via hsi_alloc_controller(), which does not go through the normal OF/platform device initialization path.

As a result, the embedded struct device does not have a DMA mask initialized by default.

After recent DMA API hardening changes, dma_map_sg() and related helpers now require a valid dma_mask to be present, otherwise the driver may crash or trigger warnings when attempting DMA mapping operations.

Fix this by explicitly initializing the DMA mask for the SSI controller device and setting a 32-bit DMA mask, which matches the hardware capabilities.

---
 drivers/hsi/controllers/omap_ssi_core.c |    6 ++++++
 1 file changed, 6 insertions(+)
drivers/hsi/controllers/omap_ssi_core.c +6 -0
@@ -502,6 +502,12 @@ static int ssi_probe(struct platform_dev
 
 	pm_runtime_enable(&pd->dev);
 
+	ssi->device.dma_mask = &ssi->device.coherent_dma_mask;
+
+	err = dma_set_mask_and_coherent(&ssi->device, DMA_BIT_MASK(32));
+	if (err)
+		goto out2;
+
 	err = ssi_hw_init(ssi);
 	if (err < 0)
 		goto out2;
author maintainer
#292 lore raw

PATCH 292/713 hugetlb: only adjust reservation during unmapping if mapcount is 0

In reply to Greg Kroah-Hartman

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

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

From: Guillaume Morin <[email protected]>

commit 5120b1e048d48596ffaec1a8412012a91adba73b upstream.

Since df7a6d1f6405, __unmap_hugepage_range can adjust reservations. In the case of folio mapped in both a parent and a child, if the parent unmaps the range first, the reservation adjustment will result in an underflow of the reserved count. Once the child unmaps the range, the count is restored. Change __unmap_hugepage_range() to check the mapcount before adjusting the reservation.

---
 mm/hugetlb.c |    8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)
mm/hugetlb.c +6 -2
@@ -5181,6 +5181,7 @@ void __unmap_hugepage_range(struct mmu_g
 	bool adjust_reservation;
 	unsigned long last_addr_mask;
 
+	i_mmap_assert_write_locked(vma->vm_file->f_mapping);
 	WARN_ON(!is_vm_hugetlb_page(vma));
 	BUG_ON(start & ~huge_page_mask(h));
 	BUG_ON(end & ~huge_page_mask(h));
@@ -5272,7 +5273,10 @@ void __unmap_hugepage_range(struct mmu_g
 
 		/*
 		 * Restore the reservation for anonymous page, otherwise the
-		 * backing page could be stolen by someone.
+		 * backing page could be stolen by someone. Restore only on the
+		 * last unmap, otherwise the owner could empty its resv map
+		 * while the folio is still mapped by a child. Note that holding
+		 * i_mmap_lock_write is needed to check the number of mappings.
 		 * If there we are freeing a surplus, do not set the restore
 		 * reservation bit.
 		 */
@@ -5280,7 +5284,7 @@ void __unmap_hugepage_range(struct mmu_g
 
 		spin_lock_irq(&hugetlb_lock);
 		if (!h->surplus_huge_pages && __vma_private_lock(vma) &&
-		    folio_test_anon(folio)) {
+		    !folio_mapped(folio) && folio_test_anon(folio)) {
 			folio_set_hugetlb_restore_reserve(folio);
 			/* Reservation to be adjusted after the spin lock */
 			adjust_reservation = true;
author maintainer
#293 lore raw

PATCH 293/713 accel/rocket: fix NULL dereference and integer overflow in rocket_job_push()

In reply to Greg Kroah-Hartman

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

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

From: Muhammad Bilal <[email protected]>

commit a85402bff218f2b8f0d806e46c16c2f3d49cdda7 upstream.

rocket_job_push() allocates a temporary array to hold all input and output GEM object pointers:

    bos = kvmalloc_array(job->in_bo_count + job->out_bo_count,
                         sizeof(void *), GFP_KERNEL);
    memcpy(bos, job->in_bos, job->in_bo_count * sizeof(void *));
    memcpy(&bos[job->in_bo_count], job->out_bos, ...);

Two bugs exist:

1. Missing NULL check: if kvmalloc_array() fails, bos is NULL and the subsequent memcpy() dereferences it, causing a kernel NULL pointer dereference.

2. Integer overflow: in_bo_count and out_bo_count are both u32, set directly from userspace-supplied in_bo_handle_count and out_bo_handle_count with no prior validation. Their sum is computed in u32 arithmetic and can wrap to a smaller value, causing the allocation count passed to kvmalloc_array() to be smaller than intended. Subsequent uses still operate on the original counts when copying and locking objects, which may lead to out-of-bounds accesses on the temporary array.

Fix by using check_add_overflow() to detect count overflow before the allocation, and adding a NULL check on the allocation result.

---
 drivers/accel/rocket/rocket_job.c |   14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)
drivers/accel/rocket/rocket_job.c +10 -4
@@ -8,6 +8,7 @@
 #include <drm/drm_gem.h>
 #include <drm/rocket_accel.h>
 #include <linux/interrupt.h>
+#include <linux/overflow.h>
 #include <linux/iommu.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
@@ -188,14 +189,19 @@ static int rocket_job_push(struct rocket
 	struct rocket_device *rdev = job->rdev;
 	struct drm_gem_object **bos;
 	struct ww_acquire_ctx acquire_ctx;
+	u32 bo_count;
 	int ret = 0;
 
-	bos = kvmalloc_array(job->in_bo_count + job->out_bo_count, sizeof(void *),
-			     GFP_KERNEL);
+	if (check_add_overflow(job->in_bo_count, job->out_bo_count, &bo_count))
+		return -EINVAL;
+
+	bos = kvmalloc_array(bo_count, sizeof(*bos), GFP_KERNEL);
+	if (!bos)
+		return -ENOMEM;
 	memcpy(bos, job->in_bos, job->in_bo_count * sizeof(void *));
 	memcpy(&bos[job->in_bo_count], job->out_bos, job->out_bo_count * sizeof(void *));
 
-	ret = drm_gem_lock_reservations(bos, job->in_bo_count + job->out_bo_count, &acquire_ctx);
+	ret = drm_gem_lock_reservations(bos, bo_count, &acquire_ctx);
 	if (ret)
 		goto err;
 
@@ -220,7 +226,7 @@ static int rocket_job_push(struct rocket
 	rocket_attach_object_fences(job->out_bos, job->out_bo_count, job->inference_done_fence);
 
 err_unlock:
-	drm_gem_unlock_reservations(bos, job->in_bo_count + job->out_bo_count, &acquire_ctx);
+	drm_gem_unlock_reservations(bos, bo_count, &acquire_ctx);
 err:
 	kvfree(bos);
 
author maintainer
#294 lore raw

PATCH 294/713 accel/rocket: initialize job domain before cleanup paths

In reply to Greg Kroah-Hartman

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

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

From: Shuvam Pandey <[email protected]>

commit 70e6a33d68a9b03335c5426332666e52d07f45d6 upstream.

rocket_ioctl_submit_job() releases rjob through rocket_job_put() on allocation error paths. rocket_job_cleanup() unconditionally calls rocket_iommu_domain_put(job->domain), but job->domain is assigned only after task copying and BO lookups. A failure before that assignment can therefore clean up a job with a NULL domain pointer.

Take the per-file domain reference before the first error path can release rjob. Also clear rjob->tasks after freeing it in rocket_copy_tasks(), so the common cleanup path cannot free the task array again after a task-copy error.

---
 drivers/accel/rocket/rocket_job.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/accel/rocket/rocket_job.c +2 -2
@@ -103,6 +103,7 @@ rocket_copy_tasks(struct drm_device *dev
 
 fail:
 	kvfree(rjob->tasks);
+	rjob->tasks = NULL;
 	return ret;
 }
 
@@ -555,6 +556,7 @@ static int rocket_ioctl_submit_job(struc
 	kref_init(&rjob->refcount);
 
 	rjob->rdev = rdev;
+	rjob->domain = rocket_iommu_domain_get(file_priv);
 
 	ret = drm_sched_job_init(&rjob->base,
 				 &file_priv->sched_entity,
@@ -580,8 +582,6 @@ static int rocket_ioctl_submit_job(struc
 
 	rjob->out_bo_count = job->out_bo_handle_count;
 
-	rjob->domain = rocket_iommu_domain_get(file_priv);
-
 	ret = rocket_job_push(rjob);
 	if (ret)
 		goto out_cleanup_job;
author maintainer
#295 lore raw

PATCH 295/713 accel/rocket: Fix error path handling in rocket_job_run()

In reply to Greg Kroah-Hartman

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

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

From: ZhaoJinming <[email protected]>

commit 9b2dedadf6a91ac3fc9fae268bb556a041222711 upstream.

In rocket_job_run(), after taking an extra fence reference for job->done_fence via dma_fence_get(), the error paths have three bugs:

- The dma_fence reference held by job->done_fence is never released, causing a reference leak.
- pm_runtime_get_sync() increments the usage counter even on failure, but the error path does not decrement it, leaking the runtime PM reference and preventing the NPU from suspending.
- A valid but unsignaled fence is returned to the DRM scheduler, which triggers WARN("Fence ... released with pending signals!") when the scheduler drops its reference.

Fix by replacing pm_runtime_get_sync() with pm_runtime_resume_and_get() which auto-balances the usage counter on failure, releasing both fence references on error, and returning ERR_PTR(ret) instead of the unsignaled fence.

Fixes 0810d5ad88a1 ("accel/rocket: Add job submission IOCTL")
[tomeu: Refactored error paths to use consolidated goto labels]
---
 drivers/accel/rocket/rocket_job.c |   14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)
drivers/accel/rocket/rocket_job.c +11 -3
@@ -317,13 +317,13 @@ static struct dma_fence *rocket_job_run(
 		dma_fence_put(job->done_fence);
 	job->done_fence = dma_fence_get(fence);
 
-	ret = pm_runtime_get_sync(core->dev);
+	ret = pm_runtime_resume_and_get(core->dev);
 	if (ret < 0)
-		return fence;
+		goto err_put_fences;
 
 	ret = iommu_attach_group(job->domain->domain, core->iommu_group);
 	if (ret < 0)
-		return fence;
+		goto err_put_pm;
 
 	scoped_guard(mutex, &core->job_lock) {
 		core->in_flight_job = job;
@@ -331,6 +331,14 @@ static struct dma_fence *rocket_job_run(
 	}
 
 	return fence;
+
+err_put_pm:
+	pm_runtime_put(core->dev);
+err_put_fences:
+	dma_fence_put(job->done_fence);
+	job->done_fence = NULL;
+	dma_fence_put(fence);
+	return ERR_PTR(ret);
 }
 
 static void rocket_job_handle_irq(struct rocket_core *core)
author maintainer
#296 lore raw

PATCH 296/713 acpi/apei/ghes: Use raw_spinlock_t for CXL CPER work locks

In reply to Greg Kroah-Hartman

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

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

From: Terry Bowman <[email protected]>

commit 6625ca499c3131ef63be3215f8f942d7a097ea3a upstream.

The CXL CPER work registration and unregistration helpers acquire cxl_cper_work_lock and cxl_cper_prot_err_work_lock with a spinlock guard(), which leaves local interrupts enabled. The corresponding post paths (cxl_cper_post_event(), cxl_cper_post_prot_err()) execute in hard IRQ context (they are called from the GHES error notification path) and acquire the same locks with an irqsave guard().

If a CPU is holding one of these locks via a spinlock guard() when a GHES interrupt arrives on the same CPU, the IRQ handler spins on the held lock waiting for it to release, while the lock holder is preempted by the IRQ. The result is a deadlock.

Convert both locks from spinlock_t to raw_spinlock_t and use guard() at all call sites. On PREEMPT_RT kernels spinlock_t is backed by rt_mutex and sleeping from hard IRQ context is not permitted; raw_spinlock_t is safe in both contexts.

Add WARN_ONCE to both register functions to surface double-registration bugs at runtime.

Restructure both unregister functions to clear the global work pointer under the lock before calling cancel_work_sync(), closing the window where a CPER interrupt could schedule work on a pointer about to be freed. Add kfifo_reset() after cancel_work_sync() so stale entries are not replayed on next module load.

Both kfifos are single-consumer: only one work_struct is registered at a time, enforced by the WARN_ONCE guard in the register functions. kfifo_reset() is safe outside the lock because cancel_work_sync() has already quiesced the consumer, and no new consumer can register until the current module exit completes and a fresh module init runs.

Remove the redundant cancel_work_sync() call from cxl_ras_exit() and cxl_pci_driver_exit(). The CPER unregister functions now quiesce the work internally.

Fixes 5e4a264bf8b5 ("acpi/ghes: Process CXL Component Events")
Fixes 36f257e3b0ba ("acpi/ghes, cxl/pci: Process CXL CPER Protocol Errors")
---
 drivers/acpi/apei/ghes.c |   50 +++++++++++++++++++++++++++++++----------------
 drivers/cxl/core/ras.c   |    1 
 drivers/cxl/pci.c        |    1 
 3 files changed, 33 insertions(+), 19 deletions(-)
drivers/acpi/apei/ghes.c +33 -17
@@ -749,7 +749,7 @@ static DEFINE_KFIFO(cxl_cper_prot_err_fi
 		    CXL_CPER_PROT_ERR_FIFO_DEPTH);
 
 /* Synchronize schedule_work() with cxl_cper_prot_err_work changes */
-static DEFINE_SPINLOCK(cxl_cper_prot_err_work_lock);
+static DEFINE_RAW_SPINLOCK(cxl_cper_prot_err_work_lock);
 struct work_struct *cxl_cper_prot_err_work;
 
 static void cxl_cper_post_prot_err(struct cxl_cper_sec_prot_err *prot_err,
@@ -761,7 +761,7 @@ static void cxl_cper_post_prot_err(struc
 	if (cxl_cper_sec_prot_err_valid(prot_err))
 		return;
 
-	guard(spinlock_irqsave)(&cxl_cper_prot_err_work_lock);
+	guard(raw_spinlock_irqsave)(&cxl_cper_prot_err_work_lock);
 
 	if (!cxl_cper_prot_err_work)
 		return;
@@ -780,10 +780,11 @@ static void cxl_cper_post_prot_err(struc
 
 int cxl_cper_register_prot_err_work(struct work_struct *work)
 {
-	if (cxl_cper_prot_err_work)
-		return -EINVAL;
+	guard(raw_spinlock_irqsave)(&cxl_cper_prot_err_work_lock);
 
-	guard(spinlock)(&cxl_cper_prot_err_work_lock);
+	if (WARN_ONCE(cxl_cper_prot_err_work,
+		      "CPER-CXL kfifo consumer already registered\n"))
+		return -EINVAL;
 	cxl_cper_prot_err_work = work;
 	return 0;
 }
@@ -791,11 +792,18 @@ EXPORT_SYMBOL_NS_GPL(cxl_cper_register_p
 
 int cxl_cper_unregister_prot_err_work(struct work_struct *work)
 {
-	if (cxl_cper_prot_err_work != work)
-		return -EINVAL;
+	scoped_guard(raw_spinlock_irqsave, &cxl_cper_prot_err_work_lock) {
+		if (WARN_ONCE(cxl_cper_prot_err_work != work,
+			      "CPER-CXL kfifo consumer mismatch on unregister\n"))
+			return -EINVAL;
+		cxl_cper_prot_err_work = NULL;
+	}
+
+	cancel_work_sync(work);
+
+	/* Discard stale entries so they are not replayed on next module load */
+	kfifo_reset(&cxl_cper_prot_err_fifo);
 
-	guard(spinlock)(&cxl_cper_prot_err_work_lock);
-	cxl_cper_prot_err_work = NULL;
 	return 0;
 }
 EXPORT_SYMBOL_NS_GPL(cxl_cper_unregister_prot_err_work, "CXL");
@@ -811,7 +819,7 @@ EXPORT_SYMBOL_NS_GPL(cxl_cper_prot_err_k
 DEFINE_KFIFO(cxl_cper_fifo, struct cxl_cper_work_data, CXL_CPER_FIFO_DEPTH);
 
 /* Synchronize schedule_work() with cxl_cper_work changes */
-static DEFINE_SPINLOCK(cxl_cper_work_lock);
+static DEFINE_RAW_SPINLOCK(cxl_cper_work_lock);
 struct work_struct *cxl_cper_work;
 
 static void cxl_cper_post_event(enum cxl_event_type event_type,
@@ -831,7 +839,7 @@ static void cxl_cper_post_event(enum cxl
 		return;
 	}
 
-	guard(spinlock_irqsave)(&cxl_cper_work_lock);
+	guard(raw_spinlock_irqsave)(&cxl_cper_work_lock);
 
 	if (!cxl_cper_work)
 		return;
@@ -849,10 +857,11 @@ static void cxl_cper_post_event(enum cxl
 
 int cxl_cper_register_work(struct work_struct *work)
 {
-	if (cxl_cper_work)
+	guard(raw_spinlock_irqsave)(&cxl_cper_work_lock);
+	if (WARN_ONCE(cxl_cper_work,
+		      "CXL CPER kfifo consumer already registered\n"))
 		return -EINVAL;
 
-	guard(spinlock)(&cxl_cper_work_lock);
 	cxl_cper_work = work;
 	return 0;
 }
@@ -860,11 +869,18 @@ EXPORT_SYMBOL_NS_GPL(cxl_cper_register_w
 
 int cxl_cper_unregister_work(struct work_struct *work)
 {
-	if (cxl_cper_work != work)
-		return -EINVAL;
+	scoped_guard(raw_spinlock_irqsave, &cxl_cper_work_lock) {
+		if (WARN_ONCE(cxl_cper_work != work,
+			      "CXL CPER kfifo consumer mismatch on unregister\n"))
+			return -EINVAL;
+		cxl_cper_work = NULL;
+	}
+
+	cancel_work_sync(work);
+
+	/* Discard stale entries so they are not replayed on next module load */
+	kfifo_reset(&cxl_cper_fifo);
 
-	guard(spinlock)(&cxl_cper_work_lock);
-	cxl_cper_work = NULL;
 	return 0;
 }
 EXPORT_SYMBOL_NS_GPL(cxl_cper_unregister_work, "CXL");
drivers/cxl/core/ras.c +0 -1
@@ -137,7 +137,6 @@ int cxl_ras_init(void)
 void cxl_ras_exit(void)
 {
 	cxl_cper_unregister_prot_err_work(&cxl_cper_prot_err_work);
-	cancel_work_sync(&cxl_cper_prot_err_work);
 }
 
 static void cxl_dport_map_ras(struct cxl_dport *dport)
drivers/cxl/pci.c +0 -1
@@ -1083,7 +1083,6 @@ static int __init cxl_pci_driver_init(vo
 static void __exit cxl_pci_driver_exit(void)
 {
 	cxl_cper_unregister_work(&cxl_cper_work);
-	cancel_work_sync(&cxl_cper_work);
 	pci_unregister_driver(&cxl_pci_driver);
 }
 
author maintainer
#297 lore raw

PATCH 297/713 ACPI: APEI: Fix ERST timeout unit conversion

In reply to Greg Kroah-Hartman

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

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

From: Nirmoy Das <[email protected]>

commit a685d8eea4a6899dc887e393927c16fa18ff5e9a upstream.

The ACPI specification defines bits 63:32 returned by GET_EXECUTE_OPERATION_TIMINGS as the maximum execution time in microseconds. erst_get_timeout() instead multiplies the value by NSEC_PER_MSEC.

Use NSEC_PER_USEC to express the firmware-provided microsecond timeout in the nanosecond units expected by erst_timedout().

---
 drivers/acpi/apei/erst.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/acpi/apei/erst.c +1 -1
@@ -108,7 +108,7 @@ static inline u64 erst_get_timeout(void)
 
 	if (erst_erange.attr & ERST_RANGE_SLOW) {
 		timeout = ((erst_erange.timings & ERST_EXEC_TIMING_MAX_MASK) >>
-			ERST_EXEC_TIMING_MAX_SHIFT) * NSEC_PER_MSEC;
+			ERST_EXEC_TIMING_MAX_SHIFT) * NSEC_PER_USEC;
 		if (timeout < FIRMWARE_TIMEOUT)
 			timeout = FIRMWARE_TIMEOUT;
 	}
author maintainer
#298 lore raw

PATCH 298/713 ACPI: APEI: GHES: fix ARM section length accounting after header

In reply to Greg Kroah-Hartman

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

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

From: TanZheng <[email protected]>

commit 903308ea40adf0577d82eab69882faf8836326ce upstream.

In ghes_handle_arm_hw_error(), after skipping the cper_sec_proc_arm header with (err + 1), the remaining length was reduced by sizeof(err) (pointer size) instead of sizeof(*err) (structure size).

That overestimates the bytes left for cper_arm_err_info records and can let the parser read past the CPER section when err_info_num is large enough relative to error_data_length.

Use sizeof(*err) so the length accounting matches the pointer advance and the earlier sizeof(*err) size check.

Fixes 87880af2d24e ("APEI/GHES: ARM processor Error: don't go past allocated memory")
---
 drivers/acpi/apei/ghes.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/acpi/apei/ghes.c +1 -1
@@ -576,7 +576,7 @@ static bool ghes_handle_arm_hw_error(str
 		return false;
 
 	p = (char *)(err + 1);
-	length -= sizeof(err);
+	length -= sizeof(*err);
 
 	for (i = 0; i < err->err_info_num; i++) {
 		struct cper_arm_err_info *err_info;
author maintainer
#299 lore raw

PATCH 299/713 ACPI: CPPC: Reject desired_perf reads on _CPC revision 4+

In reply to Greg Kroah-Hartman

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

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

From: Christian Loehle <[email protected]>

commit df5a1d4a8cdfda20eb2581a85e81c7d436866534 upstream.

When CPPC feedback counters cannot provide a usable sample, cppc-cpufreq calls cppc_get_desired_perf() because some platforms repurpose Desired Performance to report actual delivered performance.

ACPI 6.5 defines _CPC revision 3 and lists Read/Write as the Optional Attribute of Desired Performance. ACPI 6.6 advances _CPC to revision 4 and lists only Write, so invoking that workaround for revision 4 or later would require a register read that the interface no longer specifies.

Make cppc_get_desired_perf() return -EOPNOTSUPP for _CPC revision 4 or later. Use the revision retained in the per-CPU CPC descriptor rather than the platform-wide FADT revision.

The _CPC revision may still not accurately describe the implemented register semantics. If a nominally revision 3 platform implements a non-readable Desired Performance register, a read may return zero and make cppc_cpufreq_get_rate() report 0 kHz. Treat a zero read as unusable and fall back to the cached OSPM request, just as for a failed read.

Fixes c47195631960 ("cppc_cpufreq: Use desired perf if feedback ctrs are 0 or unchanged")
---
 drivers/acpi/cppc_acpi.c       |   17 ++++++++++++++++-
 drivers/cpufreq/cppc_cpufreq.c |    2 +-
 2 files changed, 17 insertions(+), 2 deletions(-)
drivers/acpi/cppc_acpi.c +16 -1
@@ -1316,15 +1316,30 @@ static int cppc_set_reg_val(int cpu, enu
 	return cpc_write(cpu, reg, val);
 }
 
+static bool cppc_desired_perf_readable(const struct cpc_desc *cpc_desc)
+{
+	return cpc_desc->version < CPPC_V4_REV;
+}
+
 /**
  * cppc_get_desired_perf - Get the desired performance register value.
  * @cpunum: CPU from which to get desired performance.
  * @desired_perf: Return address.
  *
- * Return: 0 for success, -EIO otherwise.
+ * Return: 0 for success, -EOPNOTSUPP for _CPC revision 4 or later, and a
+ * negative errno otherwise.
  */
 int cppc_get_desired_perf(int cpunum, u64 *desired_perf)
 {
+	struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum);
+
+	if (!cpc_desc)
+		return -ENODEV;
+
+	/* _CPC revision 4 no longer specifies Desired Performance as readable. */
+	if (!cppc_desired_perf_readable(cpc_desc))
+		return -EOPNOTSUPP;
+
 	return cppc_get_reg_val(cpunum, DESIRED_PERF, desired_perf);
 }
 EXPORT_SYMBOL_GPL(cppc_get_desired_perf);
drivers/cpufreq/cppc_cpufreq.c +1 -1
@@ -836,7 +836,7 @@ out_invalid_counters:
 	 * value first as some platforms may update the actual delivered perf
 	 * there; if failed, resort to the cached desired perf.
 	 */
-	if (cppc_get_desired_perf(cpu, &delivered_perf))
+	if (cppc_get_desired_perf(cpu, &delivered_perf) || !delivered_perf)
 		delivered_perf = cpu_data->perf_ctrls.desired_perf;
 
 	return cppc_perf_to_khz(&cpu_data->perf_caps, delivered_perf);
author maintainer
#300 lore raw

PATCH 300/713 ACPI: CPPC: Skip desired_perf read in cppc_get_perf()

In reply to Greg Kroah-Hartman

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

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

From: Christian Loehle <[email protected]>

commit d3576424e8fa702a15a1963718a07eb672db9be7 upstream.

ACPI 6.5 defines _CPC revision 3 and lists Read/Write as the Optional Attribute of Desired Performance. ACPI 6.6 advances _CPC to revision 4 and lists only Write. cppc_get_perf() nevertheless reads the register when initializing performance controls, even though cppc-cpufreq overwrites the value before using it.

Use the _CPC revision check from cppc_get_desired_perf() and leave desired_perf zero instead of reading it for _CPC revision 4 or later. Also exclude the register from PCC read-command detection so it cannot trigger an otherwise unnecessary read command.

---
 drivers/acpi/cppc_acpi.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)
drivers/acpi/cppc_acpi.c +5 -2
index 42aeb749ebe0..becb7e442b30 100644
@@ -1845,12 +1845,14 @@ int cppc_get_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
 	u64 desired_perf = 0, min = 0, max = 0, energy_perf = 0, auto_sel = 0;
 	int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu);
 	struct cppc_pcc_data *pcc_ss_data = NULL;
+	bool read_desired_perf;
 	int ret = 0, regs_in_pcc = 0;
 
 	if (!cpc_desc) {
 		pr_debug("No CPC descriptor for CPU:%d\n", cpu);
 		return -ENODEV;
 	}
+	read_desired_perf = cppc_desired_perf_readable(cpc_desc);
 
 	if (!perf_ctrls) {
 		pr_debug("Invalid perf_ctrls pointer\n");
@@ -1864,7 +1866,8 @@ int cppc_get_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
 	auto_sel_reg = &cpc_desc->cpc_regs[AUTO_SEL_ENABLE];
 
 	/* Are any of the regs PCC ?*/
-	if (CPC_IN_PCC(desired_perf_reg) || CPC_IN_PCC(min_perf_reg) ||
+	if ((read_desired_perf && CPC_IN_PCC(desired_perf_reg)) ||
+	    CPC_IN_PCC(min_perf_reg) ||
 	    CPC_IN_PCC(max_perf_reg) || CPC_IN_PCC(energy_perf_reg) ||
 	    CPC_IN_PCC(auto_sel_reg)) {
 		if (pcc_ss_id < 0) {
@@ -1896,7 +1899,7 @@ int cppc_get_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
 	}
 	perf_ctrls->min_perf = min;
 
-	if (CPC_SUPPORTED(desired_perf_reg)) {
+	if (read_desired_perf && CPC_SUPPORTED(desired_perf_reg)) {
 		ret = cpc_read(cpu, desired_perf_reg, &desired_perf);
 		if (ret)
 			goto out_err;
Signature · 1 line
2.55.0
author maintainer
#301 lore raw

PATCH 301/713 ACPI: pfr_update: fix stack buffer overflow in query_capability()

In reply to Greg Kroah-Hartman

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

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

From: Anirudh Prasad <[email protected]>

commit ced45be0073a8a31b30b4a7f68cd3a15734515de upstream.

query_capability() copies four ACPI buffer objects returned by the firmware _DSM into fixed-size u8[16] fields in struct pfru_update_cap_info using memcpy with the firmware-supplied length:

  memcpy(&cap_hdr->code_type,
         elements[CAP_CODE_TYPE_IDX].buffer.pointer,
         elements[CAP_CODE_TYPE_IDX].buffer.length);

The same pattern repeats for drv_type, platform_id, and oem_id. If the firmware returns buffer.length > 16 for any of these fields, memcpy writes past the destination array.

struct pfru_update_cap_info is stack-allocated in pfru_ioctl().

Confirmed with KASAN on 7.2-rc6: three stack-out-of-bounds reports are generated when a DSM returns 64-byte buffers, with writes reaching 44 bytes past the end of cap_hdr's [64, 156) frame window into adjacent stack redzones.

Introduce a helper pointer to out_obj->package.elements and use it to validate each buffer length against its destination field size before copying, returning -EINVAL if the firmware supplies an oversized buffer.

Fixes 0db89fa243e5 ("ACPI: Introduce Platform Firmware Runtime Update device driver")
Cc All applicable <[email protected]>
---
 drivers/acpi/pfr_update.c |   45 ++++++++++++++++++++++++---------------------
 1 file changed, 24 insertions(+), 21 deletions(-)
drivers/acpi/pfr_update.c +24 -21
@@ -120,7 +120,7 @@ static int query_capability(struct pfru_
 			    struct pfru_device *pfru_dev)
 {
 	acpi_handle handle = ACPI_HANDLE(pfru_dev->parent_dev);
-	union acpi_object *out_obj;
+	union acpi_object *out_obj, *elem;
 	int ret = -EINVAL;
 
 	out_obj = acpi_evaluate_dsm_typed(handle, &pfru_guid,
@@ -150,7 +150,9 @@ static int query_capability(struct pfru_
 		goto free_acpi_buffer;
 	}
 
-	cap_hdr->status = out_obj->package.elements[CAP_STATUS_IDX].integer.value;
+	elem = out_obj->package.elements;
+
+	cap_hdr->status = elem[CAP_STATUS_IDX].integer.value;
 	if (cap_hdr->status != DSM_SUCCEED) {
 		ret = -EBUSY;
 		dev_dbg(pfru_dev->parent_dev, "Query cap Error Status:%d\n",
@@ -158,29 +160,30 @@ static int query_capability(struct pfru_
 		goto free_acpi_buffer;
 	}
 
-	cap_hdr->update_cap = out_obj->package.elements[CAP_UPDATE_IDX].integer.value;
+	if (elem[CAP_CODE_TYPE_IDX].buffer.length > sizeof(cap_hdr->code_type) ||
+	    elem[CAP_DRV_TYPE_IDX].buffer.length > sizeof(cap_hdr->drv_type) ||
+	    elem[CAP_PLAT_ID_IDX].buffer.length > sizeof(cap_hdr->platform_id) ||
+	    elem[CAP_OEM_ID_IDX].buffer.length > sizeof(cap_hdr->oem_id))
+		goto free_acpi_buffer;
+
+	cap_hdr->update_cap = elem[CAP_UPDATE_IDX].integer.value;
 	memcpy(&cap_hdr->code_type,
-	       out_obj->package.elements[CAP_CODE_TYPE_IDX].buffer.pointer,
-	       out_obj->package.elements[CAP_CODE_TYPE_IDX].buffer.length);
-	cap_hdr->fw_version =
-		out_obj->package.elements[CAP_FW_VER_IDX].integer.value;
-	cap_hdr->code_rt_version =
-		out_obj->package.elements[CAP_CODE_RT_VER_IDX].integer.value;
+	       elem[CAP_CODE_TYPE_IDX].buffer.pointer,
+	       elem[CAP_CODE_TYPE_IDX].buffer.length);
+	cap_hdr->fw_version = elem[CAP_FW_VER_IDX].integer.value;
+	cap_hdr->code_rt_version = elem[CAP_CODE_RT_VER_IDX].integer.value;
 	memcpy(&cap_hdr->drv_type,
-	       out_obj->package.elements[CAP_DRV_TYPE_IDX].buffer.pointer,
-	       out_obj->package.elements[CAP_DRV_TYPE_IDX].buffer.length);
-	cap_hdr->drv_rt_version =
-		out_obj->package.elements[CAP_DRV_RT_VER_IDX].integer.value;
-	cap_hdr->drv_svn =
-		out_obj->package.elements[CAP_DRV_SVN_IDX].integer.value;
+	       elem[CAP_DRV_TYPE_IDX].buffer.pointer,
+	       elem[CAP_DRV_TYPE_IDX].buffer.length);
+	cap_hdr->drv_rt_version = elem[CAP_DRV_RT_VER_IDX].integer.value;
+	cap_hdr->drv_svn = elem[CAP_DRV_SVN_IDX].integer.value;
 	memcpy(&cap_hdr->platform_id,
-	       out_obj->package.elements[CAP_PLAT_ID_IDX].buffer.pointer,
-	       out_obj->package.elements[CAP_PLAT_ID_IDX].buffer.length);
+	       elem[CAP_PLAT_ID_IDX].buffer.pointer,
+	       elem[CAP_PLAT_ID_IDX].buffer.length);
 	memcpy(&cap_hdr->oem_id,
-	       out_obj->package.elements[CAP_OEM_ID_IDX].buffer.pointer,
-	       out_obj->package.elements[CAP_OEM_ID_IDX].buffer.length);
-	cap_hdr->oem_info_len =
-		out_obj->package.elements[CAP_OEM_INFO_IDX].buffer.length;
+	       elem[CAP_OEM_ID_IDX].buffer.pointer,
+	       elem[CAP_OEM_ID_IDX].buffer.length);
+	cap_hdr->oem_info_len = elem[CAP_OEM_INFO_IDX].buffer.length;
 
 	ret = 0;
 
author maintainer
#302 lore raw

PATCH 302/713 ACPI: scan: Avoid registering platform devices with resource overlaps

In reply to Greg Kroah-Hartman

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

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

From: Rafael J. Wysocki <[email protected]>

commit f234fdaae1cad8c39265e7ca0a14633076ec7154 upstream.

If acpi_dev_get_resources() returns overlapping I/O or memory resources, the subsequent registration of a platform device will fail with -EBUSY due to a resource conflict. This is reported to happen on Acer Aspire ES1-572 [1].

Avoid that by adjusting resources returned by acpi_dev_get_resources() to eliminate partial overlaps between them.

This has not been regarded as necessary before because putting overlapping resources into the _CRS of one device is really pointless, but now that the issue has been reported to actually happen in the field, it needs to be done.

Fixes ab06eb920401 ("ACPI: scan: Register platform devices for fixed event buttons")
Fixes 48fe2cddc85c ("tpm_crb: Convert ACPI driver to a platform one")
Cc All applicable <[email protected]>
[ rjw: Tweaked the new message ]
---
 drivers/acpi/acpi_platform.c | 39 ++++++++++++++++++++++++++++++++++--
 1 file changed, 37 insertions(+), 2 deletions(-)
drivers/acpi/acpi_platform.c +37 -2
index a09636a4168e..373c94de7590 100644
@@ -12,6 +12,7 @@
 #include <linux/bits.h>
 #include <linux/device.h>
 #include <linux/err.h>
+#include <linux/ioport.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/dma-mapping.h>
@@ -71,6 +72,36 @@ static struct notifier_block acpi_platform_notifier = {
 	.notifier_call = acpi_platform_device_remove_notify,
 };
 
+static unsigned int acpi_platform_adjust_resources(struct acpi_device *adev,
+						   struct resource *new_res,
+						   struct resource *resources,
+						   unsigned int count)
+{
+	unsigned int i;
+
+	if (!(new_res->flags & (IORESOURCE_IO | IORESOURCE_MEM)))
+		return count;
+
+	for (i = 0; i < count; ) {
+		struct resource *res = &resources[i];
+
+		if (resource_type(new_res) != resource_type(res) ||
+		    !resource_union(new_res, res, new_res)) {
+			i++;
+			continue;
+		}
+
+		dev_info(&adev->dev, "%pR expanded due to overlap\n", new_res);
+		/*
+		 * Eliminate the previously processed resource that overlapped
+		 * with the new one because it is not necessary any more.
+		 */
+		memmove(res, res + 1, (--count - i) * sizeof(*res));
+	}
+
+	return count;
+}
+
 static void acpi_platform_fill_resource(struct acpi_device *adev,
 	const struct resource *src, struct resource *dest)
 {
@@ -151,10 +182,14 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev,
 				return ERR_PTR(-ENOMEM);
 			}
 			count = 0;
-			list_for_each_entry(rentry, &resource_list, node)
+			list_for_each_entry(rentry, &resource_list, node) {
+				count = acpi_platform_adjust_resources(adev,
+								       rentry->res,
+								       resources,
+								       count);
 				acpi_platform_fill_resource(adev, rentry->res,
 							    &resources[count++]);
-
+			}
 			acpi_dev_free_resource_list(&resource_list);
 		}
 	}
Signature · 1 line
2.55.0
author maintainer
#303 lore raw

PATCH 303/713 ACPI: TAD: Add locking around AML evaluations

In reply to Greg Kroah-Hartman

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

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

From: Rafael J. Wysocki <[email protected]>

commit a3df8bbe0a704fa5c1609b9666b594f350558fe0 upstream.

In the ACPI TAD driver, there are hidden assumptions that the ACPI control methods used by it will not be evaluated concurrently due to ACPICA namespace and interpreter locking.

However, that may not be the case since ACPICA may drop and re-acquire the namespace and interpreter locks during the evaluation of a given object in a few cases, including the one in which the AML in question sleeps causing acpi_ex_system_do_sleep() to be called. In that case, the evaluation of one control method may be started while the evaluation of another one is still in progress.

For this reason, add a global lock to the ACPI TAD driver and acquire it every time before evaluating an ACPI control method, except for the initial evaluation of _GCP in acpi_tad_probe().

Fixes 95c513ec84f7 ("ACPI: Add Time and Alarm Device (TAD) driver")
Cc All applicable <[email protected]>
---
 drivers/acpi/acpi_tad.c |   48 +++++++++++++++++++++++++++++-------------------
 1 file changed, 29 insertions(+), 19 deletions(-)
drivers/acpi/acpi_tad.c +29 -19
@@ -27,6 +27,7 @@
 #include <linux/kernel.h>
 #include <linux/ktime.h>
 #include <linux/module.h>
+#include <linux/mutex.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/rtc.h>
@@ -86,6 +87,8 @@ static bool acpi_tad_rt_is_invalid(struc
 	    rt->daylight > 3;
 }
 
+static DEFINE_MUTEX(acpi_tad_aml_lock);
+
 static int acpi_tad_set_real_time(struct device *dev, struct acpi_tad_rt *rt)
 {
 	acpi_handle handle = ACPI_HANDLE(dev);
@@ -113,6 +116,8 @@ static int acpi_tad_set_real_time(struct
 	if (PM_RUNTIME_ACQUIRE_ERR(&pm))
 		return -ENXIO;
 
+	guard(mutex)(&acpi_tad_aml_lock);
+
 	status = acpi_evaluate_integer(handle, "_SRT", &arg_list, &retval);
 	if (ACPI_FAILURE(status) || retval)
 		return -EIO;
@@ -124,30 +129,27 @@ static int acpi_tad_evaluate_grt(struct
 {
 	acpi_handle handle = ACPI_HANDLE(dev);
 	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER };
-	union acpi_object *out_obj;
-	struct acpi_tad_rt *data;
 	acpi_status status;
 	int ret = -EIO;
 
-	status = acpi_evaluate_object(handle, "_GRT", NULL, &output);
-	if (ACPI_FAILURE(status))
-		goto out_free;
-
-	out_obj = output.pointer;
-	if (out_obj->type != ACPI_TYPE_BUFFER)
-		goto out_free;
-
-	if (out_obj->buffer.length != sizeof(*rt))
-		goto out_free;
-
-	data = (struct acpi_tad_rt *)(out_obj->buffer.pointer);
-	if (!data->valid)
-		goto out_free;
+	guard(mutex)(&acpi_tad_aml_lock);
 
-	memcpy(rt, data, sizeof(*rt));
-	ret = 0;
+	status = acpi_evaluate_object(handle, "_GRT", NULL, &output);
+	if (ACPI_SUCCESS(status)) {
+		union acpi_object *out_obj;
 
-out_free:
+		out_obj = output.pointer;
+		if (out_obj->type == ACPI_TYPE_BUFFER &&
+		    out_obj->buffer.length == sizeof(*rt)) {
+			struct acpi_tad_rt *data;
+
+			data = (struct acpi_tad_rt *)(out_obj->buffer.pointer);
+			if (data->valid) {
+				memcpy(rt, data, sizeof(*rt));
+				ret = 0;
+			}
+		}
+	}
 	ACPI_FREE(output.pointer);
 	return ret;
 }
@@ -193,6 +195,8 @@ static int __acpi_tad_wake_set(struct de
 	args[0].integer.value = timer_id;
 	args[1].integer.value = value;
 
+	guard(mutex)(&acpi_tad_aml_lock);
+
 	status = acpi_evaluate_integer(handle, method, &arg_list, &retval);
 	if (ACPI_FAILURE(status) || retval)
 		return -EIO;
@@ -215,6 +219,8 @@ static int __acpi_tad_wake_read(struct d
 
 	args[0].integer.value = timer_id;
 
+	guard(mutex)(&acpi_tad_aml_lock);
+
 	status = acpi_evaluate_integer(handle, method, &arg_list, retval);
 	if (ACPI_FAILURE(status))
 		return -EIO;
@@ -416,6 +422,8 @@ static int acpi_tad_clear_status(struct
 	if (PM_RUNTIME_ACQUIRE_ERR(&pm))
 		return -ENXIO;
 
+	guard(mutex)(&acpi_tad_aml_lock);
+
 	status = acpi_evaluate_integer(handle, "_CWS", &arg_list, &retval);
 	if (ACPI_FAILURE(status) || retval)
 		return -EIO;
@@ -456,6 +464,8 @@ static ssize_t acpi_tad_status_read(stru
 	if (PM_RUNTIME_ACQUIRE_ERR(&pm))
 		return -ENXIO;
 
+	guard(mutex)(&acpi_tad_aml_lock);
+
 	status = acpi_evaluate_integer(handle, "_GWS", &arg_list, &retval);
 	if (ACPI_FAILURE(status))
 		return -EIO;
author maintainer
#304 lore raw

PATCH 304/713 alpha/PCI: Fix I/O port accessor argument order in pci_legacy_write()

In reply to Greg Kroah-Hartman

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

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

From: Krzysztof Wilczyński <[email protected]>

commit 651fb94aaf245430590216d497fb8b02dd73d5f9 upstream.

pci_legacy_write() in arch/alpha/kernel/pci-sysfs.c passes its arguments to outb(), outw() and outl() in the wrong order:

  outb(port, val);

The Alpha I/O accessors in arch/alpha/include/asm/io.h take the value first and the port second:

extern void outb(u8 b, unsigned long port);

So the port number is written as data to the I/O address taken from the user-supplied value, and the intended write to the requested port never happens.

The arguments have been reversed since the file was added, and the function returns the access size regardless, so the caller sees success while the requested port is left untouched.

---
 arch/alpha/kernel/pci-sysfs.c |    6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
arch/alpha/kernel/pci-sysfs.c +3 -3
@@ -224,17 +224,17 @@ int pci_legacy_write(struct pci_bus *bus
 
 	switch(size) {
 	case 1:
-		outb(port, val);
+		outb(val, port);
 		return 1;
 	case 2:
 		if (port & 1)
 			return -EINVAL;
-		outw(port, val);
+		outw(val, port);
 		return 2;
 	case 4:
 		if (port & 3)
 			return -EINVAL;
-		outl(port, val);
+		outl(val, port);
 		return 4;
 	}
 	return -EINVAL;
author maintainer
#305 lore raw

PATCH 305/713 alpha: marvel: Fix irq_set_status_flags to use correct IRQ number

In reply to Greg Kroah-Hartman

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

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

From: Matt Turner <[email protected]>

commit 3a3ac1f6c6a67b3803f2643584310f78301e58a8 upstream.

Pass base + i to irq_set_status_flags() to match the IRQ number used in irq_set_chip_and_handler(). Previously, IRQ_LEVEL was set on the wrong (low-numbered) IRQ descriptors rather than the IO7 IRQs at base + i.

---
 arch/alpha/kernel/sys_marvel.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
arch/alpha/kernel/sys_marvel.c +2 -2
@@ -275,7 +275,7 @@ init_io7_irqs(struct io7 *io7,
 	/* Set up the lsi irqs.  */
 	for (i = 0; i < 128; ++i) {
 		irq_set_chip_and_handler(base + i, lsi_ops, handle_level_irq);
-		irq_set_status_flags(i, IRQ_LEVEL);
+		irq_set_status_flags(base + i, IRQ_LEVEL);
 	}
 
 	/* Disable the implemented irqs in hardware.  */
@@ -289,7 +289,7 @@ init_io7_irqs(struct io7 *io7,
 	/* Set up the msi irqs.  */
 	for (i = 128; i < (128 + 512); ++i) {
 		irq_set_chip_and_handler(base + i, msi_ops, handle_level_irq);
-		irq_set_status_flags(i, IRQ_LEVEL);
+		irq_set_status_flags(base + i, IRQ_LEVEL);
 	}
 
 	for (i = 0; i < 16; ++i)
author maintainer
#306 lore raw

PATCH 306/713 alpha: marvel: Fix lock ordering in init_io7_irqs()

In reply to Greg Kroah-Hartman

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

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

From: Matt Turner <[email protected]>

commit 24d68db713d63dfe3660c56b50e887784844baea upstream.

Move irq_set_chip_and_handler() and irq_set_status_flags() calls outside the io7->irq_lock raw spinlock. These functions take sparse_irq_lock, which is a mutex, and taking a sleeping lock while holding a raw spinlock is invalid. The raw spinlock only needs to protect the hardware CSR accesses.

This fixes the following lockdep splat during boot:

[ BUG: Invalid wait context ]
swapper/0/0 is trying to lock:
sparse_irq_lock{....}-{4:4}, at: irq_mark_irq
other info that might help us debug this:
context-{5:5}
1 lock held by swapper/0/0:
#0: &io7->irq_lock{....}-{2:2}, at: init_io7_irqs.constprop.0

---
 arch/alpha/kernel/sys_marvel.c |   25 ++++++++++++-------------
 1 file changed, 12 insertions(+), 13 deletions(-)
arch/alpha/kernel/sys_marvel.c +12 -13
@@ -263,6 +263,18 @@ init_io7_irqs(struct io7 *io7,
 	 */
 	printk("  Interrupts reported to CPU at PE %u\n", boot_cpuid);
 
+	/* Set up the lsi irqs.  */
+	for (i = 0; i < 128; ++i) {
+		irq_set_chip_and_handler(base + i, lsi_ops, handle_level_irq);
+		irq_set_status_flags(base + i, IRQ_LEVEL);
+	}
+
+	/* Set up the msi irqs.  */
+	for (i = 128; i < (128 + 512); ++i) {
+		irq_set_chip_and_handler(base + i, msi_ops, handle_level_irq);
+		irq_set_status_flags(base + i, IRQ_LEVEL);
+	}
+
 	raw_spin_lock(&io7->irq_lock);
 
 	/* set up the error irqs */
@@ -272,12 +284,6 @@ init_io7_irqs(struct io7 *io7,
 	io7_redirect_irq(io7, &io7->csrs->STV_CTL.csr, boot_cpuid);
 	io7_redirect_irq(io7, &io7->csrs->HEI_CTL.csr, boot_cpuid);
 
-	/* Set up the lsi irqs.  */
-	for (i = 0; i < 128; ++i) {
-		irq_set_chip_and_handler(base + i, lsi_ops, handle_level_irq);
-		irq_set_status_flags(base + i, IRQ_LEVEL);
-	}
-
 	/* Disable the implemented irqs in hardware.  */
 	for (i = 0; i < 0x60; ++i) 
 		init_one_io7_lsi(io7, i, boot_cpuid);
@@ -285,13 +291,6 @@ init_io7_irqs(struct io7 *io7,
 	init_one_io7_lsi(io7, 0x74, boot_cpuid);
 	init_one_io7_lsi(io7, 0x75, boot_cpuid);
 
-
-	/* Set up the msi irqs.  */
-	for (i = 128; i < (128 + 512); ++i) {
-		irq_set_chip_and_handler(base + i, msi_ops, handle_level_irq);
-		irq_set_status_flags(base + i, IRQ_LEVEL);
-	}
-
 	for (i = 0; i < 16; ++i)
 		init_one_io7_msi(io7, i, boot_cpuid);
 
author maintainer
#307 lore raw

PATCH 307/713 ARM: 9477/1: Disable broken eBPF JIT on the Risc PC

In reply to Greg Kroah-Hartman

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

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

From: Ethan Nelson-Moore <[email protected]>

commit 7e8ee82e69fde9d589272ec5e6f702358903be1f upstream.

The eBPF JIT unconditionally generates ldrh/strh instructions, which do not function correctly on the Risc PC because its bus is unable to signal half-word accesses. Work around this issue by disabling the eBPF JIT when building for ARMv3 (the Risc PC is the only currently supported machine whose kernel is built for ARMv3).

Comments from Ethan Nelson-Moore:

From LKML: https://lore.kernel.org/all/CAD++jL=0qYGoygUwGEXQL7C_ROnC7kfpRv8RA+H5tNWwYu+pQA@mail.gmail.com/

The commit message has been updated slightly relative to the version on LKML to clarify that the Risc PC is not actually ARMv3.

---
 arch/arm/Kconfig |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
arch/arm/Kconfig +1 -1
@@ -98,7 +98,7 @@ config ARM
 	select HAVE_ARCH_TRACEHOOK
 	select HAVE_ARCH_TRANSPARENT_HUGEPAGE if ARM_LPAE
 	select HAVE_ARM_SMCCC if CPU_V7
-	select HAVE_EBPF_JIT if !CPU_ENDIAN_BE32
+	select HAVE_EBPF_JIT if !CPU_ENDIAN_BE32 && !CPU_32v3
 	select HAVE_CONTEXT_TRACKING_USER
 	select HAVE_C_RECORDMCOUNT
 	select HAVE_BUILDTIME_MCOUNT_SORT
author maintainer
#308 lore raw

PATCH 308/713 ata: libata-scsi: fix DSM TRIM for sector sizes larger than 2048 bytes

In reply to Greg Kroah-Hartman

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

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

From: Niklas Cassel <[email protected]>

commit 79cce911e623c0baa0fde307ce3a434e084b881a upstream.

ata_scsi_write_same_xlat() translates a SCSI WRITE SAME command with the UNMAP bit set into an ATA DATA SET MANAGEMENT TRIM command. The TRIM descriptor is built by ata_format_dsm_trim_descr() into the 2048-byte ata_scsi_rbuf staging buffer, and the number of bytes copied is compared against the logical sector size by the caller:

	size = ata_format_dsm_trim_descr(scmd, trmax, block, n_block);
	if (size != len)		/* len == sdp->sector_size */
		goto invalid_param_len;

ata_format_dsm_trim_descr() clamps the copy length to ATA_SCSI_RBUF_SIZE (2048). On a device whose logical sector size exceeds that (e.g. a 4Kn device, where sector_size == 4096) the function can never return more than 2048, while the caller expects it to return sector_size. The comparison therefore always fails, so every TRIM is rejected with "Parameter list length error" and WARN_ON() splats on each attempt. TRIM / discard is thus completely broken on such devices.

The descriptor was incorrectly sized from the logical sector size. A DSM TRIM payload is a list of 512-byte pages, each holding up to ATA_MAX_TRIM_RNUM (64) LBA Range Entries, and is independent of the logical sector size. The Block Limits VPD page already advertises a single such page as the maximum WRITE SAME length (65535 * ATA_MAX_TRIM_RNUM logical blocks), so the block layer never sends a request that needs more than one page.

Emit exactly one 512-byte page, independent of the logical sector size, and transfer only that page (COUNT == 1). For a 512-byte-sector device this is unchanged; devices with larger logical sectors now work instead of failing every TRIM.

Fixes ef2d7392c4ec ("libata: SCT Write Same / DSM Trim")
---
 drivers/ata/libata-scsi.c |   28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)
drivers/ata/libata-scsi.c +14 -14
@@ -3545,17 +3545,13 @@ static unsigned int ata_scsi_pass_thru(s
 static size_t ata_format_dsm_trim_descr(struct scsi_cmnd *cmd, u32 trmax,
 					u64 sector, u32 count)
 {
-	struct scsi_device *sdp = cmd->device;
-	size_t len = sdp->sector_size;
+	size_t len = ATA_SECT_SIZE;
 	size_t r;
 	__le64 *buf;
 	u32 i = 0;
 	unsigned long flags;
 
-	WARN_ON(len > ATA_SCSI_RBUF_SIZE);
-
-	if (len > ATA_SCSI_RBUF_SIZE)
-		len = ATA_SCSI_RBUF_SIZE;
+	BUILD_BUG_ON(ATA_SECT_SIZE > ATA_SCSI_RBUF_SIZE);
 
 	spin_lock_irqsave(&ata_scsi_rbuf_lock, flags);
 	buf = ((void *)ata_scsi_rbuf);
@@ -3590,13 +3586,11 @@ static unsigned int ata_scsi_write_same_
 {
 	struct ata_taskfile *tf = &qc->tf;
 	struct scsi_cmnd *scmd = qc->scsicmd;
-	struct scsi_device *sdp = scmd->device;
-	size_t len = sdp->sector_size;
 	struct ata_device *dev = qc->dev;
 	const u8 *cdb = scmd->cmnd;
 	u64 block;
 	u32 n_block;
-	const u32 trmax = len >> 3;
+	const u32 trmax = ATA_MAX_TRIM_RNUM;
 	u32 size;
 	u16 fp;
 	u8 bp = 0xff;
@@ -3640,13 +3634,13 @@ static unsigned int ata_scsi_write_same_
 		goto invalid_param_len;
 
 	/*
-	 * size must match sector size in bytes
-	 * For DATA SET MANAGEMENT TRIM in ACS-2 nsect (aka count)
-	 * is defined as number of 512 byte blocks to be transferred.
+	 * The TRIM descriptor is a single 512-byte page, which is the maximum
+	 * WRITE SAME length advertised in the Block Limits VPD page. For DATA
+	 * SET MANAGEMENT TRIM the COUNT field (aka nsect) is the number of
+	 * 512-byte blocks to be transferred.
 	 */
-
 	size = ata_format_dsm_trim_descr(scmd, trmax, block, n_block);
-	if (size != len)
+	if (size != ATA_SECT_SIZE)
 		goto invalid_param_len;
 
 	if (ata_ncq_enabled(dev) && ata_fpdma_dsm_supported(dev)) {
@@ -3672,6 +3666,12 @@ static unsigned int ata_scsi_write_same_
 		     ATA_TFLAG_WRITE;
 
 	ata_qc_set_pc_nbytes(qc);
+	/*
+	 * The DSM TRIM payload is a single 512-byte page, which may be smaller
+	 * than the WRITE SAME data-out buffer (one logical block); only
+	 * transfer that page so the length matches the COUNT field.
+	 */
+	qc->nbytes = size;
 
 	return 0;
 
author maintainer
#309 lore raw

PATCH 309/713 auxdisplay: charlcd: cancel backlight work on registration failure

In reply to Greg Kroah-Hartman

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

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

From: Hongyan Xu <[email protected]>

commit e3e3bf40916c1e810df03958cfa7ba6883cdce79 upstream.

With CONFIG_CHARLCD_BL_FLASH, charlcd_init() schedules bl_work before charlcd_register() calls misc_register(). If registration fails, the caller frees the charlcd object while delayed work still contains its address.

Add charlcd_deinit() to cancel the delayed work and turn the backlight off. Use it for both registration rollback and normal unregistration.

Fixes 39f8ea46724e ("auxdisplay: charlcd: Extract character LCD core from misc/panel")
---
 drivers/auxdisplay/charlcd.c |   21 ++++++++++++++-------
 1 file changed, 14 insertions(+), 7 deletions(-)
drivers/auxdisplay/charlcd.c +14 -7
@@ -595,6 +595,16 @@ static int charlcd_init(struct charlcd *
 	return 0;
 }
 
+static void charlcd_deinit(struct charlcd *lcd)
+{
+	struct charlcd_priv *priv = charlcd_to_priv(lcd);
+
+	if (lcd->ops->backlight) {
+		cancel_delayed_work_sync(&priv->bl_work);
+		lcd->ops->backlight(lcd, CHARLCD_OFF);
+	}
+}
+
 struct charlcd *charlcd_alloc(unsigned int drvdata_size)
 {
 	struct charlcd_priv *priv;
@@ -654,8 +664,10 @@ int charlcd_register(struct charlcd *lcd
 		return ret;
 
 	ret = misc_register(&charlcd_dev);
-	if (ret)
+	if (ret) {
+		charlcd_deinit(lcd);
 		return ret;
+	}
 
 	the_charlcd = lcd;
 	register_reboot_notifier(&panel_notifier);
@@ -665,16 +677,11 @@ EXPORT_SYMBOL_GPL(charlcd_register);
 
 int charlcd_unregister(struct charlcd *lcd)
 {
-	struct charlcd_priv *priv = charlcd_to_priv(lcd);
-
 	unregister_reboot_notifier(&panel_notifier);
 	charlcd_puts(lcd, "\x0cLCD driver unloaded.\x1b[Lc\x1b[Lb\x1b[L-");
 	misc_deregister(&charlcd_dev);
 	the_charlcd = NULL;
-	if (lcd->ops->backlight) {
-		cancel_delayed_work_sync(&priv->bl_work);
-		priv->lcd.ops->backlight(&priv->lcd, CHARLCD_OFF);
-	}
+	charlcd_deinit(lcd);
 
 	return 0;
 }
author maintainer
#310 lore raw

PATCH 310/713 backlight: aw99706: Fix DT property names to match binding

In reply to Greg Kroah-Hartman

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

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

From: Junjie Cao <[email protected]>

commit daf120e464386f750b3870dc04d5bc6a786f0000 upstream.

The driver reads four tuning properties without the unit suffixes that the binding mandates: "awinic,sw-freq" instead of "awinic,sw-freq-hz", "awinic,sw-ilmt" instead of "awinic,sw-ilmt-microamp", "awinic,iled-max" instead of "awinic,iled-max-microamp", and "awinic,uvlo-thres" instead of "awinic,uvlo-thres-microvolt".

As a result, device_property_read_u32() never finds these properties in a binding-conformant device tree and silently falls back to the compiled-in defaults for switching frequency, switching current limit, max LED current, and UVLO threshold.

Fix by aligning the property name strings in aw99706_dt_props[] with the binding. No value/range changes are needed since both sides already use the same units and enumerations.

Fixes 147b38a5ad06 ("backlight: aw99706: Add support for Awinic AW99706 backlight")
---
 drivers/video/backlight/aw99706.c |    8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/video/backlight/aw99706.c +4 -4
@@ -130,23 +130,23 @@ static const struct aw99706_dt_prop aw99
 		AW99706_CFG0_REG, AW99706_DIM_MODE_MASK, 1,
 	},
 	{
-		"awinic,sw-freq", aw99706_dt_property_lookup,
+		"awinic,sw-freq-hz", aw99706_dt_property_lookup,
 		aw99706_sw_freq_tbl, ARRAY_SIZE(aw99706_sw_freq_tbl),
 		AW99706_CFG1_REG, AW99706_SW_FREQ_MASK, 750000,
 	},
 	{
-		"awinic,sw-ilmt", aw99706_dt_property_lookup,
+		"awinic,sw-ilmt-microamp", aw99706_dt_property_lookup,
 		aw99706_sw_ilmt_tbl, ARRAY_SIZE(aw99706_sw_ilmt_tbl),
 		AW99706_CFG1_REG, AW99706_SW_ILMT_MASK, 3000000,
 	},
 	{
-		"awinic,iled-max", aw99706_dt_property_iled_max_convert,
+		"awinic,iled-max-microamp", aw99706_dt_property_iled_max_convert,
 		NULL, 0,
 		AW99706_CFG2_REG, AW99706_ILED_MAX_MASK, 20000,
 
 	},
 	{
-		"awinic,uvlo-thres", aw99706_dt_property_lookup,
+		"awinic,uvlo-thres-microvolt", aw99706_dt_property_lookup,
 		aw99706_ulvo_thres_tbl, ARRAY_SIZE(aw99706_ulvo_thres_tbl),
 		AW99706_CFG2_REG, AW99706_UVLOSEL_MASK, 2200000,
 	},
author maintainer
#311 lore raw

PATCH 311/713 backlight: aw99706: Honor the core blank state in update_status()

In reply to Greg Kroah-Hartman

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

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

From: Junjie Cao <[email protected]>

commit fbf8b5d251e8b4e1b01c591be313c63cb9e1526a upstream.

update_status() passes props.brightness straight to the hardware and ignores the power/blank state tracked by the core. Writing 4 to the bl_power sysfs attribute or blanking the framebuffer therefore leaves the backlight lit.

Use backlight_get_brightness(), which returns 0 while the device is blanked.

Fixes 147b38a5ad06 ("backlight: aw99706: Add support for Awinic AW99706 backlight")
---
 drivers/video/backlight/aw99706.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/video/backlight/aw99706.c +1 -1
@@ -335,7 +335,7 @@ static int aw99706_bl_update_status(stru
 {
 	struct aw99706_device *aw = bl_get_data(bl);
 
-	return aw99706_update_brightness(aw, bl->props.brightness);
+	return aw99706_update_brightness(aw, backlight_get_brightness(bl));
 }
 
 static const struct backlight_ops aw99706_bl_ops = {
author maintainer
#312 lore raw

PATCH 312/713 block: validate user space vectors during extraction

In reply to Greg Kroah-Hartman

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

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

From: Keith Busch <[email protected]>

commit 14b007e178811db72fbb1ebb3535160db6ec1e6a upstream.

The bio-based drivers don't necessarily check the alignment split, and stacking block drivers don't always handle a misalignment detected after submitting the bio. Validate user vectors against the device's dma_alignment as the bio is built from the iov_iter, rejecting misaligned early with -EINVAL.

Fixes 5ff3f74e145a ("block: simplify direct io validity check")
Fixes 7eac33186957 ("iomap: simplify direct io validity check")
---
 block/bio.c          |   56 ++++++++++++++++++++++++++++++++++++++++++++++++---
 block/blk-map.c      |    2 -
 block/fops.c         |    2 -
 fs/iomap/direct-io.c |    1 
 include/linux/bio.h  |    2 -
 include/linux/uio.h  |   10 ++++++++-
 lib/iov_iter.c       |    9 +++++++-
 7 files changed, 74 insertions(+), 8 deletions(-)
block/bio.c +53 -3
@@ -1221,10 +1221,45 @@ static int bio_iov_iter_align_down(struc
 	return 0;
 }
 
+#ifdef CONFIG_DEBUG_KERNEL
+static inline bool bio_iov_bvec_aligned(const struct bio *bio,
+					unsigned mem_align_mask)
+{
+	struct bvec_iter iter;
+	struct bio_vec bv;
+
+	/*
+	 * Correct callers never break the alignment requirements, so this
+	 * exhaustive check is only paid for in debug builds.
+	 */
+	for_each_mp_bvec(bv, bio->bi_io_vec, iter, bio->bi_iter)
+		if ((bv.bv_offset | bv.bv_len) & mem_align_mask)
+			return false;
+	return true;
+}
+#else
+static inline bool bio_iov_bvec_aligned(const struct bio *bio,
+					unsigned mem_align_mask)
+{
+	/*
+	 * We forward the bio_vec as-is, so ITER_BVEC callers must provide
+	 * segments already aligned to the device's DMA alignment. The only
+	 * unchecked user-controllable offset that reaches here is an io_uring
+	 * registered buffer where just the first segment can be unaligned
+	 * (the rest is virtually contiguous), so checking only that one is
+	 * sufficient to know if the entire vector is valid.
+	 */
+	return !(mp_bvec_iter_offset(bio->bi_io_vec, bio->bi_iter) &
+							mem_align_mask);
+}
+#endif
+
 /**
  * bio_iov_iter_get_pages - add user or kernel pages to a bio
  * @bio: bio to add pages to
  * @iter: iov iterator describing the region to be added
+ * @mem_align_mask: the mask the source address and length must be aligned to,
+ *	0 for no requirement
  * @len_align_mask: the mask to align the total size to, 0 for any length
  *
  * This takes either an iterator pointing to user memory, or one pointing to
@@ -1243,7 +1278,7 @@ static int bio_iov_iter_align_down(struc
  * is returned only if 0 pages could be pinned.
  */
 int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter,
-			   unsigned len_align_mask)
+			   unsigned mem_align_mask, unsigned len_align_mask)
 {
 	iov_iter_extraction_t flags = 0;
 
@@ -1252,6 +1287,10 @@ int bio_iov_iter_get_pages(struct bio *b
 
 	if (iov_iter_is_bvec(iter)) {
 		bio_iov_bvec_set(bio, iter);
+
+		if (!bio_iov_bvec_aligned(bio, mem_align_mask))
+			return -EINVAL;
+
 		iov_iter_advance(iter, bio->bi_iter.bi_size);
 		return 0;
 	}
@@ -1266,8 +1305,19 @@ int bio_iov_iter_get_pages(struct bio *b
 
 		ret = iov_iter_extract_bvecs(iter, bio->bi_io_vec,
 				BIO_MAX_SIZE - bio->bi_iter.bi_size,
-				&bio->bi_vcnt, bio->bi_max_vecs, flags);
+				&bio->bi_vcnt, bio->bi_max_vecs,
+				mem_align_mask, flags);
 		if (ret <= 0) {
+			/*
+			 * A misaligned vector fails the whole I/O.  Release any
+			 * pages pinned by earlier iterations before returning
+			 * since this bio won't be submitted to release them.
+			 */
+			if (ret == -EINVAL) {
+				bio_release_pages(bio, false);
+				bio_clear_flag(bio, BIO_PAGE_PINNED);
+				bio->bi_vcnt = 0;
+			}
 			if (!bio->bi_vcnt)
 				return ret;
 			break;
@@ -1380,7 +1430,7 @@ static int bio_iov_iter_bounce_read(stru
 
 	do {
 		ret = iov_iter_extract_bvecs(iter, bio->bi_io_vec + 1, len,
-				&bio->bi_vcnt, bio->bi_max_vecs - 1, 0);
+				&bio->bi_vcnt, bio->bi_max_vecs - 1, 0, 0);
 		if (ret <= 0) {
 			if (!bio->bi_vcnt)
 				goto out_folio_put;
block/blk-map.c +1 -1
@@ -274,7 +274,7 @@ static int bio_map_user_iov(struct reque
 	 * No alignment requirements on our part to support arbitrary
 	 * passthrough commands.
 	 */
-	ret = bio_iov_iter_get_pages(bio, iter, 0);
+	ret = bio_iov_iter_get_pages(bio, iter, 0, 0);
 	if (ret)
 		goto out_put;
 	ret = blk_rq_append_bio(rq, bio);
block/fops.c +1 -1
@@ -46,7 +46,7 @@ static bool blkdev_dio_invalid(struct bl
 static inline int blkdev_iov_iter_get_pages(struct bio *bio,
 		struct iov_iter *iter, struct block_device *bdev)
 {
-	return bio_iov_iter_get_pages(bio, iter,
+	return bio_iov_iter_get_pages(bio, iter, bdev_dma_alignment(bdev),
 			bdev_logical_block_size(bdev) - 1);
 }
 
fs/iomap/direct-io.c +1 -0
@@ -358,6 +358,7 @@ static ssize_t iomap_dio_bio_iter_one(st
 				iomap_max_bio_size(&iter->iomap), alignment);
 	else
 		ret = bio_iov_iter_get_pages(bio, dio->submit.iter,
+					     bdev_dma_alignment(bio->bi_bdev),
 					     alignment - 1);
 	if (unlikely(ret))
 		goto out_put_bio;
include/linux/bio.h +1 -1
@@ -477,7 +477,7 @@ int bdev_rw_virt(struct block_device *bd
 		size_t len, enum req_op op);
 
 int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter,
-		unsigned len_align_mask);
+		unsigned mem_align_mask, unsigned len_align_mask);
 
 void bio_iov_bvec_set(struct bio *bio, const struct iov_iter *iter);
 void __bio_release_pages(struct bio *bio, bool mark_dirty);
include/linux/uio.h +9 -1
@@ -389,9 +389,17 @@ ssize_t iov_iter_extract_pages(struct io
 			       size_t maxsize, unsigned int maxpages,
 			       iov_iter_extraction_t extraction_flags,
 			       size_t *offset0);
+/*
+ * Block-layer consumers (e.g. bio_iov_iter_get_pages()) require that the
+ * segments of an ITER_BVEC iterator are already aligned to the target device's
+ * DMA alignment, and forward them as-is.  In-kernel users that build their own
+ * bvecs must not create sub-aligned segments; iov_iter_extract_bvecs() enforces
+ * the same for the segments it extracts via @mem_align_mask.
+ */
 ssize_t iov_iter_extract_bvecs(struct iov_iter *iter, struct bio_vec *bv,
 		size_t max_size, unsigned short *nr_vecs,
-		unsigned short max_vecs, iov_iter_extraction_t extraction_flags);
+		unsigned short max_vecs, unsigned mem_align_mask,
+		iov_iter_extraction_t extraction_flags);
 
 /**
  * iov_iter_extract_will_pin - Indicate how pages from the iterator will be retained
lib/iov_iter.c +8 -1
@@ -1904,6 +1904,8 @@ static unsigned int get_contig_folio_len
  * @max_size:	maximum size to extract from @iter
  * @nr_vecs:	number of vectors in @bv (on in and output)
  * @max_vecs:	maximum vectors in @bv, including those filled before calling
+ * @mem_align_mask:	reject with -EINVAL if the source address or
+ *		length is not aligned to this mask
  * @extraction_flags: flags to qualify request
  *
  * Like iov_iter_extract_pages(), but returns physically contiguous ranges
@@ -1915,14 +1917,19 @@ static unsigned int get_contig_folio_len
  */
 ssize_t iov_iter_extract_bvecs(struct iov_iter *iter, struct bio_vec *bv,
 		size_t max_size, unsigned short *nr_vecs,
-		unsigned short max_vecs, iov_iter_extraction_t extraction_flags)
+		unsigned short max_vecs, unsigned mem_align_mask,
+		iov_iter_extraction_t extraction_flags)
 {
+	unsigned long start = (unsigned long)iter_iov_addr(iter);
 	unsigned short entries_left = max_vecs - *nr_vecs;
 	unsigned short nr_pages, i = 0;
 	size_t left, offset, len;
 	struct page **pages;
 	ssize_t size;
 
+	if ((start | iter_iov_len(iter)) & mem_align_mask)
+		return -EINVAL;
+
 	/*
 	 * Move page array up in the allocated memory for the bio vecs as far as
 	 * possible so that we can start filling biovecs from the beginning
author maintainer
#313 lore raw

PATCH 313/713 block: set QUEUE_FLAG_DYING unconditionally in blk_mark_disk_dead()

In reply to Greg Kroah-Hartman

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

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

From: 胡连勤 <[email protected]>

commit 7e9a46004b471eaf69b082c473d865316a4158e0 upstream.

Disks created via blk_mq_alloc_disk_for_queue() (e.g. SCSI SD disks) do not have GD_OWNS_QUEUE set. Currently __blk_mark_disk_dead() only sets QUEUE_FLAG_DYING when GD_OWNS_QUEUE is set, so for such disks blk_queue_enter() and __bio_queue_enter() cannot detect the dying state via blk_queue_dying() and remain blocked waiting for I/O that will never complete after surprise removal.

blk_mark_disk_dead() is the explicit "surprise removal" API -- the caller has already decided the disk is dead. Setting QUEUE_FLAG_DYING unconditionally here is appropriate: any in-flight I/O from other threads should get -ENODEV immediately from blk_queue_enter() regardless of GD_OWNS_QUEUE ownership.

For disks that already have GD_OWNS_QUEUE set, __blk_mark_disk_dead() will set the flag again which is harmless.

---
 block/genhd.c |    1 +
 1 file changed, 1 insertion(+)
block/genhd.c +1 -0
@@ -681,6 +681,7 @@ static bool __blk_mark_disk_dead(struct
  */
 void blk_mark_disk_dead(struct gendisk *disk)
 {
+	blk_queue_flag_set(QUEUE_FLAG_DYING, disk->queue);
 	__blk_mark_disk_dead(disk);
 	blk_report_disk_dead(disk, true);
 }
author maintainer
#314 lore raw

PATCH 314/713 Bluetooth: btusb: Add ASUS USB-BT540 for Realtek 8761CU

In reply to Greg Kroah-Hartman

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

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

From: Christoph Zwerschke <[email protected]>

commit 980084de4d9b25193398d89a1c0430ba3501b683 upstream.

Add the vendor/product ID (0x0b05, 0x1bef) to the usb_device_id table for the Realtek RTL8761CU-based ASUS USB-BT540 adapter. It binds via the generic Bluetooth class today, so BTUSB_REALTEK is never set and the rtl8761cu firmware is not loaded, leaving the controller non-functional. With the entry the driver loads rtl_bt/rtl8761cu_fw.bin (already shipped by linux-firmware) and the adapter works (tested: A2DP and ASHA).

Similar to commit bc597f0cc44f
("Bluetooth: btusb: Add TP-Link UB600 for Realtek 8761BUV").

Device info from /sys/kernel/debug/usb/devices:

T:  Bus=01 Lev=01 Prnt=01 Port=01 Cnt=01 Dev#= 22 Spd=12   MxCh= 0
D:  Ver= 1.10 Cls=e0(wlcon) Sub=01 Prot=01 MxPS=64 #Cfgs=  1
P:  Vendor=0b05 ProdID=1bef Rev= 2.00
S:  Manufacturer=Realtek
S:  Product=Bluetooth Controller
C:* #Ifs= 2 Cfg#= 1 Atr=e0 MxPwr=100mA
I:* If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=81(I) Atr=03(Int.) MxPS=  64 Ivl=1ms
E:  Ad=02(O) Atr=02(Bulk) MxPS=  64 Ivl=0ms
E:  Ad=82(I) Atr=02(Bulk) MxPS=  64 Ivl=0ms
I:* If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=83(I) Atr=01(Isoc) MxPS=   0 Ivl=1ms
E:  Ad=03(O) Atr=01(Isoc) MxPS=   0 Ivl=1ms
I:  If#= 1 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=83(I) Atr=01(Isoc) MxPS=   9 Ivl=1ms
E:  Ad=03(O) Atr=01(Isoc) MxPS=   9 Ivl=1ms
I:  If#= 1 Alt= 2 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=83(I) Atr=01(Isoc) MxPS=  17 Ivl=1ms
E:  Ad=03(O) Atr=01(Isoc) MxPS=  17 Ivl=1ms
I:  If#= 1 Alt= 3 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=83(I) Atr=01(Isoc) MxPS=  25 Ivl=1ms
E:  Ad=03(O) Atr=01(Isoc) MxPS=  25 Ivl=1ms
I:  If#= 1 Alt= 4 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=83(I) Atr=01(Isoc) MxPS=  33 Ivl=1ms
E:  Ad=03(O) Atr=01(Isoc) MxPS=  33 Ivl=1ms
I:  If#= 1 Alt= 5 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=83(I) Atr=01(Isoc) MxPS=  49 Ivl=1ms
E:  Ad=03(O) Atr=01(Isoc) MxPS=  49 Ivl=1ms
I:  If#= 1 Alt= 6 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=83(I) Atr=01(Isoc) MxPS=  63 Ivl=1ms
E:  Ad=03(O) Atr=01(Isoc) MxPS=  63 Ivl=1ms
---
 drivers/bluetooth/btusb.c |    4 ++++
 1 file changed, 4 insertions(+)
drivers/bluetooth/btusb.c +4 -0
@@ -850,6 +850,10 @@ static const struct usb_device_id quirks
 	{ USB_DEVICE(0x37ad, 0x0600), .driver_info = BTUSB_REALTEK |
 						     BTUSB_WIDEBAND_SPEECH },
 
+	/* Additional Realtek 8761CU Bluetooth devices */
+	{ USB_DEVICE(0x0b05, 0x1bef), .driver_info = BTUSB_REALTEK |
+						     BTUSB_WIDEBAND_SPEECH },
+
 	/* Additional Realtek 8821AE Bluetooth devices */
 	{ USB_DEVICE(0x0b05, 0x17dc), .driver_info = BTUSB_REALTEK },
 	{ USB_DEVICE(0x13d3, 0x3414), .driver_info = BTUSB_REALTEK },
author maintainer
#315 lore raw

PATCH 315/713 Bluetooth: btusb: Add ASUS USB-BT600 for Realtek 8761CU

In reply to Greg Kroah-Hartman

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

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

From: Christoph Zwerschke <[email protected]>

commit 6f0624b4427e38c3bb63a951c536cf8adaee1238 upstream.

Add the vendor/product ID (0x0b05, 0x1d70) to the usb_device_id table for the Realtek RTL8761CU-based ASUS USB-BT600 adapter. It binds via the generic Bluetooth class today, so BTUSB_REALTEK is never set and the rtl8761cu firmware is not loaded, leaving the controller non-functional. With the entry the driver loads rtl_bt/rtl8761cu_fw.bin (already shipped by linux-firmware) and the adapter works (tested: A2DP and ASHA).

Similar to commit bc597f0cc44f
("Bluetooth: btusb: Add TP-Link UB600 for Realtek 8761BUV").

Device info from /sys/kernel/debug/usb/devices:

T:  Bus=01 Lev=01 Prnt=01 Port=01 Cnt=01 Dev#= 23 Spd=12   MxCh= 0
D:  Ver= 1.10 Cls=e0(wlcon) Sub=01 Prot=01 MxPS=64 #Cfgs=  1
P:  Vendor=0b05 ProdID=1d70 Rev= 2.00
S:  Manufacturer=Realtek
S:  Product=Bluetooth Controller
C:* #Ifs= 2 Cfg#= 1 Atr=e0 MxPwr=100mA
I:* If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=81(I) Atr=03(Int.) MxPS=  64 Ivl=1ms
E:  Ad=02(O) Atr=02(Bulk) MxPS=  64 Ivl=0ms
E:  Ad=82(I) Atr=02(Bulk) MxPS=  64 Ivl=0ms
I:* If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=83(I) Atr=01(Isoc) MxPS=   0 Ivl=1ms
E:  Ad=03(O) Atr=01(Isoc) MxPS=   0 Ivl=1ms
I:  If#= 1 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=83(I) Atr=01(Isoc) MxPS=   9 Ivl=1ms
E:  Ad=03(O) Atr=01(Isoc) MxPS=   9 Ivl=1ms
I:  If#= 1 Alt= 2 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=83(I) Atr=01(Isoc) MxPS=  17 Ivl=1ms
E:  Ad=03(O) Atr=01(Isoc) MxPS=  17 Ivl=1ms
I:  If#= 1 Alt= 3 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=83(I) Atr=01(Isoc) MxPS=  25 Ivl=1ms
E:  Ad=03(O) Atr=01(Isoc) MxPS=  25 Ivl=1ms
I:  If#= 1 Alt= 4 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=83(I) Atr=01(Isoc) MxPS=  33 Ivl=1ms
E:  Ad=03(O) Atr=01(Isoc) MxPS=  33 Ivl=1ms
I:  If#= 1 Alt= 5 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=83(I) Atr=01(Isoc) MxPS=  49 Ivl=1ms
E:  Ad=03(O) Atr=01(Isoc) MxPS=  49 Ivl=1ms
I:  If#= 1 Alt= 6 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=83(I) Atr=01(Isoc) MxPS=  63 Ivl=1ms
E:  Ad=03(O) Atr=01(Isoc) MxPS=  63 Ivl=1ms
---
 drivers/bluetooth/btusb.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/bluetooth/btusb.c +2 -0
@@ -853,6 +853,8 @@ static const struct usb_device_id quirks
 	/* Additional Realtek 8761CU Bluetooth devices */
 	{ USB_DEVICE(0x0b05, 0x1bef), .driver_info = BTUSB_REALTEK |
 						     BTUSB_WIDEBAND_SPEECH },
+	{ USB_DEVICE(0x0b05, 0x1d70), .driver_info = BTUSB_REALTEK |
+						     BTUSB_WIDEBAND_SPEECH },
 
 	/* Additional Realtek 8821AE Bluetooth devices */
 	{ USB_DEVICE(0x0b05, 0x17dc), .driver_info = BTUSB_REALTEK },
author maintainer
#316 lore raw

PATCH 316/713 Bluetooth: btusb: limit RTL8761B BROKEN_EXT_SCAN quirk to 0bda:a728

In reply to Greg Kroah-Hartman

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

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

From: Junjie Cao <[email protected]>

commit ca0583c24661749508a0979189c254388a685559 upstream.

Commit 5ead2063611a ("Bluetooth: btrtl: fix RTL8761B/BU broken LE extended scan") set HCI_QUIRK_BROKEN_EXT_SCAN for every CHIP_ID_8761B device to cure repeated 0x2042 failures on an 0bda:a728 dongle. The brokenness is per-dongle, not per-chip: on a TP-Link UB500 (2357:0604, RTL8761BU, fw 0xdfc6d922) extended scan works, and the legacy scan path the quirk forces is what is broken -- LE Set Scan Enable (0x200c) times out with -110 about 30 s after firmware load, btusb resets the device, and the adapter re-enumerates in an endless loop (382 firmware reloads in one boot). 7.1.8, which predates the stable backport, runs clean on this unit; 7.1.9 loops.

Move the quirk from btrtl's chip-wide switch to a btusb device-table flag on the USB id the original fix was verified against. Other 8761B dongles return to their earlier long-standing behaviour.

Fixes 5ead2063611a ("Bluetooth: btrtl: fix RTL8761B/BU broken LE extended scan")
---
 drivers/bluetooth/btrtl.c |   13 -------------
 drivers/bluetooth/btusb.c |    8 ++++++++
 2 files changed, 8 insertions(+), 13 deletions(-)
drivers/bluetooth/btrtl.c +0 -13
@@ -1343,19 +1343,6 @@ void btrtl_set_quirks(struct hci_dev *hd
 	if (!btrtl_dev->ic_info)
 		return;
 
-	switch (btrtl_dev->project_id) {
-	case CHIP_ID_8761B:
-		/* RTL8761B/BU reports HCI version 5.1 but does not support
-		 * the LE Extended Scan commands (Opcode 0x2042), causing
-		 * repeated -EBUSY failures when BlueZ attempts extended
-		 * scanning while a connection is active.
-		 */
-		hci_set_quirk(hdev, HCI_QUIRK_BROKEN_EXT_SCAN);
-		break;
-	default:
-		break;
-	}
-
 	switch (btrtl_dev->ic_info->lmp_subver) {
 	case RTL_ROM_LMP_8703B:
 		/* 8723CS reports two pages for local ext features,
drivers/bluetooth/btusb.c +8 -0
@@ -67,6 +67,7 @@ static struct usb_driver btusb_driver;
 #define BTUSB_INTEL_NO_WBS_SUPPORT	BIT(26)
 #define BTUSB_ACTIONS_SEMI		BIT(27)
 #define BTUSB_BARROT			BIT(28)
+#define BTUSB_BROKEN_EXT_SCAN		BIT(29)
 
 static const struct usb_device_id btusb_table[] = {
 	/* Generic Bluetooth USB device */
@@ -615,6 +616,10 @@ static const struct usb_device_id quirks
 	{ USB_DEVICE(0x0489, 0xe130), .driver_info = BTUSB_REALTEK |
 						     BTUSB_WIDEBAND_SPEECH },
 
+	/* Realtek 8761BU Bluetooth devices */
+	{ USB_DEVICE(0x0bda, 0xa728), .driver_info = BTUSB_REALTEK |
+						     BTUSB_BROKEN_EXT_SCAN },
+
 	/* Realtek Bluetooth devices */
 	{ USB_VENDOR_AND_INTERFACE_INFO(0x0bda, 0xe0, 0x01, 0x01),
 	  .driver_info = BTUSB_REALTEK },
@@ -4372,6 +4377,9 @@ static int btusb_probe(struct usb_interf
 	if (id->driver_info & BTUSB_INVALID_LE_STATES)
 		hci_set_quirk(hdev, HCI_QUIRK_BROKEN_LE_STATES);
 
+	if (id->driver_info & BTUSB_BROKEN_EXT_SCAN)
+		hci_set_quirk(hdev, HCI_QUIRK_BROKEN_EXT_SCAN);
+
 	if (id->driver_info & BTUSB_DIGIANSWER) {
 		data->cmdreq_type = USB_TYPE_VENDOR;
 		hci_set_quirk(hdev, HCI_QUIRK_RESET_ON_CLOSE);
author maintainer
#317 lore raw

PATCH 317/713 Bluetooth: eir: Fix OOB read in eir_get_service_data()

In reply to Greg Kroah-Hartman

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

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

From: HyeongJun An <[email protected]>

commit 4beb198bc59b242404a47c21990bc84165052c8a upstream.

eir_get_service_data() walks the advertising data for a Service Data field with a matching UUID. On a mismatch it advances:

    eir += dlen;
    eir_len -= dlen;

eir_get_data() reports dlen as the field's data length, but the field spans dlen + 2 bytes once its length and type bytes count, and more when non-Service-Data fields were skipped to reach it. The pointer lands correctly on the next field. eir_len does not, and the shortfall compounds across fields until eir_get_data() reads the length and type bytes of a "field" past the end of the buffer.

For an ISO broadcast sink that buffer is hcon->le_per_adv_data[], filled from the periodic advertising reports of a remote broadcaster. A PA payload packed with mismatching Service Data fields walks off the array into the rest of struct hci_conn. A drifted field that matches the BAA UUID puts those bytes in iso_pi(sk)->base, where user space reads them back with getsockopt(BT_ISO_BASE).

Recompute eir_len from the end of the buffer each iteration.

Fixes 8f9ae5b3ae80 ("Bluetooth: eir: Add helpers for managing service data")
Assisted-by Claude:claude-opus-5
---
 net/bluetooth/eir.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
net/bluetooth/eir.c +2 -1
@@ -369,6 +369,7 @@ u8 eir_create_scan_rsp(struct hci_dev *h
 
 void *eir_get_service_data(u8 *eir, size_t eir_len, u16 uuid, size_t *len)
 {
+	const u8 *eir_end = eir + eir_len;
 	size_t dlen;
 
 	while ((eir = eir_get_data(eir, eir_len, EIR_SERVICE_DATA, &dlen))) {
@@ -381,7 +382,7 @@ void *eir_get_service_data(u8 *eir, size
 		}
 
 		eir += dlen;
-		eir_len -= dlen;
+		eir_len = eir_end - eir;
 	}
 
 	return NULL;
author maintainer
#318 lore raw

PATCH 318/713 bnx2x: fix double free in bnx2x_init_firmware() error path

In reply to Greg Kroah-Hartman

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

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

From: Jiangshan Yi <[email protected]>

commit d2796ffe38cb4155afe0eab23636295b096c27a5 upstream.

bnx2x_init_firmware() frees bp->init_ops, bp->init_data and bp->init_ops_offsets in its error path without setting them to NULL. The cleanup function bnx2x_release_firmware() frees the same three pointers unconditionally, so if init_firmware fails and release_firmware is later called (e.g. from __bnx2x_remove or through the function state machine), all three are freed a second time.

Set each pointer to NULL after kfree() in the error path so that the subsequent kfree(NULL) in bnx2x_release_firmware() is a safe no-op.

---
 drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c |    3 +++
 1 file changed, 3 insertions(+)
drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +3 -0
@@ -13473,10 +13473,13 @@ static int bnx2x_init_firmware(struct bn
 
 iro_alloc_err:
 	kfree(bp->init_ops_offsets);
+	bp->init_ops_offsets = NULL;
 init_offsets_alloc_err:
 	kfree(bp->init_ops);
+	bp->init_ops = NULL;
 init_ops_alloc_err:
 	kfree(bp->init_data);
+	bp->init_data = NULL;
 request_firmware_exit:
 	release_firmware(bp->firmware);
 	bp->firmware = NULL;
author maintainer
#319 lore raw

PATCH 319/713 bnxt_en: Write doorbell when linearizing skb fails

In reply to Greg Kroah-Hartman

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

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

From: Joe Damato <[email protected]>

commit 00eeab0c644a881a1dc86fbffb7e6047a6ce8ecd upstream.

When the driver is handed a burst of packets, the doorbell is deferred until the end. If the last packet has a huge number of frags, but fails to linearize, the doorbell will not be written adding latency on TX for any packets in the ring and holding their DMA mappings until the next TX. Note that the queue is not stopped, so this issue would delay pending BDs until the next TX.

This issue was discovered by Sashiko and reading the code verifies that, while unlikely, it is possible.

Fix this by jumping to tx_free, which replicates the same pre-existing logic but also writes the doorbell.

---
 drivers/net/ethernet/broadcom/bnxt/bnxt.c |    7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)
drivers/net/ethernet/broadcom/bnxt/bnxt.c +2 -5
@@ -501,11 +501,8 @@ static netdev_tx_t bnxt_start_xmit(struc
 	if (skb_shinfo(skb)->nr_frags > TX_MAX_FRAGS) {
 		netdev_warn_once(dev, "SKB has too many (%d) fragments, max supported is %d.  SKB will be linearized.\n",
 				 skb_shinfo(skb)->nr_frags, TX_MAX_FRAGS);
-		if (skb_linearize(skb)) {
-			dev_kfree_skb_any(skb);
-			dev_core_stats_tx_dropped_inc(dev);
-			return NETDEV_TX_OK;
-		}
+		if (skb_linearize(skb))
+			goto tx_free;
 	}
 #endif
 	if (skb_is_gso(skb) &&
author maintainer
#320 lore raw

PATCH 320/713 bpf, x86: Fix per-CPU address resolution into an extended register

In reply to Greg Kroah-Hartman

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

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

From: Vineet Gupta <[email protected]>

commit 5bbbce02e500d47d8e259a45be5a7be9741d0533 upstream.

The destination of the per-CPU address MOV is encoded in ModRM.reg, which is extended by REX.R, but the REX prefix is built with add_1mod(), which sets REX.B. REX.B extends ModRM.rm and SIB.base, and this instruction addresses memory as disp32 with no base, so the bit has no effect at all and the high register bit is simply lost.

Every is_ereg() destination therefore resolves to the wrong register, picking whichever one shares the low three bits:

R5 -> RAX R7 -> RBP R8 -> RSI R9 -> RDI

With BPF_REG_5, whose reg2hex is 0, the emitted

65 49 03 04 25 <off> add %gs:<off>,%rax

adds the per-CPU offset to RAX rather than R8. The destination keeps the unadjusted address and RAX is clobbered, so the program goes on to dereference a pointer that was never made per-CPU:

  BUG: unable to handle page fault for address: 0000607e386a8894
  RIP: bpf_prog_707837aafd2aa9ae_update_percpu_data+0x93/0xc9
  Call Trace:
   __bpf_prog_test_run_raw_tp+0x2dc/0x7d0
   __flush_smp_call_function_queue+0x1e9/0xc80
  Kernel panic - not syncing: Fatal exception in interrupt

R5 is the mildest of the four, aliasing a scratch register and faulting at the store. R7 aliases RBP and would corrupt the frame pointer, R8 and R9 alias the argument registers.

Use add_2mod() so the register goes through REX.R, matching how add_2reg() places it in ModRM.reg and how emit_priv_frame_ptr() hardcodes 0x4c for the same instruction with R9. Encodings for the non-extended registers are unchanged.

Problem showed up when trying to resurrect BPF_GCC CI (selftests built with BPF_GCC).

This has gone unnoticed because clang reloads the address into R1 before each per-CPU access, so the destination is never an extended register. GCC keeps several per-CPU addresses live at once, and test_progs-bpf_gcc panics the kernel in global_percpu_data/init, where the address of a .percpu variable ends up in R5.

Fixes 7bdbf7446305 ("bpf: add special internal-only MOV instruction to resolve per-CPU addrs")
---
 arch/x86/net/bpf_jit_comp.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
arch/x86/net/bpf_jit_comp.c +1 -1
@@ -1887,7 +1887,7 @@ static int do_jit(struct bpf_verifier_en
 				EMIT_mov(dst_reg, src_reg);
 #ifdef CONFIG_SMP
 				/* add <dst>, gs:[<off>] */
-				EMIT2(0x65, add_1mod(0x48, dst_reg));
+				EMIT2(0x65, add_2mod(0x48, 0, dst_reg));
 				EMIT3(0x03, add_2reg(0x04, 0, dst_reg), 0x25);
 				EMIT((u32)(unsigned long)&this_cpu_off, 4);
 #endif
author maintainer
#321 lore raw

PATCH 321/713 bpf: Disable preemption in __bpf_get_stack

In reply to Greg Kroah-Hartman

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

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

From: Daniel Borkmann <[email protected]>

commit b1a47b2708d4e95dbd23aee2ec83752190897b3f upstream.

get_perf_callchain() returns a per-CPU perf_callchain_entry buffer and releases its recursion slot via put_callchain_entry() before returning, so nothing keeps the entry reserved while __bpf_get_stack() consumes it below.

A preemptible BPF program (e.g. a non-sleepable raw tracepoint program on a PREEMPT kernel, which runs under migrate_disable() but not preempt_disable()) can be scheduled out between obtaining the entry and the copy. Another task scheduled on the same CPU then reuses the same per-CPU buffer and overwrites trace->nr with a larger value. copy_len is then computed from the inflated trace->nr and can exceed the caller's buffer, causing an out-of-bounds write in the memcpy() and in the build_id path.

The rcu_read_lock() taken here alone does not prevent this. It is only taken on the may_fault path, and under CONFIG_PREEMPT_RCU it does not disable preemption; it merely keeps perf's callchain buffer array alive (freed via call_rcu()) and does nothing to stop another task from reusing the entry.

Disable preemption around obtaining the callchain entry and copying it into the caller's buffer, so the entry cannot be reused underneath us and trace->nr stays bounded by max_depth. Build ID resolution may fault and is therefore deferred until after preemption is re-enabled; by then the instruction pointers have already been copied into buf, so it operates only on that private copy. Note, preempt_disable() also subsumes the buffer-lifetime guarantee the rcu_read_lock() provided, since a preempt-disabled section is an RCU read-side critical section for the callchain buffers' call_rcu() reclaim.

---
 kernel/bpf/stackmap.c |    3 +++
 1 file changed, 3 insertions(+)
kernel/bpf/stackmap.c +3 -0
@@ -718,6 +718,7 @@ static long __bpf_get_stack(struct pt_re
 
 	max_depth = stack_map_calculate_max_depth(size, elem_size, flags);
 
+	preempt_disable();
 	if (may_fault)
 		rcu_read_lock(); /* need RCU for perf's callchain below */
 
@@ -734,6 +735,7 @@ static long __bpf_get_stack(struct pt_re
 	if (unlikely(!trace) || trace->nr < skip) {
 		if (may_fault)
 			rcu_read_unlock();
+		preempt_enable();
 		goto err_fault;
 	}
 
@@ -754,6 +756,7 @@ static long __bpf_get_stack(struct pt_re
 	/* trace/ips should not be dereferenced after this point */
 	if (may_fault)
 		rcu_read_unlock();
+	preempt_enable();
 
 	if (user_build_id)
 		stack_map_get_build_id_offset(buf, trace_nr, user, may_fault);
author maintainer
#322 lore raw

PATCH 322/713 buffer: avoid tail commit walk for uptodate folios

In reply to Greg Kroah-Hartman

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

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

From: Jia Zhu <[email protected]>

commit f10b9cc1eb20637351f4e33372bfb464f89de59b upstream.

block_commit_write() always walks every buffer_head attached to the folio. That was cheap for order-0 folios, but large folios can contain hundreds of buffer_heads. For a small buffered overwrite of an already-uptodate large folio, the commit work is therefore proportional to the folio size rather than the copied range.

This became visible with ext4 regular-file large folios, where cached small overwrites reach block_commit_write() through block_write_end(). Before ext4 enabled large folios for regular files, this path was only hit with order-0 folios for normal ext4 buffered writes, so the full walk was bounded. The ext4 large-folio commit is therefore the regression point for this generic helper cost.

The full walk is still needed when the folio is not uptodate, because block_commit_write() uses per-buffer uptodate state to decide whether the whole folio can be marked uptodate. Keep those folios on the old full-buffer path.

For a folio that was already uptodate on entry, the commit no longer needs tail buffers for folio-uptodate discovery. The copied range has already been processed once block_start reaches @to, so stop there and avoid the suffix walk.

---
 fs/buffer.c |    3 +++
 1 file changed, 3 insertions(+)
fs/buffer.c +3 -0
@@ -2177,6 +2177,7 @@ void block_commit_write(struct folio *fo
 {
 	size_t block_start, block_end;
 	bool partial = false;
+	bool uptodate = folio_test_uptodate(folio);
 	unsigned blocksize;
 	struct buffer_head *bh, *head;
 
@@ -2199,6 +2200,8 @@ void block_commit_write(struct folio *fo
 			clear_buffer_new(bh);
 
 		block_start = block_end;
+		if (uptodate && block_start >= to)
+			break;
 		bh = bh->b_this_page;
 	} while (bh != head);
 
author maintainer
#323 lore raw

PATCH 323/713 bpf: Harden bloom filter sizing and indexing on 32-bit kernels

In reply to Greg Kroah-Hartman

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

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

From: Jérémy Jean <[email protected]>

commit 11c1e836710dcba03e50454a4eedfdbaf8d3050e upstream.

bloom_map_alloc() has two 32-bit-specific problems when the computed bitmap reaches the U32_MAX fallback case.

First, BITS_TO_BYTES(U32_MAX) is evaluated with 32-bit arithmetic. The addition performed by DIV_ROUND_UP wraps, so the map allocates only the fixed-size bloom filter object while keeping bitset_mask == U32_MAX. Subsequent updates can then write past the allocated object.

Second, fixing only the allocation size is not sufficient. The bloom hash is a u32, but set_bit() takes a signed long bit number and x86 test_bit() eventually feeds the index to variable_test_bit(long, ...). On 32-bit kernels, hashes in [0x80000000, U32_MAX] therefore become negative bit offsets. x86 bt/bts with a memory operand interpret those offsets relative to the supplied base, so a map with bitset_mask == U32_MAX can read or write before bloom->bitset even after allocating the full 512 MiB bitmap.

Keep the U32_MAX fallback, but split each hash into a word pointer and an in-word bit number before calling test_bit() or set_bit(). The bitops argument is then always in [0, BITS_PER_LONG - 1], while BIT_WORD(h) still selects the intended word in the full bitmap.

Compute the bitset size from (u64)bitset_mask + 1 before passing the final size to bpf_map_area_alloc(). This fixes the original under-allocation and keeps the allocated storage consistent with the addressable bitset.

Exploitation note: local privilege escalation is possible on a 32-bit x86 kernel using the under-allocation bug from a binary with CAP_BPF.

Assisted-by Codex:gpt-5
---
 kernel/bpf/bloom_filter.c |   19 +++++++++----------
 1 file changed, 9 insertions(+), 10 deletions(-)
kernel/bpf/bloom_filter.c +9 -10
@@ -41,7 +41,7 @@ static long bloom_map_peek_elem(struct b
 
 	for (i = 0; i < bloom->nr_hash_funcs; i++) {
 		h = hash(bloom, value, map->value_size, i);
-		if (!test_bit(h, bloom->bitset))
+		if (!test_bit(h % BITS_PER_LONG, bloom->bitset + BIT_WORD(h)))
 			return -ENOENT;
 	}
 
@@ -57,9 +57,13 @@ static long bloom_map_push_elem(struct b
 	if (flags != BPF_ANY)
 		return -EINVAL;
 
+	/*
+	 * On 32-bit architectures, hashes larger than INT_MAX would be
+	 * treated as negative by set_bit().
+	 */
 	for (i = 0; i < bloom->nr_hash_funcs; i++) {
 		h = hash(bloom, value, map->value_size, i);
-		set_bit(h, bloom->bitset);
+		set_bit(h % BITS_PER_LONG, bloom->bitset + BIT_WORD(h));
 	}
 
 	return 0;
@@ -94,9 +98,10 @@ static int bloom_map_alloc_check(union b
 
 static struct bpf_map *bloom_map_alloc(union bpf_attr *attr)
 {
-	u32 bitset_bytes, bitset_mask, nr_hash_funcs, nr_bits;
+	u32 bitset_mask, nr_hash_funcs, nr_bits;
 	int numa_node = bpf_map_attr_numa_node(attr);
 	struct bpf_bloom_filter *bloom;
+	u64 bitset_bytes;
 
 	if (attr->key_size != 0 || attr->value_size == 0 ||
 	    attr->max_entries == 0 ||
@@ -127,22 +132,16 @@ static struct bpf_map *bloom_map_alloc(u
 	if (check_mul_overflow(attr->max_entries, nr_hash_funcs, &nr_bits) ||
 	    check_mul_overflow(nr_bits / 5, (u32)7, &nr_bits) ||
 	    nr_bits > (1UL << 31)) {
-		/* The bit array size is 2^32 bits but to avoid overflowing the
-		 * u32, we use U32_MAX, which will round up to the equivalent
-		 * number of bytes
-		 */
-		bitset_bytes = BITS_TO_BYTES(U32_MAX);
 		bitset_mask = U32_MAX;
 	} else {
 		if (nr_bits <= BITS_PER_LONG)
 			nr_bits = BITS_PER_LONG;
 		else
 			nr_bits = roundup_pow_of_two(nr_bits);
-		bitset_bytes = BITS_TO_BYTES(nr_bits);
 		bitset_mask = nr_bits - 1;
 	}
 
-	bitset_bytes = roundup(bitset_bytes, sizeof(unsigned long));
+	bitset_bytes = BITS_TO_LONGS((u64)bitset_mask + 1) * sizeof(unsigned long);
 	bloom = bpf_map_area_alloc(sizeof(*bloom) + bitset_bytes, numa_node);
 
 	if (!bloom)
author maintainer
#324 lore raw

PATCH 324/713 dm-io: clone the source bio instead of copying its biovec

In reply to Greg Kroah-Hartman

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

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

From: Keith Busch <[email protected]>

commit 62dc37a819a5a5de5cba989ad9e96ee214b9253e upstream.

For DM_IO_BIO requests, do_region() built each destination bio by walking the source bio's biovec and re-adding the pages one at a time, tracking the remaining transfer in sectors. The vector lengths are byte granular and need not be sector aligned (e.g. a misaligned O_DIRECT buffer split across pages), so the sector-based accounting could lose a sub-sector
fragment: to_sector() truncated the remainder and the outer loop spun forever submitting empty bios, hanging the I/O.

There is no need to rebuild the biovec at all. The destination reads into (or writes from) exactly the same pages as the source bio, so the bio can simply clone the source's biovec with bio_alloc_clone() and remap it to the target device. The clone inherits the source's iterator and alignment, and the block layer splits it to the target's limits on submission, so the whole region maps to a single cloned bio with no manual page copying or sector accounting.

This removes the per-page copy path (and its open-coded bvec dpages helpers) for bio-backed I/O and fixes the hang on misaligned direct I/O to a dm-mirror device. Page-list, vma and kmem sources keep the existing copy path.

Fixes 7eac33186957 ("iomap: simplify direct io validity check")
Fixes 5ff3f74e145a ("block: simplify direct io validity check")
---
 drivers/md/dm-io.c |   67 ++++++++++++++++++-----------------------------------
 1 file changed, 24 insertions(+), 43 deletions(-)
drivers/md/dm-io.c +24 -43
@@ -170,12 +170,11 @@ struct dpages {
 			 struct page **p, unsigned long *len, unsigned int *offset);
 	void (*next_page)(struct dpages *dp);
 
-	union {
-		unsigned int context_u;
-		struct bvec_iter context_bi;
-	};
+	unsigned int context_u;
 	void *context_ptr;
 
+	struct bio *orig_bio;
+
 	void *vma_invalidate_address;
 	unsigned long vma_invalidate_size;
 };
@@ -211,44 +210,6 @@ static void list_dp_init(struct dpages *
 }
 
 /*
- * Functions for getting the pages from a bvec.
- */
-static void bio_get_page(struct dpages *dp, struct page **p,
-			 unsigned long *len, unsigned int *offset)
-{
-	struct bio_vec bvec = bvec_iter_bvec((struct bio_vec *)dp->context_ptr,
-					     dp->context_bi);
-
-	*p = bvec.bv_page;
-	*len = bvec.bv_len;
-	*offset = bvec.bv_offset;
-
-	/* avoid figuring it out again in bio_next_page() */
-	dp->context_bi.bi_sector = (sector_t)bvec.bv_len;
-}
-
-static void bio_next_page(struct dpages *dp)
-{
-	unsigned int len = (unsigned int)dp->context_bi.bi_sector;
-
-	bvec_iter_advance((struct bio_vec *)dp->context_ptr,
-			  &dp->context_bi, len);
-}
-
-static void bio_dp_init(struct dpages *dp, struct bio *bio)
-{
-	dp->get_page = bio_get_page;
-	dp->next_page = bio_next_page;
-
-	/*
-	 * We just use bvec iterator to retrieve pages, so it is ok to
-	 * access the bvec table directly here
-	 */
-	dp->context_ptr = bio->bi_io_vec;
-	dp->context_bi = bio->bi_iter;
-}
-
-/*
  * Functions for getting the pages from a VMA.
  */
 static void vm_get_page(struct dpages *dp,
@@ -332,6 +293,21 @@ static void do_region(const blk_opf_t op
 		return;
 	}
 
+	if (dp->orig_bio) {
+		bio = bio_alloc_clone(where->bdev, dp->orig_bio, GFP_NOIO,
+				      &io->client->bios);
+		bio->bi_iter.bi_sector = where->sector;
+		bio->bi_iter.bi_size = where->count << SECTOR_SHIFT;
+		bio->bi_opf = opf;
+		bio->bi_end_io = endio;
+		bio->bi_ioprio = ioprio;
+		store_io_and_region_in_bio(bio, io, region);
+
+		atomic_inc(&io->count);
+		submit_bio(bio);
+		return;
+	}
+
 	/*
 	 * where->count may be zero if op holds a flush and we need to
 	 * send a zero-sized flush.
@@ -468,6 +444,7 @@ static int dp_init(struct dm_io_request
 
 	dp->vma_invalidate_address = NULL;
 	dp->vma_invalidate_size = 0;
+	dp->orig_bio = NULL;
 
 	switch (io_req->mem.type) {
 	case DM_IO_PAGE_LIST:
@@ -475,7 +452,11 @@ static int dp_init(struct dm_io_request
 		break;
 
 	case DM_IO_BIO:
-		bio_dp_init(dp, io_req->mem.ptr.bio);
+		/*
+		 * The destination bios clone this bio's biovec directly, so
+		 * there are no per-page accessors to set up here.
+		 */
+		dp->orig_bio = io_req->mem.ptr.bio;
 		break;
 
 	case DM_IO_VMA:
author maintainer
#325 lore raw

PATCH 325/713 dm-io: report non-retryable errors separatedly

In reply to Greg Kroah-Hartman

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

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

From: Mikulas Patocka <[email protected]>

commit 47a5e62f39875f371bded6e34ffb9cf15ccd813d upstream.

The error codes BLK_STS_NOTSUPP and BLK_STS_INVAL should not cause leg failure on dm-raid1. This patch changes the interface to dm-io, so that it reports two error bitmaps - error_bits and unsup_bits. The unsup_bit bitmap tracks BLK_STS_NOTSUPP or BLK_STS_INVAL errors, the error_bits bitmap tracks all the other errors.

dm-raid1 is changed so that it won't fail a leg if it receives an error in the unsup_bits bitmap.

This patch (with 62dc37a819a5) fixes misbehavior if the user uses unaligned bio vectors on dm-raid1.

Fixes 7eac33186957 ("iomap: simplify direct io validity check")
Fixes 5ff3f74e145a ("block: simplify direct io validity check")
---
 drivers/md/dm-bufio.c           |   10 +++++-----
 drivers/md/dm-integrity.c       |   32 +++++++++++++++++++-------------
 drivers/md/dm-io.c              |   29 +++++++++++++++++++++--------
 drivers/md/dm-kcopyd.c          |   10 +++++-----
 drivers/md/dm-log.c             |    4 ++--
 drivers/md/dm-raid1.c           |   26 +++++++++++++++++---------
 drivers/md/dm-snap-persistent.c |    4 ++--
 drivers/md/dm-verity-target.c   |    2 +-
 drivers/md/dm-writecache.c      |   14 ++++++++------
 include/linux/dm-io.h           |    6 +++---
 10 files changed, 83 insertions(+), 54 deletions(-)
drivers/md/dm-bufio.c +5 -5
@@ -1287,11 +1287,11 @@ static void free_buffer(struct dm_buffer
  * dm-io completion routine. It just calls b->bio.bi_end_io, pretending
  * that the request was handled directly with bio interface.
  */
-static void dmio_complete(unsigned long error, void *context)
+static void dmio_complete(unsigned long error, unsigned long unsup, void *context)
 {
 	struct dm_buffer *b = context;
 
-	b->end_io(b, unlikely(error != 0) ? BLK_STS_IOERR : 0);
+	b->end_io(b, unlikely(error != 0) ? BLK_STS_IOERR : unlikely(unsup != 0) ? BLK_STS_NOTSUPP : 0);
 }
 
 static void use_dmio(struct dm_buffer *b, enum req_op op, sector_t sector,
@@ -1319,7 +1319,7 @@ static void use_dmio(struct dm_buffer *b
 		io_req.mem.ptr.vma = (char *)b->data + offset;
 	}
 
-	r = dm_io(&io_req, 1, &region, NULL, ioprio);
+	r = dm_io(&io_req, 1, &region, NULL, NULL, ioprio);
 	if (unlikely(r))
 		b->end_io(b, errno_to_blk_status(r));
 }
@@ -2220,7 +2220,7 @@ int dm_bufio_issue_flush(struct dm_bufio
 	if (WARN_ON_ONCE(dm_bufio_in_request()))
 		return -EINVAL;
 
-	return dm_io(&io_req, 1, &io_reg, NULL, IOPRIO_DEFAULT);
+	return dm_io(&io_req, 1, &io_reg, NULL, NULL, IOPRIO_DEFAULT);
 }
 EXPORT_SYMBOL_GPL(dm_bufio_issue_flush);
 
@@ -2246,7 +2246,7 @@ int dm_bufio_issue_discard(struct dm_buf
 	if (WARN_ON_ONCE(dm_bufio_in_request()))
 		return -EINVAL; /* discards are optional */
 
-	return dm_io(&io_req, 1, &io_reg, NULL, IOPRIO_DEFAULT);
+	return dm_io(&io_req, 1, &io_reg, NULL, NULL, IOPRIO_DEFAULT);
 }
 EXPORT_SYMBOL_GPL(dm_bufio_issue_discard);
 
drivers/md/dm-integrity.c +19 -13
@@ -562,7 +562,7 @@ static int sync_rw_sb(struct dm_integrit
 		}
 	}
 
-	r = dm_io(&io_req, 1, &io_loc, NULL, IOPRIO_DEFAULT);
+	r = dm_io(&io_req, 1, &io_loc, NULL, NULL, IOPRIO_DEFAULT);
 	if (unlikely(r))
 		return r;
 
@@ -1035,12 +1035,14 @@ static void encrypt_journal(struct dm_in
 		return crypt_journal(ic, encrypt, section, n_sections, comp);
 }
 
-static void complete_journal_io(unsigned long error, void *context)
+static void complete_journal_io(unsigned long error, unsigned long unsup, void *context)
 {
 	struct journal_completion *comp = context;
 
 	if (unlikely(error != 0))
 		dm_integrity_io_error(comp->ic, "writing journal", -EIO);
+	else if (unlikely(unsup != 0))
+		dm_integrity_io_error(comp->ic, "writing journal", -EOPNOTSUPP);
 	complete_journal_op(comp);
 }
 
@@ -1055,7 +1057,7 @@ static void rw_journal_sectors(struct dm
 
 	if (unlikely(dm_integrity_failed(ic))) {
 		if (comp)
-			complete_journal_io(-1UL, comp);
+			complete_journal_io(-1UL, -1UL, comp);
 		return;
 	}
 
@@ -1080,13 +1082,13 @@ static void rw_journal_sectors(struct dm
 	io_loc.sector = ic->start + SB_SECTORS + sector;
 	io_loc.count = n_sectors;
 
-	r = dm_io(&io_req, 1, &io_loc, NULL, IOPRIO_DEFAULT);
+	r = dm_io(&io_req, 1, &io_loc, NULL, NULL, IOPRIO_DEFAULT);
 	if (unlikely(r)) {
 		dm_integrity_io_error(ic, (opf & REQ_OP_MASK) == REQ_OP_READ ?
 				      "reading journal" : "writing journal", r);
 		if (comp) {
 			WARN_ONCE(1, "asynchronous dm_io failed: %d", r);
-			complete_journal_io(-1UL, comp);
+			complete_journal_io(-1UL, -1UL, comp);
 		}
 	}
 }
@@ -1177,7 +1179,7 @@ static void copy_from_journal(struct dm_
 	BUG_ON((target | n_sectors | offset) & (unsigned int)(ic->sectors_per_block - 1));
 
 	if (unlikely(dm_integrity_failed(ic))) {
-		fn(-1UL, data);
+		fn(-1UL, -1UL, data);
 		return;
 	}
 
@@ -1197,10 +1199,10 @@ static void copy_from_journal(struct dm_
 	io_loc.sector = target;
 	io_loc.count = n_sectors;
 
-	r = dm_io(&io_req, 1, &io_loc, NULL, IOPRIO_DEFAULT);
+	r = dm_io(&io_req, 1, &io_loc, NULL, NULL, IOPRIO_DEFAULT);
 	if (unlikely(r)) {
 		WARN_ONCE(1, "asynchronous dm_io failed: %d", r);
-		fn(-1UL, data);
+		fn(-1UL, -1UL, data);
 	}
 }
 
@@ -1493,12 +1495,14 @@ struct flush_request {
 	struct completion comp;
 };
 
-static void flush_notify(unsigned long error, void *fr_)
+static void flush_notify(unsigned long error, unsigned long unsup, void *fr_)
 {
 	struct flush_request *fr = fr_;
 
 	if (unlikely(error != 0))
 		dm_integrity_io_error(fr->ic, "flushing disk cache", -EIO);
+	else if (unlikely(unsup != 0))
+		dm_integrity_io_error(fr->ic, "flushing disk cache", -EOPNOTSUPP);
 	complete(&fr->comp);
 }
 
@@ -1521,7 +1525,7 @@ static void dm_integrity_flush_buffers(s
 		fr.io_reg.count = 0;
 		fr.ic = ic;
 		init_completion(&fr.comp);
-		r = dm_io(&fr.io_req, 1, &fr.io_reg, NULL, IOPRIO_DEFAULT);
+		r = dm_io(&fr.io_req, 1, &fr.io_reg, NULL, NULL, IOPRIO_DEFAULT);
 		BUG_ON(r);
 	}
 
@@ -1837,7 +1841,7 @@ static noinline void integrity_recheck(s
 			buffer_offset = (sector - io_loc.sector) << SECTOR_SHIFT;
 			io_loc.count = round_up(io_loc.count, alignment);
 
-			r = dm_io(&io_req, 1, &io_loc, NULL, IOPRIO_DEFAULT);
+			r = dm_io(&io_req, 1, &io_loc, NULL, NULL, IOPRIO_DEFAULT);
 			if (unlikely(r)) {
 				dio->bi_status = errno_to_blk_status(r);
 				goto free_ret;
@@ -2890,7 +2894,7 @@ release_flush_bios:
 	}
 }
 
-static void complete_copy_from_journal(unsigned long error, void *context)
+static void complete_copy_from_journal(unsigned long error, unsigned long unsup, void *context)
 {
 	struct journal_io *io = context;
 	struct journal_completion *comp = io->comp;
@@ -2900,6 +2904,8 @@ static void complete_copy_from_journal(u
 	mempool_free(io, &ic->journal_io_mempool);
 	if (unlikely(error != 0))
 		dm_integrity_io_error(ic, "copying from journal", -EIO);
+	else if (unlikely(unsup != 0))
+		dm_integrity_io_error(ic, "copying from journal", -EOPNOTSUPP);
 	complete_journal_op(comp);
 }
 
@@ -3215,7 +3221,7 @@ next_chunk:
 	io_loc.sector = get_data_sector(ic, area, offset);
 	io_loc.count = n_sectors;
 
-	r = dm_io(&io_req, 1, &io_loc, NULL, IOPRIO_DEFAULT);
+	r = dm_io(&io_req, 1, &io_loc, NULL, NULL, IOPRIO_DEFAULT);
 	if (unlikely(r)) {
 		dm_integrity_io_error(ic, "reading data", r);
 		goto err;
drivers/md/dm-io.c +21 -8
@@ -33,6 +33,7 @@ struct dm_io_client {
  */
 struct io {
 	unsigned long error_bits;
+	unsigned long unsup_bits;
 	atomic_t count;
 	struct dm_io_client *client;
 	io_notify_fn callback;
@@ -119,6 +120,7 @@ static void retrieve_io_and_region_from_
 static void complete_io(struct io *io)
 {
 	unsigned long error_bits = io->error_bits;
+	unsigned long unsup_bits = io->unsup_bits;
 	io_notify_fn fn = io->callback;
 	void *context = io->context;
 
@@ -127,13 +129,17 @@ static void complete_io(struct io *io)
 					     io->vma_invalidate_size);
 
 	mempool_free(io, &io->client->pool);
-	fn(error_bits, context);
+	fn(error_bits, unsup_bits, context);
 }
 
 static void dec_count(struct io *io, unsigned int region, blk_status_t error)
 {
-	if (error)
-		set_bit(region, &io->error_bits);
+	if (unlikely(error)) {
+		if (error == BLK_STS_NOTSUPP || error == BLK_STS_INVAL)
+			set_bit(region, &io->unsup_bits);
+		else
+			set_bit(region, &io->error_bits);
+	}
 
 	if (atomic_dec_and_test(&io->count))
 		complete_io(io);
@@ -394,6 +400,7 @@ static void async_io(struct dm_io_client
 
 	io = mempool_alloc(&client->pool, GFP_NOIO);
 	io->error_bits = 0;
+	io->unsup_bits = 0;
 	atomic_set(&io->count, 1); /* see dispatch_io() */
 	io->client = client;
 	io->callback = fn;
@@ -407,20 +414,23 @@ static void async_io(struct dm_io_client
 
 struct sync_io {
 	unsigned long error_bits;
+	unsigned long unsup_bits;
 	struct completion wait;
 };
 
-static void sync_io_complete(unsigned long error, void *context)
+static void sync_io_complete(unsigned long error, unsigned long unsup, void *context)
 {
 	struct sync_io *sio = context;
 
 	sio->error_bits = error;
+	sio->unsup_bits = unsup;
 	complete(&sio->wait);
 }
 
 static int sync_io(struct dm_io_client *client, unsigned int num_regions,
 		   struct dm_io_region *where, blk_opf_t opf, struct dpages *dp,
-		   unsigned long *error_bits, unsigned short ioprio)
+		   unsigned long *error_bits, unsigned long *unsup_bits,
+		   unsigned short ioprio)
 {
 	struct sync_io sio;
 
@@ -433,8 +443,10 @@ static int sync_io(struct dm_io_client *
 
 	if (error_bits)
 		*error_bits = sio.error_bits;
+	if (unsup_bits)
+		*unsup_bits = sio.unsup_bits;
 
-	return sio.error_bits ? -EIO : 0;
+	return sio.error_bits ? -EIO : sio.unsup_bits ? -EOPNOTSUPP : 0;
 }
 
 static int dp_init(struct dm_io_request *io_req, struct dpages *dp,
@@ -481,7 +493,7 @@ static int dp_init(struct dm_io_request
 
 int dm_io(struct dm_io_request *io_req, unsigned int num_regions,
 	  struct dm_io_region *where, unsigned long *sync_error_bits,
-	  unsigned short ioprio)
+	  unsigned long *sync_unsup_bits, unsigned short ioprio)
 {
 	int r;
 	struct dpages dp;
@@ -497,7 +509,8 @@ int dm_io(struct dm_io_request *io_req,
 
 	if (!io_req->notify.fn)
 		return sync_io(io_req->client, num_regions, where,
-			       io_req->bi_opf, &dp, sync_error_bits, ioprio);
+			       io_req->bi_opf, &dp, sync_error_bits,
+			       sync_unsup_bits, ioprio);
 
 	async_io(io_req->client, num_regions, where, io_req->bi_opf, &dp,
 		 io_req->notify.fn, io_req->notify.context, ioprio);
drivers/md/dm-kcopyd.c +5 -5
@@ -517,16 +517,16 @@ static int run_complete_job(struct kcopy
 	return 0;
 }
 
-static void complete_io(unsigned long error, void *context)
+static void complete_io(unsigned long error, unsigned long unsup, void *context)
 {
 	struct kcopyd_job *job = context;
 	struct dm_kcopyd_client *kc = job->kc;
 
 	io_job_finish(kc->throttle);
 
-	if (error) {
+	if (unlikely((error | unsup) != 0)) {
 		if (op_is_write(job->op))
-			job->write_err |= error;
+			job->write_err |= error | unsup;
 		else
 			job->read_err = 1;
 
@@ -578,9 +578,9 @@ static int run_io_job(struct kcopyd_job
 	io_job_start(job->kc->throttle);
 
 	if (job->op == REQ_OP_READ)
-		r = dm_io(&io_req, 1, &job->source, NULL, IOPRIO_DEFAULT);
+		r = dm_io(&io_req, 1, &job->source, NULL, NULL, IOPRIO_DEFAULT);
 	else
-		r = dm_io(&io_req, job->num_dests, job->dests, NULL, IOPRIO_DEFAULT);
+		r = dm_io(&io_req, job->num_dests, job->dests, NULL, NULL, IOPRIO_DEFAULT);
 
 	return r;
 }
drivers/md/dm-log.c +2 -2
@@ -300,7 +300,7 @@ static int rw_header(struct log_c *lc, e
 {
 	lc->io_req.bi_opf = op;
 
-	return dm_io(&lc->io_req, 1, &lc->header_location, NULL, IOPRIO_DEFAULT);
+	return dm_io(&lc->io_req, 1, &lc->header_location, NULL, NULL, IOPRIO_DEFAULT);
 }
 
 static int flush_header(struct log_c *lc)
@@ -313,7 +313,7 @@ static int flush_header(struct log_c *lc
 
 	lc->io_req.bi_opf = REQ_OP_WRITE | REQ_PREFLUSH;
 
-	return dm_io(&lc->io_req, 1, &null_location, NULL, IOPRIO_DEFAULT);
+	return dm_io(&lc->io_req, 1, &null_location, NULL, NULL, IOPRIO_DEFAULT);
 }
 
 static int read_header(struct log_c *log)
drivers/md/dm-raid1.c +17 -9
@@ -258,7 +258,7 @@ out:
 static int mirror_flush(struct dm_target *ti)
 {
 	struct mirror_set *ms = ti->private;
-	unsigned long error_bits;
+	unsigned long error_bits, unsup_bits;
 
 	unsigned int i;
 	struct dm_io_region io[MAX_NR_MIRRORS];
@@ -277,8 +277,8 @@ static int mirror_flush(struct dm_target
 	}
 
 	error_bits = -1;
-	dm_io(&io_req, ms->nr_mirrors, io, &error_bits, IOPRIO_DEFAULT);
-	if (unlikely(error_bits != 0)) {
+	dm_io(&io_req, ms->nr_mirrors, io, &error_bits, &unsup_bits, IOPRIO_DEFAULT);
+	if (unlikely((error_bits | unsup_bits) != 0)) {
 		for (i = 0; i < ms->nr_mirrors; i++)
 			if (test_bit(i, &error_bits))
 				fail_mirror(ms->mirror + i,
@@ -511,7 +511,7 @@ static void hold_bio(struct mirror_set *
  * Reads
  *---------------------------------------------------------------
  */
-static void read_callback(unsigned long error, void *context)
+static void read_callback(unsigned long error, unsigned long unsup, void *context)
 {
 	struct bio *bio = context;
 	struct mirror *m;
@@ -520,6 +520,8 @@ static void read_callback(unsigned long
 	bio_set_m(bio, NULL);
 
 	if (likely(!error)) {
+		if (unlikely(unsup != 0))
+			bio->bi_status = BLK_STS_INVAL;
 		bio_endio(bio);
 		return;
 	}
@@ -553,7 +555,7 @@ static void read_async_bio(struct mirror
 
 	map_region(&io, m, bio);
 	bio_set_m(bio, m);
-	BUG_ON(dm_io(&io_req, 1, &io, NULL, IOPRIO_DEFAULT));
+	BUG_ON(dm_io(&io_req, 1, &io, NULL, NULL, IOPRIO_DEFAULT));
 }
 
 static inline int region_in_sync(struct mirror_set *ms, region_t region,
@@ -600,7 +602,7 @@ static void do_reads(struct mirror_set *
  * NOSYNC:	increment pending, just write to the default mirror
  *---------------------------------------------------------------------
  */
-static void write_callback(unsigned long error, void *context)
+static void write_callback(unsigned long error, unsigned long unsup, void *context)
 {
 	unsigned int i;
 	struct bio *bio = context;
@@ -617,7 +619,7 @@ static void write_callback(unsigned long
 	 * This way we handle both writes to SYNC and NOSYNC
 	 * regions with the same code.
 	 */
-	if (likely(!error)) {
+	if (likely(!(error | unsup))) {
 		bio_endio(bio);
 		return;
 	}
@@ -632,6 +634,12 @@ static void write_callback(unsigned long
 		return;
 	}
 
+	if (!error && unsup) {
+		bio->bi_status = BLK_STS_INVAL;
+		bio_endio(bio);
+		return;
+	}
+
 	for (i = 0; i < ms->nr_mirrors; i++)
 		if (test_bit(i, &error))
 			fail_mirror(ms->mirror + i, DM_RAID1_WRITE_ERROR);
@@ -680,7 +688,7 @@ static void do_write(struct mirror_set *
 	 */
 	bio_set_m(bio, get_default_mirror(ms));
 
-	BUG_ON(dm_io(&io_req, ms->nr_mirrors, io, NULL, IOPRIO_DEFAULT));
+	BUG_ON(dm_io(&io_req, ms->nr_mirrors, io, NULL, NULL, IOPRIO_DEFAULT));
 }
 
 static void do_writes(struct mirror_set *ms, struct bio_list *writes)
@@ -1262,7 +1270,7 @@ static int mirror_end_io(struct dm_targe
 		return DM_ENDIO_DONE;
 	}
 
-	if (*error == BLK_STS_NOTSUPP)
+	if (*error == BLK_STS_NOTSUPP || *error == BLK_STS_INVAL)
 		goto out;
 
 	if (bio->bi_opf & REQ_RAHEAD)
drivers/md/dm-snap-persistent.c +2 -2
@@ -223,7 +223,7 @@ static void do_metadata(struct work_stru
 {
 	struct mdata_req *req = container_of(work, struct mdata_req, work);
 
-	req->result = dm_io(req->io_req, 1, req->where, NULL, IOPRIO_DEFAULT);
+	req->result = dm_io(req->io_req, 1, req->where, NULL, NULL, IOPRIO_DEFAULT);
 }
 
 /*
@@ -247,7 +247,7 @@ static int chunk_io(struct pstore *ps, v
 	struct mdata_req req;
 
 	if (!metadata)
-		return dm_io(&io_req, 1, &where, NULL, IOPRIO_DEFAULT);
+		return dm_io(&io_req, 1, &where, NULL, NULL, IOPRIO_DEFAULT);
 
 	req.where = &where;
 	req.io_req = &io_req;
drivers/md/dm-verity-target.c +1 -1
@@ -395,7 +395,7 @@ static noinline int verity_recheck(struc
 	io_loc.bdev = v->data_dev->bdev;
 	io_loc.sector = cur_block << (v->data_dev_block_bits - SECTOR_SHIFT);
 	io_loc.count = 1 << (v->data_dev_block_bits - SECTOR_SHIFT);
-	r = dm_io(&io_req, 1, &io_loc, NULL, IOPRIO_DEFAULT);
+	r = dm_io(&io_req, 1, &io_loc, NULL, NULL, IOPRIO_DEFAULT);
 	if (unlikely(r))
 		goto free_ret;
 
drivers/md/dm-writecache.c +8 -6
@@ -474,12 +474,14 @@ struct io_notify {
 	atomic_t count;
 };
 
-static void writecache_notify_io(unsigned long error, void *context)
+static void writecache_notify_io(unsigned long error, unsigned long unsup, void *context)
 {
 	struct io_notify *endio = context;
 
 	if (unlikely(error != 0))
 		writecache_error(endio->wc, -EIO, "error writing metadata");
+	else if (unlikely(unsup != 0))
+		writecache_error(endio->wc, -EOPNOTSUPP, "error writing metadata");
 	BUG_ON(atomic_read(&endio->count) <= 0);
 	if (atomic_dec_and_test(&endio->count))
 		complete(&endio->c);
@@ -530,11 +532,11 @@ static void ssd_commit_flushed(struct dm
 		req.notify.context = &endio;
 
 		/* writing via async dm-io (implied by notify.fn above) won't return an error */
-		(void) dm_io(&req, 1, &region, NULL, IOPRIO_DEFAULT);
+		(void) dm_io(&req, 1, &region, NULL, NULL, IOPRIO_DEFAULT);
 		i = j;
 	}
 
-	writecache_notify_io(0, &endio);
+	writecache_notify_io(0, 0, &endio);
 	wait_for_completion_io(&endio.c);
 
 	if (wait_for_ios)
@@ -567,7 +569,7 @@ static void ssd_commit_superblock(struct
 	req.notify.fn = NULL;
 	req.notify.context = NULL;
 
-	r = dm_io(&req, 1, &region, NULL, IOPRIO_DEFAULT);
+	r = dm_io(&req, 1, &region, NULL, NULL, IOPRIO_DEFAULT);
 	if (unlikely(r))
 		writecache_error(wc, r, "error writing superblock");
 }
@@ -595,7 +597,7 @@ static void writecache_disk_flush(struct
 	req.client = wc->dm_io;
 	req.notify.fn = NULL;
 
-	r = dm_io(&req, 1, &region, NULL, IOPRIO_DEFAULT);
+	r = dm_io(&req, 1, &region, NULL, NULL, IOPRIO_DEFAULT);
 	if (unlikely(r))
 		writecache_error(wc, r, "error flushing metadata: %d", r);
 }
@@ -989,7 +991,7 @@ static int writecache_read_metadata(stru
 	req.client = wc->dm_io;
 	req.notify.fn = NULL;
 
-	return dm_io(&req, 1, &region, NULL, IOPRIO_DEFAULT);
+	return dm_io(&req, 1, &region, NULL, NULL, IOPRIO_DEFAULT);
 }
 
 static void writecache_resume(struct dm_target *ti)
include/linux/dm-io.h +3 -3
@@ -27,7 +27,7 @@ struct page_list {
 	struct page *page;
 };
 
-typedef void (*io_notify_fn)(unsigned int long error, void *context);
+typedef void (*io_notify_fn)(unsigned long int error, unsigned long int unsup, void *context);
 
 enum dm_io_mem_type {
 	DM_IO_PAGE_LIST,/* Page list */
@@ -80,8 +80,8 @@ void dm_io_client_destroy(struct dm_io_c
  * error occurred doing io to the corresponding region.
  */
 int dm_io(struct dm_io_request *io_req, unsigned int num_regions,
-	  struct dm_io_region *region, unsigned int long *sync_error_bits,
-	  unsigned short ioprio);
+	  struct dm_io_region *region, unsigned long int *sync_error_bits,
+	  unsigned long int *sync_unsup_bits, unsigned short ioprio);
 
 #endif	/* __KERNEL__ */
 #endif	/* _LINUX_DM_IO_H */
author maintainer
#326 lore raw

PATCH 326/713 dm-era: fix shadowed superblock leak on take-snap failure

In reply to Greg Kroah-Hartman

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

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

From: liyouhong <[email protected]>

commit 39c5aa3bd8ec3912d2cd0b3fe092642b0d2b0713 upstream.

metadata_take_snap() bumps the live superblock refcount and then dm_tm_shadow_block() allocates a new block for the metadata snapshot. If the subsequent dm_sm_inc_block() of writeset_tree_root or era_array_root fails, the function only unlocks the clone and returns. The newly allocated shadow block is never returned to the metadata space map, so each failed take-snap permanently leaks one metadata block.

Free the clone with dm_sm_dec_block() on those error paths, matching the final step of metadata_drop_snap().

Fixes eec40579d848 ("dm: add era target")
---
 drivers/md/dm-era-target.c |    5 +++++
 1 file changed, 5 insertions(+)
drivers/md/dm-era-target.c +5 -0
@@ -1034,6 +1034,7 @@ static int metadata_checkpoint(struct er
 static int metadata_take_snap(struct era_metadata *md)
 {
 	int r, inc;
+	dm_block_t location;
 	struct dm_block *clone;
 
 	if (md->metadata_snap != SUPERBLOCK_LOCATION) {
@@ -1071,7 +1072,9 @@ static int metadata_take_snap(struct era
 	r = dm_sm_inc_block(md->sm, md->writeset_tree_root);
 	if (r) {
 		DMERR("%s: couldn't inc writeset tree root", __func__);
+		location = dm_block_location(clone);
 		dm_tm_unlock(md->tm, clone);
+		dm_sm_dec_block(md->sm, location);
 		return r;
 	}
 
@@ -1079,7 +1082,9 @@ static int metadata_take_snap(struct era
 	if (r) {
 		DMERR("%s: couldn't inc era tree root", __func__);
 		dm_sm_dec_block(md->sm, md->writeset_tree_root);
+		location = dm_block_location(clone);
 		dm_tm_unlock(md->tm, clone);
+		dm_sm_dec_block(md->sm, location);
 		return r;
 	}
 
author maintainer
#327 lore raw

PATCH 327/713 dm raid1: reserve space for NUL-terminator in build_constructor_string()

In reply to Greg Kroah-Hartman

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

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

From: Ilya Krutskih <[email protected]>

commit 73c37fe54cd056d07461b142ab0b8b81e1ef6ad8 upstream.

Reserve space for the termination NUL after the maximum 20 decimal digits of a long long value to avoid buffer overflow in sprintf().

Fixes f5db4af466e2 ("dm raid1: add userspace log")
---
 drivers/md/dm-log-userspace-base.c |    1 +
 1 file changed, 1 insertion(+)
drivers/md/dm-log-userspace-base.c +1 -0
@@ -139,6 +139,7 @@ static int build_constructor_string(stru
 		str_size += strlen(argv[i]) + 1; /* +1 for space between args */
 
 	str_size += 20; /* Max number of chars in a printed u64 number */
+	str_size++; /* For NUL-terminator */
 
 	str = kzalloc(str_size, GFP_KERNEL);
 	if (!str) {
author maintainer
#328 lore raw

PATCH 328/713 dm array: validate array block headers on read

In reply to Greg Kroah-Hartman

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

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

From: Bryam Vargas <[email protected]>

commit 2965787723084835b18dfe993cd450ebf5bd4540 upstream.

array_block_check() validates blocknr and csum and nothing else, while node_check(), next to it, has bounded the structural fields since both were written. dm_array_cursor_next() takes its loop bound from the on-disk nr_entries and element_at() is unguarded pointer arithmetic, so a count larger than the block holds keeps the cursor in one block while the index grows past it and the read walks off the dm-bufio buffer -- dm_cache_load_mappings() drives it once per cache block at activation.

Check the header against itself: reject a zero value_size, require max_entries to equal calc_max_entries() for that value_size and block size, and require nr_entries to fit. Equality rather than an upper bound, since a count below the real capacity trips BUG_ON() in fill_ablock() and trim_ablock(). Metadata dm-array writes satisfies all three.

Fixes 6513c29f44f2 ("dm persistent data: add transactional array")
---
 drivers/md/persistent-data/dm-array.c |   37 ++++++++++++++++++++++++++--------
 1 file changed, 29 insertions(+), 8 deletions(-)
drivers/md/persistent-data/dm-array.c +29 -8
@@ -38,6 +38,14 @@ struct array_block {
  */
 #define CSUM_XOR 595846735
 
+/*
+ * Each array block can hold this many values.
+ */
+static uint32_t calc_max_entries(size_t value_size, size_t size_of_block)
+{
+	return (size_of_block - sizeof(struct array_block)) / value_size;
+}
+
 static void array_block_prepare_for_write(const struct dm_block_validator *v,
 					  struct dm_block *b,
 					  size_t size_of_block)
@@ -55,6 +63,7 @@ static int array_block_check(const struc
 			     size_t size_of_block)
 {
 	struct array_block *bh_le = dm_block_data(b);
+	uint32_t nr_entries, max_entries, value_size;
 	__le32 csum_disk;
 
 	if (dm_block_location(b) != le64_to_cpu(bh_le->blocknr)) {
@@ -74,6 +83,26 @@ static int array_block_check(const struc
 		return -EILSEQ;
 	}
 
+	nr_entries = le32_to_cpu(bh_le->nr_entries);
+	max_entries = le32_to_cpu(bh_le->max_entries);
+	value_size = le32_to_cpu(bh_le->value_size);
+
+	if (!value_size) {
+		DMERR_LIMIT("%s failed: value_size is zero", __func__);
+		return -EILSEQ;
+	}
+
+	if (max_entries != calc_max_entries(value_size, size_of_block)) {
+		DMERR_LIMIT("%s failed: max_entries %u invalid for value_size %u",
+			    __func__, max_entries, value_size);
+		return -EILSEQ;
+	}
+
+	if (nr_entries > max_entries) {
+		DMERR_LIMIT("%s failed: too many entries", __func__);
+		return -EILSEQ;
+	}
+
 	return 0;
 }
 
@@ -139,14 +168,6 @@ static void dec_ablock_entries(struct dm
 }
 
 /*
- * Each array block can hold this many values.
- */
-static uint32_t calc_max_entries(size_t value_size, size_t size_of_block)
-{
-	return (size_of_block - sizeof(struct array_block)) / value_size;
-}
-
-/*
  * Allocate a new array block.  The caller will need to unlock block.
  */
 static int alloc_ablock(struct dm_array_info *info, size_t size_of_block,
author maintainer
#329 lore raw

PATCH 329/713 dm array: reject an array block whose value size is not the callers

In reply to Greg Kroah-Hartman

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

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

From: Bryam Vargas <[email protected]>

commit 4538a287bdf5d0f9a379c678e5262b9f5783f547 upstream.

array_block_check() can only compare the header against itself, so a block with value_size 4 and max_entries 1018 is internally consistent and passes. dm-cache keeps two arrays -- mappings at 8 bytes and hints at 4 -- and the roots for both live in the superblock. Point the mappings root at a hint block and __load_mappings() walks it through an info whose value size is 8, so element_at() strides 8 bytes over 4-byte entries and reaches offset 8160 of a 4096-byte block.

get_ablock() and __shadow_ablock() are the two places that hold the block and the caller at once. Reject there when the two value sizes disagree. Arrays only ever read their own blocks, so this fires on crafted metadata only.

Fixes 6513c29f44f2 ("dm persistent data: add transactional array")
---
 drivers/md/persistent-data/dm-array.c |   16 ++++++++++++++++
 1 file changed, 16 insertions(+)
drivers/md/persistent-data/dm-array.c +16 -0
@@ -246,6 +246,14 @@ static int get_ablock(struct dm_array_in
 		return r;
 
 	*ab = dm_block_data(*block);
+	if (le32_to_cpu((*ab)->value_size) != info->value_type.size) {
+		DMERR_LIMIT("%s failed: value_size %u != wanted %u", __func__,
+			    le32_to_cpu((*ab)->value_size),
+			    info->value_type.size);
+		dm_tm_unlock(info->btree_info.tm, *block);
+		return -EILSEQ;
+	}
+
 	return 0;
 }
 
@@ -308,6 +316,14 @@ static int __shadow_ablock(struct dm_arr
 		return r;
 
 	*ab = dm_block_data(*block);
+	if (le32_to_cpu((*ab)->value_size) != info->value_type.size) {
+		DMERR_LIMIT("%s failed: value_size %u != wanted %u", __func__,
+			    le32_to_cpu((*ab)->value_size),
+			    info->value_type.size);
+		dm_tm_unlock(info->btree_info.tm, *block);
+		return -EILSEQ;
+	}
+
 	if (inc)
 		inc_ablock_entries(info, *ab);
 
author maintainer
#330 lore raw

PATCH 330/713 coresight: etm3x: Fix cntr_val_show() to match cntr_val_store() behavior

In reply to Greg Kroah-Hartman

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

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

From: Kuan-Wei Chiu <[email protected]>

commit 41fb4e925528aefa4b7a5f76c7f81db99c0d0f38 upstream.

The cntr_val_show() function was intended to print the values of all counters using a loop. However, due to a buffer overwrite issue with sprintf(), it effectively only displayed the value of the last counter.

The companion function, cntr_val_store(), allows users to modify a specific counter selected by 'cntr_idx'. To maintain consistency between read and write operations and to align with the ETM4x driver behavior, modify cntr_val_show() to report only the value of the currently selected counter.

This change removes the loop and the "counter %d:" prefix, printing only the hexadecimal value. It also adopts sysfs_emit() for standard sysfs output formatting.

---
 drivers/hwtracing/coresight/coresight-etm3x-sysfs.c |   15 ++++-----------
 1 file changed, 4 insertions(+), 11 deletions(-)
drivers/hwtracing/coresight/coresight-etm3x-sysfs.c +4 -11
@@ -717,26 +717,19 @@ static DEVICE_ATTR_RW(cntr_rld_event);
 static ssize_t cntr_val_show(struct device *dev,
 			     struct device_attribute *attr, char *buf)
 {
-	int i, ret = 0;
 	u32 val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
 	struct etm_config *config = &drvdata->config;
 
 	if (!coresight_get_mode(drvdata->csdev)) {
 		spin_lock(&drvdata->spinlock);
-		for (i = 0; i < drvdata->nr_cntr; i++)
-			ret += sprintf(buf, "counter %d: %x\n",
-				       i, config->cntr_val[i]);
+		val = config->cntr_val[config->cntr_idx];
 		spin_unlock(&drvdata->spinlock);
-		return ret;
+	} else {
+		val = etm_readl(drvdata, ETMCNTVRn(config->cntr_idx));
 	}
 
-	for (i = 0; i < drvdata->nr_cntr; i++) {
-		val = etm_readl(drvdata, ETMCNTVRn(i));
-		ret += sprintf(buf, "counter %d: %x\n", i, val);
-	}
-
-	return ret;
+	return sysfs_emit(buf, "%#x\n", val);
 }
 
 static ssize_t cntr_val_store(struct device *dev,
author maintainer
#331 lore raw

PATCH 331/713 cpufreq: apple-soc: Fix OPP table cleanup

In reply to Greg Kroah-Hartman

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

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

From: Haoxiang Li <[email protected]>

commit d87cb889dc7ab1f2deecadf2a5e9023184bd7900 upstream.

apple_soc_cpufreq_init() adds OPP tables from firmware, but some failure paths do not remove them. The driver also uses dev_pm_opp_remove_all_dynamic(), which is not the right cleanup helper for OPP tables loaded from firmware.

Use the cpumask OPP helper after the policy CPU mask has been populated. Pair it with the matching cpumask remove helper on failure paths and in apple_soc_cpufreq_exit(). This also removes the separate dev_pm_opp_set_sharing_cpus() call, as the cpumask helper loads the DT OPP tables for all CPUs in the policy.

Fixes 6286bbb40576 ("cpufreq: apple-soc: Add new driver to control Apple SoC CPU P-states")
---
 drivers/cpufreq/apple-soc-cpufreq.c |   36 ++++++++++++++----------------------
 1 file changed, 14 insertions(+), 22 deletions(-)
drivers/cpufreq/apple-soc-cpufreq.c +14 -22
@@ -249,21 +249,19 @@ static int apple_soc_cpufreq_init(struct
 		return -ENODEV;
 	}
 
-	ret = dev_pm_opp_of_add_table(cpu_dev);
-	if (ret < 0) {
-		dev_err(cpu_dev, "%s: failed to add OPP table: %d\n", __func__, ret);
-		return ret;
-	}
+	priv = kzalloc_obj(*priv);
+	if (!priv)
+		return -ENOMEM;
 
 	ret = apple_soc_cpufreq_find_cluster(policy, &reg_base, &info);
 	if (ret) {
 		dev_err(cpu_dev, "%s: failed to get cluster info: %d\n", __func__, ret);
-		return ret;
+		goto out_free_priv;
 	}
 
-	ret = dev_pm_opp_set_sharing_cpus(cpu_dev, policy->cpus);
-	if (ret) {
-		dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n", __func__, ret);
+	ret = dev_pm_opp_of_cpumask_add_table(policy->cpus);
+	if (ret < 0) {
+		dev_err(cpu_dev, "%s: failed to add OPP table: %d\n", __func__, ret);
 		goto out_iounmap;
 	}
 
@@ -271,19 +269,13 @@ static int apple_soc_cpufreq_init(struct
 	if (ret <= 0) {
 		dev_dbg(cpu_dev, "OPP table is not ready, deferring probe\n");
 		ret = -EPROBE_DEFER;
-		goto out_free_opp;
-	}
-
-	priv = kzalloc_obj(*priv);
-	if (!priv) {
-		ret = -ENOMEM;
-		goto out_free_opp;
+		goto out_free_table;
 	}
 
 	ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
 	if (ret) {
 		dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret);
-		goto out_free_priv;
+		goto out_free_table;
 	}
 
 	/* Get OPP levels (p-state indexes) and stash them in driver_data */
@@ -318,12 +310,12 @@ static int apple_soc_cpufreq_init(struct
 
 out_free_cpufreq_table:
 	dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
-out_free_priv:
-	kfree(priv);
-out_free_opp:
-	dev_pm_opp_remove_all_dynamic(cpu_dev);
+out_free_table:
+	dev_pm_opp_of_cpumask_remove_table(policy->cpus);
 out_iounmap:
 	iounmap(reg_base);
+out_free_priv:
+	kfree(priv);
 	return ret;
 }
 
@@ -332,7 +324,7 @@ static void apple_soc_cpufreq_exit(struc
 	struct apple_cpu_priv *priv = policy->driver_data;
 
 	dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table);
-	dev_pm_opp_remove_all_dynamic(priv->cpu_dev);
+	dev_pm_opp_of_cpumask_remove_table(policy->cpus);
 	iounmap(priv->reg_base);
 	kfree(priv);
 }
author maintainer
#332 lore raw

PATCH 332/713 cpufreq: schedutil: Fix rate limit overflow

In reply to Greg Kroah-Hartman

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

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

From: Hui Su <[email protected]>

commit 3bff8f8e95fdc6ad19c8a1a8f87029094747e4bf upstream.

rate_limit_us is an unsigned int, while NSEC_PER_USEC is defined as 1000L. On 32-bit systems, the multiplication is therefore performed using 32-bit unsigned arithmetic before the result is assigned to freq_update_delay_ns.

For example, writing 4294968 to rate_limit_us wraps the delay from 4294968000 ns to 704 ns. This makes schedutil update far more often than configured.

Add sugov_update_rate_limit_us() to widen rate_limit_us to s64 before converting it to nanoseconds. Use the helper when updating the tunable through sysfs and when starting the governor, so both paths perform the conversion without overflow.

Fixes 9bdcb44e391d ("cpufreq: schedutil: New governor based on scheduler utilization data")
Cc All applicable <[email protected]>
---
 kernel/sched/cpufreq_schedutil.c |   15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)
kernel/sched/cpufreq_schedutil.c +13 -2
@@ -61,6 +61,17 @@ static DEFINE_PER_CPU(struct sugov_cpu,
 
 /************************ Governor internals ***********************/
 
+static void sugov_update_rate_limit_us(struct sugov_policy *sg_policy)
+{
+	/*
+	 * Cast rate_limit_us before multiplication to force 64-bit arithmetic.
+	 * Otherwise, on 32-bit platforms, both operands are converted to
+	 * 32-bit unsigned long and the multiplication may overflow.
+	 */
+	sg_policy->freq_update_delay_ns =
+		(s64)sg_policy->tunables->rate_limit_us * NSEC_PER_USEC;
+}
+
 static bool sugov_should_update_freq(struct sugov_policy *sg_policy, u64 time)
 {
 	s64 delta_ns;
@@ -606,7 +617,7 @@ rate_limit_us_store(struct gov_attr_set
 	tunables->rate_limit_us = rate_limit_us;
 
 	list_for_each_entry(sg_policy, &attr_set->policy_list, tunables_hook)
-		sg_policy->freq_update_delay_ns = rate_limit_us * NSEC_PER_USEC;
+		sugov_update_rate_limit_us(sg_policy);
 
 	return count;
 }
@@ -848,7 +859,7 @@ static int sugov_start(struct cpufreq_po
 	void (*uu)(struct update_util_data *data, u64 time, unsigned int flags);
 	unsigned int cpu;
 
-	sg_policy->freq_update_delay_ns	= sg_policy->tunables->rate_limit_us * NSEC_PER_USEC;
+	sugov_update_rate_limit_us(sg_policy);
 	sg_policy->last_freq_update_time	= 0;
 	sg_policy->next_freq			= 0;
 	sg_policy->work_in_progress		= false;
author maintainer
#333 lore raw

PATCH 333/713 cxl/features: bound fwctl command payload to the input buffer

In reply to Greg Kroah-Hartman

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

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

From: Zhenhao Wan <[email protected]>

commit f687394af983df5660b6afae7e0d06969f3af206 upstream.

fwctl_cmd_rpc() copies cmd->in_len bytes into inbuf = kvzalloc(cmd->in_len) and passes inbuf and in_len to ->fw_rpc(). The CXL callback cxlctl_fw_rpc() ignores in_len and never checks the user-controlled op_size against it.

cxlctl_set_feature() bounds op_size only from below
(op_size <= sizeof(feat_in->hdr)) and then reads op_size - sizeof(hdr) bytes from feat_in->feat_data via cxl_set_feature(). With a small in_len and a large op_size the first memcpy() already reads past the kvzalloc(in_len) buffer; the out-of-bounds bytes are placed in the mailbox payload and sent to the device, and a large enough op_size can walk into unmapped memory and oops the kernel. The Get paths pin op_size to a fixed size but likewise read the input struct without checking in_len.

Reject, at the single dispatch point, any request whose fixed header plus op_size does not fit in the copied-in buffer. The lower-bound test guards the subtraction and ensures op_size was copied in before it is read.

Fixes eb5dfcb9e36d ("cxl: Add support to handle user feature commands for set feature")
---
 drivers/cxl/core/features.c |    8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)
drivers/cxl/core/features.c +7 -1
@@ -649,7 +649,13 @@ static void *cxlctl_fw_rpc(struct fwctl_
 	struct cxl_memdev *cxlmd = fwctl_to_memdev(fwctl_dev);
 	struct cxl_features_state *cxlfs = to_cxlfs(cxlmd->cxlds);
 	const struct fwctl_rpc_cxl *rpc_in = in;
-	u16 opcode = rpc_in->opcode;
+	u16 opcode;
+
+	if (in_len < sizeof(rpc_in->hdr) ||
+	    rpc_in->op_size > in_len - sizeof(rpc_in->hdr))
+		return ERR_PTR(-EINVAL);
+
+	opcode = rpc_in->opcode;
 
 	if (!cxlctl_validate_hw_command(cxlfs, rpc_in, scope, opcode))
 		return ERR_PTR(-EINVAL);
author maintainer
#334 lore raw

PATCH 334/713 cxl/mce: Make the MCE notifier per-region

In reply to Greg Kroah-Hartman

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

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

From: Dave Jiang <[email protected]>

commit 775d0f4558f4cec0ee0c8966595d1add1791f36e upstream.

Flavien Solt reported lifetime issues with the CXL MCE notifier, which can lead to NULL dereferences and use-after-free in the MCE handler. The notifier was registered per memory device and stored in 'struct cxl_memdev_state', even though it only needs the region state (the region's SPA range and its extended linear cache size).

Instead of keeping the memory device and endpoint alive, the correct fix is to move the notifier into 'struct cxl_region' and register it from cxl_region_probe() as it should be a per-region notifier. Setup the registration to only happen for regions that have an extended linear cache as that is the only current usage.

Remove cxl_port_get_spa_cache_alias() as it is now dead code.

[ dj: Update dev_warn() when notifier fails due to kconfig. (Ben) ]

Fixes 516e5bd0b6bf ("cxl: Add mce notifier to emit aliased address for extended linear cache")
Assisted-by Claude:claude-opus-4-8
---
 drivers/cxl/core/mbox.c   |    8 --------
 drivers/cxl/core/mce.c    |   27 +++++++++++++--------------
 drivers/cxl/core/region.c |   43 +++++++++++++++----------------------------
 drivers/cxl/cxl.h         |    8 ++------
 drivers/cxl/cxlmem.h      |    2 --
 5 files changed, 30 insertions(+), 58 deletions(-)
drivers/cxl/core/mbox.c +0 -8
@@ -11,7 +11,6 @@
 
 #include "core.h"
 #include "trace.h"
-#include "mce.h"
 
 static bool cxl_raw_allow_all;
 
@@ -1526,7 +1525,6 @@ struct cxl_memdev_state *cxl_memdev_stat
 						 u16 dvsec)
 {
 	struct cxl_memdev_state *mds;
-	int rc;
 
 	mds = devm_cxl_dev_state_create(dev, CXL_DEVTYPE_CLASSMEM, serial,
 					dvsec, struct cxl_memdev_state, cxlds,
@@ -1538,12 +1536,6 @@ struct cxl_memdev_state *cxl_memdev_stat
 
 	mutex_init(&mds->event.log_lock);
 
-	rc = devm_cxl_register_mce_notifier(dev, &mds->mce_notifier);
-	if (rc == -EOPNOTSUPP)
-		dev_warn(dev, "CXL MCE unsupported\n");
-	else if (rc)
-		return ERR_PTR(rc);
-
 	return mds;
 }
 EXPORT_SYMBOL_NS_GPL(cxl_memdev_state_create, "CXL");
drivers/cxl/core/mce.c +13 -14
@@ -4,16 +4,16 @@
 #include <linux/notifier.h>
 #include <linux/set_memory.h>
 #include <asm/mce.h>
-#include <cxlmem.h>
+#include <cxl.h>
+#include "core.h"
 #include "mce.h"
 
 static int cxl_handle_mce(struct notifier_block *nb, unsigned long val,
 			  void *data)
 {
-	struct cxl_memdev_state *mds = container_of(nb, struct cxl_memdev_state,
-						    mce_notifier);
-	struct cxl_memdev *cxlmd = mds->cxlds.cxlmd;
-	struct cxl_port *endpoint = cxlmd->endpoint;
+	struct cxl_region *cxlr = container_of(nb, struct cxl_region,
+					       mce_notifier);
+	struct cxl_region_params *p = &cxlr->params;
 	struct mce *mce = data;
 	u64 spa, spa_alias;
 	unsigned long pfn;
@@ -21,26 +21,25 @@ static int cxl_handle_mce(struct notifie
 	if (!mce || !mce_usable_address(mce))
 		return NOTIFY_DONE;
 
-	if (!endpoint)
-		return NOTIFY_DONE;
-
 	spa = mce->addr & MCI_ADDR_PHYSADDR;
 
-	pfn = spa >> PAGE_SHIFT;
-	if (!pfn_valid(pfn))
+	if (!cxl_resource_contains_addr(p->res, spa))
 		return NOTIFY_DONE;
 
-	spa_alias = cxl_port_get_spa_cache_alias(endpoint, spa);
-	if (spa_alias == ~0ULL)
-		return NOTIFY_DONE;
+	if (spa >= p->res->start + p->cache_size)
+		spa_alias = spa - p->cache_size;
+	else
+		spa_alias = spa + p->cache_size;
 
 	pfn = spa_alias >> PAGE_SHIFT;
+	if (!pfn_valid(pfn))
+		return NOTIFY_DONE;
 
 	/*
 	 * Take down the aliased memory page. The original memory page flagged
 	 * by the MCE will be taken cared of by the standard MCE handler.
 	 */
-	dev_emerg(mds->cxlds.dev, "Offlining aliased SPA address0: %#llx\n",
+	dev_emerg(&cxlr->dev, "Offlining aliased SPA address0: %#llx\n",
 		  spa_alias);
 	if (!memory_failure(pfn, 0))
 		set_mce_nospec(pfn);
drivers/cxl/core/region.c +15 -28

61 lines not on this page · raw message

drivers/cxl/cxl.h +2 -6
@@ -478,6 +478,7 @@ struct cxl_region_params {
  * @coord: QoS access coordinates for the region
  * @node_notifier: notifier for setting the access coordinates to node
  * @adist_notifier: notifier for calculating the abstract distance of node
+ * @mce_notifier: notifier for MCE
  */
 struct cxl_region {
 	struct device dev;
@@ -493,6 +494,7 @@ struct cxl_region {
 	struct access_coordinate coord[ACCESS_COORDINATE_MAX];
 	struct notifier_block node_notifier;
 	struct notifier_block adist_notifier;
+	struct notifier_block mce_notifier;
 };
 
 struct cxl_nvdimm_bridge {
@@ -870,7 +872,6 @@ bool is_cxl_pmem_region(struct device *d
 struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev);
 int cxl_add_to_region(struct cxl_endpoint_decoder *cxled);
 struct cxl_dax_region *to_cxl_dax_region(struct device *dev);
-u64 cxl_port_get_spa_cache_alias(struct cxl_port *endpoint, u64 spa);
 bool cxl_region_contains_resource(const struct resource *res);
 #else
 static inline bool is_cxl_pmem_region(struct device *dev)
@@ -889,11 +890,6 @@ static inline struct cxl_dax_region *to_
 {
 	return NULL;
 }
-static inline u64 cxl_port_get_spa_cache_alias(struct cxl_port *endpoint,
-					       u64 spa)
-{
-	return 0;
-}
 static inline bool cxl_region_contains_resource(const struct resource *res)
 {
 	return false;
drivers/cxl/cxlmem.h +0 -2
@@ -431,7 +431,6 @@ static inline struct cxl_dev_state *mbox
  * @poison: poison driver state info
  * @security: security driver state info
  * @fw: firmware upload / activation state
- * @mce_notifier: MCE notifier
  *
  * See CXL 3.0 8.2.9.8.2 Capacity Configuration and Label Storage for
  * details on capacity parameters.
@@ -451,7 +450,6 @@ struct cxl_memdev_state {
 	struct cxl_poison_state poison;
 	struct cxl_security_state security;
 	struct cxl_fw_state fw;
-	struct notifier_block mce_notifier;
 };
 
 static inline struct cxl_memdev_state *
author maintainer
#335 lore raw

PATCH 335/713 cxl/pmem: Format the nvdimm serial number as unsigned decimal

In reply to Greg Kroah-Hartman

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

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

From: Alison Schofield <[email protected]>

commit 8a80d3d65cd06ee35b913d8517fb2f2319f8e70c upstream.

The CXL NVDIMM security passphrase key description and the nvdimm 'id' sysfs attribute are both derived from the CXL device serial number, but the serial number is not formatted consistently.

The key description is formatted in hexadecimal while the 'id' attribute is formatted in decimal. As a result, ndctl stores the key using a decimal description while the kernel later looks it up using a hexadecimal description. For serial numbers of 10 and above, the descriptions no longer match, preventing automatic unlock after reboot.

The decimal formatting has a second problem: both the key description and the 'id' attribute use the signed %lld format for a u64 PCIe Device Serial Number. Devices whose vendor OUI sets bit 63, such as Montage CXL devices, appear with negative decimal serial numbers.

Format the security key description and 'id' attribute as unsigned decimal, %llu, and document that the 'id' attribute is an unsigned decimal value.

The key lookup mismatch was exposed by CXL unit test cxl-security.sh when cxl_test mock serial numbers were extended to 10 and above.

A work around is described for ndctl load-key users here:
https://github.com/pmem/ndctl/issues/299

---
 Documentation/ABI/testing/sysfs-bus-nvdimm |    3 ++-
 drivers/cxl/core/pmem.c                    |   10 ++++++----
 drivers/cxl/cxl.h                          |    3 ++-
 drivers/cxl/pmem.c                         |    2 +-
 4 files changed, 11 insertions(+), 7 deletions(-)
Documentation/ABI/testing/sysfs-bus-nvdimm +2 -1
@@ -48,7 +48,8 @@ What:		/sys/bus/nd/devices/nmemX/cxl/id
 Date:		November 2022
 KernelVersion:	6.2
 Contact:	Dave Jiang <[email protected]>
-Description:	(RO) Show the id (serial) of the device. This is CXL specific.
+Description:	(RO) Show the id (serial) of the device, formatted as an
+		unsigned 64-bit decimal value. This is CXL specific.
 
 What:		/sys/bus/nd/devices/nmemX/cxl/provider
 Date:		November 2022
drivers/cxl/core/pmem.c +6 -4
@@ -219,12 +219,14 @@ static struct cxl_nvdimm *cxl_nvdimm_all
 	dev->bus = &cxl_bus_type;
 	dev->type = &cxl_nvdimm_type;
 	/*
-	 * A "%llx" string is 17-bytes vs dimm_id that is max
-	 * NVDIMM_KEY_DESC_LEN
+	 * dev_id is the nvdimm dimm_id used for security key lookup.
+	 * It must match id_show(), which emits the CXL serial as an
+	 * unsigned decimal. A u64 decimal string is at most 20 digits
+	 * plus NUL.
 	 */
-	BUILD_BUG_ON(sizeof(cxl_nvd->dev_id) < 17 ||
+	BUILD_BUG_ON(sizeof(cxl_nvd->dev_id) < 21 ||
 		     sizeof(cxl_nvd->dev_id) > NVDIMM_KEY_DESC_LEN);
-	sprintf(cxl_nvd->dev_id, "%llx", cxlmd->cxlds->serial);
+	sprintf(cxl_nvd->dev_id, "%llu", cxlmd->cxlds->serial);
 
 	return cxl_nvd;
 }
drivers/cxl/cxl.h +2 -1
@@ -505,7 +505,8 @@ struct cxl_nvdimm_bridge {
 	struct nvdimm_bus_descriptor nd_desc;
 };
 
-#define CXL_DEV_ID_LEN 19
+/* Holds a u64 serial as a decimal string: up to 20 digits + NUL */
+#define CXL_DEV_ID_LEN 21
 
 enum {
 	CXL_NVD_F_INVALIDATED = 0,
drivers/cxl/pmem.c +1 -1
@@ -52,7 +52,7 @@ static ssize_t id_show(struct device *de
 	struct cxl_nvdimm *cxl_nvd = nvdimm_provider_data(nvdimm);
 	struct cxl_dev_state *cxlds = cxl_nvd->cxlmd->cxlds;
 
-	return sysfs_emit(buf, "%lld\n", cxlds->serial);
+	return sysfs_emit(buf, "%llu\n", cxlds->serial);
 }
 static DEVICE_ATTR_RO(id);
 
author maintainer
#336 lore raw

PATCH 336/713 cxl/ras: Fix cxl_rch_get_aer_severity() wrong severity register

In reply to Greg Kroah-Hartman

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

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

From: Terry Bowman <[email protected]>

commit 9d399526128bae184b153deab42b49f5f3eea2ed upstream.

cxl_rch_get_aer_severity() classifies RCH Downstream Port uncorrectable errors as fatal or non-fatal by ANDing uncorrectable status with PCI_ERR_ROOT_FATAL_RCV. This is wrong because PCI_ERR_ROOT_FATAL_RCV is a Root Error Status register bit (bit 6), not a severity bit. ANDing it against uncorrectable status tests a reserved bit and produces incorrect severity classification.

Fix by ANDing the unmasked uncor_status against uncor_severity. Per PCIe Base Spec r6.0 Section 7.8.4.4, each bit in the Uncorrectable Error Severity register indicates whether the corresponding error is fatal (1) or non-fatal (0).

---
 drivers/cxl/core/ras_rch.c |   10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)
drivers/cxl/core/ras_rch.c +5 -5
@@ -94,11 +94,11 @@ static bool cxl_rch_get_aer_info(void __
 static bool cxl_rch_get_aer_severity(struct aer_capability_regs *aer_regs,
 				     int *severity)
 {
-	if (aer_regs->uncor_status & ~aer_regs->uncor_mask) {
-		if (aer_regs->uncor_status & PCI_ERR_ROOT_FATAL_RCV)
-			*severity = AER_FATAL;
-		else
-			*severity = AER_NONFATAL;
+	u32 uncor_status = aer_regs->uncor_status & ~aer_regs->uncor_mask;
+
+	if (uncor_status) {
+		*severity = (uncor_status & aer_regs->uncor_severity) ?
+			     AER_FATAL : AER_NONFATAL;
 		return true;
 	}
 
author maintainer
#337 lore raw

PATCH 337/713 Bluetooth: hci_bcm4377: Ignore reserved PHY in ext adv reports on BCM4378

In reply to Greg Kroah-Hartman

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

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

From: Lorenzo Stoakes (ARM) <[email protected]>

commit aec6a8d80e3da0ab5c9303a0281fd06d077f8716 upstream.

Commit ed2a2ef16a6b ("Bluetooth: Add quirk to ignore reserved PHY bits in LE Extended Adv Report") added a quirk to handle creative use of the reserved bits in the PHY fields for 4388 controllers in Apple silicon.

I observed the same issue with the BCM4378 Bluetooth controller (14e4:5f69, rev 05) on an Apple MacBook Pro (13-inch, M2, 2022):

Quoted 1 line

HCI Event: LE Meta Event (0x3e) plen 51

      LE Extended Advertising Report (0x0d)
        Num reports: 1
        Entry 0
          Event type: 0x2513
            Props: 0x0013
              Connectable
              Scannable
              Use legacy advertising PDUs
            Data status: Complete
            Reserved (0x2500)
          Legacy PDU Type: Reserved (0x2513)
          Address type: Random (0x01)
          Address: EA:C1:82:F0:24:C6 (Static)
          Primary PHY: Reserved
          Secondary PHY: No packets
          SID: no ADI field (0xff)
          TX power: 127 dBm
          RSSI: -57 dBm (0xc7)
          Periodic advertising interval: 0.00 msec (0x0000)
          Direct address type: Public (0x00)
          Direct address: 00:00:00:00:00:00 (OUI 00-00-00)
          Data length: 25

This results in the firmware rejecting connection attempts with "Unsupported Feature or Parameter Value" (0x11).

Fix the issue by using the same quirk for BCM4378 devices too.

I tested this locally and confirmed that the issue is resolved.

This was observed when attempting to connect a Kinesis Advantage 360 keyboard to the MacBook.

Assisted-by Claude:claude-fable-5
Fixes 2e7ed5f5e69b ("Bluetooth: hci_sync: Use advertised PHYs on hci_le_ext_create_conn_sync")
---
 drivers/bluetooth/hci_bcm4377.c |    1 +
 1 file changed, 1 insertion(+)
drivers/bluetooth/hci_bcm4377.c +1 -0
@@ -2490,6 +2490,7 @@ static const struct bcm4377_hw bcm4377_h
 		.has_bar0_core2_window2 = true,
 		.broken_mws_transport_config = true,
 		.broken_le_coded = true,
+		.broken_le_ext_adv_report_phy = true,
 		.send_calibration = bcm4378_send_calibration,
 		.send_ptb = bcm4378_send_ptb,
 	},
author maintainer
#338 lore raw

PATCH 338/713 Bluetooth: hci_bcm: fix usage_count leak when autosuspend_delay is negative

In reply to Greg Kroah-Hartman

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

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

From: Guangshuo Li <[email protected]>

commit dc6b7c771a963e20aedf4a21ffa22543b9837ba8 upstream.

bcm_request_irq() calls pm_runtime_use_autosuspend(), but bcm_close() does not call the matching pm_runtime_dont_use_autosuspend() when tearing down runtime PM.

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

Add the missing pm_runtime_dont_use_autosuspend() call before disabling runtime PM.

This issue was found by manual code inspection.

Fixes e88ab30d3669 ("Bluetooth: hci_bcm: Add suspend/resume runtime PM functions")
---
 drivers/bluetooth/hci_bcm.c |    1 +
 1 file changed, 1 insertion(+)
drivers/bluetooth/hci_bcm.c +1 -0
@@ -547,6 +547,7 @@ static int bcm_close(struct hci_uart *hu
 		if (IS_ENABLED(CONFIG_PM) && bdev->irq_acquired) {
 			devm_free_irq(bdev->dev, bdev->irq, bdev);
 			device_init_wakeup(bdev->dev, false);
+			pm_runtime_dont_use_autosuspend(bdev->dev);
 			pm_runtime_disable(bdev->dev);
 		}
 
author maintainer
#339 lore raw

PATCH 339/713 Bluetooth: hci_uart: Fix false success return in hci_uart_setup()

In reply to Greg Kroah-Hartman

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

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

From: Gongwei Li <[email protected]>

commit a9355799343e10014f2acfd4b6844d2335ecafea upstream.

When reading the local version information for vendor detection fails, the error is only printed and 0 is returned, which masks the setup failure from the HCI core.

Return PTR_ERR(skb) instead.

Fixes fb2ce8d11f039 ("Bluetooth: hci_uart: Add support for vendor detection flag")
Fixes 82f5169bf3d3b ("Bluetooth: hci_uart: add serdev driver support library")
---
 drivers/bluetooth/hci_ldisc.c  |    2 +-
 drivers/bluetooth/hci_serdev.c |    2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)
drivers/bluetooth/hci_ldisc.c +1 -1
@@ -451,7 +451,7 @@ static int hci_uart_setup(struct hci_dev
 	if (IS_ERR(skb)) {
 		BT_ERR("%s: Reading local version information failed (%ld)",
 		       hdev->name, PTR_ERR(skb));
-		return 0;
+		return PTR_ERR(skb);
 	}
 
 	if (skb->len != sizeof(*ver)) {
drivers/bluetooth/hci_serdev.c +1 -1
@@ -221,7 +221,7 @@ static int hci_uart_setup(struct hci_dev
 	if (IS_ERR(skb)) {
 		bt_dev_err(hdev, "Reading local version info failed (%ld)",
 			   PTR_ERR(skb));
-		return 0;
+		return PTR_ERR(skb);
 	}
 
 	if (skb->len != sizeof(*ver))
author maintainer
#340 lore raw

PATCH 340/713 Bluetooth: ISO: fix use-after-free of listener socket in iso_conn_ready

In reply to Greg Kroah-Hartman

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

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

From: Hang Nan <[email protected]>

commit 560bef609fa5992745929e8d7d458b9d88dd2830 upstream.

iso_conn_ready() looks up the BIS listener socket with iso_get_sock(), which takes a reference, and then, without re-checking its state, creates a child socket from it:

    parent = iso_get_sock(hdev, ...);
    if (!parent)
        return;

    lock_sock(parent);
    sk = iso_sock_alloc(sock_net(parent), NULL, BTPROTO_ISO, ...);
    ...
    iso_chan_add(conn, sk, parent);
    ...
    release_sock(parent);
    sock_put(parent);

If the listener socket is closed concurrently, between iso_get_sock() and lock_sock(), the reference taken by iso_get_sock() may be the last
one: the close path drops the link-list reference, and once iso_conn_ready() drops its own reference at the end of the function the socket is freed. The child socket, however, is already linked to the freed parent, and a later disconnect of the child runs iso_chan_del() -> bt_accept_unlink(), which dereferences the dangling parent pointer into the freed accept queue (a use-after-free). The same dangling pointer is also dereferenced through parent->***() in iso_chan_del().

Fix it the same way the connected (non-BIS) path was fixed in commit 0d255e63fcf3 ("Bluetooth: ISO: hold sk properly in iso_conn_ready"):
after taking the socket lock, re-check that the parent is still a listening, alive socket, and bail out otherwise.

Fixes ccf74f2390d60 ("Bluetooth: Add BTPROTO_ISO socket type")
---
 net/bluetooth/iso.c |    8 ++++++++
 1 file changed, 8 insertions(+)
net/bluetooth/iso.c +8 -0
@@ -2277,6 +2277,14 @@ static void iso_conn_ready(struct iso_co
 
 		lock_sock(parent);
 
+		/* The listener may have been closed concurrently. */
+		if (parent->sk_state != BT_LISTEN ||
+		    sock_flag(parent, SOCK_ZAPPED)) {
+			release_sock(parent);
+			sock_put(parent);
+			return;
+		}
+
 		sk = iso_sock_alloc(sock_net(parent), NULL,
 				    BTPROTO_ISO, GFP_ATOMIC, 0);
 		if (!sk) {
author maintainer
#341 lore raw

PATCH 341/713 Bluetooth: RFCOMM: serialize security confirmation handling

In reply to Greg Kroah-Hartman

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

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

From: Chengfeng Ye <[email protected]>

commit 759c185d0bbdb131357408f50b8735e04ed3caff upstream.

rfcomm_security_cfm() looks up a session on session_list and then walks its DLC list without holding rfcomm_mutex. Since RFCOMM session teardown uses rfcomm_mutex, krfcommd can close and free the same session and DLCs concurrently:

hci_rx_work krfcommd
----------- ---------
rfcomm_session_get()
rfcomm_lock() rfcomm_session_close()
rfcomm_dlc_unlink() rfcomm_session_del()
kfree(s)
rfcomm_unlock()
walk s->dlcs

The callback can then read a freed session list head and touch freed DLCs while updating their flags or timers.

Serialize the session lookup and DLC traversal in rfcomm_security_cfm() with rfcomm_mutex. This matches the existing RFCOMM session lifetime rules and prevents concurrent rfcomm_session_del() / rfcomm_dlc_unlink() from tearing the objects down while the callback is using them.

KASAN reported:

  BUG: KASAN: slab-use-after-free in rfcomm_security_cfm+0x41c/0x440
  Read of size 8 at addr ffff888111fb3960 by task kworker/u17:1/89
  Workqueue: hci0 hci_rx_work
  Call Trace:
   rfcomm_security_cfm+0x41c/0x440
   hci_encrypt_cfm+0x139/0x590
   hci_encrypt_change_evt+0x37b/0xc40
   hci_event_packet+0x71b/0xb20
   hci_rx_work+0x293/0x730
  Allocated by task 69:
   rfcomm_session_add+0x9e/0x2f0
   rfcomm_run+0x44b/0x41e0
  Freed by task 69:
   kfree+0x131/0x3c0
   rfcomm_session_del+0x188/0x220
   rfcomm_run+0x1985/0x41e0
Fixes 08c30aca9e698faddebd34f81e1196295f9dc063 ("Bluetooth: Remove RFCOMM session refcnt")
---
 net/bluetooth/rfcomm/core.c |    8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)
net/bluetooth/rfcomm/core.c +7 -1
@@ -2213,9 +2213,13 @@ static void rfcomm_security_cfm(struct h
 
 	BT_DBG("conn %p status 0x%02x encrypt 0x%02x", conn, status, encrypt);
 
+	rfcomm_lock();
+
 	s = rfcomm_session_get(&conn->hdev->bdaddr, &conn->dst);
-	if (!s)
+	if (!s) {
+		rfcomm_unlock();
 		return;
+	}
 
 	list_for_each_entry_safe(d, n, &s->dlcs, list) {
 		if (test_and_clear_bit(RFCOMM_SEC_PENDING, &d->flags)) {
@@ -2247,6 +2251,8 @@ static void rfcomm_security_cfm(struct h
 			set_bit(RFCOMM_AUTH_REJECT, &d->flags);
 	}
 
+	rfcomm_unlock();
+
 	rfcomm_schedule();
 }
 
author maintainer
#342 lore raw

PATCH 342/713 Bluetooth: hci_conn: re-enable advertising only for peripheral role

In reply to Greg Kroah-Hartman

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

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

From: Valentin Kindschi <[email protected]>

commit ed5fb41d3b6b6e665e7f97fd54bd1f9531c7477f upstream.

hci_le_conn_failed() unconditionally calls hci_enable_advertising(), although its own comment states advertising should be re-enabled only when the failed attempt was made as a peripheral.

hci_le_conn_failed() is reached from hci_conn_failed() for every failed LE connection, including outgoing central connections. For a central attempt this enable is redundant: hci_le_create_conn_sync() already restores advertising via hci_resume_advertising_sync() in its done:
block. Because hci_enable_advertising() only queues the work on cmd_sync_work, it runs *after* that resume has already succeeded and set HCI_LE_ADV.

The resulting HCI sequence, captured on a BCM43455 (no LE Extended Advertising, so legacy advertising is used):

LE Create Connection Status Success
... 13.8 s, peer never answers ...

  LE Set Advertising Parameters (0x2006)   Success   <- done: resume,
  LE Set Advertising Enable     (0x200a)   Success      HCI_LE_ADV set
  LE Create Connection Cancel   (0x200e)   Success
  LE Connection Complete                   Unknown Conn Id
  LE Set Advertising Parameters (0x2006)   Command Disallowed (0x0c)

The last command is the queued enable from hci_le_conn_failed() running as a second hci_enable_advertising_sync() pass. It clears HCI_LE_ADV (hci_sync.c, "Clear the HCI_LE_ADV bit temporarily"), then sends LE Set Advertising Parameters while the controller is still advertising, which the controller correctly rejects with Command Disallowed.

The disable-first call at the top of hci_enable_advertising_sync() cannot prevent this: hci_disable_advertising_sync() returns early without sending anything when HCI_LE_ADV is clear, so it is a no-op exactly when the flag is wrong.

hci_enable_advertising_sync() then returns without sending LE Set Advertising Enable, so HCI_LE_ADV is never set again. The legacy software rotation loop re-arms hci_schedule_adv_instance_sync() every HCI_DEFAULT_ADV_DURATION (2 s), and its "already advertising" shortcut tests HCI_LE_ADV, which can no longer become true. The command is therefore retried every 2 s indefinitely:

Bluetooth: hci0: Opcode 0x2006 failed: -16

Observed on a gateway as 5326 occurrences over 3 hours, ending only when bluetoothd was restarted. Connection attempts that succeed do not call hci_le_conn_failed() and never trigger this.

Add the role test the comment already describes. Both other hci_enable_advertising() call sites reached from a failed/closed LE connection (hci_cs_disconnect() and hci_disconn_complete_evt()) already guard on conn->role == HCI_ROLE_SLAVE; this one was missed.

Reproducing needs legacy advertising (ext_adv_capable() false, so the software rotation loop is used), simultaneous peripheral advertising and outgoing central connects, and a central connect that times out rather than failing fast.

The Fixes tag points at the commit that introduced the advertising restart into this path for the directed-advertising (peripheral) case; the role test that the later commit 0b1db38ca26b ("Bluetooth: Fix check for direct advertising") added to the sibling paths was never applied here.

Fixes 3c857757ef6e ("Bluetooth: Add directed advertising support through connect()")
Assisted-by Claude:claude-opus-5 btmon
---
 net/bluetooth/hci_conn.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
net/bluetooth/hci_conn.c +2 -1
@@ -1381,7 +1381,8 @@ static void hci_le_conn_failed(struct hc
 	/* Enable advertising in case this was a failed connection
 	 * attempt as a peripheral.
 	 */
-	hci_enable_advertising(hdev);
+	if (conn->role == HCI_ROLE_SLAVE)
+		hci_enable_advertising(hdev);
 }
 
 /* This function requires the caller holds hdev->lock */
author maintainer
#343 lore raw

PATCH 343/713 Bluetooth: hci_core: use skb_get() instead of skb_clone() for req_skb

In reply to Greg Kroah-Hartman

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

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

From: Xin Chen <[email protected]>

commit f5afdff569a09d1cb8cf19826199d024725576cb upstream.

BT enable fails intermittently with -ETIMEDOUT (-110). The kernel log shows the HCI Read Local Version command was sent and the firmware replied with status 0x00 (logged by hci_req_cmd_complete() BT_DBG), but the waiter in __hci_cmd_sync_sk() never woke up and timed out after 10 s:

  bluetooth hci0: Opcode 0xfc00              // __hci_cmd_sync_sk
  bluetooth hci0: opcode 0xfc00 plen 1       // hci_cmd_sync_add
  bluetooth hci0: skb len 4                  // hci_cmd_sync_alloc
  bluetooth hci0: length 1                   // hci_req_sync_run
  Bluetooth: hci0 cmd_cnt 1 cmd queued 1     // hci_cmd_work
  Bluetooth: hci0 type 1 len 4               // hci_send_frame
  Bluetooth: opcode 0xfc00 status 0x00       // hci_req_cmd_complete

<-- req_skb NULL: req_complete_skb not set,
hci_cmd_sync_complete() never called,
req_status stays HCI_REQ_PEND -->
<-- 10 s later: wait_event_interruptible_timeout expires -->
bluetooth hci0: end: err -110 // __hci_cmd_sync_sk

The root cause is that hci_send_cmd_sync() clones the sent command into hdev->req_skb so that hci_req_cmd_complete() can locate the registered completion callback. Under memory pressure this skb_clone() fails, leaving hdev->req_skb NULL. The firmware reply is received and processed, but hci_req_cmd_complete() finds NULL req_skb, so hci_cmd_sync_complete() is never called, req_status stays HCI_REQ_PEND, and the waiter times out with -ETIMEDOUT.

req_skb is only used to read bt_cb(skb)->hci callbacks and opcode -- it is never modified. Replace skb_clone() with skb_get(), which simply increments the reference count of hdev->sent_cmd without allocating new memory and therefore cannot fail.

This issue was first observed as a use-after-free in ttyport_close() when ttyport_open() failed, which was investigated in an earlier patch series [1]. That investigation led to the discovery of the true root cause described above.

[1] https://lore.kernel.org/all/[email protected]/

Fixes 2615fd9a7c25 ("Bluetooth: hci_sync: Fix overwriting request callback")
---
 net/bluetooth/hci_core.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/bluetooth/hci_core.c +1 -1
@@ -4091,7 +4091,7 @@ static int hci_send_cmd_sync(struct hci_
 	if (READ_ONCE(hdev->req_status) == HCI_REQ_PEND &&
 	    !hci_dev_test_and_set_flag(hdev, HCI_CMD_PENDING)) {
 		kfree_skb(hdev->req_skb);
-		hdev->req_skb = skb_clone(hdev->sent_cmd, GFP_KERNEL);
+		hdev->req_skb = skb_get(hdev->sent_cmd);
 	}
 
 	return err;
author maintainer
#344 lore raw

PATCH 344/713 Bluetooth: hci_event: clear HCI_LE_ADV only on a created connection

In reply to Greg Kroah-Hartman

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

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

From: Valentin Kindschi <[email protected]>

commit 941929abe5feaed672b9a52e330e547d333240c6 upstream.

le_conn_complete_evt() clears HCI_LE_ADV before looking at the event status, on the premise stated in its comment that all controllers stop advertising when a connection is created.

That premise only holds when a connection was actually created. On a non-zero status none was, and the controller is still advertising: after the host issues LE Create Connection Cancel the event arrives with Unknown Connection Identifier (0x02), and a connection timeout behaves the same way. Clearing the flag there leaves the host believing advertising is off while the controller has it on.

It is also wrong for extended advertising, where several sets can be advertising at once. hci_cc_le_set_ext_adv_enable() is careful about this - on disabling one set it walks hdev->adv_instances and only clears HCI_LE_ADV once no instance is still enabled. The unconditional clear here discards that bookkeeping, so one set connecting drops the flag while the others keep advertising.

The direction of the error matters. A flag left set is self-correcting:
hci_disable_advertising_sync() sends LE Set Advertising Enable(0) and the command complete puts the state back. A flag left clear is not, because that same function returns early without sending anything while the flag is clear:

- LE Set Advertising Parameters is then sent to a controller that is still advertising, and is correctly rejected with Command Disallowed (0x0c);
- hci_enable_advertising_sync() returns at that point, before the LE Set Advertising Enable that would set HCI_LE_ADV again.

On a controller without LE Extended Advertising that is reachable from
here: hci_schedule_adv_instance_sync() re-arms adv_instance_expire every HCI_DEFAULT_ADV_DURATION (2 s) and its "already advertising" shortcut tests HCI_LE_ADV, which can no longer become true, so the parameter write is retried for as long as advertising is configured:

Bluetooth: hci0: Opcode 0x2006 failed: -16

Only clear the flag when a connection was established.

Note this is not on its own sufficient to stop that retry loop - the redundant enable queued by hci_le_conn_failed() clears HCI_LE_ADV itself and recreates the same mismatch, which patch 1 addresses. This patch fixes the event handler reporting a state the controller is not in.

Verified on the affected device (BCM43455, legacy advertising only) with this patch and patch 1 applied. A 221 s btmon capture with an out-of-range peer at -90 dBm contains two outgoing connection attempts that the host cancelled, each producing exactly the event this patch changes:

  < LE Set Advertising Parameters  0x2006   Success
  < LE Set Advertising Enable      0x200a   Success
  < LE Create Connection Cancel    0x200e   Success
Quoted 1 line

LE Connection Complete Unknown Connection Identifier (0x02), central

Nothing follows either one; the next command is an unrelated scan restart 70 ms later. Over the whole capture: 7 LE Set Advertising Parameters sent, all Success; 10 LE Set Advertising Enable, all Success; no Command Disallowed of any opcode, and no 2 s cadence anywhere. Two central connections to other peers completed normally afterwards, with feature exchange and a connection parameter update, so advertising was still live across the cancelled attempts.

The extended advertising case above is a code argument, not a measurement:
this controller has no LE Extended Advertising, so that path is not exercised by the capture.

Fixes fbd96c151cdc ("Bluetooth: Fix clearing HCI_LE_ADV for LE connections")
Assisted-by Claude:claude-opus-5 btmon
---
 net/bluetooth/hci_event.c |    7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
net/bluetooth/hci_event.c +4 -3
@@ -5724,10 +5724,11 @@ static void le_conn_complete_evt(struct
 	hci_dev_lock(hdev);
 	hci_store_wake_reason(hdev, bdaddr, bdaddr_type);
 
-	/* All controllers implicitly stop advertising in the event of a
-	 * connection, so ensure that the state bit is cleared.
+	/* Advertising stops when a connection is created. On a failed
+	 * connection it keeps running, so leave the state bit alone.
 	 */
-	hci_dev_clear_flag(hdev, HCI_LE_ADV);
+	if (!status)
+		hci_dev_clear_flag(hdev, HCI_LE_ADV);
 
 	/* Check for existing connection:
 	 *
author maintainer
#345 lore raw

PATCH 345/713 Bluetooth: hci_h5: fix usage_count leak when autosuspend_delay is negative

In reply to Greg Kroah-Hartman

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

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

From: Guangshuo Li <[email protected]>

commit 853a92b97ca547a7ddd9790ff90651b2fd943498 upstream.

h5_btrtl_open() calls pm_runtime_use_autosuspend(), but h5_btrtl_close() does not call the matching pm_runtime_dont_use_autosuspend() when tearing down runtime PM.

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

Add the missing pm_runtime_dont_use_autosuspend() call before disabling runtime PM.

This issue was found by manual code inspection.

Fixes d9dd833cf6d2 ("Bluetooth: hci_h5: Add runtime suspend")
---
 drivers/bluetooth/hci_h5.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/bluetooth/hci_h5.c +3 -1
@@ -1023,8 +1023,10 @@ static void h5_btrtl_open(struct h5 *h5)
 
 static void h5_btrtl_close(struct h5 *h5)
 {
-	if (!test_bit(H5_WAKEUP_DISABLE, &h5->flags))
+	if (!test_bit(H5_WAKEUP_DISABLE, &h5->flags)) {
+		pm_runtime_dont_use_autosuspend(&h5->hu->serdev->dev);
 		pm_runtime_disable(&h5->hu->serdev->dev);
+	}
 
 	gpiod_set_value_cansleep(h5->device_wake_gpio, 0);
 	gpiod_set_value_cansleep(h5->enable_gpio, 0);
author maintainer
#346 lore raw

PATCH 346/713 Bluetooth: hci_intel: fix usage_count leak when autosuspend_delay is negative

In reply to Greg Kroah-Hartman

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

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

From: Guangshuo Li <[email protected]>

commit c7e9a8cb6918656884a0757c92465075c7555ffa upstream.

intel_set_power() calls pm_runtime_use_autosuspend() when powering on the device, but the power-off path does not call the matching pm_runtime_dont_use_autosuspend() before disabling runtime PM.

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

Add the missing pm_runtime_dont_use_autosuspend() call before disabling runtime PM.

This issue was found by manual code inspection.

Fixes 74cdad37cd24 ("Bluetooth: hci_intel: Add runtime PM support")
---
 drivers/bluetooth/hci_intel.c |    1 +
 1 file changed, 1 insertion(+)
drivers/bluetooth/hci_intel.c +1 -0
@@ -345,6 +345,7 @@ static int intel_set_power(struct hci_ua
 			devm_free_irq(&idev->pdev->dev, idev->irq, idev);
 			device_wakeup_disable(&idev->pdev->dev);
 
+			pm_runtime_dont_use_autosuspend(&idev->pdev->dev);
 			pm_runtime_disable(&idev->pdev->dev);
 		}
 	}
author maintainer
#347 lore raw

PATCH 347/713 Bluetooth: hci_sync: Clear HCI_CMD_PENDING when dropping the last request

In reply to Greg Kroah-Hartman

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

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

From: Ibrahim Abdelkader <[email protected]>

commit cb19774faa57c51efa189d8b8606aeabccebc53b upstream.

A synchronous HCI command that never receives a response leaves HCI_CMD_PENDING set: hci_req_cmd_complete() is the only place that clears it, and it only runs when a response matching the last command sent arrives.

hci_send_cmd_sync() populates hdev->req_skb only when the flag transitions from clear to set, while hci_dev_open_sync() and hci_dev_close_sync() drop req_skb without clearing the flag. After a timeout followed by either, the two disagree: the flag claims a request is outstanding while req_skb is NULL. Subsequent synchronous commands are then sent with no req_skb, so hci_event_packet() has nothing to match an arriving event against, and the caller times out even though the controller answered.

Commands answered by Command Complete recover on their own, since hci_req_cmd_complete() clears the flag as a side effect. Drivers using __hci_cmd_sync_ev() with a custom event do not, because a vendor event never reaches that path. On a WCN3988 (hci_qca over UART) this makes a controller firmware hang unrecoverable: the driver injects a hardware error and re-runs qca_setup(), qca_read_soc_version() waits for HCI_EV_VENDOR, the reply arrives within 4 ms and is discarded, and every retry fails the same way. The adapter is left down until the driver is unbound and rebound, or power is removed.

Clear the flag wherever the last request is dropped, restoring the invariant that req_skb is non-NULL exactly when HCI_CMD_PENDING is set. Verified on hardware by forcing a command timeout: without this change setup fails on every attempt, with it setup succeeds on the first.

Fixes 2615fd9a7c25 ("Bluetooth: hci_sync: Fix overwriting request callback")
---
 net/bluetooth/hci_sync.c |    2 ++
 1 file changed, 2 insertions(+)
net/bluetooth/hci_sync.c +2 -0
@@ -5374,6 +5374,7 @@ int hci_dev_open_sync(struct hci_dev *hd
 		if (hdev->req_skb) {
 			kfree_skb(hdev->req_skb);
 			hdev->req_skb = NULL;
+			hci_dev_clear_flag(hdev, HCI_CMD_PENDING);
 		}
 
 		clear_bit(HCI_RUNNING, &hdev->flags);
@@ -5558,6 +5559,7 @@ int hci_dev_close_sync(struct hci_dev *h
 	if (hdev->req_skb) {
 		kfree_skb(hdev->req_skb);
 		hdev->req_skb = NULL;
+		hci_dev_clear_flag(hdev, HCI_CMD_PENDING);
 	}
 
 	clear_bit(HCI_RUNNING, &hdev->flags);
author maintainer
#348 lore raw

PATCH 348/713 kasan: fix cache shrink race with CPU hotplug

In reply to Greg Kroah-Hartman

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

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

From: Hui Su <[email protected]>

commit 8790303cbaac52a11dfed4aab261f8ea60682525 upstream.

kasan_quarantine_remove_cache() first invokes per_cpu_remove_cache() on all online CPUs. Each callback moves objects belonging to the cache from cpu_quarantine to the CPU's shrink_qlist, where they can later be freed from task context.

kmem_cache_destroy() invokes the quarantine removal path while holding cpus_read_lock(), but kmem_cache_shrink() does not. The latter can therefore race with CPU offlining as follows:

kmem_cache_shrink() CPU hotplug
------------------- -----------
on_each_cpu()
CPU1 moves objects to
CPU1's shrink_qlist
on_each_cpu() returns
CPU1 goes offline kasan_cpu_offline()
drains cpu_quarantine leaves shrink_qlist untouched
for_each_online_cpu()
skips CPU1

The objects left on CPU1's shrink_qlist are not returned to the slab allocator. This may prevent kmem_cache_shrink() from releasing slabs that would otherwise become empty. If CPU1 remains offline, a later kmem_cache_destroy() also skips the list and can report that the cache still contains objects.

An intermittent occurrence was observed with a virtio-9p filesystem. The mount and umount commands both returned 0, but the kernel logged the following during the userspace-triggered teardown:

  [  2994.380134][  T111] BUG 9p-fcall-cache-1 (Tainted: G    B              ): Objects remaining on __kmem_cache_shutdown()
  [  2994.381140][  T111] Object 0xff11000004361118 @offset=4376
  [  2994.381607][  T111] Allocated in p9_fcall_init+0x201/0x400 age=19564 cpu=1 pid=104
  [  2994.382591][  T111]  p9_fcall_init+0x201/0x400
  [  2994.382810][  T111]  p9_tag_alloc+0x12f/0x700
  [  2994.382982][  T111]  p9_client_prepare_req+0x102/0x3e0
  [  2994.383165][  T111]  p9_client_rpc+0x1ab/0xa50
  [  2994.383334][  T111]  p9_client_getattr_dotl+0xb0/0x1a0
  [  2994.383515][  T111]  v9fs_vfs_getattr_dotl+0x115/0x360
  [  2994.383719][  T111]  vfs_getattr_nosec+0x22c/0x3a0
  [  2994.383910][  T111]  vfs_statx+0xd7/0x170
  [  2994.384062][  T111]  vfs_fstatat+0x45/0x80
  [  2994.384215][  T111]  __do_sys_newfstatat+0x84/0xe0
  [  2994.384386][  T111]  do_syscall_64+0x115/0x6a0
  [  2994.384566][  T111]  entry_SYSCALL_64_after_hwframe+0x77/0x7f
  [  2994.399720][  T111] WARNING: mm/slub.c:1244 at __kmem_cache_shutdown+0x363/0x500, CPU#0: busybox/111
  [  2994.405655][  T111] Call Trace:
  [  2994.406325][  T111]  kmem_cache_destroy+0x73/0x1b0
  [  2994.406630][  T111]  p9_client_destroy+0x271/0x3c0
  [  2994.407210][  T111]  v9fs_session_close+0x3c/0x260
  [  2994.407409][  T111]  v9fs_kill_super+0x48/0x90
  [  2994.407584][  T111]  deactivate_locked_super+0xa3/0x160
  [  2994.407778][  T111]  cleanup_mnt+0x1dd/0x3e0

Thus, a successful umount left objects in the 9p fcall cache and prevented the cache from being destroyed cleanly.

Per-CPU shrink_qlist storage exists for every possible CPU, and each list is protected by its own raw spinlock. Iterate over possible CPUs so that a list populated before its CPU went offline is drained as well.

for_each_possible_cpu() can do more work than for_each_online_cpu(), but this change only affects CONFIG_KASAN_GENERIC kernels. The extra work is limited to cache shrink and cache destruction paths and does not affect the normal allocation/free fast path. It adds one raw-spinlock-protected scan of each possible CPU's shrink list. These lists are normally empty; a non-empty list is traversed to remove objects belonging to the cache being shrunk or destroyed.

Fixes 07d067e4f2ce ("kasan: fix sleeping function called from invalid context on RT kernel")
Cc Alexander Potapenko <[email protected]>
Cc Andrey Konovalov <[email protected]>
Cc Dmitry Vyukov <[email protected]>
Cc Vincenzo Frascino <[email protected]>
Cc "Zhang, Qiang1" <[email protected]>
---
 mm/kasan/quarantine.c |    7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)
mm/kasan/quarantine.c +6 -1
@@ -355,7 +355,12 @@ void kasan_quarantine_remove_cache(struc
 	 */
 	on_each_cpu(per_cpu_remove_cache, cache, 1);
 
-	for_each_online_cpu(cpu) {
+	/*
+	 * A CPU can go offline after on_each_cpu() returns, leaving cache
+	 * objects on that CPU's shrink list. Scan all possible CPUs to
+	 * drain those lists.
+	 */
+	for_each_possible_cpu(cpu) {
 		sq = per_cpu_ptr(&shrink_qlist, cpu);
 		raw_spin_lock_irqsave(&sq->lock, flags);
 		qlist_move_cache(&sq->qlist, &to_free, cache);
author maintainer
#349 lore raw

PATCH 349/713 kho: fix size calculation in kho_preserved_memory_reserve()

In reply to Greg Kroah-Hartman

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

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

From: Pratyush Yadav (Google) <[email protected]>

commit 3a0b8fa2eb36afc88b62a95f33f0c77c71fa5ded upstream.

kho_preserved_memory_reserve() calculates the size of a preservation by doing 1 << (order + PAGE_SHIFT). Since the '1' is a 32-bit integer, it can only be shifted by 31. That is, it will only work for preservations up to 2 GiB. Larger preservations will trigger undefined behaviour.

While preservations larger than 2 GiB can't be obtained via folios currently, they can be obtained via kho_preserve_pages().

For example, memblock reserve_mem uses kho_preserve_pages(). Reservations larger than 2 GiB are valid and will trigger this bug if properly aligned.

Fix it by using 1UL for shifting.

---
 kernel/liveupdate/kexec_handover.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/liveupdate/kexec_handover.c +1 -1
@@ -501,7 +501,7 @@ static int __init kho_preserved_memory_r
 	struct page *page;
 	u64 sz;
 
-	sz = 1 << (order + PAGE_SHIFT);
+	sz = 1UL << (order + PAGE_SHIFT);
 	page = kho_get_preserved_page(phys, order);
 
 	/* Reserve the memory preserved in KHO in memblock */
author maintainer
#350 lore raw

PATCH 350/713 jbd2: bound shrinker scans by examined checkpoint buffers

In reply to Greg Kroah-Hartman

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

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

From: Max Kellermann <[email protected]>

commit 15cb16496446b94e67f7abcb049b8e2c75cd3d02 upstream.

The jbd2 shrinker currently accounts only checkpoint buffers that it successfully releases against nr_to_scan. Busy buffers therefore do not consume the scan budget.

If a checkpoint transaction contains mostly busy buffers, the shrinker can scan its entire checkpoint list while holding journal->j_list_lock. Large checkpoint lists can result in excessive lock hold times and leave other CPUs spinning on j_list_lock, causing soft lockups or RCU stalls.

Pass nr_to_scan into journal_shrink_one_cp_list() and decrement it for every buffer examined, including busy buffers. Pass NULL from checkpoint cleanup paths so their existing full-list behavior is preserved.

This restores the scan-budget semantics that existed before journal_shrink_one_cp_list() was changed to always scan a complete checkpoint list.

---
 fs/jbd2/checkpoint.c |   25 +++++++++++++------------
 1 file changed, 13 insertions(+), 12 deletions(-)
fs/jbd2/checkpoint.c +13 -12
@@ -358,15 +358,16 @@ int jbd2_cleanup_journal_tail(journal_t
 /*
  * journal_shrink_one_cp_list
  *
- * Find all the written-back checkpoint buffers in the given list
- * and try to release them. If the whole transaction is released, set
- * the 'released' parameter. Return the number of released checkpointed
- * buffers.
+ * Find written-back checkpoint buffers in the given list and try to release
+ * them. If 'nr_to_scan' is set, scan at most that many buffers. If the whole
+ * transaction is released, set the 'released' parameter. Return the number of
+ * released checkpointed buffers.
  *
  * Called with j_list_lock held.
  */
 static unsigned long journal_shrink_one_cp_list(struct journal_head *jh,
 						enum jbd2_shrink_type type,
+						unsigned long *nr_to_scan,
 						bool *released)
 {
 	struct journal_head *last_jh;
@@ -375,13 +376,15 @@ static unsigned long journal_shrink_one_
 	int ret;
 
 	*released = false;
-	if (!jh)
+	if (!jh || (nr_to_scan && !*nr_to_scan))
 		return 0;
 
 	last_jh = jh->b_cpprev;
 	do {
 		jh = next_jh;
 		next_jh = jh->b_cpnext;
+		if (nr_to_scan)
+			(*nr_to_scan)--;
 
 		if (type == JBD2_SHRINK_DESTROY) {
 			ret = __jbd2_journal_remove_checkpoint(jh);
@@ -402,7 +405,7 @@ static unsigned long journal_shrink_one_
 
 		if (need_resched())
 			break;
-	} while (jh != last_jh);
+	} while (jh != last_jh && (!nr_to_scan || *nr_to_scan));
 
 	return nr_freed;
 }
@@ -424,7 +427,6 @@ unsigned long jbd2_journal_shrink_checkp
 	tid_t first_tid = 0, last_tid = 0, next_tid = 0;
 	tid_t tid = 0;
 	unsigned long nr_freed = 0;
-	unsigned long freed;
 	bool first_set = false;
 
 again:
@@ -457,10 +459,9 @@ again:
 		next_transaction = transaction->t_cpnext;
 		tid = transaction->t_tid;
 
-		freed = journal_shrink_one_cp_list(transaction->t_checkpoint_list,
-						   JBD2_SHRINK_BUSY_SKIP, &released);
-		nr_freed += freed;
-		(*nr_to_scan) -= min(*nr_to_scan, freed);
+		nr_freed += journal_shrink_one_cp_list(transaction->t_checkpoint_list,
+						       JBD2_SHRINK_BUSY_SKIP,
+						       nr_to_scan, &released);
 		if (*nr_to_scan == 0)
 			break;
 		if (need_resched() || spin_needbreak(&journal->j_list_lock))
@@ -516,7 +517,7 @@ void __jbd2_journal_clean_checkpoint_lis
 		transaction = next_transaction;
 		next_transaction = transaction->t_cpnext;
 		journal_shrink_one_cp_list(transaction->t_checkpoint_list,
-					   type, &released);
+					   type, NULL, &released);
 		/*
 		 * This function only frees up some memory if possible so we
 		 * dont have an obligation to finish processing. Bail out if
author maintainer
#351 lore raw

PATCH 351/713 jbd2: check need_resched() when skipping busy checkpoint buffers

In reply to Greg Kroah-Hartman

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

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

From: Max Kellermann <[email protected]>

commit f213e12ff5c9590b1034ae8da0e6d09665c772d0 upstream.

journal_shrink_one_cp_list() skips busy checkpoint buffers when called with JBD2_SHRINK_BUSY_SKIP. The continue statement on this path also skips the need_resched() check at the end of the loop body.

Consequently, when a checkpoint list contains mostly busy buffers, the shrinker can walk the entire list while holding journal->j_list_lock, even when a reschedule has been requested. Large checkpoint lists under memory pressure can therefore cause long lock hold times and leave other CPUs spinning on j_list_lock, resulting in soft lockups or RCU stalls.

Route the busy-buffer path through the need_resched() check so that the shrinker can release j_list_lock and reschedule promptly, restoring parity with the clean-buffer path, which already checks need_resched(). This does not change which checkpoint buffers are eligible for removal.

---
 fs/jbd2/checkpoint.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
fs/jbd2/checkpoint.c +2 -1
@@ -392,7 +392,7 @@ static unsigned long journal_shrink_one_
 			ret = jbd2_journal_try_remove_checkpoint(jh);
 			if (ret < 0) {
 				if (type == JBD2_SHRINK_BUSY_SKIP)
-					continue;
+					goto next;
 				break;
 			}
 		}
@@ -403,6 +403,7 @@ static unsigned long journal_shrink_one_
 			break;
 		}
 
+next:
 		if (need_resched())
 			break;
 	} while (jh != last_jh && (!nr_to_scan || *nr_to_scan));
author maintainer
#352 lore raw

PATCH 352/713 ipip: fix skb leak in collect_md mode when metadata_dst allocation fails

In reply to Greg Kroah-Hartman

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

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

From: Anton Danilov <[email protected]>

commit 6776efe4a52f289a3fc18f8adf19b035a7d8e1bb upstream.

In collect_md mode ipip_tunnel_rcv() returns 0 without freeing the skb when ip_tun_rx_dst() fails to allocate the metadata_dst. ipip_rcv() and mplsip_rcv() are registered as xfrm_tunnel handlers, so tunnel4_rcv() and tunnelmpls4_rcv() read the zero return as "the packet has been consumed" and do not free it either. The skb is leaked.

The other tunnel drivers all dispose of the packet at this point:
ip6_tunnel.c jumps to its drop label, ip_gre.c and ip6_gre.c return PACKET_REJECT, which makes gre_rcv() free the skb. Only ipip returns 0.

Jump to the existing drop label instead. It frees the skb and still returns 0, so the packet keeps being reported as consumed, which is what we want here: the outer header has already been pulled, and neither the remaining handlers nor an ICMP unreachable have any use for it.

Triggering this needs an ipip or mplsip tunnel in collect_md mode and an atomic allocation failure, which is why it has gone unnoticed.

---
 net/ipv4/ipip.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/ipv4/ipip.c +1 -1
@@ -248,7 +248,7 @@ static int ipip_tunnel_rcv(struct sk_buf
 
 			tun_dst = ip_tun_rx_dst(skb, flags, 0, 0);
 			if (!tun_dst)
-				return 0;
+				goto drop;
 			ip_tunnel_md_udp_encap(skb, &tun_dst->u.tun_info);
 		}
 		skb_reset_mac_header(skb);
author maintainer
#353 lore raw

PATCH 353/713 ip: orphan prefetched skbs before multicast forwarding

In reply to Greg Kroah-Hartman

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

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

From: Zhiling Zou <[email protected]>

commit e36ce6e78fe3fc3c071a26750783b7ba081ce10d upstream.

IPv4 and IPv6 input preserve an skb->sk association installed by bpf_sk_assign() so that local delivery can use the selected socket under RCU. Both address families can also prefetch a socket in UDP early demux. In both paths (BPF and UDP early demux) a reference is not guaranteed to be held on the socket.

When a multicast packet is not locally deliverable, IPv6 hands the original skb to ip6_mr_input(). IPv4's ip_mr_input() similarly keeps the original skb when local delivery is not needed. Either path can put the skb on an unresolved multicast route queue or forward it after the receive-side RCU section ends.

After the prefetched socket is destroyed, a later skb free invokes sock_pfree() and dereferences the stale skb->sk. Orphan the skb before each non-local multicast forwarding path. Local delivery retains the original skb; the existing skb_clone() calls provide multicast forwarding with a socket-free clone.

---
 net/ipv4/ipmr.c      |    3 +++
 net/ipv6/ip6_input.c |    1 +
 2 files changed, 4 insertions(+)
net/ipv4/ipmr.c +3 -0
@@ -2213,6 +2213,9 @@ int ip_mr_input(struct sk_buff *skb)
 	if (IPCB(skb)->flags & IPSKB_FORWARDED)
 		goto dont_forward;
 
+	if (!local)
+		skb_orphan(skb);
+
 	mrt = ipmr_rt_fib_lookup(net, skb);
 	if (IS_ERR(mrt)) {
 		kfree_skb(skb);
net/ipv6/ip6_input.c +1 -0
@@ -622,6 +622,7 @@ int ip6_mc_input(struct sk_buff *skb)
 		if (deliver) {
 			skb2 = skb_clone(skb, GFP_ATOMIC);
 		} else {
+			skb_orphan(skb);
 			skb2 = skb;
 			skb = NULL;
 		}
author maintainer
#354 lore raw

PATCH 354/713 ip6_tunnel: use skb_cow_head() in ip6_tnl_xmit()

In reply to Greg Kroah-Hartman

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

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

From: Zhiling Zou <[email protected]>

commit 87f21b59ddc618eff9670c174842964ad65fdade upstream.

ip6_tnl_xmit() may need to expand headroom before it can push the outer IPv6 and optional encap headers. It currently does that with skb_realloc_headroom(), copies skb->sk ownership, consumes the original skb, and then continues processing with the replacement skb kept only in its local variable.

That is safe only if the helper cannot fail afterwards. But this helper still has post-reallocation error exits. collect_md tunnels reject non-NONE encap after the replacement, and ip6_tnl_encap() can also fail later. In those cases the helper returns an error to its callers while the caller still only has the original skb pointer.

Both ip6_tnl_start_xmit() and the IPv6 GRE paths free the caller skb on error, so they can end up freeing an skb that ip6_tnl_xmit() already consumed.

Use skb_cow_head() instead. It provides the required headroom and writability without privately replacing the caller-owned skb, so later error returns cannot leave callers with a stale pointer.

The Ethernet users, ip6gretap and ip6erspan, clear IFF_TX_SKB_SHARING and already call skb_cow_head() before entering ip6_tnl_xmit(). They do not rely on the removed skb_shared() reallocation. This also makes the IPv6 tunnel path consistent with ip_tunnel_xmit().

---
 net/ipv6/ip6_tunnel.c |   15 ++-------------
 1 file changed, 2 insertions(+), 13 deletions(-)
net/ipv6/ip6_tunnel.c +2 -13
@@ -1236,19 +1236,8 @@ route_lookup:
 	 */
 	max_headroom += LL_RESERVED_SPACE(tdev);
 
-	if (skb_headroom(skb) < max_headroom || skb_shared(skb) ||
-	    (skb_cloned(skb) && !skb_clone_writable(skb, 0))) {
-		struct sk_buff *new_skb;
-
-		new_skb = skb_realloc_headroom(skb, max_headroom);
-		if (!new_skb)
-			goto tx_err_dst_release;
-
-		if (skb->sk)
-			skb_set_owner_w(new_skb, skb->sk);
-		consume_skb(skb);
-		skb = new_skb;
-	}
+	if (skb_cow_head(skb, max_headroom))
+		goto tx_err_dst_release;
 
 	if (t->parms.collect_md) {
 		if (t->encap.type != TUNNEL_ENCAP_NONE)
author maintainer
#355 lore raw

PATCH 355/713 ip6_gre: fix hardware header length for NBMA tunnels

In reply to Greg Kroah-Hartman

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

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

From: Zhiling Zou <[email protected]>

commit 505b6d296c486ef7d1274f279d4c43a172f63224 upstream.

ip6gre_tnl_link_config_route() accumulates the lower device's hardware header length into dev->hard_header_len whenever header_ops is set. This is incorrect for both users of header_ops.

ip6gretap and ip6erspan have a fixed Ethernet hardware header length. For an NBMA ip6gre tunnel, ip6gre_header() creates only the GRE header, the optional FOU or GUE header, and the outer IPv6 header. The lower device header is headroom needed later, not part of the tunnel device's hardware header.

Keep the lower device header in needed_headroom. Set hard_header_len to the tunnel header length only for ARPHRD_IP6GRE devices with header_ops, and leave the fixed Ethernet header length unchanged for tap and erspan devices.

---
 net/ipv6/ip6_gre.c |   13 ++++---------
 1 file changed, 4 insertions(+), 9 deletions(-)
net/ipv6/ip6_gre.c +4 -9
@@ -1137,13 +1137,8 @@ static void ip6gre_tnl_link_config_route
 			return;
 
 		if (rt->dst.dev) {
-			unsigned short dst_len = rt->dst.dev->hard_header_len +
-						 t_hlen;
-
-			if (t->dev->header_ops)
-				dev->hard_header_len = dst_len;
-			else
-				dev->needed_headroom = dst_len;
+			dev->needed_headroom = rt->dst.dev->hard_header_len +
+					       t_hlen;
 
 			if (set_mtu) {
 				int mtu = rt->dst.dev->mtu - t_hlen;
@@ -1171,8 +1166,8 @@ static int ip6gre_calc_hlen(struct ip6_t
 
 	t_hlen = tunnel->hlen + sizeof(struct ipv6hdr);
 
-	if (tunnel->dev->header_ops)
-		tunnel->dev->hard_header_len = LL_MAX_HEADER + t_hlen;
+	if (tunnel->dev->header_ops && tunnel->dev->type == ARPHRD_IP6GRE)
+		tunnel->dev->hard_header_len = t_hlen;
 	else
 		tunnel->dev->needed_headroom = LL_MAX_HEADER + t_hlen;
 
author maintainer
#356 lore raw

PATCH 356/713 ipv6: rpl: fix NULL dereference of idev in ipv6_rpl_srh_rcv()

In reply to Greg Kroah-Hartman

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

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

From: Andrea Mayer <[email protected]>

commit f826df95332c07380206dbd54178b6eefb311aba upstream.

ipv6_rpl_srh_rcv() dereferences idev from __in6_dev_get() without a NULL check when reading idev->cnf.rpl_seg_enabled.

When the device's MTU drops below IPV6_MIN_MTU, addrconf_ifdown() clears dev->ip6_ptr through RCU_INIT_POINTER(). A packet that passed the idev check in ip6_rcv_core() can then reach ipv6_rpl_srh_rcv() with dev->ip6_ptr already NULL.

Reproduced by flooding the receiving interface with ping6 traffic while flapping its MTU between 1500 and 1200:

 BUG: KASAN: null-ptr-deref in ipv6_rpl_srh_rcv+0xb3/0x1070
 Read of size 4 at addr 00000000000006b4 by task ping6/394

 CPU: 2 UID: 0 PID: 394 Comm: ping6 Not tainted 7.2.0-rc7-micro-vm-dev-00095-g24ef02f934ee #240 PREEMPT(full)
 Call Trace:
  <IRQ>
  kasan_report+0xc6/0x100
  ipv6_rpl_srh_rcv+0xb3/0x1070
  ip6_protocol_deliver_rcu+0x759/0x9a0
  ip6_input_finish+0xa8/0x1b0
  ip6_input+0xe1/0x490
  ipv6_rcv+0x33d/0x460
  __netif_receive_skb_one_core+0xd6/0x130
  process_backlog+0x2cc/0xa00
  __napi_poll.constprop.0+0x56/0x270
  net_rx_action+0x327/0x730
  handle_softirqs+0x11e/0x630
  do_softirq+0xb3/0xf0
  </IRQ>

Both ipv6_rpl_srh_rcv() and ipv6_srh_rcv() are called only from ipv6_rthdr_rcv(), which already has an idev lookup.

Fix the NULL dereference on the RPL path by checking idev in ipv6_rthdr_rcv(), before it calls either function. The callees take idev as an argument and no longer call __in6_dev_get(), so the packet is now dropped in one place, with SKB_DROP_REASON_IPV6DISABLED on both paths.

---
 net/ipv6/exthdrs.c |   26 ++++++++++++--------------
 1 file changed, 12 insertions(+), 14 deletions(-)
net/ipv6/exthdrs.c +12 -14
@@ -368,23 +368,16 @@ static void seg6_update_csum(struct sk_b
 			   (__be32 *)addr);
 }
 
-static int ipv6_srh_rcv(struct sk_buff *skb)
+static int ipv6_srh_rcv(struct sk_buff *skb, struct inet6_dev *idev)
 {
 	struct inet6_skb_parm *opt = IP6CB(skb);
 	struct net *net = dev_net(skb->dev);
 	struct ipv6_sr_hdr *hdr;
-	struct inet6_dev *idev;
 	struct in6_addr *addr;
 	int accept_seg6;
 
 	hdr = (struct ipv6_sr_hdr *)skb_transport_header(skb);
 
-	idev = __in6_dev_get(skb->dev);
-	if (!idev) {
-		kfree_skb(skb);
-		return -1;
-	}
-
 	accept_seg6 = min(READ_ONCE(net->ipv6.devconf_all->seg6_enabled),
 			  READ_ONCE(idev->cnf.seg6_enabled));
 
@@ -485,12 +478,11 @@ looped_back:
 	return -1;
 }
 
-static int ipv6_rpl_srh_rcv(struct sk_buff *skb)
+static int ipv6_rpl_srh_rcv(struct sk_buff *skb, struct inet6_dev *idev)
 {
 	struct ipv6_rpl_sr_hdr *hdr, *ohdr, *chdr;
 	struct inet6_skb_parm *opt = IP6CB(skb);
 	struct net *net = dev_net(skb->dev);
-	struct inet6_dev *idev;
 	struct ipv6hdr *oldhdr;
 	unsigned int chdr_len;
 	unsigned char *buf;
@@ -499,8 +491,6 @@ static int ipv6_rpl_srh_rcv(struct sk_bu
 	u64 n = 0;
 	u32 r;
 
-	idev = __in6_dev_get(skb->dev);
-
 	accept_rpl_seg = min(READ_ONCE(net->ipv6.devconf_all->rpl_seg_enabled),
 			     READ_ONCE(idev->cnf.rpl_seg_enabled));
 	if (!accept_rpl_seg) {
@@ -689,10 +679,14 @@ static int ipv6_rthdr_rcv(struct sk_buff
 	switch (hdr->type) {
 	case IPV6_SRCRT_TYPE_4:
 		/* segment routing */
-		return ipv6_srh_rcv(skb);
+		if (!idev)
+			goto disabled;
+		return ipv6_srh_rcv(skb, idev);
 	case IPV6_SRCRT_TYPE_3:
 		/* rpl segment routing */
-		return ipv6_rpl_srh_rcv(skb);
+		if (!idev)
+			goto disabled;
+		return ipv6_rpl_srh_rcv(skb, idev);
 	default:
 		break;
 	}
@@ -837,6 +831,10 @@ unknown_rh:
 	icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
 			  (&hdr->type) - skb_network_header(skb));
 	return -1;
+
+disabled:
+	kfree_skb_reason(skb, SKB_DROP_REASON_IPV6DISABLED);
+	return -1;
 }
 
 static const struct inet6_protocol rthdr_protocol = {
author maintainer
#357 lore raw

PATCH 357/713 ipv6: use RCU iterator to dump route exceptions

In reply to Greg Kroah-Hartman

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

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

From: Yuyang Huang <[email protected]>

commit 47cdab0d51aaa9bd85f8e4904585bd5bd4df4488 upstream.

rt6_nh_dump_exceptions() uses hlist_for_each_entry() to iterate over RCU-protected exception lists. The caller holds rcu_read_lock(), but does not hold rt6_exception_lock, so rt6_insert_exception() can concurrently add an entry with hlist_add_head_rcu().

KCSAN reports this race (irrelevant details omitted):

==================================================================

  BUG: KCSAN: data-race in rt6_insert_exception / rt6_nh_dump_exceptions

write (marked) to 0xffff8a7c44c59620 of 8 bytes by interrupt on cpu 5:

    rt6_insert_exception+0x3bb/0x760
    __ip6_rt_update_pmtu+0x4fe/0x750
    ip6_sk_update_pmtu+0x19a/0x3b0
    udpv6_err+0x3ff/0x800
    icmpv6_notify+0x1e1/0x440
    icmpv6_rcv+0x8c0/0xab0
    ip6_protocol_deliver_rcu+0x616/0x840
    ip6_input_finish+0xb9/0x160
    ...
    entry_SYSCALL_64_after_hwframe+0x77/0x7f

read to 0xffff8a7c44c59620 of 8 bytes by task 549 on cpu 14:

    rt6_nh_dump_exceptions+0xb3/0x260
    rt6_dump_route+0x53e/0x5f0
    fib6_dump_node+0x6d/0xf0
    fib6_walk_continue+0x290/0x2d0
    fib6_dump_table+0x28d/0x360
    inet6_dump_fib+0x37d/0x620
    rtnl_dumpit+0x7b/0xd0
    netlink_dump+0x3ae/0x7e0
    ...
    entry_SYSCALL_64_after_hwframe+0x77/0x7f

4 locks held by dumper/549:

    ...
    #1: (rcu_read_lock){....}-{1:3}, at: inet6_dump_fib+0x88/0x620
    #2: (&tb->tb6_lock){+.-.}-{3:3}, at: fib6_dump_table+0x1e9/0x360
    #3: (rcu_read_lock){....}-{1:3}, at: rt6_dump_route+0x483/0x5f0

value changed: 0xffff8a7c44e05700 -> 0xffff8a7c45d60100

Reported by Kernel Concurrency Sanitizer on:

  CPU: 14 UID: 0 PID: 549 Comm: dumper Not tainted
  7.2.0-rc7-virtme #38 PREEMPT(lazy)
  ...

Use hlist_for_each_entry_rcu() to safely iterate over the exception list.

---
 net/ipv6/route.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/ipv6/route.c +1 -1
@@ -6029,7 +6029,7 @@ static int rt6_nh_dump_exceptions(struct
 		return 0;
 
 	for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
-		hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) {
+		hlist_for_each_entry_rcu(rt6_ex, &bucket->chain, hlist) {
 			if (w->skip) {
 				w->skip--;
 				continue;
author maintainer
#358 lore raw

PATCH 358/713 landlock: Require LANDLOCK_ACCESS_FS_MAKE_REG for whiteout creation

In reply to Greg Kroah-Hartman

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

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

From: Günther Noack <[email protected]>

commit 672fa082d48b21e1fb62cdb184fee41513e53421 upstream.

Whiteout objects are used in the upper layer of an OverlayFS to indicate that the file with this name does not exist in the unified view, even if it is present in one of the lower layer file systems.

For the userspace implementations of OverlayFS (fuse-overlayfs), whiteout objects can be created from userspace as well:

* mknod(2) with S_IFCHR and makedev(0, 0)
* renameat2(2) with RENAME_WHITEOUT,
creating the whiteout in the old place of the moved file.

This commit guards whiteout creation in both of these cases with LANDLOCK_ACCESS_FS_MAKE_REG. Whiteout objects are *not* considered character devices and are not bound to a driver.

LANDLOCK_ACCESS_FS_MAKE_REG describes the same permission class as a whiteout object: creating one is the only S_IFCHR creation that the VFS exempts from CAP_MKNOD, so it is as unprivileged as creating a regular file, while LANDLOCK_ACCESS_FS_MAKE_CHAR and LANDLOCK_ACCESS_FS_MAKE_BLOCK keep meaning the creation of devices that expose a kernel interface [1].

For the mknod(2) case, introduce a Landlock erratum. The creation of whiteout objects through mknod(2) was previously guarded using LANDLOCK_ACCESS_FS_MAKE_CHAR, and it is now guarded using LANDLOCK_ACCESS_FS_MAKE_REG.

For the renameat2(2) case, fix a bug: Before this commit, renameat2(2) with RENAME_WHITEOUT would create a directory entry even when all LANDLOCK_ACCESS_FS_MAKE_* rights were denied.

This does not affect normal renames within layered OverlayFS mounts:
When doing a regular rename() on a mounted fuse-overlayfs, it is the fuse-overlayfs daemon that exercises renameat2() with RENAME_WHITEOUT, and only the Landlock domain of that daemon is checked there.

Fixes cb2c7d1a1776 ("landlock: Support filesystem access-control")

Depends-on: 49c9e09d9610 ("landlock: Fix handling of disconnected directories")
Depends-on: fe72ce6710cb ("landlock: Add errata documentation section")

[mic: Record why LANDLOCK_ACCESS_FS_MAKE_REG is the matching right, and add link(2) to the user doc]

---
 include/uapi/linux/landlock.h    |    1 
 security/landlock/errata/abi-1.h |   23 +++++++++++++++++++++
 security/landlock/fs.c           |   41 ++++++++++++++++++++++++++++++---------
 3 files changed, 56 insertions(+), 9 deletions(-)
include/uapi/linux/landlock.h +1 -0
@@ -351,6 +351,7 @@ struct landlock_net_port_attr {
  *   device.
  * - %LANDLOCK_ACCESS_FS_MAKE_DIR: Create (or rename) a directory.
  * - %LANDLOCK_ACCESS_FS_MAKE_REG: Create (or rename or link) a regular file.
+ *   This also guards the creation of whiteout objects as used in OverlayFS.
  * - %LANDLOCK_ACCESS_FS_MAKE_SOCK: Create (or rename or link) a UNIX domain
  *   socket.
  * - %LANDLOCK_ACCESS_FS_MAKE_FIFO: Create (or rename or link) a named pipe.
security/landlock/errata/abi-1.h +23 -0
@@ -22,3 +22,26 @@
  * from their original mount points.
  */
 LANDLOCK_ERRATUM(3)
+
+/**
+ * DOC: erratum_4
+ *
+ * Erratum 4: Creation of whiteout objects
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This fix changes the access rights required for the creation of whiteout
+ * objects through :manpage:`mknod(2)`, :manpage:`renameat2(2)`, or
+ * :manpage:`link(2)`.  Creating whiteout objects is now guarded by
+ * ``LANDLOCK_ACCESS_FS_MAKE_REG`` instead of ``LANDLOCK_ACCESS_FS_MAKE_CHAR``.
+ *
+ * Whiteout objects are used in OverlayFS to mark the absence of a file in an
+ * upper file system.  Despite being created with ``S_IFCHR``, whiteout objects
+ * do not count as character devices.
+ *
+ * Impact:
+ *
+ * Sandboxed programs that create OverlayFS whiteouts (such as fuse-overlayfs)
+ * now require ``LANDLOCK_ACCESS_FS_MAKE_REG`` instead of
+ * ``LANDLOCK_ACCESS_FS_MAKE_CHAR``.
+ */
+LANDLOCK_ERRATUM(4)
security/landlock/fs.c +32 -9

109 lines not on this page · raw message

#359 lore raw

PATCH 1/3 selftests/landlock: Use an actual chardev for MAKE_CHAR audit test

From: Günther Noack <[email protected]>

[ Upstream commit 173b1bd8730825e1f6862dbd07856e7d68447a41 ]

By passing a (0, 0) device number, the audit test for LANDLOCK_ACCESS_FS_MAKE_CHAR was accidentally creating a whiteout object rather than a char device. In preparation to treating whiteout objects differently, use an actual character device instead.

---
 tools/testing/selftests/landlock/fs_test.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
tools/testing/selftests/landlock/fs_test.c +1 -1
index 86e08aa6e0a7..e82b56a74c5f 100644
@@ -7436,7 +7436,7 @@ TEST_F(audit_layout1, make_char)
 
 	enforce_fs(_metadata, ACCESS_ALL, NULL);
 
-	EXPECT_EQ(-1, mknod(file1_s1d3, S_IFCHR | 0644, 0));
+	EXPECT_EQ(-1, mknod(file1_s1d3, S_IFCHR | 0644, makedev(7, 0)));
 	EXPECT_EQ(EACCES, errno);
 	EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_char",
 				    dir_s1d3));
maintainer Applied
#360 lore raw

Re: [PATCH 7.2.y 1/3] selftests/landlock: Use an actual chardev for MAKE_CHAR audit test

Quoted 4 lines

By passing a (0, 0) device number, the audit test for LANDLOCK_ACCESS_FS_MAKE_CHAR was accidentally creating a whiteout object rather than a char device. In preparation to treating whiteout objects differently, use an actual character device instead.

Queued the series for 7.2, thanks.

Signature · 2 lines
Thanks,
Sasha
#361 lore raw

PATCH 2/3 selftests/landlock: Add tests for whiteout object creation

In reply to Greg Kroah-Hartman

From: Günther Noack <[email protected]>

[ Upstream commit ee890889b30b22f9a21636061def7a04e4f89380 ]

Add tests to check that whiteout object creation is guarded by LANDLOCK_ACCESS_FS_MAKE_REG, in the cases where these are created from userspace:

* Conventional creation with mknod()
* Linking or renaming an existing whiteout object
* renameat2() with RENAME_WHITEOUT,
which creates a new whiteout object in the source location
* renameat2() with RENAME_EXCHANGE,
with one of the renamed objects being a whiteout object

---
 tools/testing/selftests/landlock/fs_test.c | 176 +++++++++++++++++++++
 1 file changed, 176 insertions(+)
tools/testing/selftests/landlock/fs_test.c +176 -0
index e82b56a74c5f..871a5b819b98 100644
@@ -2247,6 +2247,170 @@ TEST_F_FORK(layout1, rename_file)
 			       RENAME_EXCHANGE));
 }
 
+TEST_F_FORK(layout1, rename_whiteout_denied)
+{
+	/* The affected file is a FIFO. */
+	ASSERT_EQ(0, unlink(file1_s3d3));
+	ASSERT_EQ(0, mknod(file1_s3d3, S_IFIFO | 0600, 0));
+
+	/* Deny MAKE_REG, but allow MAKE_FIFO. */
+	enforce_fs(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, NULL);
+
+	/*
+	 * Try to rename a file with RENAME_WHITEOUT.
+	 * file1_s3d3 is in dir_s3d2 (tmpfs), so it supports RENAME_WHITEOUT.
+	 * Denied, because whiteout creation is guarded with MAKE_REG.
+	 */
+	EXPECT_EQ(-1, renameat2(AT_FDCWD, file1_s3d3, AT_FDCWD,
+				TMP_DIR "/s3d1/s3d2/s3d3/f2", RENAME_WHITEOUT));
+	EXPECT_EQ(EACCES, errno);
+}
+
+static bool is_whiteout(const char *const path)
+{
+	struct stat st;
+
+	if (stat(path, &st) == -1)
+		return false;
+
+	return S_ISCHR(st.st_mode) && st.st_rdev == makedev(0, 0);
+}
+
+static bool is_fifo(const char *const path)
+{
+	struct stat st;
+
+	return stat(path, &st) == 0 && S_ISFIFO(st.st_mode);
+}
+
+TEST_F_FORK(layout1, rename_whiteout_allowed)
+{
+	const struct rule rules[] = {
+		{
+			.path = dir_s3d3,
+			.access = LANDLOCK_ACCESS_FS_MAKE_REG,
+		},
+		{},
+	};
+
+	/* The affected file is a FIFO. */
+	ASSERT_EQ(0, unlink(file1_s3d3));
+	ASSERT_EQ(0, mknod(file1_s3d3, S_IFIFO | 0600, 0));
+
+	/* Allow MAKE_REG below dir_s3d3. */
+	enforce_fs(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, rules);
+
+	/*
+	 * Rename a file with RENAME_WHITEOUT within the same directory.
+	 * Allowed, because MAKE_REG is granted for the whiteout object which
+	 * gets created in the source location.
+	 */
+	EXPECT_EQ(0, renameat2(AT_FDCWD, file1_s3d3, AT_FDCWD,
+			       TMP_DIR "/s3d1/s3d2/s3d3/f2", RENAME_WHITEOUT));
+
+	/* A whiteout object took the place of the moved FIFO. */
+	EXPECT_TRUE(is_whiteout(file1_s3d3));
+	EXPECT_TRUE(is_fifo(TMP_DIR "/s3d1/s3d2/s3d3/f2"));
+}
+
+TEST_F_FORK(layout1, rename_whiteout_reparenting)
+{
+	const struct rule rules[] = {
+		{
+			.path = dir_s3d2,
+			.access = LANDLOCK_ACCESS_FS_REFER,
+		},
+		{
+			.path = dir_s3d3,
+			.access = LANDLOCK_ACCESS_FS_MAKE_REG,
+		},
+		{},
+	};
+
+	/* The moved files are FIFOs. */
+	ASSERT_EQ(0, unlink(file1_s3d3));
+	ASSERT_EQ(0, mknod(file1_s3d3, S_IFIFO | 0600, 0));
+	ASSERT_EQ(0, unlink(file1_s3d4));
+	ASSERT_EQ(0, mknod(file1_s3d4, S_IFIFO | 0600, 0));
+
+	/* Allow REFER below dir_s3d2, but MAKE_REG only below dir_s3d3. */
+	enforce_fs(_metadata,
+		   LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER,
+		   rules);
+
+	/*
+	 * The whiteout object is created in the source directory: Moving the
+	 * FIFO out of dir_s3d4 is denied because MAKE_REG is not granted
+	 * there, even though it is granted in the destination directory
+	 * dir_s3d3.
+	 */
+	EXPECT_EQ(-1, renameat2(AT_FDCWD, file1_s3d4, AT_FDCWD,
+				TMP_DIR "/s3d1/s3d2/s3d3/f2", RENAME_WHITEOUT));
+	EXPECT_EQ(EACCES, errno);
+
+	/*
+	 * Moving the FIFO out of dir_s3d3 is allowed, because MAKE_REG is
+	 * granted there for the created whiteout object.
+	 */
+	EXPECT_EQ(0, renameat2(AT_FDCWD, file1_s3d3, AT_FDCWD,
+			       TMP_DIR "/s3d1/s3d2/s3d4/f2", RENAME_WHITEOUT));
+
+	/* A whiteout object took the place of the moved FIFO. */
+	EXPECT_TRUE(is_whiteout(file1_s3d3));
+	EXPECT_TRUE(is_fifo(TMP_DIR "/s3d1/s3d2/s3d4/f2"));
+}
+
+TEST_F_FORK(layout1, rename_whiteout_exchange)
+{
+	const char *const whiteout_s3d3 = TMP_DIR "/s3d1/s3d2/s3d3/f2";
+	const struct rule rules[] = {
+		{
+			.path = dir_s3d2,
+			.access = LANDLOCK_ACCESS_FS_REFER,
+		},
+		{
+			.path = dir_s3d3,
+			.access = LANDLOCK_ACCESS_FS_MAKE_REG,
+		},
+		{},
+	};
+
+	/* The exchanged files are FIFOs and an existing whiteout object. */
+	ASSERT_EQ(0, unlink(file1_s3d3));
+	ASSERT_EQ(0, mknod(file1_s3d3, S_IFIFO | 0600, 0));
+	ASSERT_EQ(0, mknod(whiteout_s3d3, S_IFCHR | 0600, makedev(0, 0)));
+	ASSERT_EQ(0, unlink(file1_s3d4));
+	ASSERT_EQ(0, mknod(file1_s3d4, S_IFIFO | 0600, 0));
+
+	/* Allow REFER below dir_s3d2, but MAKE_REG only below dir_s3d3. */
+	enforce_fs(_metadata,
+		   LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER,
+		   rules);
+
+	/*
+	 * With RENAME_EXCHANGE, the whiteout object moves into the source
+	 * directory of the rename: Exchanging the FIFO in dir_s3d4 with the
+	 * whiteout object is denied because MAKE_REG is not granted in
+	 * dir_s3d4, even though it is granted in the whiteout object's own
+	 * directory dir_s3d3.
+	 */
+	EXPECT_EQ(-1, renameat2(AT_FDCWD, file1_s3d4, AT_FDCWD, whiteout_s3d3,
+				RENAME_EXCHANGE));
+	EXPECT_EQ(EACCES, errno);
+
+	/*
+	 * Exchanging the FIFO in dir_s3d3 with the whiteout object is
+	 * allowed, because MAKE_REG is granted in the directory into which
+	 * the whiteout object moves.
+	 */
+	EXPECT_EQ(0, renameat2(AT_FDCWD, file1_s3d3, AT_FDCWD, whiteout_s3d3,
+			       RENAME_EXCHANGE));
+
+	/* The FIFO and the whiteout object swapped places. */
+	EXPECT_TRUE(is_whiteout(file1_s3d3));
+	EXPECT_TRUE(is_fifo(whiteout_s3d3));
+}
+
 TEST_F_FORK(layout1, rename_dir)
 {
 	const struct rule rules[] = {
@@ -3270,6 +3434,18 @@ TEST_F_FORK(layout1, make_char)
 		       makedev(1, 3));
 }
 
+TEST_F_FORK(layout1, make_whiteout)
+{
+	/*
+	 * Creates a whiteout object (creation guarded by MAKE_REG).
+	 *
+	 * Contrary to the other character devices, this does not require
+	 * CAP_MKNOD, cf. vfs_mknod().
+	 */
+	test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, S_IFCHR,
+		       makedev(0, 0));
+}
+
 TEST_F_FORK(layout1, make_block)
 {
 	/* Creates a /dev/loop0 device. */
#362 lore raw

PATCH 3/3 selftests/landlock: Add audit test for whiteout object creation

In reply to Greg Kroah-Hartman

From: Günther Noack <[email protected]>

[ Upstream commit 8c46c6acbebe0d8544fd1b55e5ddf36828d7b9ea ]

Add audit_layout1.make_whiteout: This test looks similar to audit_layout1.make_char, but creates a whiteout object through mknod(). Since whiteout object creation is now guarded with LANDLOCK_ACCESS_FS_MAKE_REG rather than LANDLOCK_ACCESS_FS_MAKE_CHAR, it also needs to log the matching denial to audit.

---
 tools/testing/selftests/landlock/fs_test.c | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)
tools/testing/selftests/landlock/fs_test.c +19 -0
index 871a5b819b98..a9130ed70af5 100644
@@ -7622,6 +7622,25 @@ TEST_F(audit_layout1, make_char)
 	EXPECT_EQ(1, records.domain);
 }
 
+TEST_F(audit_layout1, make_whiteout)
+{
+	struct audit_records records;
+
+	EXPECT_EQ(0, unlink(file1_s1d3));
+
+	enforce_fs(_metadata, ACCESS_ALL, NULL);
+
+	/* Whiteout creation is denied and logged as fs.make_reg. */
+	EXPECT_EQ(-1, mknod(file1_s1d3, S_IFCHR | 0644, makedev(0, 0)));
+	EXPECT_EQ(EACCES, errno);
+	EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_reg",
+				    dir_s1d3));
+
+	EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
+	EXPECT_EQ(0, records.access);
+	EXPECT_EQ(1, records.domain);
+}
+
 TEST_F(audit_layout1, make_dir)
 {
 	struct audit_records records;
author maintainer
#363 lore raw

PATCH 359/713 libnvdimm/labels: Prevent integer overflow in __nd_label_validate()

In reply to Greg Kroah-Hartman

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

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

From: Bryam Vargas <[email protected]>

commit 037770686126155eafc44501312989e2837b9659 upstream.

The on-media namespace index field nslot is a u32 read from the DIMM label storage area. __nd_label_validate() bounds it against the config area size, but sizeof_namespace_label() returns unsigned, so the product nslot * label_size is evaluated in 32-bit and wraps modulo 2^32 before the comparison. A crafted nslot passes the bound and is then used as the loop trip count in nd_label_data_init(), whose memset() walks off the end of the config_size buffer: an out-of-bounds write.

The field is not trusted -- it comes from the medium, or from userspace via ND_CMD_SET_CONFIG_DATA. Evaluate the product in 64-bit so the bound check is exact; conforming labels are unaffected.

The check was safe when introduced by commit 4a826c83db4e ("libnvdimm:
namespace indices: read and validate"): it multiplied by sizeof(struct nd_namespace_label), a size_t, so on a 64-bit build the product did not wrap. Commit 564e871aa66f ("libnvdimm, label: add v1.2 nvdimm label definitions") narrowed it to 32 bits when the label size became a runtime value read via sizeof_namespace_label().

---
 drivers/nvdimm/label.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/nvdimm/label.c +1 -1
@@ -202,7 +202,7 @@ static int __nd_label_validate(struct nv
 		}
 
 		nslot = __le32_to_cpu(nsindex[i]->nslot);
-		if (nslot * sizeof_namespace_label(ndd)
+		if ((u64)nslot * sizeof_namespace_label(ndd)
 				+ 2 * sizeof_namespace_index(ndd)
 				> ndd->nsarea.config_size) {
 			dev_dbg(dev, "nsindex%d nslot: %u invalid, config_size: %#x\n",
author maintainer
#364 lore raw

PATCH 360/713 mailbox: qcom-ipcc: fix duplicate channel allocation across holes

In reply to Greg Kroah-Hartman

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

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

From: Anup Vishwakarma <[email protected]>

commit 66c7bcad72430a02c860521031350b84b31ad9a8 upstream.

The IPCC of_xlate() both scans for a free mailbox channel and checks for duplicate references to the same underlying IPCC channel. When a channel has been shutdown it might have left a hole in the channel list, which would terminate the search without considering duplicates later in the list.

Continue the traversal of the channel list to detect and reject duplicates, while keeping track of the first free channel.

Fixes d6fbfdbc1274 ("mailbox: qcom-ipcc: Fix IPCC mbox channel exhaustion")
---
 drivers/mailbox/qcom-ipcc.c |   17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)
drivers/mailbox/qcom-ipcc.c +11 -6
@@ -167,7 +167,7 @@ static struct mbox_chan *qcom_ipcc_mbox_
 {
 	struct qcom_ipcc *ipcc = to_qcom_ipcc(mbox);
 	struct qcom_ipcc_chan_info *mchan;
-	struct mbox_chan *chan;
+	struct mbox_chan *chan, *free_chan = NULL;
 	struct device *dev;
 	int chan_id;
 
@@ -180,16 +180,21 @@ static struct mbox_chan *qcom_ipcc_mbox_
 		chan = &ipcc->chans[chan_id];
 		mchan = chan->con_priv;
 
-		if (!mchan)
-			break;
-		else if (mchan->client_id == ph->args[0] &&
-				mchan->signal_id == ph->args[1])
+		if (!mchan) {
+			/* Keep scanning past holes to reject duplicate channel requests. */
+			if (!free_chan)
+				free_chan = chan;
+		} else if (mchan->client_id == ph->args[0] &&
+				mchan->signal_id == ph->args[1]) {
 			return ERR_PTR(-EBUSY);
+		}
 	}
 
-	if (chan_id >= mbox->num_chans)
+	if (!free_chan)
 		return ERR_PTR(-EBUSY);
 
+	chan = free_chan;
+
 	mchan = devm_kzalloc(dev, sizeof(*mchan), GFP_KERNEL);
 	if (!mchan)
 		return ERR_PTR(-ENOMEM);