From: Greg Kroah-Hartman Date: Thu, 9 Jul 2026 14:43:13 +0000 (+0200) Subject: 5.10-stable patches X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=5df5bdcd2a228a59131eadbd6c77158d77be479d;p=thirdparty%2Fkernel%2Fstable-queue.git 5.10-stable patches added patches: apparmor-fix-use-after-free-in-rawdata-dedup-loop.patch apparmor-mediate-the-implicit-connect-of-tcp-fast-open-sendmsg.patch f2fs-adjust-zone-capacity-when-considering-valid-block-count.patch f2fs-bound-i_inline_xattr_size-for-non-inline-xattr-inodes.patch f2fs-fix-listxattr-handling-of-corrupted-xattr-entries.patch f2fs-fix-potential-deadlock-in-f2fs_balance_fs.patch f2fs-fix-to-round-down-start-offset-of-fallocate-for-pin-file.patch f2fs-validate-orphan-inode-entry-count.patch fbdev-fbcon-fix-out-of-bounds-read-in-err_out-of-fbcon_do_set_font.patch hdlc_ppp-sync-per-proto-timers-before-freeing-hdlc-state.patch i2c-core-fix-adapter-debugfs-creation.patch i2c-core-fix-adapter-registration-race.patch i2c-core-fix-hang-on-adapter-registration-failure.patch i2c-core-fix-irq-domain-leak-on-adapter-registration-failure.patch i2c-core-fix-null-deref-on-adapter-registration-failure.patch net-ip_gre-require-cap_net_admin-in-the-device-netns-for-changelink.patch net-skmsg-preserve-sg.copy-across-sg-transforms.patch nfsv4-flexfiles-reject-zero-filehandle-version-count.patch skmsg-convert-struct-sk_msg_sg-copy-to-a-bitmap.patch --- diff --git a/queue-5.10/apparmor-fix-use-after-free-in-rawdata-dedup-loop.patch b/queue-5.10/apparmor-fix-use-after-free-in-rawdata-dedup-loop.patch new file mode 100644 index 0000000000..f89bbe9171 --- /dev/null +++ b/queue-5.10/apparmor-fix-use-after-free-in-rawdata-dedup-loop.patch @@ -0,0 +1,111 @@ +From stable+bounces-270557-greg=kroah.com@vger.kernel.org Thu Jul 2 17:30:47 2026 +From: Sasha Levin +Date: Thu, 2 Jul 2026 11:26:28 -0400 +Subject: apparmor: fix use-after-free in rawdata dedup loop +To: stable@vger.kernel.org +Cc: Ruslan Valiyev , Colin Ian King , John Johansen , Sasha Levin +Message-ID: <20260702152628.3530333-1-sashal@kernel.org> + +From: Ruslan Valiyev + +[ Upstream commit 6f060496d03e4dc560a40f73770bd08335cb7a27 ] + +aa_replace_profiles() walks ns->rawdata_list to dedup the incoming +policy blob against entries already attached to existing profiles. +Per the kernel-doc on struct aa_loaddata, list membership does not +hold a reference: profiles hold pcount, and when the last pcount +drops, do_ploaddata_rmfs() is queued on a workqueue that takes +ns->lock and removes the entry. Between dropping the last pcount +and the workqueue running, an entry remains on the list with +pcount == 0. + +aa_get_profile_loaddata() is an unconditional kref_get() on +pcount, so when the dedup loop hits such an entry, refcount +hardening reports + + refcount_t: addition on 0; use-after-free. + +inside aa_replace_profiles(), and the poisoned counter then +trips "saturated" and "underflow" warnings on the subsequent +uses of the same loaddata. + +Before commit a0b7091c4de4 ("apparmor: fix race on rawdata +dereference") the dedup path used a get_unless_zero-style helper +on a single counter, so the existing "if (tmp)" guard was +meaningful. The split-refcount refactor introduced +aa_get_profile_loaddata(), which has plain kref_get() semantics, +and the guard quietly became a no-op. + +Introduce aa_get_profile_loaddata_not0(), matching the existing +_not0 convention used by aa_get_profile_not0(), and use it for +the rawdata_list dedup lookup so dying entries are skipped. + +Reproduced on x86_64 with v7.1-rc5 in QEMU+KVM running Ubuntu +24.04 + stress-ng 0.17.06: + + stress-ng --apparmor 1 --klog-check --timeout 60s + +Without this patch the three refcount_t warnings fire within a +few seconds. With it the same 60 s run is clean. Coverage is a +smoke-test only; a longer soak with CONFIG_KASAN, CONFIG_KCSAN +and CONFIG_PROVE_LOCKING would be welcome from anyone with the +cycles. + +Fixes: a0b7091c4de4 ("apparmor: fix race on rawdata dereference") +Reported-by: Colin Ian King +Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221513 +Cc: stable@vger.kernel.org +Signed-off-by: Ruslan Valiyev +Signed-off-by: John Johansen +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + security/apparmor/include/policy_unpack.h | 19 +++++++++++++++++++ + security/apparmor/policy.c | 8 ++++++-- + 2 files changed, 25 insertions(+), 2 deletions(-) + +--- a/security/apparmor/include/policy_unpack.h ++++ b/security/apparmor/include/policy_unpack.h +@@ -122,6 +122,25 @@ aa_get_profile_loaddata(struct aa_loadda + return data; + } + ++/** ++ * aa_get_profile_loaddata_not0 - get a profile reference count if not zero ++ * @data: reference to get a count on ++ * ++ * Like aa_get_profile_loaddata(), but safe to call on an entry that may ++ * be on a list (e.g. ns->rawdata_list) where the last pcount has already ++ * dropped and the deferred cleanup has not yet run. ++ * ++ * Returns: pointer to reference, or %NULL if @data is NULL or its ++ * profile refcount has already reached zero. ++ */ ++static inline struct aa_loaddata * ++aa_get_profile_loaddata_not0(struct aa_loaddata *data) ++{ ++ if (data && kref_get_unless_zero(&data->pcount)) ++ return data; ++ return NULL; ++} ++ + void __aa_loaddata_update(struct aa_loaddata *data, long revision); + bool aa_rawdata_eq(struct aa_loaddata *l, struct aa_loaddata *r); + void aa_loaddata_kref(struct kref *kref); +--- a/security/apparmor/policy.c ++++ b/security/apparmor/policy.c +@@ -976,8 +976,12 @@ ssize_t aa_replace_profiles(struct aa_ns + if (aa_rawdata_eq(rawdata_ent, udata)) { + struct aa_loaddata *tmp; + +- tmp = aa_get_profile_loaddata(rawdata_ent); +- /* check we didn't fail the race */ ++ /* ++ * Entries remain on rawdata_list with ++ * pcount == 0 until do_ploaddata_rmfs() ++ * runs; only take a live profile ref. ++ */ ++ tmp = aa_get_profile_loaddata_not0(rawdata_ent); + if (tmp) { + aa_put_profile_loaddata(udata); + udata = tmp; diff --git a/queue-5.10/apparmor-mediate-the-implicit-connect-of-tcp-fast-open-sendmsg.patch b/queue-5.10/apparmor-mediate-the-implicit-connect-of-tcp-fast-open-sendmsg.patch new file mode 100644 index 0000000000..863148c29f --- /dev/null +++ b/queue-5.10/apparmor-mediate-the-implicit-connect-of-tcp-fast-open-sendmsg.patch @@ -0,0 +1,62 @@ +From stable+bounces-271597-greg=kroah.com@vger.kernel.org Fri Jul 3 03:29:49 2026 +From: Sasha Levin +Date: Thu, 2 Jul 2026 21:29:42 -0400 +Subject: apparmor: mediate the implicit connect of TCP fast open sendmsg +To: stable@vger.kernel.org +Cc: Bryam Vargas , John Johansen , Sasha Levin +Message-ID: <20260703012942.3803836-1-sashal@kernel.org> + +From: Bryam Vargas + +[ Upstream commit 4d587cd8a72155089a627130bbd4716ec0856e21 ] + +sendmsg()/sendto() with MSG_FASTOPEN is a combination of connect(2) and +write(2): it opens the connection in the SYN. apparmor_socket_sendmsg() +only checks AA_MAY_SEND, so a profile that grants send but denies connect +lets a confined task open an outbound TCP/MPTCP connection that connect(2) +would have refused, bypassing connect mediation. + +Mediate the implicit connect when MSG_FASTOPEN is set and a destination +is supplied. Add it to apparmor_socket_sendmsg() (not the shared +aa_sock_msg_perm() helper, which recvmsg also uses) and call aa_sk_perm() +directly, mirroring the selinux and tomoyo fixes. sk_is_tcp() does not +cover MPTCP fast open, so the SOCK_STREAM/IPPROTO_MPTCP arm is explicit. + +Fixes: cf60af03ca4e ("net-tcp: Fast Open client - sendmsg(MSG_FASTOPEN)") +Cc: stable@vger.kernel.org +Signed-off-by: Bryam Vargas +Signed-off-by: John Johansen +[ inlined absent sk_is_tcp()/sk_is_inet() helpers into the equivalent family/type/protocol checks ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + security/apparmor/lsm.c | 18 +++++++++++++++++- + 1 file changed, 17 insertions(+), 1 deletion(-) + +--- a/security/apparmor/lsm.c ++++ b/security/apparmor/lsm.c +@@ -951,7 +951,23 @@ static int aa_sock_msg_perm(const char * + static int apparmor_socket_sendmsg(struct socket *sock, + struct msghdr *msg, int size) + { +- return aa_sock_msg_perm(OP_SENDMSG, AA_MAY_SEND, sock, msg, size); ++ int error = aa_sock_msg_perm(OP_SENDMSG, AA_MAY_SEND, sock, msg, size); ++ ++ if (error) ++ return error; ++ ++ /* TCP fast open carries connect() semantics in sendmsg(); mediate ++ * the implicit connect so it cannot bypass the connect permission. ++ */ ++ if ((msg->msg_flags & MSG_FASTOPEN) && msg->msg_name && ++ (sock->sk->sk_family == AF_INET || ++ sock->sk->sk_family == AF_INET6) && ++ sock->sk->sk_type == SOCK_STREAM && ++ (sock->sk->sk_protocol == IPPROTO_TCP || ++ sock->sk->sk_protocol == IPPROTO_MPTCP)) ++ error = aa_sk_perm(OP_CONNECT, AA_MAY_CONNECT, sock->sk); ++ ++ return error; + } + + /** diff --git a/queue-5.10/f2fs-adjust-zone-capacity-when-considering-valid-block-count.patch b/queue-5.10/f2fs-adjust-zone-capacity-when-considering-valid-block-count.patch new file mode 100644 index 0000000000..55b8c80297 --- /dev/null +++ b/queue-5.10/f2fs-adjust-zone-capacity-when-considering-valid-block-count.patch @@ -0,0 +1,142 @@ +From stable+bounces-271779-greg=kroah.com@vger.kernel.org Fri Jul 3 15:39:29 2026 +From: Sasha Levin +Date: Fri, 3 Jul 2026 09:31:00 -0400 +Subject: f2fs: adjust zone capacity when considering valid block count +To: stable@vger.kernel.org +Cc: Jaegeuk Kim , Chao Yu , Sasha Levin +Message-ID: <20260703133101.4123681-1-sashal@kernel.org> + +From: Jaegeuk Kim + +[ Upstream commit 074b5ea2900ea8e40f8e7a3fd37e0a55ad3d5874 ] + +This patch fixes counting unusable blocks set by zone capacity when +checking the valid block count in a section. + +Reviewed-by: Chao Yu +Signed-off-by: Jaegeuk Kim +Stable-dep-of: 4275b59673eb ("f2fs: fix to round down start offset of fallocate for pin file") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/f2fs/debug.c | 2 +- + fs/f2fs/file.c | 6 +++--- + fs/f2fs/gc.c | 4 ++-- + fs/f2fs/segment.c | 7 +++---- + fs/f2fs/segment.h | 8 ++++---- + 5 files changed, 13 insertions(+), 14 deletions(-) + +--- a/fs/f2fs/debug.c ++++ b/fs/f2fs/debug.c +@@ -39,7 +39,7 @@ void f2fs_update_sit_info(struct f2fs_sb + + bimodal = 0; + total_vblocks = 0; +- blks_per_sec = BLKS_PER_SEC(sbi); ++ blks_per_sec = CAP_BLKS_PER_SEC(sbi); + hblks_per_sec = blks_per_sec / 2; + for (segno = 0; segno < MAIN_SEGS(sbi); segno += sbi->segs_per_sec) { + vblocks = get_valid_blocks(sbi, segno, true); +--- a/fs/f2fs/file.c ++++ b/fs/f2fs/file.c +@@ -1681,7 +1681,7 @@ static int expand_inode_data(struct inod + return 0; + + if (f2fs_is_pinned_file(inode)) { +- block_t sec_blks = BLKS_PER_SEC(sbi); ++ block_t sec_blks = CAP_BLKS_PER_SEC(sbi); + block_t sec_len = roundup(map.m_len, sec_blks); + + map.m_len = sec_blks; +@@ -2595,7 +2595,7 @@ do_more: + ret = -EAGAIN; + goto out; + } +- range->start += BLKS_PER_SEC(sbi); ++ range->start += CAP_BLKS_PER_SEC(sbi); + if (range->start <= end) + goto do_more; + out: +@@ -2717,7 +2717,7 @@ static int f2fs_defragment_range(struct + goto out; + } + +- sec_num = DIV_ROUND_UP(total, BLKS_PER_SEC(sbi)); ++ sec_num = DIV_ROUND_UP(total, CAP_BLKS_PER_SEC(sbi)); + + /* + * make sure there are enough free section for LFS allocation, this can +--- a/fs/f2fs/gc.c ++++ b/fs/f2fs/gc.c +@@ -441,7 +441,7 @@ static void atgc_lookup_victim(struct f2 + unsigned long long age, u, accu; + unsigned long long max_mtime = sit_i->dirty_max_mtime; + unsigned long long min_mtime = sit_i->dirty_min_mtime; +- unsigned int sec_blocks = BLKS_PER_SEC(sbi); ++ unsigned int sec_blocks = CAP_BLKS_PER_SEC(sbi); + unsigned int vblocks; + unsigned int dirty_threshold = max(am->max_candidate_count, + am->candidate_ratio * +@@ -1421,7 +1421,7 @@ next_step: + */ + if ((gc_type == BG_GC && has_not_enough_free_secs(sbi, 0, 0)) || + (!force_migrate && get_valid_blocks(sbi, segno, true) == +- BLKS_PER_SEC(sbi))) ++ CAP_BLKS_PER_SEC(sbi))) + return submitted; + + if (check_valid_map(sbi, segno, off) == 0) +--- a/fs/f2fs/segment.c ++++ b/fs/f2fs/segment.c +@@ -820,7 +820,7 @@ static void __locate_dirty_segment(struc + get_valid_blocks(sbi, segno, true); + + f2fs_bug_on(sbi, unlikely(!valid_blocks || +- valid_blocks == BLKS_PER_SEC(sbi))); ++ valid_blocks == CAP_BLKS_PER_SEC(sbi))); + + if (!IS_CURSEC(sbi, secno)) + set_bit(secno, dirty_i->dirty_secmap); +@@ -856,7 +856,7 @@ static void __remove_dirty_segment(struc + unsigned int secno = GET_SEC_FROM_SEG(sbi, segno); + + if (!valid_blocks || +- valid_blocks == BLKS_PER_SEC(sbi)) { ++ valid_blocks == CAP_BLKS_PER_SEC(sbi)) { + clear_bit(secno, dirty_i->dirty_secmap); + return; + } +@@ -4577,7 +4577,6 @@ static void init_dirty_segmap(struct f2f + struct free_segmap_info *free_i = FREE_I(sbi); + unsigned int segno = 0, offset = 0, secno; + block_t valid_blocks, usable_blks_in_seg; +- block_t blks_per_sec = BLKS_PER_SEC(sbi); + + while (1) { + /* find dirty segment based on free segmap */ +@@ -4606,7 +4605,7 @@ static void init_dirty_segmap(struct f2f + valid_blocks = get_valid_blocks(sbi, segno, true); + secno = GET_SEC_FROM_SEG(sbi, segno); + +- if (!valid_blocks || valid_blocks == blks_per_sec) ++ if (!valid_blocks || valid_blocks == CAP_BLKS_PER_SEC(sbi)) + continue; + if (IS_CURSEC(sbi, secno)) + continue; +--- a/fs/f2fs/segment.h ++++ b/fs/f2fs/segment.h +@@ -608,10 +608,10 @@ static inline bool has_not_enough_free_s + get_pages(sbi, F2FS_DIRTY_DENTS) + + get_pages(sbi, F2FS_DIRTY_IMETA); + unsigned int total_dent_blocks = get_pages(sbi, F2FS_DIRTY_DENTS); +- unsigned int node_secs = total_node_blocks / BLKS_PER_SEC(sbi); +- unsigned int dent_secs = total_dent_blocks / BLKS_PER_SEC(sbi); +- unsigned int node_blocks = total_node_blocks % BLKS_PER_SEC(sbi); +- unsigned int dent_blocks = total_dent_blocks % BLKS_PER_SEC(sbi); ++ unsigned int node_secs = total_node_blocks / CAP_BLKS_PER_SEC(sbi); ++ unsigned int dent_secs = total_dent_blocks / CAP_BLKS_PER_SEC(sbi); ++ unsigned int node_blocks = total_node_blocks % CAP_BLKS_PER_SEC(sbi); ++ unsigned int dent_blocks = total_dent_blocks % CAP_BLKS_PER_SEC(sbi); + unsigned int free, need_lower, need_upper; + + if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) diff --git a/queue-5.10/f2fs-bound-i_inline_xattr_size-for-non-inline-xattr-inodes.patch b/queue-5.10/f2fs-bound-i_inline_xattr_size-for-non-inline-xattr-inodes.patch new file mode 100644 index 0000000000..aa1cecd393 --- /dev/null +++ b/queue-5.10/f2fs-bound-i_inline_xattr_size-for-non-inline-xattr-inodes.patch @@ -0,0 +1,85 @@ +From stable+bounces-271857-greg=kroah.com@vger.kernel.org Fri Jul 3 20:50:25 2026 +From: Sasha Levin +Date: Fri, 3 Jul 2026 14:50:18 -0400 +Subject: f2fs: bound i_inline_xattr_size for non-inline-xattr inodes +To: stable@vger.kernel.org +Cc: Bryam Vargas , Chao Yu , Jaegeuk Kim , Sasha Levin +Message-ID: <20260703185018.285454-1-sashal@kernel.org> + +From: Bryam Vargas + +[ Upstream commit 378acf3cf19b6af6cba55e8dd1154c4e1504bae8 ] + +When the flexible_inline_xattr feature is enabled, do_read_inode() loads +the on-disk i_inline_xattr_size unconditionally: + + if (f2fs_sb_has_flexible_inline_xattr(sbi)) + fi->i_inline_xattr_size = le16_to_cpu(ri->i_inline_xattr_size); + +but sanity_check_inode() only range-checks it when the inode also has the +FI_INLINE_XATTR flag set. An inode that carries an inline dentry or inline +data but not FI_INLINE_XATTR -- the normal layout for an inline +directory -- therefore keeps a fully attacker-controlled +i_inline_xattr_size from a crafted image. + +get_inline_xattr_addrs() returns that value with no flag gating, so it +feeds the inode geometry: + + MAX_INLINE_DATA() = 4 * (CUR_ADDRS_PER_INODE - i_inline_xattr_size - 1) + NR_INLINE_DENTRY() = MAX_INLINE_DATA() * BITS_PER_BYTE / (...) + addrs_per_page() = CUR_ADDRS_PER_INODE - i_inline_xattr_size + +A large i_inline_xattr_size drives MAX_INLINE_DATA() and NR_INLINE_DENTRY() +negative, so make_dentry_ptr_inline() sets d->max (int) to a negative +value. The inline directory walk then compares an unsigned long bit_pos +against that negative d->max, which is promoted to a huge unsigned bound, +and reads far past the inline area: + + while (bit_pos < d->max) /* fs/f2fs/dir.c */ + ... test_bit_le(bit_pos, d->bitmap) / d->dentry[bit_pos] ... + +Mounting a crafted image and reading such a directory triggers an +out-of-bounds read in f2fs_fill_dentries(); the same underflow also +corrupts ADDRS_PER_INODE for regular files. + +Validate i_inline_xattr_size against MAX_INLINE_XATTR_SIZE whenever the +flexible_inline_xattr feature is enabled -- i.e. whenever the value is +loaded from disk and consumed -- and keep the lower MIN_INLINE_XATTR_SIZE +bound gated on inodes that actually carry an inline xattr, so legitimate +inodes with i_inline_xattr_size == 0 are still accepted. + +Cc: stable@vger.kernel.org +Fixes: 6afc662e68b5 ("f2fs: support flexible inline xattr size") +Signed-off-by: Bryam Vargas +Reviewed-by: Chao Yu +Signed-off-by: Jaegeuk Kim +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/f2fs/inode.c | 13 +++++++------ + 1 file changed, 7 insertions(+), 6 deletions(-) + +--- a/fs/f2fs/inode.c ++++ b/fs/f2fs/inode.c +@@ -254,14 +254,15 @@ static bool sanity_check_inode(struct in + return false; + } + +- if (f2fs_has_extra_attr(inode) && +- f2fs_sb_has_flexible_inline_xattr(sbi) && +- f2fs_has_inline_xattr(inode) && +- (!fi->i_inline_xattr_size || +- fi->i_inline_xattr_size > MAX_INLINE_XATTR_SIZE)) { ++ if (f2fs_sb_has_flexible_inline_xattr(sbi) && ++ (fi->i_inline_xattr_size > MAX_INLINE_XATTR_SIZE || ++ (f2fs_has_inline_xattr(inode) && ++ fi->i_inline_xattr_size < ++ sizeof(struct f2fs_xattr_header) / sizeof(__le32)))) { + set_sbi_flag(sbi, SBI_NEED_FSCK); +- f2fs_warn(sbi, "%s: inode (ino=%lx) has corrupted i_inline_xattr_size: %d, max: %zu", ++ f2fs_warn(sbi, "%s: inode (ino=%lx) has corrupted i_inline_xattr_size: %d, min: %zu, max: %zu", + __func__, inode->i_ino, fi->i_inline_xattr_size, ++ sizeof(struct f2fs_xattr_header) / sizeof(__le32), + MAX_INLINE_XATTR_SIZE); + return false; + } diff --git a/queue-5.10/f2fs-fix-listxattr-handling-of-corrupted-xattr-entries.patch b/queue-5.10/f2fs-fix-listxattr-handling-of-corrupted-xattr-entries.patch new file mode 100644 index 0000000000..58b2503e36 --- /dev/null +++ b/queue-5.10/f2fs-fix-listxattr-handling-of-corrupted-xattr-entries.patch @@ -0,0 +1,47 @@ +From stable+bounces-271947-greg=kroah.com@vger.kernel.org Sat Jul 4 13:54:59 2026 +From: Sasha Levin +Date: Sat, 4 Jul 2026 07:54:49 -0400 +Subject: f2fs: fix listxattr handling of corrupted xattr entries +To: stable@vger.kernel.org +Cc: Keshav Verma , stable@kernel.org, Chao Yu , Jaegeuk Kim , Sasha Levin +Message-ID: <20260704115449.629037-1-sashal@kernel.org> + +From: Keshav Verma + +[ Upstream commit 5ef5bc304f23c3fe255d4936472378dcb74d0e94 ] + +Validate the xattr entry before reading its fields in f2fs_listxattr(). +Return -EFSCORRUPTED when the entry is outside the valid xattr storage +area instead of returning a successful partial result. + +Fixes: 688078e7f36c ("f2fs: fix to avoid memory leakage in f2fs_listxattr") +Cc: stable@kernel.org +Reviewed-by: Chao Yu +Signed-off-by: Keshav Verma +Signed-off-by: Jaegeuk Kim +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/f2fs/xattr.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/fs/f2fs/xattr.c ++++ b/fs/f2fs/xattr.c +@@ -575,8 +575,7 @@ ssize_t f2fs_listxattr(struct dentry *de + last_base_addr = (void *)base_addr + XATTR_SIZE(inode); + + list_for_each_xattr(entry, base_addr) { +- const struct xattr_handler *handler = +- f2fs_xattr_handler(entry->e_name_index); ++ const struct xattr_handler *handler; + const char *prefix; + size_t prefix_len; + size_t size; +@@ -590,6 +589,7 @@ ssize_t f2fs_listxattr(struct dentry *de + goto cleanup; + } + ++ handler = f2fs_xattr_handler(entry->e_name_index); + if (!handler || (handler->list && !handler->list(dentry))) + continue; + diff --git a/queue-5.10/f2fs-fix-potential-deadlock-in-f2fs_balance_fs.patch b/queue-5.10/f2fs-fix-potential-deadlock-in-f2fs_balance_fs.patch new file mode 100644 index 0000000000..72502f679e --- /dev/null +++ b/queue-5.10/f2fs-fix-potential-deadlock-in-f2fs_balance_fs.patch @@ -0,0 +1,252 @@ +From stable+bounces-271944-greg=kroah.com@vger.kernel.org Sat Jul 4 13:49:23 2026 +From: Sasha Levin +Date: Sat, 4 Jul 2026 07:49:15 -0400 +Subject: f2fs: fix potential deadlock in f2fs_balance_fs() +To: stable@vger.kernel.org +Cc: Ruipeng Qi , Chao Yu , Jaegeuk Kim , Sasha Levin +Message-ID: <20260704114915.620065-1-sashal@kernel.org> + +From: Ruipeng Qi + +[ Upstream commit dd3114870771562036fdcf5abe813956f36d224d ] + +When the f2fs filesystem space is nearly exhausted, we encounter deadlock +issues as below: + +INFO: task A:1890 blocked for more than 120 seconds. + Tainted: G O 6.12.41-g3fe07ddf05ab #1 +"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. +task:A state:D stack:0 pid:1890 tgid:1626 ppid:1153 flags:0x00000204 +Call trace: + __switch_to+0xf4/0x158 + __schedule+0x27c/0x908 + schedule+0x3c/0x118 + io_schedule+0x44/0x68 + folio_wait_bit_common+0x174/0x370 + folio_wait_bit+0x20/0x38 + folio_wait_writeback+0x54/0xc8 + truncate_inode_partial_folio+0x70/0x1e0 + truncate_inode_pages_range+0x1b0/0x450 + truncate_pagecache+0x54/0x88 + f2fs_file_write_iter+0x3e8/0xb80 + do_iter_readv_writev+0xf0/0x1e0 + vfs_writev+0x138/0x2c8 + do_writev+0x88/0x130 + __arm64_sys_writev+0x28/0x40 + invoke_syscall+0x50/0x120 + el0_svc_common.constprop.0+0xc8/0xf0 + do_el0_svc+0x24/0x38 + el0_svc+0x30/0xf8 + el0t_64_sync_handler+0x120/0x130 + el0t_64_sync+0x190/0x198 + +INFO: task kworker/u8:11:2680853 blocked for more than 120 seconds. + Tainted: G O 6.12.41-g3fe07ddf05ab #1 +"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. +task:kworker/u8:11 state:D stack:0 pid:2680853 tgid:2680853 ppid:2 flags:0x00000208 +Workqueue: writeback wb_workfn (flush-254:0) +Call trace: + __switch_to+0xf4/0x158 + __schedule+0x27c/0x908 + schedule+0x3c/0x118 + io_schedule+0x44/0x68 + folio_wait_bit_common+0x174/0x370 + __filemap_get_folio+0x214/0x348 + pagecache_get_page+0x20/0x70 + f2fs_get_read_data_page+0x150/0x3e8 + f2fs_get_lock_data_page+0x2c/0x160 + move_data_page+0x50/0x478 + do_garbage_collect+0xd38/0x1528 + f2fs_gc+0x240/0x7e0 + f2fs_balance_fs+0x1a0/0x208 + f2fs_write_single_data_page+0x6e4/0x730 + f2fs_write_cache_pages+0x378/0x9b0 + f2fs_write_data_pages+0x2e4/0x388 + do_writepages+0x8c/0x2c8 + __writeback_single_inode+0x4c/0x498 + writeback_sb_inodes+0x234/0x4a8 + __writeback_inodes_wb+0x58/0x118 + wb_writeback+0x2f8/0x3c0 + wb_workfn+0x2c4/0x508 + process_one_work+0x180/0x408 + worker_thread+0x258/0x368 + kthread+0x118/0x128 + ret_from_fork+0x10/0x200 + +INFO: task kworker/u8:8:2641297 blocked for more than 120 seconds. + Tainted: G O 6.12.41-g3fe07ddf05ab #1 +"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. +task:kworker/u8:8 state:D stack:0 pid:2641297 tgid:2641297 ppid:2 flags:0x00000208 +Workqueue: writeback wb_workfn (flush-254:0) +Call trace: + __switch_to+0xf4/0x158 + __schedule+0x27c/0x908 + rt_mutex_schedule+0x30/0x60 + __rt_mutex_slowlock_locked.constprop.0+0x460/0x8a8 + rwbase_write_lock+0x24c/0x378 + down_write+0x1c/0x30 + f2fs_balance_fs+0x184/0x208 + f2fs_write_inode+0xf4/0x328 + __writeback_single_inode+0x370/0x498 + writeback_sb_inodes+0x234/0x4a8 + __writeback_inodes_wb+0x58/0x118 + wb_writeback+0x2f8/0x3c0 + wb_workfn+0x2c4/0x508 + process_one_work+0x180/0x408 + worker_thread+0x258/0x368 + kthread+0x118/0x128 + ret_from_fork+0x10/0x20 + +INFO: task B:1902 blocked for more than 120 seconds. + Tainted: G O 6.12.41-g3fe07ddf05ab #1 +"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. +task:B state:D stack:0 pid:1902 tgid:1626 ppid:1153 flags:0x0000020c +Call trace: + __switch_to+0xf4/0x158 + __schedule+0x27c/0x908 + rt_mutex_schedule+0x30/0x60 + __rt_mutex_slowlock_locked.constprop.0+0x460/0x8a8 + rwbase_write_lock+0x24c/0x378 + down_write+0x1c/0x30 + f2fs_balance_fs+0x184/0x208 + f2fs_map_blocks+0x94c/0x1110 + f2fs_file_write_iter+0x228/0xb80 + do_iter_readv_writev+0xf0/0x1e0 + vfs_writev+0x138/0x2c8 + do_writev+0x88/0x130 + __arm64_sys_writev+0x28/0x40 + invoke_syscall+0x50/0x120 + el0_svc_common.constprop.0+0xc8/0xf0 + do_el0_svc+0x24/0x38 + el0_svc+0x30/0xf8 + el0t_64_sync_handler+0x120/0x130 + el0t_64_sync+0x190/0x198 + +INFO: task sync:2769849 blocked for more than 120 seconds. + Tainted: G O 6.12.41-g3fe07ddf05ab #1 +"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. +task:sync state:D stack:0 pid:2769849 tgid:2769849 ppid:736 flags:0x0000020c +Call trace: + __switch_to+0xf4/0x158 + __schedule+0x27c/0x908 + schedule+0x3c/0x118 + wb_wait_for_completion+0xb0/0xe8 + sync_inodes_sb+0xc8/0x2b0 + sync_inodes_one_sb+0x24/0x38 + iterate_supers+0xa8/0x138 + ksys_sync+0x54/0xc8 + __arm64_sys_sync+0x18/0x30 + invoke_syscall+0x50/0x120 + el0_svc_common.constprop.0+0xc8/0xf0 + do_el0_svc+0x24/0x38 + el0_svc+0x30/0xf8 + el0t_64_sync_handler+0x120/0x130 + el0t_64_sync+0x190/0x198 + +The root cause is a potential deadlock between the following tasks: + +kworker/u8:11 Thread A +- f2fs_write_single_data_page + - f2fs_do_write_data_page + - folio_start_writeback(X) + - f2fs_outplace_write_data + - bio_add_folio(X) + - folio_unlock(X) + - truncate_inode_pages_range + - __filemap_get_folio(X, FGP_LOCK) + - truncate_inode_partial_folio(X) + - folio_wait_writeback(X) + - f2fs_balance_fs + - f2fs_gc + - do_garbage_collect + - move_data_page + - f2fs_get_lock_data_page + - __filemap_get_folio(X, FGP_LOCK) + +Both threads try to access folio X. Thread A holds the lock but waits +for writeback, while kworker waits for the lock. This causes a deadlock. + +Other threads also enter D state, waiting for locks such as gc_lock and +writepages. + +OPU/IPU DATA folio are all affected by this issue. To avoid such +potential deadlocks, always commit these cached folios before +triggering f2fs_gc() in f2fs_balance_fs(). + +Suggested-by: Chao Yu +Reviewed-by: Chao Yu +Signed-off-by: Ruipeng Qi +Signed-off-by: Jaegeuk Kim +Stable-dep-of: 8b4468ec023d ("f2fs: fix potential deadlock in gc_merge path of f2fs_balance_fs()") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/f2fs/data.c | 29 +++++++++++++++++++++++++++++ + fs/f2fs/f2fs.h | 1 + + fs/f2fs/segment.c | 7 +++++++ + 3 files changed, 37 insertions(+) + +--- a/fs/f2fs/data.c ++++ b/fs/f2fs/data.c +@@ -906,6 +906,35 @@ void f2fs_submit_merged_ipu_write(struct + } + } + ++void f2fs_submit_all_merged_ipu_writes(struct f2fs_sb_info *sbi) ++{ ++ struct bio_entry *be, *tmp; ++ struct f2fs_bio_info *io; ++ enum temp_type temp; ++ ++ for (temp = HOT; temp < NR_TEMP_TYPE; temp++) { ++ LIST_HEAD(list); ++ ++ io = sbi->write_io[DATA] + temp; ++ ++ /* A lockless list_empty() check is safe here: any bios from ++ * other kworkers that we miss will be submitted by those ++ * kworkers accordingly. ++ */ ++ if (list_empty(&io->bio_list)) ++ continue; ++ ++ down_write(&io->bio_list_lock); ++ list_splice_init(&io->bio_list, &list); ++ up_write(&io->bio_list_lock); ++ ++ list_for_each_entry_safe(be, tmp, &list, list) { ++ __submit_bio(sbi, be->bio, DATA); ++ del_bio_entry(be); ++ } ++ } ++} ++ + int f2fs_merge_page_bio(struct f2fs_io_info *fio) + { + struct bio *bio = *fio->bio; +--- a/fs/f2fs/f2fs.h ++++ b/fs/f2fs/f2fs.h +@@ -3475,6 +3475,7 @@ void f2fs_submit_merged_write_cond(struc + nid_t ino, enum page_type type); + void f2fs_submit_merged_ipu_write(struct f2fs_sb_info *sbi, + struct bio **bio, struct page *page); ++void f2fs_submit_all_merged_ipu_writes(struct f2fs_sb_info *sbi); + void f2fs_flush_merged_writes(struct f2fs_sb_info *sbi); + int f2fs_submit_page_bio(struct f2fs_io_info *fio); + int f2fs_merge_page_bio(struct f2fs_io_info *fio); +--- a/fs/f2fs/segment.c ++++ b/fs/f2fs/segment.c +@@ -513,6 +513,13 @@ void f2fs_balance_fs(struct f2fs_sb_info + * dir/node pages without enough free segments. + */ + if (has_not_enough_free_secs(sbi, 0, 0)) { ++ /* ++ * Submit all cached OPU/IPU DATA bios before triggering ++ * foreground GC to avoid potential deadlocks. ++ */ ++ f2fs_submit_merged_write(sbi, DATA); ++ f2fs_submit_all_merged_ipu_writes(sbi); ++ + down_write(&sbi->gc_lock); + f2fs_gc(sbi, false, false, false, NULL_SEGNO); + } diff --git a/queue-5.10/f2fs-fix-to-round-down-start-offset-of-fallocate-for-pin-file.patch b/queue-5.10/f2fs-fix-to-round-down-start-offset-of-fallocate-for-pin-file.patch new file mode 100644 index 0000000000..cb9d602ba1 --- /dev/null +++ b/queue-5.10/f2fs-fix-to-round-down-start-offset-of-fallocate-for-pin-file.patch @@ -0,0 +1,75 @@ +From stable+bounces-271780-greg=kroah.com@vger.kernel.org Fri Jul 3 15:39:31 2026 +From: Sasha Levin +Date: Fri, 3 Jul 2026 09:31:01 -0400 +Subject: f2fs: fix to round down start offset of fallocate for pin file +To: stable@vger.kernel.org +Cc: Sunmin Jeong , Yunji Kang , Yeongjin Gil , Sungjong Seo , Chao Yu , Jaegeuk Kim , Sasha Levin +Message-ID: <20260703133101.4123681-2-sashal@kernel.org> + +From: Sunmin Jeong + +[ Upstream commit 4275b59673eb60b02eec3997816c83f1f4b909c4 ] + +Currently, the length of fallocate for pin file is section-aligned to +keep allocated sections from being selected as victims of GC. However, +for the case that the start offset of fallocate is not aligned in +section, the allocated sections can't be fully utilized. It's because a +new section is allocated by f2fs_allocate_pinning_section() after using +blks_per_sec blocks regardless of the start offset. As a result, several +unexpected dirty segments may be created, including blocks assigned to +the pinned file. + +To address this issue, let's round down the start offset of fallocate +to the length of section. + +The reproducing scenario is as below + +chunk=$(((2<<20)+4096)) # 2MB + 4KB +touch test +f2fs_io pinfile set test +f2fs_io fallocate 0 0 $chunk test +f2fs_io fallocate 0 $chunk $chunk test +f2fs_io fallocate 0 $((chunk*2)) $chunk test +f2fs_io fiemap 0 $((chunk*3)) test + +Fiemap: offset = 0 len = 12288 + logical addr. physical addr. length flags +0 0000000000000000 000000068c600000 0000000000400000 00001088 +1 0000000000400000 000000003d400000 0000000000001000 00001088 +2 0000000000401000 00000003eb200000 0000000000200000 00001088 +3 0000000000601000 00000005e4200000 0000000000001000 00001088 +4 0000000000602000 0000000605400000 0000000000200000 00001089 + +Cc: stable@vger.kernel.org +Fixes: f5a53edcf01e ("f2fs: support aligned pinned file") +Reviewed-by: Yunji Kang +Reviewed-by: Yeongjin Gil +Reviewed-by: Sungjong Seo +Signed-off-by: Sunmin Jeong +Reviewed-by: Chao Yu +Signed-off-by: Jaegeuk Kim +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/f2fs/file.c | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +--- a/fs/f2fs/file.c ++++ b/fs/f2fs/file.c +@@ -1682,8 +1682,15 @@ static int expand_inode_data(struct inod + + if (f2fs_is_pinned_file(inode)) { + block_t sec_blks = CAP_BLKS_PER_SEC(sbi); +- block_t sec_len = roundup(map.m_len, sec_blks); ++ block_t sec_len; + ++ if (map.m_lblk % sec_blks) { ++ map.m_lblk = rounddown(map.m_lblk, sec_blks); ++ map.m_len = pg_end - map.m_lblk; ++ if (off_end) ++ map.m_len++; ++ } ++ sec_len = roundup(map.m_len, sec_blks); + map.m_len = sec_blks; + next_alloc: + if (has_not_enough_free_secs(sbi, 0, diff --git a/queue-5.10/f2fs-validate-orphan-inode-entry-count.patch b/queue-5.10/f2fs-validate-orphan-inode-entry-count.patch new file mode 100644 index 0000000000..d0dae4972b --- /dev/null +++ b/queue-5.10/f2fs-validate-orphan-inode-entry-count.patch @@ -0,0 +1,68 @@ +From stable+bounces-271844-greg=kroah.com@vger.kernel.org Fri Jul 3 20:25:13 2026 +From: Sasha Levin +Date: Fri, 3 Jul 2026 14:14:20 -0400 +Subject: f2fs: validate orphan inode entry count +To: stable@vger.kernel.org +Cc: Wenjie Qi , stable@kernel.org, Wenjie Qi , Chao Yu , Jaegeuk Kim , Sasha Levin +Message-ID: <20260703181420.250032-1-sashal@kernel.org> + +From: Wenjie Qi + +[ Upstream commit 846c499a65816d13f1186e3090e825e8bb8bcb8b ] + +f2fs_recover_orphan_inodes() trusts the orphan block entry_count when +replaying orphan inodes from the checkpoint pack. A corrupted entry_count +larger than F2FS_ORPHANS_PER_BLOCK makes the recovery loop read past the +ino[] array and interpret footer or following data as inode numbers. + +On a crafted image, mounting an unpatched kernel can drive orphan recovery +into f2fs_bug_on() and panic the kernel. Validate entry_count before +consuming entries so corrupted checkpoint data fails the mount with +-EFSCORRUPTED and requests fsck instead. + +Set ERROR_INCONSISTENT_ORPHAN as well, so the corruption reason can be +recorded in the superblock s_errors[] field. This gives fsck a persistent +hint even though mount-time orphan recovery failure may leave no chance to +persist SBI_NEED_FSCK through a checkpoint. + +Cc: stable@kernel.org +Fixes: 127e670abfa7 ("f2fs: add checkpoint operations") +Signed-off-by: Wenjie Qi +Reviewed-by: Chao Yu +Signed-off-by: Jaegeuk Kim +[ Folio API ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/f2fs/checkpoint.c | 13 ++++++++++++- + 1 file changed, 12 insertions(+), 1 deletion(-) + +--- a/fs/f2fs/checkpoint.c ++++ b/fs/f2fs/checkpoint.c +@@ -723,6 +723,7 @@ int f2fs_recover_orphan_inodes(struct f2 + for (i = 0; i < orphan_blocks; i++) { + struct page *page; + struct f2fs_orphan_block *orphan_blk; ++ unsigned int entry_count; + + page = f2fs_get_meta_page(sbi, start_blk + i); + if (IS_ERR(page)) { +@@ -731,7 +732,17 @@ int f2fs_recover_orphan_inodes(struct f2 + } + + orphan_blk = (struct f2fs_orphan_block *)page_address(page); +- for (j = 0; j < le32_to_cpu(orphan_blk->entry_count); j++) { ++ entry_count = le32_to_cpu(orphan_blk->entry_count); ++ if (entry_count > F2FS_ORPHANS_PER_BLOCK) { ++ f2fs_err(sbi, "invalid orphan inode entry count %u", ++ entry_count); ++ set_sbi_flag(sbi, SBI_NEED_FSCK); ++ err = -EFSCORRUPTED; ++ f2fs_put_page(page, 1); ++ goto out; ++ } ++ ++ for (j = 0; j < entry_count; j++) { + nid_t ino = le32_to_cpu(orphan_blk->ino[j]); + err = recover_orphan_inode(sbi, ino); + if (err) { diff --git a/queue-5.10/fbdev-fbcon-fix-out-of-bounds-read-in-err_out-of-fbcon_do_set_font.patch b/queue-5.10/fbdev-fbcon-fix-out-of-bounds-read-in-err_out-of-fbcon_do_set_font.patch new file mode 100644 index 0000000000..550b0450ff --- /dev/null +++ b/queue-5.10/fbdev-fbcon-fix-out-of-bounds-read-in-err_out-of-fbcon_do_set_font.patch @@ -0,0 +1,70 @@ +From stable+bounces-272074-greg=kroah.com@vger.kernel.org Sun Jul 5 16:25:07 2026 +From: Sasha Levin +Date: Sun, 5 Jul 2026 10:24:59 -0400 +Subject: fbdev: fbcon: fix out-of-bounds read in err_out of fbcon_do_set_font() +To: stable@vger.kernel.org +Cc: Mingyu Wang <25181214217@stu.xidian.edu.cn>, Thomas Zimmermann , Helge Deller , Sasha Levin +Message-ID: <20260705142459.1798975-1-sashal@kernel.org> + +From: Mingyu Wang <25181214217@stu.xidian.edu.cn> + +[ Upstream commit 8fdc8c2057eea08d40ce2c8eed41ff9e451c65c2 ] + +When fbcon_do_set_font() fails (e.g., due to a memory allocation failure +inside vc_resize() under heavy memory pressure), it jumps to the `err_out` +label to roll back the console state. However, the current rollback logic +forgets to restore the `hi_font` state, leading to a severe state machine +corruption. + +Earlier in the function, `set_vc_hi_font()` might be called to change +`vc->vc_hi_font_mask` and mutate the screen buffer. If `vc_resize()` +subsequently fails, the `err_out` path restores `vc_font.charcount` +but entirely skips rolling back the `vc_hi_font_mask` and the screen +buffer. + +This mismatch leaves the terminal in a desynchronized state. Because +`vc_hi_font_mask` remains set, the VT subsystem will still accept +character indices greater than 255 from userspace and write them to the +screen buffer. Subsequent rendering calls (e.g., `fbcon_putcs()`) will +then use these inflated indices to access the reverted, 256-character +font array, leading to a deterministic out-of-bounds read and potential +kernel memory disclosure. + +Fix this by adding the missing rollback logic for the `hi_font` mask +and screen buffer in the error path. + +Fixes: a5a923038d70 ("fbdev: fbcon: Properly revert changes when vc_resize() failed") +Cc: stable@vger.kernel.org +Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn> +Reviewed-by: Thomas Zimmermann +Signed-off-by: Helge Deller +[ Adjust context ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/video/fbdev/core/fbcon.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +--- a/drivers/video/fbdev/core/fbcon.c ++++ b/drivers/video/fbdev/core/fbcon.c +@@ -2426,6 +2426,7 @@ static int fbcon_do_set_font(struct vc_d + struct fbcon_display *p = &fb_display[vc->vc_num]; + int resize, ret, old_userfont, old_width, old_height, old_charcount; + u8 *old_data = vc->vc_font.data; ++ unsigned short old_hi_font_mask = vc->vc_hi_font_mask; + + resize = (w != vc->vc_font.width) || (h != vc->vc_font.height); + vc->vc_font.data = (void *)(p->fontdata = data); +@@ -2479,6 +2480,12 @@ err_out: + vc->vc_font.height = old_height; + vc->vc_font.charcount = old_charcount; + ++ /* Restore the hi_font state and screen buffer */ ++ if (old_hi_font_mask && !vc->vc_hi_font_mask) ++ set_vc_hi_font(vc, true); ++ else if (!old_hi_font_mask && vc->vc_hi_font_mask) ++ set_vc_hi_font(vc, false); ++ + return ret; + } + diff --git a/queue-5.10/hdlc_ppp-sync-per-proto-timers-before-freeing-hdlc-state.patch b/queue-5.10/hdlc_ppp-sync-per-proto-timers-before-freeing-hdlc-state.patch new file mode 100644 index 0000000000..24ce56477a --- /dev/null +++ b/queue-5.10/hdlc_ppp-sync-per-proto-timers-before-freeing-hdlc-state.patch @@ -0,0 +1,114 @@ +From stable+bounces-272117-greg=kroah.com@vger.kernel.org Mon Jul 6 04:28:31 2026 +From: Sasha Levin +Date: Sun, 5 Jul 2026 22:28:22 -0400 +Subject: hdlc_ppp: sync per-proto timers before freeing hdlc state +To: stable@vger.kernel.org +Cc: Fan Wu , Jakub Kicinski , Sasha Levin +Message-ID: <20260706022823.2081953-1-sashal@kernel.org> + +From: Fan Wu + +[ Upstream commit c78a4e41ab5ead6193ad8a2dd92e8906bae659fa ] + +Each PPP control protocol (LCP/IPCP/IPV6CP) embedded in struct ppp +registers a timer via timer_setup(). That struct ppp is the +hdlc->state allocation, which detach_hdlc_protocol() frees with kfree() +in both teardown paths: unregister_hdlc_device() and the re-attach inside +attach_hdlc_protocol(). + +The ppp proto never registered a .detach callback, so +detach_hdlc_protocol() performs no timer synchronization before the +kfree(). The only cancel, timer_delete(&proto->timer) in ppp_cp_event(), +is partial (it does not wait for a running callback) and only runs on the +->CLOSED transition; ppp_stop()/ppp_close() do not sync either. A +ppp_timer callback already executing (blocked on ppp->lock) survives the +kfree and then dereferences proto->state / ppp->lock in freed memory, +leading to a use-after-free. + +Fix this by adding a .detach helper that calls timer_shutdown_sync() on +every per-proto timer. detach_hdlc_protocol() invokes proto->detach(dev) +before kfree(hdlc->state), so timer_shutdown_sync() +now runs on both free paths. +timer_shutdown_sync() is used instead of timer_delete_sync() because the +keepalive path re-arms the timer through add_timer()/mod_timer() and +shutdown blocks any re-activation during teardown. + +Initialize the per-protocol timers in ppp_ioctl() when the protocol is +attached, and remove the now-redundant timer_setup() from ppp_start(), so +that the timers are initialized exactly once at attach time and +ppp_timer_release() never operates on uninitialized timer_list +structures. attach_hdlc_protocol() uses kmalloc() (not kzalloc), so +struct ppp's protos[i].timer is uninitialized garbage until the first +timer_setup(); without this init-at-attach, attaching the PPP protocol +without ever bringing the device up would leave timer_shutdown_sync() +operating on uninitialized memory in .detach. Moving the init out of +ppp_start() (which only runs on NETDEV_UP) into the attach path makes the +initialization unconditional and avoids initializing the same timer_list +twice. + +This bug was found by static analysis. + +Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") +Cc: stable@vger.kernel.org +Signed-off-by: Fan Wu +Link: https://patch.msgid.link/20260617020518.116319-1-fanwu01@zju.edu.cn +Signed-off-by: Jakub Kicinski +[ replaced timer_shutdown_sync() with del_timer_sync() since timer_shutdown_sync() is not available in 5.10 ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/wan/hdlc_ppp.c | 15 +++++++++++++-- + 1 file changed, 13 insertions(+), 2 deletions(-) + +--- a/drivers/net/wan/hdlc_ppp.c ++++ b/drivers/net/wan/hdlc_ppp.c +@@ -624,7 +624,6 @@ static void ppp_start(struct net_device + for (i = 0; i < IDX_COUNT; i++) { + struct proto *proto = &ppp->protos[i]; + proto->dev = dev; +- timer_setup(&proto->timer, ppp_timer, 0); + proto->state = CLOSED; + } + ppp->protos[IDX_LCP].pid = PID_LCP; +@@ -644,6 +643,15 @@ static void ppp_close(struct net_device + ppp_tx_flush(); + } + ++static void ppp_timer_release(struct net_device *dev) ++{ ++ struct ppp *ppp = get_ppp(dev); ++ int i; ++ ++ for (i = 0; i < IDX_COUNT; i++) ++ del_timer_sync(&ppp->protos[i].timer); ++} ++ + static struct hdlc_proto proto = { + .start = ppp_start, + .stop = ppp_stop, +@@ -652,6 +660,7 @@ static struct hdlc_proto proto = { + .ioctl = ppp_ioctl, + .netif_rx = ppp_rx, + .module = THIS_MODULE, ++ .detach = ppp_timer_release, + }; + + static const struct header_ops ppp_header_ops = { +@@ -662,7 +671,7 @@ static int ppp_ioctl(struct net_device * + { + hdlc_device *hdlc = dev_to_hdlc(dev); + struct ppp *ppp; +- int result; ++ int i, result; + + switch (ifr->ifr_settings.type) { + case IF_GET_PROTO: +@@ -689,6 +698,8 @@ static int ppp_ioctl(struct net_device * + return result; + + ppp = get_ppp(dev); ++ for (i = 0; i < IDX_COUNT; i++) ++ timer_setup(&ppp->protos[i].timer, ppp_timer, 0); + spin_lock_init(&ppp->lock); + ppp->req_timeout = 2; + ppp->cr_retries = 10; diff --git a/queue-5.10/i2c-core-fix-adapter-debugfs-creation.patch b/queue-5.10/i2c-core-fix-adapter-debugfs-creation.patch new file mode 100644 index 0000000000..7317793b94 --- /dev/null +++ b/queue-5.10/i2c-core-fix-adapter-debugfs-creation.patch @@ -0,0 +1,80 @@ +From stable+bounces-272090-greg=kroah.com@vger.kernel.org Sun Jul 5 21:06:23 2026 +From: Sasha Levin +Date: Sun, 5 Jul 2026 15:06:11 -0400 +Subject: i2c: core: fix adapter debugfs creation +To: stable@vger.kernel.org +Cc: Johan Hovold , Wolfram Sang , Sasha Levin +Message-ID: <20260705190612.1987801-4-sashal@kernel.org> + +From: Johan Hovold + +[ Upstream commit 07d5fb537928aad4369aaff0cbae73ba38a719af ] + +Clients can be registered from bus notifier callbacks so the debugfs +directory needs to be created before registering the adapter as clients +use that directory as their debugfs parent. + +Move debugfs creation before adapter registration to avoid having +clients create their debugfs directories in the debugfs root (which is +also more likely to fail due to name collisions). + +Note that failure to allocate the adapter name must now be handled +explicitly as debugfs_create_dir() cannot handle a NULL name (unlike +device_add() which returns an error). + +Fixes: 73febd775bdb ("i2c: create debugfs entry per adapter") +Cc: stable@vger.kernel.org # 6.8 +Cc: Wolfram Sang +Signed-off-by: Johan Hovold +Signed-off-by: Wolfram Sang +Stable-dep-of: ba14d7cf2fe7 ("i2c: core: fix adapter registration race") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/i2c/i2c-core-base.c | 19 ++++++++++++------- + 1 file changed, 12 insertions(+), 7 deletions(-) + +--- a/drivers/i2c/i2c-core-base.c ++++ b/drivers/i2c/i2c-core-base.c +@@ -1456,10 +1456,19 @@ static int i2c_register_adapter(struct i + goto out_list; + } + +- dev_set_name(&adap->dev, "i2c-%d", adap->nr); ++ res = dev_set_name(&adap->dev, "i2c-%d", adap->nr); ++ if (res) ++ goto err_remove_irq_domain; ++ + adap->dev.bus = &i2c_bus_type; + adap->dev.type = &i2c_adapter_type; +- res = device_register(&adap->dev); ++ device_initialize(&adap->dev); ++ ++ pm_runtime_no_callbacks(&adap->dev); ++ pm_suspend_ignore_children(&adap->dev, true); ++ pm_runtime_enable(&adap->dev); ++ ++ res = device_add(&adap->dev); + if (res) { + pr_err("adapter '%s': can't register device (%d)\n", adap->name, res); + goto err_put_adap; +@@ -1469,10 +1478,6 @@ static int i2c_register_adapter(struct i + if (res) + goto out_reg; + +- pm_runtime_no_callbacks(&adap->dev); +- pm_suspend_ignore_children(&adap->dev, true); +- pm_runtime_enable(&adap->dev); +- + res = i2c_init_recovery(adap); + if (res == -EPROBE_DEFER) + goto out_reg; +@@ -1509,7 +1514,7 @@ err_put_adap: + init_completion(&adap->dev_released); + put_device(&adap->dev); + wait_for_completion(&adap->dev_released); +- ++err_remove_irq_domain: + i2c_host_notify_irq_teardown(adap); + out_list: + mutex_lock(&core_lock); diff --git a/queue-5.10/i2c-core-fix-adapter-registration-race.patch b/queue-5.10/i2c-core-fix-adapter-registration-race.patch new file mode 100644 index 0000000000..0e4f1462cd --- /dev/null +++ b/queue-5.10/i2c-core-fix-adapter-registration-race.patch @@ -0,0 +1,65 @@ +From stable+bounces-272091-greg=kroah.com@vger.kernel.org Sun Jul 5 21:06:23 2026 +From: Sasha Levin +Date: Sun, 5 Jul 2026 15:06:12 -0400 +Subject: i2c: core: fix adapter registration race +To: stable@vger.kernel.org +Cc: Johan Hovold , Wolfram Sang , Sasha Levin +Message-ID: <20260705190612.1987801-5-sashal@kernel.org> + +From: Johan Hovold + +[ Upstream commit ba14d7cf2fe7284610a29854bdff22b2537d3ce6 ] + +Adapters can be looked up based on their id using i2c_get_adapter() +which takes a reference to the embedded struct device. + +Make sure that the adapter (including its struct device) has been +initialised before adding it to the IDR to avoid accessing uninitialised +data which could, for example, lead to NULL-pointer dereferences or +use-after-free. + +Note that the i2c-dev chardev, which is registered from a bus notifier, +currently uses i2c_get_adapter() so the adapter needs to be added to the +IDR before registration. + +Fixes: 6e13e6418418 ("i2c: Add i2c_add_numbered_adapter()") +Cc: stable@vger.kernel.org # 2.6.22 +Signed-off-by: Johan Hovold +Signed-off-by: Wolfram Sang +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/i2c/i2c-core-base.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +--- a/drivers/i2c/i2c-core-base.c ++++ b/drivers/i2c/i2c-core-base.c +@@ -1468,6 +1468,10 @@ static int i2c_register_adapter(struct i + pm_suspend_ignore_children(&adap->dev, true); + pm_runtime_enable(&adap->dev); + ++ mutex_lock(&core_lock); ++ idr_replace(&i2c_adapter_idr, adap, adap->nr); ++ mutex_unlock(&core_lock); ++ + res = device_add(&adap->dev); + if (res) { + pr_err("adapter '%s': can't register device (%d)\n", adap->name, res); +@@ -1535,7 +1539,7 @@ static int __i2c_add_numbered_adapter(st + int id; + + mutex_lock(&core_lock); +- id = idr_alloc(&i2c_adapter_idr, adap, adap->nr, adap->nr + 1, GFP_KERNEL); ++ id = idr_alloc(&i2c_adapter_idr, NULL, adap->nr, adap->nr + 1, GFP_KERNEL); + mutex_unlock(&core_lock); + if (WARN(id < 0, "couldn't get idr")) + return id == -ENOSPC ? -EBUSY : id; +@@ -1571,7 +1575,7 @@ int i2c_add_adapter(struct i2c_adapter * + } + + mutex_lock(&core_lock); +- id = idr_alloc(&i2c_adapter_idr, adapter, ++ id = idr_alloc(&i2c_adapter_idr, NULL, + __i2c_first_dynamic_bus_num, 0, GFP_KERNEL); + mutex_unlock(&core_lock); + if (WARN(id < 0, "couldn't get idr")) diff --git a/queue-5.10/i2c-core-fix-hang-on-adapter-registration-failure.patch b/queue-5.10/i2c-core-fix-hang-on-adapter-registration-failure.patch new file mode 100644 index 0000000000..de5cd36794 --- /dev/null +++ b/queue-5.10/i2c-core-fix-hang-on-adapter-registration-failure.patch @@ -0,0 +1,114 @@ +From stable+bounces-272088-greg=kroah.com@vger.kernel.org Sun Jul 5 21:06:23 2026 +From: Sasha Levin +Date: Sun, 5 Jul 2026 15:06:09 -0400 +Subject: i2c: core: fix hang on adapter registration failure +To: stable@vger.kernel.org +Cc: Johan Hovold , Phil Reid , Wolfram Sang , Sasha Levin +Message-ID: <20260705190612.1987801-2-sashal@kernel.org> + +From: Johan Hovold + +[ Upstream commit 3c7e164344e5bcf6f274bbf59a3274f5caad9bc1 ] + +Clients may be registered from bus notifier callbacks when the adapter +is registered. On a subsequent error during registration, the adapter +references taken by such clients prevent the wait for the references to +be released from ever completing. + +Fix this by refactoring client deregistration and deregistering also on +late adapter registration failures. + +Fixes: f8756c67b3de ("i2c: core: call of_i2c_setup_smbus_alert in i2c_register_adapter") +Cc: stable@vger.kernel.org # 4.15 +Cc: Phil Reid +Signed-off-by: Johan Hovold +Signed-off-by: Wolfram Sang +Stable-dep-of: ba14d7cf2fe7 ("i2c: core: fix adapter registration race") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/i2c/i2c-core-base.c | 49 ++++++++++++++++++++++++++------------------ + 1 file changed, 29 insertions(+), 20 deletions(-) + +--- a/drivers/i2c/i2c-core-base.c ++++ b/drivers/i2c/i2c-core-base.c +@@ -61,6 +61,7 @@ + static DEFINE_MUTEX(core_lock); + static DEFINE_IDR(i2c_adapter_idr); + ++static void i2c_deregister_clients(struct i2c_adapter *adap); + static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver); + + static DEFINE_STATIC_KEY_FALSE(i2c_trace_msg_key); +@@ -1502,6 +1503,7 @@ static int i2c_register_adapter(struct i + return 0; + + out_reg: ++ i2c_deregister_clients(adap); + init_completion(&adap->dev_released); + device_unregister(&adap->dev); + wait_for_completion(&adap->dev_released); +@@ -1644,29 +1646,10 @@ static int __process_removed_adapter(str + return 0; + } + +-/** +- * i2c_del_adapter - unregister I2C adapter +- * @adap: the adapter being unregistered +- * Context: can sleep +- * +- * This unregisters an I2C adapter which was previously registered +- * by @i2c_add_adapter or @i2c_add_numbered_adapter. +- */ +-void i2c_del_adapter(struct i2c_adapter *adap) ++static void i2c_deregister_clients(struct i2c_adapter *adap) + { +- struct i2c_adapter *found; + struct i2c_client *client, *next; + +- /* First make sure that this adapter was ever added */ +- mutex_lock(&core_lock); +- found = idr_find(&i2c_adapter_idr, adap->nr); +- mutex_unlock(&core_lock); +- if (found != adap) { +- pr_debug("attempting to delete unregistered adapter [%s]\n", adap->name); +- return; +- } +- +- i2c_acpi_remove_space_handler(adap); + /* Tell drivers about this removal */ + mutex_lock(&core_lock); + bus_for_each_drv(&i2c_bus_type, NULL, adap, +@@ -1692,6 +1675,32 @@ void i2c_del_adapter(struct i2c_adapter + * them up properly, so we give them a chance to do that first. */ + device_for_each_child(&adap->dev, NULL, __unregister_client); + device_for_each_child(&adap->dev, NULL, __unregister_dummy); ++} ++ ++/** ++ * i2c_del_adapter - unregister I2C adapter ++ * @adap: the adapter being unregistered ++ * Context: can sleep ++ * ++ * This unregisters an I2C adapter which was previously registered ++ * by @i2c_add_adapter or @i2c_add_numbered_adapter. ++ */ ++void i2c_del_adapter(struct i2c_adapter *adap) ++{ ++ struct i2c_adapter *found; ++ ++ /* First make sure that this adapter was ever added */ ++ mutex_lock(&core_lock); ++ found = idr_find(&i2c_adapter_idr, adap->nr); ++ mutex_unlock(&core_lock); ++ if (found != adap) { ++ pr_debug("attempting to delete unregistered adapter [%s]\n", adap->name); ++ return; ++ } ++ ++ i2c_acpi_remove_space_handler(adap); ++ ++ i2c_deregister_clients(adap); + + #ifdef CONFIG_I2C_COMPAT + class_compat_remove_link(i2c_adapter_compat_class, &adap->dev, diff --git a/queue-5.10/i2c-core-fix-irq-domain-leak-on-adapter-registration-failure.patch b/queue-5.10/i2c-core-fix-irq-domain-leak-on-adapter-registration-failure.patch new file mode 100644 index 0000000000..dec1cd1e98 --- /dev/null +++ b/queue-5.10/i2c-core-fix-irq-domain-leak-on-adapter-registration-failure.patch @@ -0,0 +1,50 @@ +From stable+bounces-272087-greg=kroah.com@vger.kernel.org Sun Jul 5 21:06:19 2026 +From: Sasha Levin +Date: Sun, 5 Jul 2026 15:06:08 -0400 +Subject: i2c: core: fix irq domain leak on adapter registration failure +To: stable@vger.kernel.org +Cc: Johan Hovold , Benjamin Tissoires , Wolfram Sang , Sasha Levin +Message-ID: <20260705190612.1987801-1-sashal@kernel.org> + +From: Johan Hovold + +[ Upstream commit 8ce19524e4cc2462685f596a6402fbd8fb984ab2 ] + +Make sure to tear down the host notify irq domain on adapter +registration failure to avoid leaking it. + +This issue was flagged by Sashiko when reviewing another adapter +registration fix. + +Fixes: 4d5538f5882a ("i2c: use an IRQ to report Host Notify events, not alert") +Cc: stable@vger.kernel.org # 4.10 +Cc: Benjamin Tissoires +Signed-off-by: Johan Hovold +Signed-off-by: Wolfram Sang +Stable-dep-of: ba14d7cf2fe7 ("i2c: core: fix adapter registration race") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/i2c/i2c-core-base.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +--- a/drivers/i2c/i2c-core-base.c ++++ b/drivers/i2c/i2c-core-base.c +@@ -1461,7 +1461,7 @@ static int i2c_register_adapter(struct i + res = device_register(&adap->dev); + if (res) { + pr_err("adapter '%s': can't register device (%d)\n", adap->name, res); +- goto out_list; ++ goto err_remove_irq_domain; + } + + res = of_i2c_setup_smbus_alert(adap); +@@ -1505,6 +1505,8 @@ out_reg: + init_completion(&adap->dev_released); + device_unregister(&adap->dev); + wait_for_completion(&adap->dev_released); ++err_remove_irq_domain: ++ i2c_host_notify_irq_teardown(adap); + out_list: + mutex_lock(&core_lock); + idr_remove(&i2c_adapter_idr, adap->nr); diff --git a/queue-5.10/i2c-core-fix-null-deref-on-adapter-registration-failure.patch b/queue-5.10/i2c-core-fix-null-deref-on-adapter-registration-failure.patch new file mode 100644 index 0000000000..9e9e2ddac1 --- /dev/null +++ b/queue-5.10/i2c-core-fix-null-deref-on-adapter-registration-failure.patch @@ -0,0 +1,57 @@ +From stable+bounces-272089-greg=kroah.com@vger.kernel.org Sun Jul 5 21:06:28 2026 +From: Sasha Levin +Date: Sun, 5 Jul 2026 15:06:10 -0400 +Subject: i2c: core: fix NULL-deref on adapter registration failure +To: stable@vger.kernel.org +Cc: Johan Hovold , Joe Hattori , Wolfram Sang , Sasha Levin +Message-ID: <20260705190612.1987801-3-sashal@kernel.org> + +From: Johan Hovold + +[ Upstream commit 2295d2bb101faa663fbc45fadbb3fec45f107441 ] + +If adapter registration ever fails the release callback would trigger a +NULL-pointer dereference as the completion struct has not been +initialised. + +Note that before the offending commit this would instead have resulted +in a minor memory leak of the adapter name. + +Fixes: 3f8c4f5e9a57 ("i2c: core: fix reference leak in i2c_register_adapter()") +Cc: stable@vger.kernel.org +Cc: Joe Hattori +Signed-off-by: Johan Hovold +Signed-off-by: Wolfram Sang +Stable-dep-of: ba14d7cf2fe7 ("i2c: core: fix adapter registration race") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/i2c/i2c-core-base.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +--- a/drivers/i2c/i2c-core-base.c ++++ b/drivers/i2c/i2c-core-base.c +@@ -1462,7 +1462,7 @@ static int i2c_register_adapter(struct i + res = device_register(&adap->dev); + if (res) { + pr_err("adapter '%s': can't register device (%d)\n", adap->name, res); +- goto err_remove_irq_domain; ++ goto err_put_adap; + } + + res = of_i2c_setup_smbus_alert(adap); +@@ -1504,10 +1504,12 @@ static int i2c_register_adapter(struct i + + out_reg: + i2c_deregister_clients(adap); ++ device_del(&adap->dev); ++err_put_adap: + init_completion(&adap->dev_released); +- device_unregister(&adap->dev); ++ put_device(&adap->dev); + wait_for_completion(&adap->dev_released); +-err_remove_irq_domain: ++ + i2c_host_notify_irq_teardown(adap); + out_list: + mutex_lock(&core_lock); diff --git a/queue-5.10/net-ip_gre-require-cap_net_admin-in-the-device-netns-for-changelink.patch b/queue-5.10/net-ip_gre-require-cap_net_admin-in-the-device-netns-for-changelink.patch new file mode 100644 index 0000000000..42449fef94 --- /dev/null +++ b/queue-5.10/net-ip_gre-require-cap_net_admin-in-the-device-netns-for-changelink.patch @@ -0,0 +1,99 @@ +From stable+bounces-270565-greg=kroah.com@vger.kernel.org Thu Jul 2 17:42:18 2026 +From: Sasha Levin +Date: Thu, 2 Jul 2026 11:36:22 -0400 +Subject: net: ip_gre: require CAP_NET_ADMIN in the device netns for changelink +To: stable@vger.kernel.org +Cc: Maoyi Xie , Xiao Liang , Kuniyuki Iwashima , Jakub Kicinski , Sasha Levin +Message-ID: <20260702153622.3533221-1-sashal@kernel.org> + +From: Maoyi Xie + +[ Upstream commit 8165f7ff57d9667d2bb477ef6af83ede7fed4ad7 ] + +A tunnel changelink() operates on at most two netns, dev_net(dev) and +the tunnel link netns t->net. They differ once the device is created in +or moved to a netns other than the one the request runs in. The rtnl +changelink path checks CAP_NET_ADMIN only against dev_net(dev), so a +caller privileged there but not in t->net can rewrite a tunnel that +lives in t->net. + +Add rtnl_dev_link_net_capable() next to rtnl_get_net_ns_capable() in +net/core/rtnetlink.c. It requires CAP_NET_ADMIN in the link netns and is +skipped when the link netns is dev_net(dev), where the rtnl path already +checked it. The other patches in this series use the same helper. + +Gate ipgre_changelink() and erspan_changelink() with it, at the top of +the op before any attribute is parsed, because the parsers update live +tunnel fields first. ipgre_netlink_parms() sets t->collect_md before +ip_tunnel_changelink() runs. + +Commit 8b484efd5cb4 ("ip6: vti: Use ip6_tnl.net in +vti6_siocdevprivate().") added the same check on the ioctl path. This +adds it on RTM_NEWLINK. + +Reported-by: Xiao Liang +Closes: https://lore.kernel.org/netdev/CABAhCOSzP1vaThGV35_VnsRCb=87_CPjPVsTHbq905k8A+BuUg@mail.gmail.com/ +Fixes: b57708add314 ("gre: add x-netns support") +Cc: stable@vger.kernel.org +Signed-off-by: Maoyi Xie +Reviewed-by: Kuniyuki Iwashima +Link: https://patch.msgid.link/20260612085941.3158249-2-maoyixie.tju@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + include/net/rtnetlink.h | 2 ++ + net/core/rtnetlink.c | 8 ++++++++ + net/ipv4/ip_gre.c | 6 ++++++ + 3 files changed, 16 insertions(+) + +--- a/include/net/rtnetlink.h ++++ b/include/net/rtnetlink.h +@@ -183,6 +183,8 @@ int rtnl_configure_link(struct net_devic + int rtnl_nla_parse_ifinfomsg(struct nlattr **tb, const struct nlattr *nla_peer, + struct netlink_ext_ack *exterr); + struct net *rtnl_get_net_ns_capable(struct sock *sk, int netnsid); ++bool rtnl_dev_link_net_capable(const struct net_device *dev, ++ const struct net *link_net); + + #define MODULE_ALIAS_RTNL_LINK(kind) MODULE_ALIAS("rtnl-link-" kind) + +--- a/net/core/rtnetlink.c ++++ b/net/core/rtnetlink.c +@@ -2024,6 +2024,14 @@ struct net *rtnl_get_net_ns_capable(stru + } + EXPORT_SYMBOL_GPL(rtnl_get_net_ns_capable); + ++bool rtnl_dev_link_net_capable(const struct net_device *dev, ++ const struct net *link_net) ++{ ++ return net_eq(link_net, dev_net(dev)) || ++ ns_capable(link_net->user_ns, CAP_NET_ADMIN); ++} ++EXPORT_SYMBOL_GPL(rtnl_dev_link_net_capable); ++ + static int rtnl_valid_dump_ifinfo_req(const struct nlmsghdr *nlh, + bool strict_check, struct nlattr **tb, + struct netlink_ext_ack *extack) +--- a/net/ipv4/ip_gre.c ++++ b/net/ipv4/ip_gre.c +@@ -1407,6 +1407,9 @@ static int ipgre_changelink(struct net_d + struct ip_tunnel_parm p; + int err; + ++ if (!rtnl_dev_link_net_capable(dev, t->net)) ++ return -EPERM; ++ + err = ipgre_newlink_encap_setup(dev, data); + if (err) + return err; +@@ -1436,6 +1439,9 @@ static int erspan_changelink(struct net_ + struct ip_tunnel_parm p; + int err; + ++ if (!rtnl_dev_link_net_capable(dev, t->net)) ++ return -EPERM; ++ + err = ipgre_newlink_encap_setup(dev, data); + if (err) + return err; diff --git a/queue-5.10/net-skmsg-preserve-sg.copy-across-sg-transforms.patch b/queue-5.10/net-skmsg-preserve-sg.copy-across-sg-transforms.patch new file mode 100644 index 0000000000..9a7f7e9f3d --- /dev/null +++ b/queue-5.10/net-skmsg-preserve-sg.copy-across-sg-transforms.patch @@ -0,0 +1,292 @@ +From stable+bounces-270555-greg=kroah.com@vger.kernel.org Thu Jul 2 17:30:39 2026 +From: Sasha Levin +Date: Thu, 2 Jul 2026 11:26:23 -0400 +Subject: net: skmsg: preserve sg.copy across SG transforms +To: stable@vger.kernel.org +Cc: Yiming Qian , Keenan Dong , Jakub Kicinski , Sasha Levin +Message-ID: <20260702152623.3530248-2-sashal@kernel.org> + +From: Yiming Qian + +[ Upstream commit 406e8a651a7b854c41fecd5117bb282b3a6c2c6b ] + +The sk_msg sg.copy bitmap is part of the scatterlist entry ownership +state. A set bit tells sk_msg_compute_data_pointers() not to expose the +entry through writable BPF ctx->data. This protects entries backed by +pages that are not private to the sk_msg, such as splice-backed file +page-cache pages. + +Several sk_msg transform paths move, copy, split, or compact +msg->sg.data[] entries without moving the matching sg.copy bit. This can +make an externally backed entry arrive at a new slot with a clear copy +bit. A later SK_MSG verdict can then expose sg_virt(sge) as writable +ctx->data and BPF stores can modify the original page cache. + +Keep sg.copy synchronized with sg.data[] whenever entries are +transferred, shifted, split, or copied into a new sk_msg. Clear the bit +when an entry is replaced by a newly allocated private page or freed. +This covers the BPF pull/push/pop helpers, sk_msg_shift_left/right(), +sk_msg_xfer(), and tls_split_open_record(), including the partial tail +entry created during TLS open-record splitting. + +Fixes: d3b18ad31f93 ("tls: add bpf support to sk_msg handling") +Cc: stable@vger.kernel.org +Reported-by: Yiming Qian +Reported-by: Keenan Dong +Signed-off-by: Yiming Qian +Link: https://patch.msgid.link/20260610062137.49075-1-yimingqian591@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + include/linux/skmsg.h | 15 +++++++++++---- + net/core/filter.c | 27 +++++++++++++++++++++++++++ + net/core/skmsg.c | 2 ++ + net/tls/tls_sw.c | 4 ++++ + 4 files changed, 44 insertions(+), 4 deletions(-) + +--- a/include/linux/skmsg.h ++++ b/include/linux/skmsg.h +@@ -4,6 +4,7 @@ + #ifndef _LINUX_SKMSG_H + #define _LINUX_SKMSG_H + ++#include + #include + #include + #include +@@ -187,11 +188,14 @@ static inline void sk_msg_xfer(struct sk + int which, u32 size) + { + dst->sg.data[which] = src->sg.data[which]; ++ __assign_bit(which, dst->sg.copy, test_bit(which, src->sg.copy)); + dst->sg.data[which].length = size; + dst->sg.size += size; + src->sg.size -= size; + src->sg.data[which].length -= size; + src->sg.data[which].offset += size; ++ if (!src->sg.data[which].length) ++ __clear_bit(which, src->sg.copy); + } + + static inline void sk_msg_xfer_full(struct sk_msg *dst, struct sk_msg *src) +@@ -261,16 +265,19 @@ static inline void sk_msg_page_add(struc + static inline void sk_msg_sg_copy(struct sk_msg *msg, u32 i, bool copy_state) + { + do { +- if (copy_state) +- __set_bit(i, msg->sg.copy); +- else +- __clear_bit(i, msg->sg.copy); ++ __assign_bit(i, msg->sg.copy, copy_state); + sk_msg_iter_var_next(i); + if (i == msg->sg.end) + break; + } while (1); + } + ++static inline void sk_msg_sg_copy_assign(struct sk_msg *dst, u32 dst_i, ++ const struct sk_msg *src, u32 src_i) ++{ ++ __assign_bit(dst_i, dst->sg.copy, test_bit(src_i, src->sg.copy)); ++} ++ + static inline void sk_msg_sg_copy_set(struct sk_msg *msg, u32 start) + { + sk_msg_sg_copy(msg, start, true); +--- a/net/core/filter.c ++++ b/net/core/filter.c +@@ -2718,11 +2718,13 @@ BPF_CALL_4(bpf_msg_pull_data, struct sk_ + poffset += len; + sge->length = 0; + put_page(sg_page(sge)); ++ __clear_bit(i, msg->sg.copy); + + sk_msg_iter_var_next(i); + } while (i != last_sge); + + sg_set_page(&msg->sg.data[first_sge], page, copy, 0); ++ __clear_bit(first_sge, msg->sg.copy); + + /* To repair sg ring we need to shift entries. If we only + * had a single entry though we can just replace it and +@@ -2748,9 +2750,11 @@ BPF_CALL_4(bpf_msg_pull_data, struct sk_ + break; + + msg->sg.data[i] = msg->sg.data[move_from]; ++ sk_msg_sg_copy_assign(msg, i, msg, move_from); + msg->sg.data[move_from].length = 0; + msg->sg.data[move_from].page_link = 0; + msg->sg.data[move_from].offset = 0; ++ __clear_bit(move_from, msg->sg.copy); + sk_msg_iter_var_next(i); + } while (1); + +@@ -2779,6 +2783,7 @@ BPF_CALL_4(bpf_msg_push_data, struct sk_ + { + struct scatterlist sge, nsge, nnsge, rsge = {0}, *psge; + u32 new, i = 0, l = 0, space, copy = 0, offset = 0; ++ bool sge_copy, nsge_copy, nnsge_copy, rsge_copy = false; + u8 *raw, *to, *from; + struct page *page; + +@@ -2851,6 +2856,7 @@ BPF_CALL_4(bpf_msg_push_data, struct sk_ + sk_msg_iter_var_prev(i); + psge = sk_msg_elem(msg, i); + rsge = sk_msg_elem_cpy(msg, i); ++ rsge_copy = test_bit(i, msg->sg.copy); + + psge->length = start - offset; + rsge.length -= psge->length; +@@ -2875,24 +2881,32 @@ BPF_CALL_4(bpf_msg_push_data, struct sk_ + + /* Shift one or two slots as needed */ + sge = sk_msg_elem_cpy(msg, new); ++ sge_copy = test_bit(new, msg->sg.copy); + sg_unmark_end(&sge); + + nsge = sk_msg_elem_cpy(msg, i); ++ nsge_copy = test_bit(i, msg->sg.copy); + if (rsge.length) { + sk_msg_iter_var_next(i); + nnsge = sk_msg_elem_cpy(msg, i); ++ nnsge_copy = test_bit(i, msg->sg.copy); + sk_msg_iter_next(msg, end); + } + + while (i != msg->sg.end) { + msg->sg.data[i] = sge; ++ __assign_bit(i, msg->sg.copy, sge_copy); + sge = nsge; ++ sge_copy = nsge_copy; + sk_msg_iter_var_next(i); + if (rsge.length) { + nsge = nnsge; ++ nsge_copy = nnsge_copy; + nnsge = sk_msg_elem_cpy(msg, i); ++ nnsge_copy = test_bit(i, msg->sg.copy); + } else { + nsge = sk_msg_elem_cpy(msg, i); ++ nsge_copy = test_bit(i, msg->sg.copy); + } + } + +@@ -2906,6 +2920,7 @@ place_new: + get_page(sg_page(&rsge)); + sk_msg_iter_var_next(new); + msg->sg.data[new] = rsge; ++ __assign_bit(new, msg->sg.copy, rsge_copy); + } + + sk_msg_reset_curr(msg); +@@ -2933,25 +2948,33 @@ static void sk_msg_shift_left(struct sk_ + prev = i; + sk_msg_iter_var_next(i); + msg->sg.data[prev] = msg->sg.data[i]; ++ sk_msg_sg_copy_assign(msg, prev, msg, i); + } while (i != msg->sg.end); + + sk_msg_iter_prev(msg, end); ++ __clear_bit(msg->sg.end, msg->sg.copy); + } + + static void sk_msg_shift_right(struct sk_msg *msg, int i) + { + struct scatterlist tmp, sge; ++ bool tmp_copy, sge_copy; + + sk_msg_iter_next(msg, end); + sge = sk_msg_elem_cpy(msg, i); ++ sge_copy = test_bit(i, msg->sg.copy); + sk_msg_iter_var_next(i); + tmp = sk_msg_elem_cpy(msg, i); ++ tmp_copy = test_bit(i, msg->sg.copy); + + while (i != msg->sg.end) { + msg->sg.data[i] = sge; ++ __assign_bit(i, msg->sg.copy, sge_copy); + sk_msg_iter_var_next(i); + sge = tmp; ++ sge_copy = tmp_copy; + tmp = sk_msg_elem_cpy(msg, i); ++ tmp_copy = test_bit(i, msg->sg.copy); + } + } + +@@ -3011,6 +3034,8 @@ BPF_CALL_4(bpf_msg_pop_data, struct sk_m + struct scatterlist *nsge, *sge = sk_msg_elem(msg, i); + int a = start - offset; + int b = sge->length - pop - a; ++ u32 sge_i = i; ++ bool sge_copy = test_bit(i, msg->sg.copy); + + sk_msg_iter_var_next(i); + +@@ -3023,6 +3048,7 @@ BPF_CALL_4(bpf_msg_pop_data, struct sk_m + sg_set_page(nsge, + sg_page(sge), + b, sge->offset + pop + a); ++ __assign_bit(i, msg->sg.copy, sge_copy); + } else { + struct page *page, *orig; + u8 *to, *from; +@@ -3039,6 +3065,7 @@ BPF_CALL_4(bpf_msg_pop_data, struct sk_m + memcpy(to, from, a); + memcpy(to + a, from + a + pop, b); + sg_set_page(sge, page, a + b, 0); ++ __clear_bit(sge_i, msg->sg.copy); + put_page(orig); + } + pop = 0; +--- a/net/core/skmsg.c ++++ b/net/core/skmsg.c +@@ -65,6 +65,7 @@ int sk_msg_alloc(struct sock *sk, struct + sge = &msg->sg.data[msg->sg.end]; + sg_unmark_end(sge); + sg_set_page(sge, pfrag->page, use, orig_offset); ++ __clear_bit(msg->sg.end, msg->sg.copy); + get_page(pfrag->page); + sk_msg_iter_next(msg, end); + } +@@ -185,6 +186,7 @@ static int sk_msg_free_elem(struct sock + sk_mem_uncharge(sk, len); + put_page(sg_page(sge)); + } ++ __clear_bit(i, msg->sg.copy); + memset(sge, 0, sizeof(*sge)); + return len; + } +--- a/net/tls/tls_sw.c ++++ b/net/tls/tls_sw.c +@@ -564,6 +564,7 @@ static int tls_split_open_record(struct + struct scatterlist *sge, *osge, *nsge; + u32 orig_size = msg_opl->sg.size; + struct scatterlist tmp = { }; ++ u32 tmp_i = 0; + struct sk_msg *msg_npl; + struct tls_rec *new; + int ret; +@@ -585,6 +586,7 @@ static int tls_split_open_record(struct + if (sge->length > apply) { + u32 len = sge->length - apply; + ++ tmp_i = i; + get_page(sg_page(sge)); + sg_set_page(&tmp, sg_page(sge), len, + sge->offset + apply); +@@ -616,6 +618,7 @@ static int tls_split_open_record(struct + nsge = sk_msg_elem(msg_npl, j); + if (tmp.length) { + memcpy(nsge, &tmp, sizeof(*nsge)); ++ sk_msg_sg_copy_assign(msg_npl, j, msg_opl, tmp_i); + sk_msg_iter_var_next(j); + nsge = sk_msg_elem(msg_npl, j); + } +@@ -623,6 +626,7 @@ static int tls_split_open_record(struct + osge = sk_msg_elem(msg_opl, i); + while (osge->length) { + memcpy(nsge, osge, sizeof(*nsge)); ++ sk_msg_sg_copy_assign(msg_npl, j, msg_opl, i); + sg_unmark_end(nsge); + sk_msg_iter_var_next(i); + sk_msg_iter_var_next(j); diff --git a/queue-5.10/nfsv4-flexfiles-reject-zero-filehandle-version-count.patch b/queue-5.10/nfsv4-flexfiles-reject-zero-filehandle-version-count.patch new file mode 100644 index 0000000000..9ab6c9e4e5 --- /dev/null +++ b/queue-5.10/nfsv4-flexfiles-reject-zero-filehandle-version-count.patch @@ -0,0 +1,58 @@ +From stable+bounces-272073-greg=kroah.com@vger.kernel.org Sun Jul 5 16:20:51 2026 +From: Sasha Levin +Date: Sun, 5 Jul 2026 10:20:39 -0400 +Subject: NFSv4/flexfiles: reject zero filehandle version count +To: stable@vger.kernel.org +Cc: Michael Bommarito , Anna Schumaker , Sasha Levin +Message-ID: <20260705142039.1791851-1-sashal@kernel.org> + +From: Michael Bommarito + +[ Upstream commit 2c6bb3c40bc24f6aa8dfbe6fe98c3ad6389203f2 ] + +ff_layout_alloc_lseg() decodes the filehandle-version array count +from the flexfiles layout body. The value is used as the count for +kzalloc_objs(), and the current code only rejects NULL. + +A zero count yields ZERO_SIZE_PTR, which can be stored in +dss_info->fh_versions even though later flexfiles paths assume that at +least one filehandle version exists. + +Reject fh_count == 0 before the allocation, matching the existing zero +version_count validation in the flexfiles GETDEVICEINFO parser. + +A QEMU/KASAN run with a malformed flexfiles layout hit: + + KASAN: null-ptr-deref in range [0x0000000000000010-0x0000000000000017] + RIP: 0010:ff_layout_encode_ff_layoutupdate.isra.0+0x15f/0x750 + ff_layout_encode_layoutreturn+0x683/0x970 + nfs4_xdr_enc_layoutreturn+0x278/0x3a0 + Kernel panic - not syncing: Fatal exception + +The patched kernel rejects the malformed layout without KASAN/oops/panic, +and a valid fh_count=1 regression still opens, reads, and unmounts cleanly. + +Cc: stable@vger.kernel.org +Fixes: d67ae825a59d ("pnfs/flexfiles: Add the FlexFile Layout Driver") +Assisted-by: Claude:claude-opus-4-7 +Signed-off-by: Michael Bommarito +Signed-off-by: Anna Schumaker +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/nfs/flexfilelayout/flexfilelayout.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/fs/nfs/flexfilelayout/flexfilelayout.c ++++ b/fs/nfs/flexfilelayout/flexfilelayout.c +@@ -454,6 +454,10 @@ ff_layout_alloc_lseg(struct pnfs_layout_ + if (!p) + goto out_err_free; + fh_count = be32_to_cpup(p); ++ if (fh_count == 0) { ++ rc = -EINVAL; ++ goto out_err_free; ++ } + + fls->mirror_array[i]->fh_versions = + kcalloc(fh_count, sizeof(struct nfs_fh), diff --git a/queue-5.10/series b/queue-5.10/series index 7fcf346a6c..0569543865 100644 --- a/queue-5.10/series +++ b/queue-5.10/series @@ -10,3 +10,22 @@ kvm-replace-guest-triggerable-bug_on-in-ioeventfd-da.patch perf-trace-beauty-fcntl-fix-build-with-older-kernel-headers.patch bus-mhi-host-add-alignment-check-for-event-ring-read-pointer.patch netfilter-nf_log-validate-mac-header-was-set-before-dumping-it.patch +skmsg-convert-struct-sk_msg_sg-copy-to-a-bitmap.patch +net-skmsg-preserve-sg.copy-across-sg-transforms.patch +apparmor-fix-use-after-free-in-rawdata-dedup-loop.patch +net-ip_gre-require-cap_net_admin-in-the-device-netns-for-changelink.patch +apparmor-mediate-the-implicit-connect-of-tcp-fast-open-sendmsg.patch +f2fs-adjust-zone-capacity-when-considering-valid-block-count.patch +f2fs-fix-to-round-down-start-offset-of-fallocate-for-pin-file.patch +f2fs-validate-orphan-inode-entry-count.patch +f2fs-bound-i_inline_xattr_size-for-non-inline-xattr-inodes.patch +f2fs-fix-potential-deadlock-in-f2fs_balance_fs.patch +f2fs-fix-listxattr-handling-of-corrupted-xattr-entries.patch +nfsv4-flexfiles-reject-zero-filehandle-version-count.patch +fbdev-fbcon-fix-out-of-bounds-read-in-err_out-of-fbcon_do_set_font.patch +i2c-core-fix-irq-domain-leak-on-adapter-registration-failure.patch +i2c-core-fix-hang-on-adapter-registration-failure.patch +i2c-core-fix-null-deref-on-adapter-registration-failure.patch +i2c-core-fix-adapter-debugfs-creation.patch +i2c-core-fix-adapter-registration-race.patch +hdlc_ppp-sync-per-proto-timers-before-freeing-hdlc-state.patch diff --git a/queue-5.10/skmsg-convert-struct-sk_msg_sg-copy-to-a-bitmap.patch b/queue-5.10/skmsg-convert-struct-sk_msg_sg-copy-to-a-bitmap.patch new file mode 100644 index 0000000000..e31e727e82 --- /dev/null +++ b/queue-5.10/skmsg-convert-struct-sk_msg_sg-copy-to-a-bitmap.patch @@ -0,0 +1,96 @@ +From stable+bounces-270554-greg=kroah.com@vger.kernel.org Thu Jul 2 17:30:36 2026 +From: Sasha Levin +Date: Thu, 2 Jul 2026 11:26:22 -0400 +Subject: skmsg: convert struct sk_msg_sg::copy to a bitmap +To: stable@vger.kernel.org +Cc: Eric Dumazet , "David S. Miller" , Sasha Levin +Message-ID: <20260702152623.3530248-1-sashal@kernel.org> + +From: Eric Dumazet + +[ Upstream commit 5a8fb33e530512ee67a11b30f3451a4f030f4b01 ] + +We have plans for increasing MAX_SKB_FRAGS, but sk_msg_sg::copy +is currently an unsigned long, limiting MAX_SKB_FRAGS to 30 on 32bit arches. + +Convert it to a bitmap, as Jakub suggested. + +Signed-off-by: Eric Dumazet +Signed-off-by: David S. Miller +Stable-dep-of: 406e8a651a7b ("net: skmsg: preserve sg.copy across SG transforms") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + include/linux/skmsg.h | 11 +++++------ + net/core/filter.c | 4 ++-- + 2 files changed, 7 insertions(+), 8 deletions(-) + +--- a/include/linux/skmsg.h ++++ b/include/linux/skmsg.h +@@ -29,7 +29,7 @@ struct sk_msg_sg { + u32 end; + u32 size; + u32 copybreak; +- unsigned long copy; ++ DECLARE_BITMAP(copy, MAX_MSG_FRAGS + 2); + /* The extra two elements: + * 1) used for chaining the front and sections when the list becomes + * partitioned (e.g. end < start). The crypto APIs require the +@@ -38,7 +38,6 @@ struct sk_msg_sg { + */ + struct scatterlist data[MAX_MSG_FRAGS + 2]; + }; +-static_assert(BITS_PER_LONG >= NR_MSG_FRAG_IDS); + + /* UAPI in filter.c depends on struct sk_msg_sg being first element. */ + struct sk_msg { +@@ -235,7 +234,7 @@ static inline void sk_msg_compute_data_p + { + struct scatterlist *sge = sk_msg_elem(msg, msg->sg.start); + +- if (test_bit(msg->sg.start, &msg->sg.copy)) { ++ if (test_bit(msg->sg.start, msg->sg.copy)) { + msg->data = NULL; + msg->data_end = NULL; + } else { +@@ -254,7 +253,7 @@ static inline void sk_msg_page_add(struc + sg_set_page(sge, page, len, offset); + sg_unmark_end(sge); + +- __set_bit(msg->sg.end, &msg->sg.copy); ++ __set_bit(msg->sg.end, msg->sg.copy); + msg->sg.size += len; + sk_msg_iter_next(msg, end); + } +@@ -263,9 +262,9 @@ static inline void sk_msg_sg_copy(struct + { + do { + if (copy_state) +- __set_bit(i, &msg->sg.copy); ++ __set_bit(i, msg->sg.copy); + else +- __clear_bit(i, &msg->sg.copy); ++ __clear_bit(i, msg->sg.copy); + sk_msg_iter_var_next(i); + if (i == msg->sg.end) + break; +--- a/net/core/filter.c ++++ b/net/core/filter.c +@@ -2677,7 +2677,7 @@ BPF_CALL_4(bpf_msg_pull_data, struct sk_ + * account for the headroom. + */ + bytes_sg_total = start - offset + bytes; +- if (!test_bit(i, &msg->sg.copy) && bytes_sg_total <= len) ++ if (!test_bit(i, msg->sg.copy) && bytes_sg_total <= len) + goto out; + + /* At this point we need to linearize multiple scatterlist +@@ -2900,7 +2900,7 @@ place_new: + /* Place newly allocated data buffer */ + sk_mem_charge(msg->sk, len); + msg->sg.size += len; +- __clear_bit(new, &msg->sg.copy); ++ __clear_bit(new, msg->sg.copy); + sg_set_page(&msg->sg.data[new], page, len + copy, 0); + if (rsge.length) { + get_page(sg_page(&rsge));