From 9eb71456e28c770fef156fe471f1d822a3a91ce2 Mon Sep 17 00:00:00 2001 From: Andrew Lukoshko Date: Wed, 22 Jul 2026 12:21:08 +0000 Subject: [PATCH] Fix 13 more Important CVEs ahead of RHEL; bump to 211.34.3 Backport upstream stable fixes for newly RHEL-Affected Important CVEs (patches 1113-1120, 1122-1126), closest-to-6.12 base, verified zero-fuzz full %prep replay: CVE-2026-63886 63887 63888 (iscsi target), 63899 63956 (usb-serial), 63917 (ip6 vti), 63922 63924 (ipv6 exthdrs), 63939 (kvm sev), 63994 (tunnels), 64017 (blk-mq), 64026 (rxrpc), 64030 (mac80211) CVE-2026-63950 (mm/rmap) dropped: not applicable to el10 (try_to_unmap_one has no nr_pages batching variable; the stale-nr_pages bug the fix targets cannot occur). --- ...i-validate-chap-r-length-before-base.patch | 95 +++ ...i-bound-iscsi-encode-text-output-app.patch | 204 ++++++ ...i-fix-crc-overread-and-double-free-i.patch | 124 ++++ ...ort-fix-memory-corruption-with-small.patch | 45 ++ ...i-use-ip6-tnl-net-in-vti6-changelink.patch | 78 +++ ...resh-nh-pointer-after-ipv6-hop-jumbo.patch | 42 ++ ...refresh-nh-after-handling-hao-option.patch | 56 ++ ...the-correct-max-length-of-the-in-ghc.patch | 109 +++ ...ss-m8-fix-memory-corruption-with-sma.patch | 46 ++ ...oad-network-headers-after-skb-cow-in.patch | 86 +++ ...q-pop-cached-request-if-it-is-usable.patch | 115 +++ ...decrypt-vs-splice-by-copying-data-to.patch | 654 ++++++++++++++++++ ...unds-check-link-id-in-ieee80211-ml-e.patch | 51 ++ kernel.spec | 45 +- 14 files changed, 1748 insertions(+), 2 deletions(-) create mode 100644 1113-scsi-target-iscsi-validate-chap-r-length-before-base.patch create mode 100644 1114-scsi-target-iscsi-bound-iscsi-encode-text-output-app.patch create mode 100644 1115-scsi-target-iscsi-fix-crc-overread-and-double-free-i.patch create mode 100644 1116-usb-serial-mxuport-fix-memory-corruption-with-small.patch create mode 100644 1117-ip6-vti-use-ip6-tnl-net-in-vti6-changelink.patch create mode 100644 1118-ipv6-exthdrs-refresh-nh-pointer-after-ipv6-hop-jumbo.patch create mode 100644 1119-ipv6-exthdrs-refresh-nh-after-handling-hao-option.patch create mode 100644 1120-kvm-sev-compute-the-correct-max-length-of-the-in-ghc.patch create mode 100644 1122-usb-serial-cypress-m8-fix-memory-corruption-with-sma.patch create mode 100644 1123-tunnels-load-network-headers-after-skb-cow-in.patch create mode 100644 1124-blk-mq-pop-cached-request-if-it-is-usable.patch create mode 100644 1125-rxrpc-fix-data-decrypt-vs-splice-by-copying-data-to.patch create mode 100644 1126-wifi-mac80211-bounds-check-link-id-in-ieee80211-ml-e.patch diff --git a/1113-scsi-target-iscsi-validate-chap-r-length-before-base.patch b/1113-scsi-target-iscsi-validate-chap-r-length-before-base.patch new file mode 100644 index 000000000..aaea4621b --- /dev/null +++ b/1113-scsi-target-iscsi-validate-chap-r-length-before-base.patch @@ -0,0 +1,95 @@ +From bf154c657828ed05399bca5d98cf1611bb048b12 Mon Sep 17 00:00:00 2001 +From: Alexandru Hossu +Date: Thu, 21 May 2026 17:11:21 +0200 +Subject: [PATCH] scsi: target: iscsi: Validate CHAP_R length before base64 + decode + +commit 85db7391310b1304d2dc8ae3b0b12105a9567147 upstream. + +chap_server_compute_hash() allocates client_digest as +kzalloc(chap->digest_size) and then, for BASE64-encoded responses, +passes chap_r directly to chap_base64_decode() without checking whether +the input length could produce more than digest_size bytes of output. + +chap_base64_decode() writes to the destination unconditionally as long +as there is input to consume. With MAX_RESPONSE_LENGTH set to 128 and +the "0b" prefix stripped by extract_param(), up to 127 base64 characters +can reach the decoder. 127 characters decode to 95 bytes. For SHA-256 +(digest_size=32) this overflows client_digest by 63 bytes; for MD5 +(digest_size=16) the overflow is 79 bytes. + +The length check at line 344 fires after the write has already happened. + +The HEX branch in the same switch statement already validates the length +up front. Apply the same approach to the BASE64 branch: strip trailing +base64 padding characters, then reject any input whose data length +exceeds DIV_ROUND_UP(digest_size * 4, 3) before calling the decoder. + +Stripping trailing '=' before the comparison handles both padded and +unpadded encodings. chap_base64_decode() already returns early on '=', +so the full original string is still passed to the decoder unchanged. + +The mutual CHAP path decodes CHAP_C into initiatorchg_binhex, which is +kzalloc(CHAP_CHALLENGE_STR_LEN). extract_param() caps initiatorchg at +CHAP_CHALLENGE_STR_LEN characters, so at most CHAP_CHALLENGE_STR_LEN-1 +base64 characters reach the decoder. The maximum decoded size, +DIV_ROUND_UP((CHAP_CHALLENGE_STR_LEN-1) * 3, 4), is less than +CHAP_CHALLENGE_STR_LEN, so no overflow is possible there. A comment is +added at the call site to document this. + +Fixes: 1e5733883421 ("scsi: target: iscsi: Support base64 in CHAP") +Cc: stable@vger.kernel.org +Signed-off-by: Alexandru Hossu +Reviewed-by: David Disseldorp +Link: https://patch.msgid.link/20260521151121.808477-1-hossu.alexandru@gmail.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Greg Kroah-Hartman + +[ CVE-2026-63886: backported to AlmaLinux 10 (kernel-6.12.0-211.34.1.el10_2) from linux-6.12.y commit bf154c657828ed05399bca5d98cf1611bb048b12; applies cleanly, zero-fuzz. ] + +diff --git a/drivers/target/iscsi/iscsi_target_auth.c b/drivers/target/iscsi/iscsi_target_auth.c +index c8a248bd11be..02a4c9aff98d 100644 +--- a/drivers/target/iscsi/iscsi_target_auth.c ++++ b/drivers/target/iscsi/iscsi_target_auth.c +@@ -339,13 +339,22 @@ static int chap_server_compute_hash( + goto out; + } + break; +- case BASE64: ++ case BASE64: { ++ size_t r_len = strlen(chap_r); ++ ++ while (r_len > 0 && chap_r[r_len - 1] == '=') ++ r_len--; ++ if (r_len > DIV_ROUND_UP(chap->digest_size * 4, 3)) { ++ pr_err("Malformed CHAP_R: base64 payload too long\n"); ++ goto out; ++ } + if (chap_base64_decode(client_digest, chap_r, strlen(chap_r)) != + chap->digest_size) { + pr_err("Malformed CHAP_R: invalid BASE64\n"); + goto out; + } + break; ++ } + default: + pr_err("Could not find CHAP_R\n"); + goto out; +@@ -472,6 +481,14 @@ static int chap_server_compute_hash( + } + break; + case BASE64: ++ /* ++ * No overflow check needed: initiatorchg_binhex is ++ * CHAP_CHALLENGE_STR_LEN bytes and extract_param() caps ++ * initiatorchg at CHAP_CHALLENGE_STR_LEN characters, so ++ * the decoded output is at most DIV_ROUND_UP( ++ * (CHAP_CHALLENGE_STR_LEN - 1) * 3, 4) bytes, which is ++ * less than CHAP_CHALLENGE_STR_LEN. ++ */ + initiatorchg_len = chap_base64_decode(initiatorchg_binhex, + initiatorchg, + strlen(initiatorchg)); +-- +2.50.1 (Apple Git-155) + diff --git a/1114-scsi-target-iscsi-bound-iscsi-encode-text-output-app.patch b/1114-scsi-target-iscsi-bound-iscsi-encode-text-output-app.patch new file mode 100644 index 000000000..296004e11 --- /dev/null +++ b/1114-scsi-target-iscsi-bound-iscsi-encode-text-output-app.patch @@ -0,0 +1,204 @@ +From 30bf335e8fe170322080ee001f05ca29c50680b3 Mon Sep 17 00:00:00 2001 +From: Michael Bommarito +Date: Mon, 11 May 2026 14:49:14 -0400 +Subject: [PATCH] scsi: target: iscsi: Bound iscsi_encode_text_output() appends + to rsp_buf + +commit bf33e01f88388c43e285492a63e539df6ffed64c upstream. + +iscsi_encode_text_output() concatenates "key=value\0" records into +login->rsp_buf, an 8192-byte kzalloc(MAX_KEY_VALUE_PAIRS) buffer +allocated in iscsit_alloc_login_setup_buffer(). The three sprintf() call +sites in this function (lines 1398, 1411, 1424 in v7.1-rc2) never check +the remaining buffer capacity: + + *length += sprintf(output_buf, "%s=%s", er->key, er->value); + *length += 1; + output_buf = textbuf + *length; + +The 8192-byte ceiling at iscsi_target_check_login_request() bounds the +*input* Login PDU payload, but a single PDU can carry up to 2048 minimal +four-byte "a=b\0" pairs, each unknown key expanding to a 16-byte +"a=NotUnderstood\0" output record via iscsi_add_notunderstood_response(). +2048 * 16 = 32 KiB of output into an 8 KiB buffer, producing a ~24 KiB +heap overrun in the kmalloc-8k slab. + +The fix introduces a static iscsi_encode_text_record() helper that uses +snprintf() with a per-call bounds check against the remaining buffer, +and threads a u32 textbuf_size parameter through +iscsi_encode_text_output(). Both call sites in +iscsi_target_handle_csg_zero() (PHASE_SECURITY) and +iscsi_target_handle_csg_one() (PHASE_OPERATIONAL) pass +MAX_KEY_VALUE_PAIRS. On overflow the encoder logs the condition, calls +iscsi_release_extra_responses() to drop queued records, and returns -1; +both caller sites now emit ISCSI_STATUS_CLS_INITIATOR_ERR / +ISCSI_LOGIN_STATUS_INIT_ERR via iscsit_tx_login_rsp() before returning, +so the initiator sees an explicit failed-login response rather than a +silent connection drop. (Prior to this patch only the PHASE_OPERATIONAL +caller did that; the PHASE_SECURITY caller is converted to the same +shape.) + +Fixes: e48354ce078c ("iscsi-target: Add iSCSI fabric support for target v4.1") +Cc: stable@vger.kernel.org +Assisted-by: Claude:claude-opus-4-7 +Signed-off-by: Michael Bommarito +Tested-by: John Garry +Reviewed-by: John Garry +Signed-off-by: Martin K. Petersen +Signed-off-by: Greg Kroah-Hartman + +[ CVE-2026-63887: backported to AlmaLinux 10 (kernel-6.12.0-211.34.1.el10_2) from linux-6.12.y commit 30bf335e8fe170322080ee001f05ca29c50680b3; applies cleanly, zero-fuzz. ] + +diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c +index fa3fb5f4e6bc..f98734524693 100644 +--- a/drivers/target/iscsi/iscsi_target_nego.c ++++ b/drivers/target/iscsi/iscsi_target_nego.c +@@ -899,10 +899,14 @@ static int iscsi_target_handle_csg_zero( + SENDER_TARGET, + login->rsp_buf, + &login->rsp_length, ++ MAX_KEY_VALUE_PAIRS, + conn->param_list, + conn->tpg->tpg_attrib.login_keys_workaround); +- if (ret < 0) ++ if (ret < 0) { ++ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, ++ ISCSI_LOGIN_STATUS_INIT_ERR); + return -1; ++ } + + if (!iscsi_check_negotiated_keys(conn->param_list)) { + bool auth_required = iscsi_conn_auth_required(conn); +@@ -986,6 +990,7 @@ static int iscsi_target_handle_csg_one(struct iscsit_conn *conn, struct iscsi_lo + SENDER_TARGET, + login->rsp_buf, + &login->rsp_length, ++ MAX_KEY_VALUE_PAIRS, + conn->param_list, + conn->tpg->tpg_attrib.login_keys_workaround); + if (ret < 0) { +diff --git a/drivers/target/iscsi/iscsi_target_parameters.c b/drivers/target/iscsi/iscsi_target_parameters.c +index 5b90c22ee3dc..5e15c2ea7d65 100644 +--- a/drivers/target/iscsi/iscsi_target_parameters.c ++++ b/drivers/target/iscsi/iscsi_target_parameters.c +@@ -1419,19 +1419,42 @@ int iscsi_decode_text_input( + return -1; + } + ++/* ++ * Append "key=value" plus a trailing NUL into @textbuf at *@length. ++ * Returns 0 on success and advances *@length, or -EMSGSIZE if the ++ * record (including the NUL) would not fit in the remaining buffer. ++ */ ++static int iscsi_encode_text_record(char *textbuf, u32 *length, ++ u32 textbuf_size, ++ const char *key, const char *value) ++{ ++ int n; ++ u32 avail; ++ ++ if (*length >= textbuf_size) ++ return -EMSGSIZE; ++ ++ avail = textbuf_size - *length; ++ n = snprintf(textbuf + *length, avail, "%s=%s", key, value); ++ if (n < 0 || (u32)n + 1 > avail) ++ return -EMSGSIZE; ++ ++ *length += n + 1; ++ return 0; ++} ++ + int iscsi_encode_text_output( + u8 phase, + u8 sender, + char *textbuf, + u32 *length, ++ u32 textbuf_size, + struct iscsi_param_list *param_list, + bool keys_workaround) + { +- char *output_buf = NULL; + struct iscsi_extra_response *er; + struct iscsi_param *param; +- +- output_buf = textbuf + *length; ++ int ret; + + if (iscsi_enforce_integrity_rules(phase, param_list) < 0) + return -1; +@@ -1443,10 +1466,12 @@ int iscsi_encode_text_output( + !IS_PSTATE_RESPONSE_SENT(param) && + !IS_PSTATE_REPLY_OPTIONAL(param) && + (param->phase & phase)) { +- *length += sprintf(output_buf, "%s=%s", +- param->name, param->value); +- *length += 1; +- output_buf = textbuf + *length; ++ ret = iscsi_encode_text_record(textbuf, length, ++ textbuf_size, ++ param->name, ++ param->value); ++ if (ret < 0) ++ goto err_overflow; + SET_PSTATE_RESPONSE_SENT(param); + pr_debug("Sending key: %s=%s\n", + param->name, param->value); +@@ -1456,10 +1481,12 @@ int iscsi_encode_text_output( + !IS_PSTATE_ACCEPTOR(param) && + !IS_PSTATE_PROPOSER(param) && + (param->phase & phase)) { +- *length += sprintf(output_buf, "%s=%s", +- param->name, param->value); +- *length += 1; +- output_buf = textbuf + *length; ++ ret = iscsi_encode_text_record(textbuf, length, ++ textbuf_size, ++ param->name, ++ param->value); ++ if (ret < 0) ++ goto err_overflow; + SET_PSTATE_PROPOSER(param); + iscsi_check_proposer_for_optional_reply(param, + keys_workaround); +@@ -1469,14 +1496,21 @@ int iscsi_encode_text_output( + } + + list_for_each_entry(er, ¶m_list->extra_response_list, er_list) { +- *length += sprintf(output_buf, "%s=%s", er->key, er->value); +- *length += 1; +- output_buf = textbuf + *length; ++ ret = iscsi_encode_text_record(textbuf, length, textbuf_size, ++ er->key, er->value); ++ if (ret < 0) ++ goto err_overflow; + pr_debug("Sending key: %s=%s\n", er->key, er->value); + } + iscsi_release_extra_responses(param_list); + + return 0; ++ ++err_overflow: ++ pr_err("iSCSI login response buffer (%u bytes) exhausted, dropping login.\n", ++ textbuf_size); ++ iscsi_release_extra_responses(param_list); ++ return -1; + } + + int iscsi_check_negotiated_keys(struct iscsi_param_list *param_list) +diff --git a/drivers/target/iscsi/iscsi_target_parameters.h b/drivers/target/iscsi/iscsi_target_parameters.h +index 00fbbebb8c75..d6cbe5dd4b00 100644 +--- a/drivers/target/iscsi/iscsi_target_parameters.h ++++ b/drivers/target/iscsi/iscsi_target_parameters.h +@@ -46,7 +46,7 @@ extern struct iscsi_param *iscsi_find_param_from_key(char *, struct iscsi_param_ + extern int iscsi_extract_key_value(char *, char **, char **); + extern int iscsi_update_param_value(struct iscsi_param *, char *); + extern int iscsi_decode_text_input(u8, u8, char *, u32, struct iscsit_conn *); +-extern int iscsi_encode_text_output(u8, u8, char *, u32 *, ++extern int iscsi_encode_text_output(u8, u8, char *, u32 *, u32, + struct iscsi_param_list *, bool); + extern int iscsi_check_negotiated_keys(struct iscsi_param_list *); + extern void iscsi_set_connection_parameters(struct iscsi_conn_ops *, +-- +2.50.1 (Apple Git-155) + diff --git a/1115-scsi-target-iscsi-fix-crc-overread-and-double-free-i.patch b/1115-scsi-target-iscsi-fix-crc-overread-and-double-free-i.patch new file mode 100644 index 000000000..9369d761c --- /dev/null +++ b/1115-scsi-target-iscsi-fix-crc-overread-and-double-free-i.patch @@ -0,0 +1,124 @@ +From ec9f19d52074a191ed1756ed4a7d39fff1a2085c Mon Sep 17 00:00:00 2001 +From: Michael Bommarito +Date: Fri, 5 Jun 2026 22:47:23 -0400 +Subject: [PATCH] scsi: target: iscsi: Fix CRC overread and double-free in + iscsit_handle_text_cmd() + +[ Upstream commit 778c2ab142c625a8a8afa570e0f9b7873f445d99 ] + +Two latent bugs in the Text-phase handler, both present since the +original LIO integration in commit e48354ce078c ("iscsi-target: Add +iSCSI fabric support for target v4.1"): + +1) DataDigest CRC buffer overread (4 bytes past text_in). + + text_in is kzalloc()'d at ALIGN(payload_length, 4). rx_size is then + incremented by ISCSI_CRC_LEN to make room for the received DataDigest + in the iovec, but the same (now-bumped) rx_size is passed as the + buffer length to iscsit_crc_buf(): + + if (conn->conn_ops->DataDigest) { + ... + rx_size += ISCSI_CRC_LEN; + } + ... + if (conn->conn_ops->DataDigest) { + data_crc = iscsit_crc_buf(text_in, rx_size, 0, NULL); + + iscsit_crc_buf() walks rx_size bytes of text_in with crc32c(), so + when DataDigest is negotiated it reads 4 bytes past the end of the + text_in allocation. KASAN reproduces this directly on the unpatched + mainline tree as slab-out-of-bounds in crc32c() called from the Text + PDU path. The OOB bytes feed crc32c() and are then compared against + the initiator-supplied checksum, so the value does not flow back to + the attacker, but the kernel does read past the buffer on every Text + PDU with DataDigest=CRC32C. + + Fix by passing the actual padded payload length + (ALIGN(payload_length, 4)) that was used for the kzalloc(). + +2) Stale cmd->text_in_ptr re-free (double-free) on ERL>0 bad DataDigest + drop. + + On DataDigest mismatch with ErrorRecoveryLevel > 0 the handler + silently drops the PDU and lets the initiator plug the CmdSN gap: + + kfree(text_in); + return 0; + + cmd->text_in_ptr still points at the freed buffer. The next Text + Request on the same ITT re-enters iscsit_setup_text_cmd(), which + unconditionally does + + kfree(cmd->text_in_ptr); + cmd->text_in_ptr = NULL; + + freeing the same pointer a second time. Session teardown via + iscsit_release_cmd() has the same shape and hits the same double-free + if the connection is dropped before a second Text Request arrives. + + On an unmodified mainline tree the bug-1 CRC overread fires first on + the initial valid Text Request and perturbs the subsequent state, so + #4 was isolated by building a kernel with only the bug-1 hunk of this + patch applied plus temporary printk() observability around the three + relevant kfree() sites. The observability prints are not part of + this patch. On that build, a three-PDU Text Request sequence after + login produces two back-to-back splats: + + BUG: KASAN: double-free in iscsit_setup_text_cmd+0x?? + BUG: KASAN: double-free in iscsit_release_cmd+0x?? + + showing the same pointer freed in the ERL>0 drop path and again in + iscsit_setup_text_cmd() (next Text Request on the same ITT) and once + more in iscsit_release_cmd() (session teardown). On distro kernels + with CONFIG_SLAB_FREELIST_HARDENED=y (default) the double-free + becomes a remote kernel BUG(); on non-hardened kernels it corrupts + the slab freelist. + + Fix by clearing cmd->text_in_ptr after the kfree() in the ERL>0 drop + path. With both hunks applied #4 is directly observable on the stock + tree without observability printks; fixing bug-1 alone would mask #4 + less, not more, so the hunks are submitted together. + +Both fixes are one-liners. The Text PDU state machine is unchanged and +the wire protocol is unaffected. + +Fixes: e48354ce078c ("iscsi-target: Add iSCSI fabric support for target v4.1") +Cc: stable@vger.kernel.org +Assisted-by: Claude:claude-opus-4-7 +Signed-off-by: Michael Bommarito +Tested-by: John Garry +Reviewed-by: John Garry +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman + +[ CVE-2026-63888: backported to AlmaLinux 10 (kernel-6.12.0-211.34.1.el10_2) from linux-6.12.y commit ec9f19d52074a191ed1756ed4a7d39fff1a2085c; applies cleanly, zero-fuzz. ] + +diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c +index 68bbdf3ee101..8a7d308da991 100644 +--- a/drivers/target/iscsi/iscsi_target.c ++++ b/drivers/target/iscsi/iscsi_target.c +@@ -2329,8 +2329,9 @@ iscsit_handle_text_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd, + + if (conn->conn_ops->DataDigest) { + iscsit_do_crypto_hash_buf(conn->conn_rx_hash, +- text_in, rx_size, 0, NULL, +- &data_crc); ++ text_in, ++ ALIGN(payload_length, 4), ++ 0, NULL, &data_crc); + + if (checksum != data_crc) { + pr_err("Text data CRC32C DataDigest" +@@ -2350,6 +2351,7 @@ iscsit_handle_text_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd, + " Command CmdSN: 0x%08x due to" + " DataCRC error.\n", hdr->cmdsn); + kfree(text_in); ++ cmd->text_in_ptr = NULL; + return 0; + } + } else { +-- +2.50.1 (Apple Git-155) + diff --git a/1116-usb-serial-mxuport-fix-memory-corruption-with-small.patch b/1116-usb-serial-mxuport-fix-memory-corruption-with-small.patch new file mode 100644 index 000000000..2ef449b32 --- /dev/null +++ b/1116-usb-serial-mxuport-fix-memory-corruption-with-small.patch @@ -0,0 +1,45 @@ +From e906545641d34fb1a09a65b4b5cfdff40eb09681 Mon Sep 17 00:00:00 2001 +From: Johan Hovold +Date: Fri, 22 May 2026 16:19:50 +0200 +Subject: [PATCH] USB: serial: mxuport: fix memory corruption with small + endpoint + +commit 4085f0dbb1ce2251c9a5938d693de6593f0ab2bd upstream. + +Make sure that the bulk-out endpoint max packet size is at least eight +bytes to avoid user-controlled slab corruption should a malicious device +report a smaller size. + +Fixes: ee467a1f2066 ("USB: serial: add Moxa UPORT 12XX/14XX/16XX driver") +Cc: stable@vger.kernel.org # 3.14 +Cc: Andrew Lunn +Reviewed-by: Greg Kroah-Hartman +Signed-off-by: Johan Hovold +Signed-off-by: Greg Kroah-Hartman + +CVE-2026-63899 + +[ Backported from linux-6.12.y commit e906545641d34fb1a09a65b4b5cfdff40eb09681 (mainline 4085f0dbb1ce2251c9a5938d693de6593f0ab2bd); applies cleanly to AlmaLinux 10 (kernel-6.12.0-211.34.1.el10_2), zero-fuzz. ] + +diff --git a/drivers/usb/serial/mxuport.c b/drivers/usb/serial/mxuport.c +index ad5fdf55a02e..c9b9928c473a 100644 +--- a/drivers/usb/serial/mxuport.c ++++ b/drivers/usb/serial/mxuport.c +@@ -962,6 +962,14 @@ static int mxuport_calc_num_ports(struct usb_serial *serial, + */ + BUILD_BUG_ON(ARRAY_SIZE(epds->bulk_out) < 16); + ++ /* ++ * The bulk-out buffers must be large enough for the four-byte header ++ * (and following data), but assume anything smaller than eight bytes ++ * is broken. ++ */ ++ if (usb_endpoint_maxp(epds->bulk_out[0]) < 8) ++ return -EINVAL; ++ + for (i = 1; i < num_ports; ++i) + epds->bulk_out[i] = epds->bulk_out[0]; + +-- +2.50.1 (Apple Git-155) + diff --git a/1117-ip6-vti-use-ip6-tnl-net-in-vti6-changelink.patch b/1117-ip6-vti-use-ip6-tnl-net-in-vti6-changelink.patch new file mode 100644 index 000000000..9cc705137 --- /dev/null +++ b/1117-ip6-vti-use-ip6-tnl-net-in-vti6-changelink.patch @@ -0,0 +1,78 @@ +From 225b467e3b631f38be22e4b38062a1fed02fdd21 Mon Sep 17 00:00:00 2001 +From: Kuniyuki Iwashima +Date: Thu, 21 May 2026 21:05:54 +0800 +Subject: [PATCH] ip6: vti: Use ip6_tnl.net in vti6_changelink(). + +commit 11b326fb0a374f4654f9be22d0f0f7abd9f7d3fe upstream. + +ip netns add ns1 +ip netns add ns2 +ip -n ns1 link add vti6_test type vti6 remote ::1 local ::2 key 7 +ip -n ns1 link set vti6_test netns ns2 +ip -n ns2 link set vti6_test type vti6 remote ::3 local ::4 key 9 +ip netns del ns2 +ip netns del ns1 +[ 132.495484] ------------[ cut here ]------------ +[ 132.497609] kernel BUG at net/core/dev.c:12376! + +Commit 61220ab34948 ("vti6: Enable namespace changing") dropped +NETIF_F_NETNS_LOCAL from vti6 devices. A vti6 tunnel can then +move through IFLA_NET_NS_FD. After the move dev_net(dev) points +at the new netns while t->net stays at the creation netns. + +vti6_changelink() and vti6_update() still use dev_net(dev) and +dev_net(t->dev). They unlink from one per netns hash and relink +into another. The creation netns is left with a stale entry. +cleanup_net() of that netns later walks freed memory. + +Reachable from an unprivileged user namespace (unshare --user +--map-root-user --net). Cross tenant scope on container hosts. + +Fixes: 61220ab34948 ("vti6: Enable namespace changing") +Reported-by: Maoyi Xie +Reviewed-by: Eric Dumazet +Cc: stable@vger.kernel.org # v5.15+ +Signed-off-by: Kuniyuki Iwashima +Link: https://patch.msgid.link/20260521130555.3421684-2-maoyixie.tju@gmail.com +Signed-off-by: Paolo Abeni +Signed-off-by: Greg Kroah-Hartman + +[ CVE-2026-63917: backported to AlmaLinux 10 (kernel-6.12.0-211.34.1.el10_2) from linux-6.12.y commit 225b467e3b631f38be22e4b38062a1fed02fdd21; applies cleanly, zero-fuzz. ] + +diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c +index b931092da512..2ac88593a954 100644 +--- a/net/ipv6/ip6_vti.c ++++ b/net/ipv6/ip6_vti.c +@@ -722,10 +722,11 @@ vti6_tnl_change(struct ip6_tnl *t, const struct __ip6_tnl_parm *p, + static int vti6_update(struct ip6_tnl *t, struct __ip6_tnl_parm *p, + bool keep_mtu) + { +- struct net *net = dev_net(t->dev); +- struct vti6_net *ip6n = net_generic(net, vti6_net_id); ++ struct net *net = t->net; ++ struct vti6_net *ip6n; + int err; + ++ ip6n = net_generic(net, vti6_net_id); + vti6_tnl_unlink(ip6n, t); + synchronize_net(); + err = vti6_tnl_change(t, p, keep_mtu); +@@ -1036,11 +1037,12 @@ static int vti6_changelink(struct net_device *dev, struct nlattr *tb[], + struct nlattr *data[], + struct netlink_ext_ack *extack) + { +- struct ip6_tnl *t; ++ struct ip6_tnl *t = netdev_priv(dev); ++ struct net *net = t->net; + struct __ip6_tnl_parm p; +- struct net *net = dev_net(dev); +- struct vti6_net *ip6n = net_generic(net, vti6_net_id); ++ struct vti6_net *ip6n; + ++ ip6n = net_generic(net, vti6_net_id); + if (dev == ip6n->fb_tnl_dev) + return -EINVAL; + +-- +2.50.1 (Apple Git-155) + diff --git a/1118-ipv6-exthdrs-refresh-nh-pointer-after-ipv6-hop-jumbo.patch b/1118-ipv6-exthdrs-refresh-nh-pointer-after-ipv6-hop-jumbo.patch new file mode 100644 index 000000000..2f366c000 --- /dev/null +++ b/1118-ipv6-exthdrs-refresh-nh-pointer-after-ipv6-hop-jumbo.patch @@ -0,0 +1,42 @@ +From 72af7beae774e46ed543f3f2f267bf0a141bfcdd Mon Sep 17 00:00:00 2001 +From: Justin Iurman +Date: Fri, 22 May 2026 13:20:13 +0200 +Subject: [PATCH] ipv6: exthdrs: refresh nh pointer after ipv6_hop_jumbo() + +commit d47548a36639095939f4747d4c43f2271366f565 upstream. + +ipv6_hop_jumbo() calls pskb_trim_rcsum(), which can change skb pointers. +Let's recompute nh pointer to make sure any change won't mess things up. + +Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") +Cc: stable@vger.kernel.org +Signed-off-by: Justin Iurman +Reviewed-by: Ido Schimmel +Link: https://patch.msgid.link/20260522112013.12342-1-justin.iurman@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Greg Kroah-Hartman + +CVE-2026-63924 + +Backported from linux-6.12.y commit 72af7beae774e46ed543f3f2f267bf0a141bfcdd +(mainline d47548a36639095939f4747d4c43f2271366f565); applies cleanly to the +a10 kernel-6.12.0-211.34.1.el10_2 tree, zero-fuzz (line offset only). Must be +applied before CVE-2026-63922 (HAO) and after the already-shipped +CVE-2026-64132 (1111, ipv6 ioam), matching upstream order. + +diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c +index 43e34fe448ff..d179077a2955 100644 +--- a/net/ipv6/exthdrs.c ++++ b/net/ipv6/exthdrs.c +@@ -184,6 +184,8 @@ static bool ip6_parse_tlv(bool hopbyhop, + case IPV6_TLV_JUMBO: + if (!ipv6_hop_jumbo(skb, off)) + return false; ++ ++ nh = skb_network_header(skb); + break; + case IPV6_TLV_CALIPSO: + if (!ipv6_hop_calipso(skb, off)) +-- +2.50.1 (Apple Git-155) + diff --git a/1119-ipv6-exthdrs-refresh-nh-after-handling-hao-option.patch b/1119-ipv6-exthdrs-refresh-nh-after-handling-hao-option.patch new file mode 100644 index 000000000..9e128133d --- /dev/null +++ b/1119-ipv6-exthdrs-refresh-nh-after-handling-hao-option.patch @@ -0,0 +1,56 @@ +From ff375ed1cba81392346c5bfbf0bb7a13b2946f99 Mon Sep 17 00:00:00 2001 +From: Zhengchuan Liang +Date: Fri, 22 May 2026 17:42:26 +0800 +Subject: [PATCH] ipv6: exthdrs: refresh nh after handling HAO option + +commit f7b52afe3592eae66e160586b45a3f2242972c63 upstream. + +ip6_parse_tlv() caches skb_network_header(skb) in nh while walking +IPv6 TLVs. + +ipv6_dest_hao() may call pskb_expand_head() for a cloned skb, which can +move the skb head and invalidate the cached network header pointer. +Refresh nh after ipv6_dest_hao() returns so any trailing padding or TLVs +are parsed from the current skb head. + +This matches the existing pattern used in ip6_parse_tlv() after helpers +that can modify skb header storage. + +Fixes: a831f5bbc89a ("[IPV6] MIP6: Add inbound interface of home address option.") +Cc: stable@kernel.org +Reported-by: Yuan Tan +Reported-by: Xin Liu +Co-developed-by: Luxing Yin +Signed-off-by: Luxing Yin +Signed-off-by: Zhengchuan Liang +Signed-off-by: Ren Wei +Reviewed-by: Justin Iurman +Reviewed-by: Ido Schimmel +Link: https://patch.msgid.link/7aba1debc2196189172499e5769802b026f8caf8.1779247873.git.zcliangcn@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Greg Kroah-Hartman + +CVE-2026-63922 + +Backported from linux-6.12.y commit ff375ed1cba81392346c5bfbf0bb7a13b2946f99 +(mainline f7b52afe3592eae66e160586b45a3f2242972c63); applies cleanly to the +a10 kernel-6.12.0-211.34.1.el10_2 tree, zero-fuzz (line offset only). Must be +applied after CVE-2026-63924 (JUMBO) and the already-shipped CVE-2026-64132 +(1111, ipv6 ioam), matching upstream order. + +diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c +index d179077a2955..e91afe5ec0b5 100644 +--- a/net/ipv6/exthdrs.c ++++ b/net/ipv6/exthdrs.c +@@ -203,6 +203,8 @@ static bool ip6_parse_tlv(bool hopbyhop, + case IPV6_TLV_HAO: + if (!ipv6_dest_hao(skb, off)) + return false; ++ ++ nh = skb_network_header(skb); + break; + #endif + default: +-- +2.50.1 (Apple Git-155) + diff --git a/1120-kvm-sev-compute-the-correct-max-length-of-the-in-ghc.patch b/1120-kvm-sev-compute-the-correct-max-length-of-the-in-ghc.patch new file mode 100644 index 000000000..6bbb2b883 --- /dev/null +++ b/1120-kvm-sev-compute-the-correct-max-length-of-the-in-ghc.patch @@ -0,0 +1,109 @@ +From 6ca9400d36005ffdca25f80186bea781c7e1dc4c Mon Sep 17 00:00:00 2001 +From: Sean Christopherson +Date: Fri, 1 May 2026 13:22:31 -0700 +Subject: [PATCH] KVM: SEV: Compute the correct max length of the in-GHCB + scratch area + +commit 5867d7e202e09f037cefe77f7af4413c7c0fa088 upstream. + +When setting the length of the GHCB scratch area, and the area is in the +GHCB shared buffer, set the effective length of the scratch area to the max +possible size given the start of the guest-provided pointer, and the end of +the shared buffer. + +The code was "fine" when first introduced, as KVM doesn't consult the +length of the buffer when emulating MMIO, because the passed in @len always +specifies the *max* size required. But for PSC requests, the incoming @len +is just the minimum length (to process the header), and KVM needs to know +the full size of the scratch area to avoid buffer overflows (spoiler alert). + +Opportunistically rename @len => @min_len to better reflect its role. + +Fixes: 9b54e248d264 ("KVM: SEV: Add support to handle Page State Change VMGEXIT") +Cc: stable@vger.kernel.org +Reviewed-by: Tom Lendacky +Reviewed-by: Michael Roth +Signed-off-by: Sean Christopherson +Message-ID: <20260501202250.2115252-7-seanjc@google.com> +Signed-off-by: Paolo Bonzini +Signed-off-by: Greg Kroah-Hartman + +CVE-2026-63939 + +Backported from linux-6.12.y commit 6ca9400d36005ffdca25f80186bea781c7e1dc4c +(mainline 5867d7e202e09f037cefe77f7af4413c7c0fa088). Adapted to the a10 +kernel-6.12.0-211.34.1.el10_2 tree: the hunk that adds the in-GHCB +ghcb_sa_len assignment had its trailing context adjusted because the a10 +setup_vmgexit_scratch() else-branch lacks the upstream "GHCB v2 requires the +scratch area to be within the GHCB" check. Applies on top of CVE-2026-63794 +(1102, sev_dbg_crypt) which touches a different function. Zero-fuzz. + +diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c +index 3ff71ce..d218568 100644 +--- a/arch/x86/kvm/svm/sev.c ++++ b/arch/x86/kvm/svm/sev.c +@@ -3521,7 +3521,7 @@ int pre_sev_run(struct vcpu_svm *svm, int cpu) + } + + #define GHCB_SCRATCH_AREA_LIMIT (16ULL * PAGE_SIZE) +-static int setup_vmgexit_scratch(struct vcpu_svm *svm, bool sync, u64 len) ++static int setup_vmgexit_scratch(struct vcpu_svm *svm, bool sync, u64 min_len) + { + struct vmcb_control_area *control = &svm->vmcb->control; + u64 ghcb_scratch_beg, ghcb_scratch_end; +@@ -3534,10 +3534,10 @@ static int setup_vmgexit_scratch(struct vcpu_svm *svm, bool sync, u64 len) + goto e_scratch; + } + +- scratch_gpa_end = scratch_gpa_beg + len; ++ scratch_gpa_end = scratch_gpa_beg + min_len; + if (scratch_gpa_end < scratch_gpa_beg) { + pr_err("vmgexit: scratch length (%#llx) not valid for scratch address (%#llx)\n", +- len, scratch_gpa_beg); ++ min_len, scratch_gpa_beg); + goto e_scratch; + } + +@@ -3561,21 +3561,23 @@ static int setup_vmgexit_scratch(struct vcpu_svm *svm, bool sync, u64 len) + + scratch_va = (void *)svm->sev_es.ghcb; + scratch_va += (scratch_gpa_beg - control->ghcb_gpa); ++ ++ svm->sev_es.ghcb_sa_len = ghcb_scratch_end - scratch_gpa_beg; + } else { + /* + * The guest memory must be read into a kernel buffer, so + * limit the size + */ +- if (len > GHCB_SCRATCH_AREA_LIMIT) { ++ if (min_len > GHCB_SCRATCH_AREA_LIMIT) { + pr_err("vmgexit: scratch area exceeds KVM limits (%#llx requested, %#llx limit)\n", +- len, GHCB_SCRATCH_AREA_LIMIT); ++ min_len, GHCB_SCRATCH_AREA_LIMIT); + goto e_scratch; + } +- scratch_va = kvzalloc(len, GFP_KERNEL_ACCOUNT); ++ scratch_va = kvzalloc(min_len, GFP_KERNEL_ACCOUNT); + if (!scratch_va) + return -ENOMEM; + +- if (kvm_read_guest(svm->vcpu.kvm, scratch_gpa_beg, scratch_va, len)) { ++ if (kvm_read_guest(svm->vcpu.kvm, scratch_gpa_beg, scratch_va, min_len)) { + /* Unable to copy scratch area from guest */ + pr_err("vmgexit: kvm_read_guest for scratch area failed\n"); + +@@ -3591,11 +3593,10 @@ static int setup_vmgexit_scratch(struct vcpu_svm *svm, bool sync, u64 len) + */ + svm->sev_es.ghcb_sa_sync = sync; + svm->sev_es.ghcb_sa_free = true; ++ svm->sev_es.ghcb_sa_len = min_len; + } + + svm->sev_es.ghcb_sa = scratch_va; +- svm->sev_es.ghcb_sa_len = len; +- + return 0; + + e_scratch: +-- +2.50.1 diff --git a/1122-usb-serial-cypress-m8-fix-memory-corruption-with-sma.patch b/1122-usb-serial-cypress-m8-fix-memory-corruption-with-sma.patch new file mode 100644 index 000000000..9d8077afb --- /dev/null +++ b/1122-usb-serial-cypress-m8-fix-memory-corruption-with-sma.patch @@ -0,0 +1,46 @@ +From 1ef25704bd3b625fd151c09feee459479f71ee64 Mon Sep 17 00:00:00 2001 +From: Johan Hovold +Date: Thu, 4 Jun 2026 10:36:36 +0200 +Subject: [PATCH] USB: serial: cypress_m8: fix memory corruption with small + endpoint + +commit e1a9d791fd66ab2431b9e6f6f835823809869047 upstream. + +Make sure that the interrupt-out endpoint max packet size is at least +eight bytes to avoid user-controlled slab corruption or NULL-pointer +dereference should a malicious device report a smaller size. + +Fixes: 3416eaa1f8f8 ("USB: cypress_m8: Packet format is separate from characteristic size") +Cc: stable@vger.kernel.org # 2.6.26 +Reviewed-by: Greg Kroah-Hartman +Signed-off-by: Johan Hovold +[ johan: adjust context for 6.18 ] +Signed-off-by: Johan Hovold +Signed-off-by: Sasha Levin + +CVE-2026-63956 + +[ Backported from linux-6.12.y commit 1ef25704bd3b625fd151c09feee459479f71ee64 (mainline e1a9d791fd66ab2431b9e6f6f835823809869047); applies cleanly to AlmaLinux 10 (kernel-6.12.0-211.34.1.el10_2), zero-fuzz. ] + +diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c +index eb47f35aab0c..905f6a560e04 100644 +--- a/drivers/usb/serial/cypress_m8.c ++++ b/drivers/usb/serial/cypress_m8.c +@@ -445,6 +445,14 @@ static int cypress_generic_port_probe(struct usb_serial_port *port) + return -ENODEV; + } + ++ /* ++ * The buffer must be large enough for the one or two-byte header (and ++ * following data), but assume anything smaller than eight bytes is ++ * broken. ++ */ ++ if (port->interrupt_out_size < 8) ++ return -EINVAL; ++ + priv = kzalloc(sizeof(struct cypress_private), GFP_KERNEL); + if (!priv) + return -ENOMEM; +-- +2.50.1 (Apple Git-155) + diff --git a/1123-tunnels-load-network-headers-after-skb-cow-in.patch b/1123-tunnels-load-network-headers-after-skb-cow-in.patch new file mode 100644 index 000000000..6253fd567 --- /dev/null +++ b/1123-tunnels-load-network-headers-after-skb-cow-in.patch @@ -0,0 +1,86 @@ +From 50750d86a2e5266aba0c295483b3397843198b11 Mon Sep 17 00:00:00 2001 +From: Eric Dumazet +Date: Mon, 25 May 2026 20:13:35 +0000 +Subject: [PATCH] tunnels: load network headers after skb_cow() in + iptunnel_pmtud_build_icmp[v6]() + +[ Upstream commit b4bc94353050b1fa7b702bd4c6600710dd926cff ] + +Sashiko found that iptunnel_pmtud_build_icmp() and +iptunnel_pmtud_build_icmpv6() were caching ip_hdr() and ipv6_hdr() +before an skb_cow() call which can reallocate skb->head. + +Fix this possible UAF by initializing the local variables +after the skb_cow() call. + +Remove skb_reset_network_header() calls which were not needed. + +Fixes: 4cb47a8644cc ("tunnels: PMTU discovery support for directly bridged IP packets") +Signed-off-by: Eric Dumazet +Reviewed-by: Stefano Brivio +Link: https://patch.msgid.link/20260525201335.2361845-1-edumazet@google.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin + +CVE-2026-63994 + +[ Backported from linux-6.12.y commit 50750d86a2e5266aba0c295483b3397843198b11 (mainline b4bc94353050b1fa7b702bd4c6600710dd926cff); applies cleanly to AlmaLinux 10 (kernel-6.12.0-211.34.1.el10_2), zero-fuzz. ] + +diff --git a/net/ipv4/ip_tunnel_core.c b/net/ipv4/ip_tunnel_core.c +index 507f2f9ec400..cf496644d3df 100644 +--- a/net/ipv4/ip_tunnel_core.c ++++ b/net/ipv4/ip_tunnel_core.c +@@ -210,7 +210,7 @@ EXPORT_SYMBOL_GPL(iptunnel_handle_offloads); + */ + static int iptunnel_pmtud_build_icmp(struct sk_buff *skb, int mtu) + { +- const struct iphdr *iph = ip_hdr(skb); ++ const struct iphdr *iph; + struct icmphdr *icmph; + struct iphdr *niph; + struct ethhdr eh; +@@ -224,7 +224,6 @@ static int iptunnel_pmtud_build_icmp(struct sk_buff *skb, int mtu) + + skb_copy_bits(skb, skb_mac_offset(skb), &eh, ETH_HLEN); + pskb_pull(skb, ETH_HLEN); +- skb_reset_network_header(skb); + + err = pskb_trim(skb, 576 - sizeof(*niph) - sizeof(*icmph)); + if (err) +@@ -234,7 +233,7 @@ static int iptunnel_pmtud_build_icmp(struct sk_buff *skb, int mtu) + err = skb_cow(skb, sizeof(*niph) + sizeof(*icmph) + ETH_HLEN); + if (err) + return err; +- ++ iph = ip_hdr(skb); + icmph = skb_push(skb, sizeof(*icmph)); + *icmph = (struct icmphdr) { + .type = ICMP_DEST_UNREACH, +@@ -306,7 +305,7 @@ static int iptunnel_pmtud_check_icmp(struct sk_buff *skb, int mtu) + */ + static int iptunnel_pmtud_build_icmpv6(struct sk_buff *skb, int mtu) + { +- const struct ipv6hdr *ip6h = ipv6_hdr(skb); ++ const struct ipv6hdr *ip6h; + struct icmp6hdr *icmp6h; + struct ipv6hdr *nip6h; + struct ethhdr eh; +@@ -321,7 +320,6 @@ static int iptunnel_pmtud_build_icmpv6(struct sk_buff *skb, int mtu) + + skb_copy_bits(skb, skb_mac_offset(skb), &eh, ETH_HLEN); + pskb_pull(skb, ETH_HLEN); +- skb_reset_network_header(skb); + + err = pskb_trim(skb, IPV6_MIN_MTU - sizeof(*nip6h) - sizeof(*icmp6h)); + if (err) +@@ -332,6 +330,7 @@ static int iptunnel_pmtud_build_icmpv6(struct sk_buff *skb, int mtu) + if (err) + return err; + ++ ip6h = ipv6_hdr(skb); + icmp6h = skb_push(skb, sizeof(*icmp6h)); + *icmp6h = (struct icmp6hdr) { + .icmp6_type = ICMPV6_PKT_TOOBIG, +-- +2.50.1 (Apple Git-155) + diff --git a/1124-blk-mq-pop-cached-request-if-it-is-usable.patch b/1124-blk-mq-pop-cached-request-if-it-is-usable.patch new file mode 100644 index 000000000..99d0f81e0 --- /dev/null +++ b/1124-blk-mq-pop-cached-request-if-it-is-usable.patch @@ -0,0 +1,115 @@ +From dc278e9bf2b9513a763353e6b9cc21e0f532954e Mon Sep 17 00:00:00 2001 +From: Keith Busch +Date: Thu, 21 May 2026 12:02:53 -0700 +Subject: [PATCH] blk-mq: pop cached request if it is usable + +When submitting a bio to blk-mq, if the task should sleep after peeking +a cached request, but before it pops it, the plug flushes and calls +blk_mq_free_plug_rqs, freeing the cached_rqs. This creates a +use-after-free bug. Fix this by popping the cached request before any +possible blocking calls if it is suitable for use. + +Popping this request first holds a queue reference, so avoid any +serialization races with queue freezes and can safely proceed with +dispatching that request to the driver. This potentially increases a +timing window from when a driver wants to freeze its queue to when +requests stop being dispatched. That scenario is off the fast path +though, and drivers need to appropriately handle requests during a +freeze request anyway. + +The downside is the popped element needs to be individually freed when +we performed a bio plug merge. The cached request would have had to be +freed later anyway, but this patch does it inline with building the plug +list instead of after flushing it. + +Fixes: b0077e269f6c1 ("blk-mq: make sure active queue usage is held for bio_integrity_prep()") +Fixes: 7b4f36cd22a65 ("block: ensure we hold a queue reference when using queue limits") +Signed-off-by: Keith Busch +Link: https://patch.msgid.link/20260521190253.242065-1-kbusch@meta.com +Signed-off-by: Jens Axboe + +CVE-2026-64017 + +[ Backported from mainline commit dc278e9bf2b9513a763353e6b9cc21e0f532954e; applies cleanly to AlmaLinux 10 (kernel-6.12.0-211.34.1.el10_2), zero-fuzz. ] + +diff --git a/block/blk-mq.c b/block/blk-mq.c +index d0c37daf568f..28c2d931e75e 100644 +--- a/block/blk-mq.c ++++ b/block/blk-mq.c +@@ -3077,7 +3077,7 @@ static struct request *blk_mq_get_new_requests(struct request_queue *q, + /* + * Check if there is a suitable cached request and return it. + */ +-static struct request *blk_mq_peek_cached_request(struct blk_plug *plug, ++static struct request *blk_mq_get_cached_request(struct blk_plug *plug, + struct request_queue *q, blk_opf_t opf) + { + enum hctx_type type = blk_mq_get_hctx_type(opf); +@@ -3093,27 +3093,10 @@ static struct request *blk_mq_peek_cached_request(struct blk_plug *plug, + return NULL; + if (op_is_flush(rq->cmd_flags) != op_is_flush(opf)) + return NULL; ++ rq_list_pop(&plug->cached_rqs); + return rq; + } + +-static void blk_mq_use_cached_rq(struct request *rq, struct blk_plug *plug, +- struct bio *bio) +-{ +- if (rq_list_pop(&plug->cached_rqs) != rq) +- WARN_ON_ONCE(1); +- +- /* +- * If any qos ->throttle() end up blocking, we will have flushed the +- * plug and hence killed the cached_rq list as well. Pop this entry +- * before we throttle. +- */ +- rq_qos_throttle(rq->q, bio); +- +- blk_mq_rq_time_init(rq, blk_time_get_ns()); +- rq->cmd_flags = bio->bi_opf; +- INIT_LIST_HEAD(&rq->queuelist); +-} +- + static bool bio_unaligned(const struct bio *bio, struct request_queue *q) + { + unsigned int bs_mask = queue_logical_block_size(q) - 1; +@@ -3152,7 +3135,7 @@ void blk_mq_submit_bio(struct bio *bio) + /* + * If the plug has a cached request for this queue, try to use it. + */ +- rq = blk_mq_peek_cached_request(plug, q, bio->bi_opf); ++ rq = blk_mq_get_cached_request(plug, q, bio->bi_opf); + + /* + * A BIO that was released from a zone write plug has already been +@@ -3211,7 +3194,10 @@ void blk_mq_submit_bio(struct bio *bio) + + new_request: + if (rq) { +- blk_mq_use_cached_rq(rq, plug, bio); ++ rq_qos_throttle(rq->q, bio); ++ blk_mq_rq_time_init(rq, blk_time_get_ns()); ++ rq->cmd_flags = bio->bi_opf; ++ INIT_LIST_HEAD(&rq->queuelist); + } else { + rq = blk_mq_get_new_requests(q, plug, bio); + if (unlikely(!rq)) { +@@ -3257,12 +3243,10 @@ void blk_mq_submit_bio(struct bio *bio) + return; + + queue_exit: +- /* +- * Don't drop the queue reference if we were trying to use a cached +- * request and thus didn't acquire one. +- */ + if (!rq) + blk_queue_exit(q); ++ else ++ blk_mq_free_request(rq); + } + + #ifdef CONFIG_BLK_MQ_STACKING +-- +2.50.1 (Apple Git-155) + diff --git a/1125-rxrpc-fix-data-decrypt-vs-splice-by-copying-data-to.patch b/1125-rxrpc-fix-data-decrypt-vs-splice-by-copying-data-to.patch new file mode 100644 index 000000000..03753614f --- /dev/null +++ b/1125-rxrpc-fix-data-decrypt-vs-splice-by-copying-data-to.patch @@ -0,0 +1,654 @@ +From b94a6ccbaf1104dd980150a65fdeb2f69d17d2f5 Mon Sep 17 00:00:00 2001 +From: David Howells +Date: Fri, 29 May 2026 17:42:07 -0400 +Subject: [PATCH] rxrpc: Fix DATA decrypt vs splice() by copying data to buffer + in recvmsg + +[ Upstream commit d2bc90cf6c75cb96d2ce549be6c35efa3099d25b ] + +This improves the fix for CVE-2026-43500. + +Fix the pagecache corruption from in-place decryption of a DATA packet +transmitted locally by splice() by getting rid of the packet sharing in the +I/O thread and unconditionally extracting the packet content into a bounce +buffer in which the buffer is decrypted. recvmsg() (or the kernel +equivalent) then copies the data from the bounce buffer to the destination +buffer. The sk_buff then remains unmodified. + +This has an additional advantage in that the packet is then arranged in the +buffer with the correct alignment required for the crypto algorithms to +process directly. The performance of the crypto does seem to be a little +faster and, surprisingly, the unencrypted performance doesn't seem to +change much - possibly due to removing complexity from the I/O thread. + +Yet another advantage is that the I/O thread doesn't have to copy packets +which would slow down packet distribution, ACK generation, etc.. + +The buffer belongs to the call and is allocated initially at 2K, +sufficiently large to hold a whole jumbo subpacket, but the buffer will be +increased in size if needed. However, to take this work, MSG_PEEK may +cause a later packet to be decrypted into the buffer, in which case the +earlier one will need re-decrypting for a subsequent recvmsg(). + +Note that rx_pkt_offset may legitimately see 0 as a valid offset now, so +switch to using USHRT_MAX to indicate an invalid offset. + +Note also that I would generally prefer to replace the buffers of the +current sk_buff with a new kmalloc'd buffer of the right size, ditching the +old data and frags as this makes the handling of MSG_PEEK easier and +removes the re-decryption issue, but this looks like quite a complicated +thing to achieve. skb_morph() looks half way to what I want, but I don't +want to have to allocate a new sk_buff. + +Fixes: d0d5c0cd1e71 ("rxrpc: Use skb_unshare() rather than skb_cow_data()") +Reported-by: Hyunwoo Kim +Closes: https://lore.kernel.org/r/afKV2zGR6rrelPC7@v4bel/ +Signed-off-by: David Howells +cc: Simon Horman +cc: Jiayuan Chen +cc: linux-afs@lists.infradead.org +Reviewed-by: Jeffrey Altman +Tested-by: Marc Dionne +Link: https://patch.msgid.link/20260515230516.2718212-3-dhowells@redhat.com +Signed-off-by: Jakub Kicinski +Stable-dep-of: 8bfab4b6ffc2 ("rxrpc: Fix RESPONSE packet verification to extract skb to a linear buffer") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman + +CVE-2026-64026 + +Backported from linux-6.12.y commit b94a6ccbaf1104dd980150a65fdeb2f69d17d2f5 +(mainline d2bc90cf6c75cb96d2ce549be6c35efa3099d25b). + +Adaptations for the a10 kernel-6.12.0-211.34.1.el10_2 tree: + - rxgk: The 6.12.y stable commit touches only 6 files because the yfs-rxgk + (GSSAPI) security class does not exist in 6.12.y stable. a10 DOES ship a + functional rxgk security class, so the rxgk.c and rxgk_common.h hunks from + the mainline commit are included as well (otherwise rxgk DATA would be read + undecrypted from the bounce buffer and remain vulnerable to the splice + corruption). rxgk_verify_packet_encrypted() is adapted to a10, which lacks + the upstream crypto_krb5_check_data_len() pre-check (separate commit). + - net/rxrpc/call_event.c: a10 carries the newer rx_queue batching rework, so + the packet-unshare block that this fix removes has a different shape + (skb_cloned() only, rxrpc_skb_put_call_rx); it is collapsed to a plain + rxrpc_input_call_packet(call, skb) as upstream does. + - net/rxrpc/rxkad.c: a10's rxkad_verify_packet_2() lacks the upstream + round_down() length alignment and the crypto_skcipher_decrypt() return-value + check (separate commits); those are preserved as-is (not introduced here). + - net/rxrpc/recvmsg.c: a10 lacks the upstream kdebug("verify = %d") line; + preserved as-is. + - net/rxrpc/ar-internal.h, call_object.c: context differs only by a10's extra + rx_queue field/init and the rxrpc_cleanup_rx_buffers() rename. + +Vulnerable pre-image confirmed present in a10 (in-place skb decryption via +skb_to_sgvec in rxkad/rxgk, RXRPC_RX_VERIFIED, rx_pkt_offset==0 recvmsg path, +call_event unshare copy). Applies zero-fuzz on the a10 tree. + +diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h +index 2baa99b..a08e45c 100644 +--- a/net/rxrpc/ar-internal.h ++++ b/net/rxrpc/ar-internal.h +@@ -213,8 +213,6 @@ struct rxrpc_skb_priv { + struct { + u16 offset; /* Offset of data */ + u16 len; /* Length of data */ +- u8 flags; +-#define RXRPC_RX_VERIFIED 0x01 + }; + struct { + rxrpc_seq_t first_ack; /* First packet in acks table */ +@@ -774,6 +772,11 @@ struct rxrpc_call { + struct sk_buff_head recvmsg_queue; /* Queue of packets ready for recvmsg() */ + struct sk_buff_head rx_queue; /* Queue of packets for this call to receive */ + struct sk_buff_head rx_oos_queue; /* Queue of out of sequence packets */ ++ void *rx_dec_buffer; /* Decryption buffer */ ++ unsigned short rx_dec_bsize; /* rx_dec_buffer size */ ++ unsigned short rx_dec_offset; /* Decrypted packet data offset */ ++ unsigned short rx_dec_len; /* Decrypted packet data len */ ++ rxrpc_seq_t rx_dec_seq; /* Packet in decryption buffer */ + + rxrpc_seq_t rx_highest_seq; /* Higest sequence number received */ + rxrpc_seq_t rx_consumed; /* Highest packet consumed */ +diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c +index fdd6832..fec59d9 100644 +--- a/net/rxrpc/call_event.c ++++ b/net/rxrpc/call_event.c +@@ -332,25 +332,7 @@ bool rxrpc_input_call_event(struct rxrpc_call *call) + + saw_ack |= sp->hdr.type == RXRPC_PACKET_TYPE_ACK; + +- if (sp->hdr.type == RXRPC_PACKET_TYPE_DATA && +- sp->hdr.securityIndex != 0 && +- skb_cloned(skb)) { +- /* Unshare the packet so that it can be +- * modified by in-place decryption. +- */ +- struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC); +- +- if (nskb) { +- rxrpc_new_skb(nskb, rxrpc_skb_new_unshared); +- rxrpc_input_call_packet(call, nskb); +- rxrpc_free_skb(nskb, rxrpc_skb_put_call_rx); +- } else { +- /* OOM - Drop the packet. */ +- rxrpc_see_skb(skb, rxrpc_skb_see_unshare_nomem); +- } +- } else { +- rxrpc_input_call_packet(call, skb); +- } ++ rxrpc_input_call_packet(call, skb); + rxrpc_free_skb(skb, rxrpc_skb_put_call_rx); + did_receive = true; + } +diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c +index 918f41d..ffe0a89 100644 +--- a/net/rxrpc/call_object.c ++++ b/net/rxrpc/call_object.c +@@ -152,6 +152,7 @@ struct rxrpc_call *rxrpc_alloc_call(struct rxrpc_sock *rx, gfp_t gfp, + spin_lock_init(&call->notify_lock); + refcount_set(&call->ref, 1); + call->debug_id = debug_id; ++ call->rx_pkt_offset = USHRT_MAX; + call->tx_total_len = -1; + call->tx_jumbo_max = 1; + call->next_rx_timo = 20 * HZ; +@@ -553,6 +554,7 @@ static void rxrpc_cleanup_rx_buffers(struct rxrpc_call *call) + rxrpc_purge_queue(&call->recvmsg_queue); + rxrpc_purge_queue(&call->rx_queue); + rxrpc_purge_queue(&call->rx_oos_queue); ++ kfree(call->rx_dec_buffer); + } + + /* +diff --git a/net/rxrpc/insecure.c b/net/rxrpc/insecure.c +index 0a260df..7a26c60 100644 +--- a/net/rxrpc/insecure.c ++++ b/net/rxrpc/insecure.c +@@ -32,9 +32,6 @@ static int none_secure_packet(struct rxrpc_call *call, struct rxrpc_txbuf *txb) + + static int none_verify_packet(struct rxrpc_call *call, struct sk_buff *skb) + { +- struct rxrpc_skb_priv *sp = rxrpc_skb(skb); +- +- sp->flags |= RXRPC_RX_VERIFIED; + return 0; + } + +diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c +index 7fa7e77..fc01ee3 100644 +--- a/net/rxrpc/recvmsg.c ++++ b/net/rxrpc/recvmsg.c +@@ -147,15 +147,52 @@ static void rxrpc_rotate_rx_window(struct rxrpc_call *call) + } + + /* +- * Decrypt and verify a DATA packet. ++ * Decrypt and verify a DATA packet. The content of the packet is pulled out ++ * into a flat buffer rather than decrypting in place in the skbuff. This also ++ * has the advantage of aligning the buffer correctly for the crypto routines. ++ * ++ * We keep track of the sequence number of the packet currently decrypted into ++ * the buffer in ->rx_dec_seq. If MSG_PEEK is used and steps onto a new ++ * packet, subsequent recvmsg() calls will have to go back and re-decrypt the ++ * current packet. + */ + static int rxrpc_verify_data(struct rxrpc_call *call, struct sk_buff *skb) + { + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); ++ int ret; + +- if (sp->flags & RXRPC_RX_VERIFIED) +- return 0; +- return call->security->verify_packet(call, skb); ++ if (sp->len > call->rx_dec_bsize) { ++ /* Make sure we can hold a 1412-byte jumbo subpacket and make ++ * sure that the buffer size is aligned to a crypto blocksize. ++ */ ++ size_t size = clamp(round_up(sp->len, 32), 2048, 65535); ++ void *buffer = krealloc(call->rx_dec_buffer, size, GFP_NOFS); ++ ++ if (!buffer) ++ return -ENOMEM; ++ call->rx_dec_buffer = buffer; ++ call->rx_dec_bsize = size; ++ } ++ ++ ret = -EFAULT; ++ if (skb_copy_bits(skb, sp->offset, call->rx_dec_buffer, sp->len) < 0) ++ goto err; ++ ++ call->rx_dec_offset = 0; ++ call->rx_dec_len = sp->len; ++ call->rx_dec_seq = sp->hdr.seq; ++ ret = call->security->verify_packet(call, skb); ++ if (ret < 0) ++ goto err; ++ return 0; ++ ++err: ++ kfree(call->rx_dec_buffer); ++ call->rx_dec_buffer = NULL; ++ call->rx_dec_bsize = 0; ++ call->rx_dec_offset = 0; ++ call->rx_dec_len = 0; ++ return ret; + } + + /* +@@ -283,16 +320,21 @@ static int rxrpc_recvmsg_data(struct socket *sock, struct rxrpc_call *call, + if (msg) + sock_recv_timestamp(msg, sock->sk, skb); + +- if (rx_pkt_offset == 0) { ++ if (call->rx_dec_seq != sp->hdr.seq || ++ !call->rx_dec_buffer) { + ret2 = rxrpc_verify_data(call, skb); + trace_rxrpc_recvdata(call, rxrpc_recvmsg_next, seq, +- sp->offset, sp->len, ret2); ++ call->rx_dec_offset, ++ call->rx_dec_len, ret2); + if (ret2 < 0) { + ret = ret2; + goto out; + } +- rx_pkt_offset = sp->offset; +- rx_pkt_len = sp->len; ++ } ++ ++ if (rx_pkt_offset == USHRT_MAX) { ++ rx_pkt_offset = call->rx_dec_offset; ++ rx_pkt_len = call->rx_dec_len; + } else { + trace_rxrpc_recvdata(call, rxrpc_recvmsg_cont, seq, + rx_pkt_offset, rx_pkt_len, 0); +@@ -304,10 +346,10 @@ static int rxrpc_recvmsg_data(struct socket *sock, struct rxrpc_call *call, + if (copy > remain) + copy = remain; + if (copy > 0) { +- ret2 = skb_copy_datagram_iter(skb, rx_pkt_offset, iter, +- copy); +- if (ret2 < 0) { +- ret = ret2; ++ ret2 = copy_to_iter(call->rx_dec_buffer + rx_pkt_offset, ++ copy, iter); ++ if (ret2 != copy) { ++ ret = -EFAULT; + goto out; + } + +@@ -328,7 +370,7 @@ static int rxrpc_recvmsg_data(struct socket *sock, struct rxrpc_call *call, + /* The whole packet has been transferred. */ + if (sp->hdr.flags & RXRPC_LAST_PACKET) + ret = 1; +- rx_pkt_offset = 0; ++ rx_pkt_offset = USHRT_MAX; + rx_pkt_len = 0; + + skb = skb_peek_next(skb, &call->recvmsg_queue); +diff --git a/net/rxrpc/rxgk.c b/net/rxrpc/rxgk.c +index 8d17b49..b9d2da9 100644 +--- a/net/rxrpc/rxgk.c ++++ b/net/rxrpc/rxgk.c +@@ -473,8 +473,9 @@ static int rxgk_verify_packet_integrity(struct rxrpc_call *call, + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + struct rxgk_header *hdr; + struct krb5_buffer metadata; +- unsigned int offset = sp->offset, len = sp->len; ++ unsigned int len = call->rx_dec_len; + size_t data_offset = 0, data_len = len; ++ void *data = call->rx_dec_buffer, *p = data; + u32 ac = 0; + int ret = -ENOMEM; + +@@ -496,16 +497,15 @@ static int rxgk_verify_packet_integrity(struct rxrpc_call *call, + + metadata.len = sizeof(*hdr); + metadata.data = hdr; +- ret = rxgk_verify_mic_skb(gk->krb5, gk->rx_Kc, &metadata, +- skb, &offset, &len, &ac); ++ ret = rxgk_verify_mic(gk->krb5, gk->rx_Kc, &metadata, &p, &len, &ac); + kfree(hdr); + if (ret < 0) { + if (ret != -ENOMEM) + rxrpc_abort_eproto(call, skb, ac, + rxgk_abort_1_verify_mic_eproto); + } else { +- sp->offset = offset; +- sp->len = len; ++ call->rx_dec_offset = p - data; ++ call->rx_dec_len = len; + } + + put_gk: +@@ -522,49 +522,46 @@ static int rxgk_verify_packet_encrypted(struct rxrpc_call *call, + struct sk_buff *skb) + { + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); +- struct rxgk_header hdr; +- unsigned int offset = sp->offset, len = sp->len; ++ struct rxgk_header *hdr; ++ unsigned int offset = 0, len = call->rx_dec_len; ++ void *data = call->rx_dec_buffer, *p = data; + int ret; + u32 ac = 0; + + _enter(""); + +- ret = rxgk_decrypt_skb(gk->krb5, gk->rx_enc, skb, &offset, &len, &ac); ++ ret = rxgk_decrypt(gk->krb5, gk->rx_enc, &p, &len, &ac); + if (ret < 0) { + if (ret != -ENOMEM) + rxrpc_abort_eproto(call, skb, ac, rxgk_abort_2_decrypt_eproto); + goto error; + } ++ offset = p - data; + +- if (len < sizeof(hdr)) { ++ if (len < sizeof(*hdr)) { + ret = rxrpc_abort_eproto(call, skb, RXGK_PACKETSHORT, + rxgk_abort_2_short_header); + goto error; + } + + /* Extract the header from the skb */ +- ret = skb_copy_bits(skb, offset, &hdr, sizeof(hdr)); +- if (ret < 0) { +- ret = rxrpc_abort_eproto(call, skb, RXGK_PACKETSHORT, +- rxgk_abort_2_short_encdata); +- goto error; +- } +- offset += sizeof(hdr); +- len -= sizeof(hdr); +- +- if (ntohl(hdr.epoch) != call->conn->proto.epoch || +- ntohl(hdr.cid) != call->cid || +- ntohl(hdr.call_number) != call->call_id || +- ntohl(hdr.seq) != sp->hdr.seq || +- ntohl(hdr.sec_index) != call->security_ix || +- ntohl(hdr.data_len) > len) { ++ hdr = data + offset; ++ offset += sizeof(*hdr); ++ len -= sizeof(*hdr); ++ ++ if (ntohl(hdr->epoch) != call->conn->proto.epoch || ++ ntohl(hdr->cid) != call->cid || ++ ntohl(hdr->call_number) != call->call_id || ++ ntohl(hdr->seq) != sp->hdr.seq || ++ ntohl(hdr->sec_index) != call->security_ix || ++ ntohl(hdr->data_len) > len) { + ret = rxrpc_abort_eproto(call, skb, RXGK_SEALEDINCON, + rxgk_abort_2_short_data); + goto error; + } + +- sp->offset = offset; +- sp->len = ntohl(hdr.data_len); ++ call->rx_dec_offset = offset; ++ call->rx_dec_len = ntohl(hdr->data_len); + ret = 0; + error: + rxgk_put(gk); +diff --git a/net/rxrpc/rxgk_common.h b/net/rxrpc/rxgk_common.h +index 80164d8..bae8b98 100644 +--- a/net/rxrpc/rxgk_common.h ++++ b/net/rxrpc/rxgk_common.h +@@ -104,6 +104,49 @@ int rxgk_decrypt_skb(const struct krb5_enctype *krb5, + return ret; + } + ++/* ++ * Apply decryption and checksumming functions a flat data buffer. The data ++ * point and length are updated to reflect the actual content of the encrypted ++ * region. ++ */ ++static inline int rxgk_decrypt(const struct krb5_enctype *krb5, ++ struct crypto_aead *aead, ++ void **_data, unsigned int *_len, ++ int *_error_code) ++{ ++ struct scatterlist sg[1]; ++ size_t offset = 0, len = *_len; ++ int ret; ++ ++ sg_init_one(sg, *_data, len); ++ ++ ret = crypto_krb5_decrypt(krb5, aead, sg, 1, &offset, &len); ++ switch (ret) { ++ case 0: ++ if (offset & 3) { ++ *_error_code = RXGK_INCONSISTENCY; ++ ret = -EPROTO; ++ break; ++ } ++ *_data += offset; ++ *_len = len; ++ break; ++ case -EBADMSG: /* Checksum mismatch. */ ++ case -EPROTO: ++ *_error_code = RXGK_SEALEDINCON; ++ break; ++ case -EMSGSIZE: ++ *_error_code = RXGK_PACKETSHORT; ++ break; ++ case -ENOPKG: /* Would prefer RXGK_BADETYPE, but not available for YFS. */ ++ default: ++ *_error_code = RXGK_INCONSISTENCY; ++ break; ++ } ++ ++ return ret; ++} ++ + /* + * Check the MIC on a region of an skbuff. The offset and length are updated + * to reflect the actual content of the secure region. +@@ -147,3 +190,42 @@ int rxgk_verify_mic_skb(const struct krb5_enctype *krb5, + + return ret; + } ++ ++/* ++ * Check the MIC on a flat buffer. The data pointer and length are updated to ++ * reflect the actual content of the secure region. ++ */ ++static inline ++int rxgk_verify_mic(const struct krb5_enctype *krb5, ++ struct crypto_shash *shash, ++ const struct krb5_buffer *metadata, ++ void **_data, unsigned int *_len, ++ u32 *_error_code) ++{ ++ struct scatterlist sg[1]; ++ size_t offset = 0, len = *_len; ++ int ret; ++ ++ sg_init_one(sg, *_data, len); ++ ++ ret = crypto_krb5_verify_mic(krb5, shash, metadata, sg, 1, &offset, &len); ++ switch (ret) { ++ case 0: ++ *_data += offset; ++ *_len = len; ++ break; ++ case -EBADMSG: /* Checksum mismatch */ ++ case -EPROTO: ++ *_error_code = RXGK_SEALEDINCON; ++ break; ++ case -EMSGSIZE: ++ *_error_code = RXGK_PACKETSHORT; ++ break; ++ case -ENOPKG: /* Would prefer RXGK_BADETYPE, but not available for YFS. */ ++ default: ++ *_error_code = RXGK_INCONSISTENCY; ++ break; ++ } ++ ++ return ret; ++} +diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c +index 3657c06..cf780ba 100644 +--- a/net/rxrpc/rxkad.c ++++ b/net/rxrpc/rxkad.c +@@ -425,27 +425,24 @@ static int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb, + rxrpc_seq_t seq, + struct skcipher_request *req) + { +- struct rxkad_level1_hdr sechdr; ++ struct rxkad_level1_hdr *sechdr; + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + struct rxrpc_crypt iv; +- struct scatterlist sg[16]; +- u32 data_size, buf; ++ struct scatterlist sg[1]; ++ void *data = call->rx_dec_buffer; ++ u32 len = sp->len, data_size, buf; + u16 check; +- int ret; + + _enter(""); + +- if (sp->len < 8) ++ if (len < 8) + return rxrpc_abort_eproto(call, skb, RXKADSEALEDINCON, + rxkad_abort_1_short_header); + + /* Decrypt the skbuff in-place. TODO: We really want to decrypt + * directly into the target buffer. + */ +- sg_init_table(sg, ARRAY_SIZE(sg)); +- ret = skb_to_sgvec(skb, sg, sp->offset, 8); +- if (unlikely(ret < 0)) +- return ret; ++ sg_init_one(sg, data, len); + + /* start the decryption afresh */ + memset(&iv, 0, sizeof(iv)); +@@ -457,13 +454,11 @@ static int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb, + skcipher_request_zero(req); + + /* Extract the decrypted packet length */ +- if (skb_copy_bits(skb, sp->offset, &sechdr, sizeof(sechdr)) < 0) +- return rxrpc_abort_eproto(call, skb, RXKADDATALEN, +- rxkad_abort_1_short_encdata); +- sp->offset += sizeof(sechdr); +- sp->len -= sizeof(sechdr); ++ sechdr = data; ++ call->rx_dec_offset = sizeof(*sechdr); ++ len -= sizeof(*sechdr); + +- buf = ntohl(sechdr.data_size); ++ buf = ntohl(sechdr->data_size); + data_size = buf & 0xffff; + + check = buf >> 16; +@@ -472,10 +467,10 @@ static int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb, + if (check != 0) + return rxrpc_abort_eproto(call, skb, RXKADSEALEDINCON, + rxkad_abort_1_short_check); +- if (data_size > sp->len) ++ if (data_size > len) + return rxrpc_abort_eproto(call, skb, RXKADDATALEN, + rxkad_abort_1_short_data); +- sp->len = data_size; ++ call->rx_dec_len = data_size; + + _leave(" = 0 [dlen=%x]", data_size); + return 0; +@@ -489,40 +484,24 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb, + struct skcipher_request *req) + { + const struct rxrpc_key_token *token; +- struct rxkad_level2_hdr sechdr; ++ struct rxkad_level2_hdr *sechdr; + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + struct rxrpc_crypt iv; +- struct scatterlist _sg[4], *sg; +- u32 data_size, buf; ++ struct scatterlist sg[1]; ++ void *data = call->rx_dec_buffer; ++ u32 len = sp->len, data_size, buf; + u16 check; +- int nsg, ret; + +- _enter(",{%d}", sp->len); ++ _enter(",{%d}", len); + +- if (sp->len < 8) ++ if (len < 8) + return rxrpc_abort_eproto(call, skb, RXKADSEALEDINCON, + rxkad_abort_2_short_header); + +- /* Decrypt the skbuff in-place. TODO: We really want to decrypt +- * directly into the target buffer. ++ /* Decrypt in place in the call's decryption buffer. TODO: We really ++ * want to decrypt directly into the target buffer. + */ +- sg = _sg; +- nsg = skb_shinfo(skb)->nr_frags + 1; +- if (nsg <= 4) { +- nsg = 4; +- } else { +- sg = kmalloc_array(nsg, sizeof(*sg), GFP_NOIO); +- if (!sg) +- return -ENOMEM; +- } +- +- sg_init_table(sg, nsg); +- ret = skb_to_sgvec(skb, sg, sp->offset, sp->len); +- if (unlikely(ret < 0)) { +- if (sg != _sg) +- kfree(sg); +- return ret; +- } ++ sg_init_one(sg, data, len); + + /* decrypt from the session key */ + token = call->conn->key->payload.data[0]; +@@ -530,20 +509,16 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb, + + skcipher_request_set_sync_tfm(req, call->conn->rxkad.cipher); + skcipher_request_set_callback(req, 0, NULL, NULL); +- skcipher_request_set_crypt(req, sg, sg, sp->len, iv.x); ++ skcipher_request_set_crypt(req, sg, sg, len, iv.x); + crypto_skcipher_decrypt(req); + skcipher_request_zero(req); +- if (sg != _sg) +- kfree(sg); + + /* Extract the decrypted packet length */ +- if (skb_copy_bits(skb, sp->offset, &sechdr, sizeof(sechdr)) < 0) +- return rxrpc_abort_eproto(call, skb, RXKADDATALEN, +- rxkad_abort_2_short_len); +- sp->offset += sizeof(sechdr); +- sp->len -= sizeof(sechdr); ++ sechdr = data; ++ call->rx_dec_offset = sizeof(*sechdr); ++ len -= sizeof(*sechdr); + +- buf = ntohl(sechdr.data_size); ++ buf = ntohl(sechdr->data_size); + data_size = buf & 0xffff; + + check = buf >> 16; +@@ -553,17 +528,18 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb, + return rxrpc_abort_eproto(call, skb, RXKADSEALEDINCON, + rxkad_abort_2_short_check); + +- if (data_size > sp->len) ++ if (data_size > len) + return rxrpc_abort_eproto(call, skb, RXKADDATALEN, + rxkad_abort_2_short_data); + +- sp->len = data_size; ++ call->rx_dec_len = data_size; + _leave(" = 0 [dlen=%x]", data_size); + return 0; + } + + /* +- * Verify the security on a received packet and the subpackets therein. ++ * Verify the security on a received (sub)packet. If the packet needs ++ * modifying (e.g. decrypting), it must be copied. + */ + static int rxkad_verify_packet(struct rxrpc_call *call, struct sk_buff *skb) + { diff --git a/1126-wifi-mac80211-bounds-check-link-id-in-ieee80211-ml-e.patch b/1126-wifi-mac80211-bounds-check-link-id-in-ieee80211-ml-e.patch new file mode 100644 index 000000000..e5fea0ecc --- /dev/null +++ b/1126-wifi-mac80211-bounds-check-link-id-in-ieee80211-ml-e.patch @@ -0,0 +1,51 @@ +From f718506edd2d9c6a308ded9d13c632bf7b7d5a2c Mon Sep 17 00:00:00 2001 +From: Alexandru Hossu +Date: Fri, 15 May 2026 12:29:08 +0200 +Subject: [PATCH] wifi: mac80211: bounds-check link_id in ieee80211_ml_epcs + +IEEE80211_MLE_STA_EPCS_CONTROL_LINK_ID is 0x000f, so link_id extracted +from a PRIO_ACCESS ML element PER_STA_PROFILE subelement can be 0..15. +sdata->link[] has IEEE80211_MLD_MAX_NUM_LINKS (15) entries (indices 0..14), +making index 15 out-of-bounds. + +A connected WiFi 7 AP can trigger this by sending an EPCS Enable Response +action frame with a PER_STA_PROFILE subelement where link_id = 15. The +unsolicited-notification path (dialog_token = 0) is reachable any time +EPCS is already enabled, without any prior client request. + +sdata->link[15] reads into the first word of sdata->activate_links_work +(a wiphy_work whose embedded list_head is non-NULL after INIT_LIST_HEAD), +so the NULL check on the result does not catch the invalid access. The +garbage pointer is then passed to ieee80211_sta_wmm_params(), which +dereferences link->sdata and crashes the kernel. + +The same class of bug was fixed for ieee80211_ml_reconfiguration() by +commit 162d331d833d ("wifi: mac80211: bounds-check link_id in +ieee80211_ml_reconfiguration"). + +Fixes: de86c5f60839 ("wifi: mac80211: Add support for EPCS configuration") +Signed-off-by: Alexandru Hossu +Link: https://patch.msgid.link/20260515102908.1653088-1-hossu.alexandru@gmail.com +Signed-off-by: Johannes Berg + +CVE-2026-64030 + +[ Backported from mainline commit f718506edd2d9c6a308ded9d13c632bf7b7d5a2c; applies cleanly to AlmaLinux 10 (kernel-6.12.0-211.34.1.el10_2), zero-fuzz. ] + +diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c +index 0a0f27836d57..ca1d29daf019 100644 +--- a/net/mac80211/mlme.c ++++ b/net/mac80211/mlme.c +@@ -11232,6 +11232,9 @@ static void ieee80211_ml_epcs(struct ieee80211_sub_if_data *sdata, + control = get_unaligned_le16(pos); + link_id = control & IEEE80211_MLE_STA_EPCS_CONTROL_LINK_ID; + ++ if (link_id >= IEEE80211_MLD_MAX_NUM_LINKS) ++ continue; ++ + link = sdata_dereference(sdata->link[link_id], sdata); + if (!link) + continue; +-- +2.50.1 (Apple Git-155) + diff --git a/kernel.spec b/kernel.spec index 4b563d908..d140ae69a 100644 --- a/kernel.spec +++ b/kernel.spec @@ -176,13 +176,13 @@ Summary: The Linux kernel %define specrpmversion 6.12.0 %define specversion 6.12.0 %define patchversion 6.12 -%define pkgrelease 211.34.2 +%define pkgrelease 211.34.3 %define kversion 6 %define tarfile_release 6.12.0-211.34.1.el10_2 # This is needed to do merge window version magic %define patchlevel 12 # This allows pkg_release to have configurable %%{?dist} tag -%define specrelease 211.34.2%{?buildid}%{?dist} +%define specrelease 211.34.3%{?buildid}%{?dist} # This defines the kabi tarball version %define kabiversion 6.12.0-211.34.1.el10_2 @@ -1152,6 +1152,19 @@ Patch1109: 1109-netfs-fix-early-put-of-sink-folio-in-netfs-read-gaps.patch Patch1110: 1110-ixgbevf-fix-use-after-free-in-vepa-multicast-source-pru.patch Patch1111: 1111-ipv6-ioam-refresh-hdr-pointer-before-ioam6-event.patch Patch1112: 1112-i2c-stub-reject-i2c-block-transfers-with-invalid-length.patch +Patch1113: 1113-scsi-target-iscsi-validate-chap-r-length-before-base.patch +Patch1114: 1114-scsi-target-iscsi-bound-iscsi-encode-text-output-app.patch +Patch1115: 1115-scsi-target-iscsi-fix-crc-overread-and-double-free-i.patch +Patch1116: 1116-usb-serial-mxuport-fix-memory-corruption-with-small.patch +Patch1117: 1117-ip6-vti-use-ip6-tnl-net-in-vti6-changelink.patch +Patch1118: 1118-ipv6-exthdrs-refresh-nh-pointer-after-ipv6-hop-jumbo.patch +Patch1119: 1119-ipv6-exthdrs-refresh-nh-after-handling-hao-option.patch +Patch1120: 1120-kvm-sev-compute-the-correct-max-length-of-the-in-ghc.patch +Patch1122: 1122-usb-serial-cypress-m8-fix-memory-corruption-with-sma.patch +Patch1123: 1123-tunnels-load-network-headers-after-skb-cow-in.patch +Patch1124: 1124-blk-mq-pop-cached-request-if-it-is-usable.patch +Patch1125: 1125-rxrpc-fix-data-decrypt-vs-splice-by-copying-data-to.patch +Patch1126: 1126-wifi-mac80211-bounds-check-link-id-in-ieee80211-ml-e.patch # END OF PATCH DEFINITIONS %description @@ -2026,6 +2039,19 @@ ApplyPatch 1109-netfs-fix-early-put-of-sink-folio-in-netfs-read-gaps.patch ApplyPatch 1110-ixgbevf-fix-use-after-free-in-vepa-multicast-source-pru.patch ApplyPatch 1111-ipv6-ioam-refresh-hdr-pointer-before-ioam6-event.patch ApplyPatch 1112-i2c-stub-reject-i2c-block-transfers-with-invalid-length.patch +ApplyPatch 1113-scsi-target-iscsi-validate-chap-r-length-before-base.patch +ApplyPatch 1114-scsi-target-iscsi-bound-iscsi-encode-text-output-app.patch +ApplyPatch 1115-scsi-target-iscsi-fix-crc-overread-and-double-free-i.patch +ApplyPatch 1116-usb-serial-mxuport-fix-memory-corruption-with-small.patch +ApplyPatch 1117-ip6-vti-use-ip6-tnl-net-in-vti6-changelink.patch +ApplyPatch 1118-ipv6-exthdrs-refresh-nh-pointer-after-ipv6-hop-jumbo.patch +ApplyPatch 1119-ipv6-exthdrs-refresh-nh-after-handling-hao-option.patch +ApplyPatch 1120-kvm-sev-compute-the-correct-max-length-of-the-in-ghc.patch +ApplyPatch 1122-usb-serial-cypress-m8-fix-memory-corruption-with-sma.patch +ApplyPatch 1123-tunnels-load-network-headers-after-skb-cow-in.patch +ApplyPatch 1124-blk-mq-pop-cached-request-if-it-is-usable.patch +ApplyPatch 1125-rxrpc-fix-data-decrypt-vs-splice-by-copying-data-to.patch +ApplyPatch 1126-wifi-mac80211-bounds-check-link-id-in-ieee80211-ml-e.patch # END OF PATCH APPLICATIONS # Any further pre-build tree manipulations happen here. @@ -4537,6 +4563,21 @@ fi\ # # %changelog +* Wed Jul 22 2026 Andrew Lukoshko - 6.12.0-211.34.3 +- scsi: target: iscsi: Validate CHAP_R length before base64 decode (Alexandru Hossu) {CVE-2026-63886} +- scsi: target: iscsi: Bound iscsi_encode_text_output() appends to rsp_buf (Michael Bommarito) {CVE-2026-63887} +- scsi: target: iscsi: Fix CRC overread and double-free in iscsit_handle_text_cmd() (Michael Bommarito) {CVE-2026-63888} +- USB: serial: mxuport: fix memory corruption with small endpoint (Johan Hovold) {CVE-2026-63899} +- ip6: vti: Use ip6_tnl.net in vti6_changelink() (Kuniyuki Iwashima) {CVE-2026-63917} +- ipv6: exthdrs: refresh nh pointer after ipv6_hop_jumbo() (Justin Iurman) {CVE-2026-63924} +- ipv6: exthdrs: refresh nh after handling HAO option (Zhengchuan Liang) {CVE-2026-63922} +- KVM: SEV: Compute the correct max length of the in-GHCB scratch area (Sean Christopherson) {CVE-2026-63939} +- USB: serial: cypress_m8: fix memory corruption with small endpoint (Johan Hovold) {CVE-2026-63956} +- tunnels: load network headers after skb_cow() in iptunnel_pmtud_build_icmp[v6]() (Eric Dumazet) {CVE-2026-63994} +- blk-mq: pop cached request if it is usable (Keith Busch) {CVE-2026-64017} +- rxrpc: Fix DATA decrypt vs splice() by copying data to buffer in recvmsg (David Howells) {CVE-2026-64026} +- wifi: mac80211: bounds-check link_id in ieee80211_ml_epcs (Alexandru Hossu) {CVE-2026-64030} + * Tue Jul 21 2026 Andrew Lukoshko - 6.12.0-211.34.2 - NFSD: Fix SECINFO_NO_NAME decode error cleanup (Guannan Wang) {CVE-2026-53398} - nfsd: release layout stid on setlease failure (Chris Mason) {CVE-2026-53399}