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).
205 lines
7.5 KiB
Diff
205 lines
7.5 KiB
Diff
From 30bf335e8fe170322080ee001f05ca29c50680b3 Mon Sep 17 00:00:00 2001
|
|
From: Michael Bommarito <michael.bommarito@gmail.com>
|
|
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 <michael.bommarito@gmail.com>
|
|
Tested-by: John Garry <john.g.garry@oracle.com>
|
|
Reviewed-by: John Garry <john.g.garry@oracle.com>
|
|
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
|
|
[ 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)
|
|
|