Linux SCSI subsystem development
 help / color / mirror / Atom feed
From: Bryam Vargas <hexlabsecurity@proton.me>
To: "Martin K . Petersen" <martin.petersen@oracle.com>
Cc: Mike Christie <michael.christie@oracle.com>,
	Maurizio Lombardi <mlombard@redhat.com>,
	John Garry <john.g.garry@oracle.com>,
	James Bottomley <James.Bottomley@HansenPartnership.com>,
	David Disseldorp <ddiss@suse.de>,
	linux-scsi@vger.kernel.org, target-devel@vger.kernel.org,
	linux-kernel@vger.kernel.org
Subject: [PATCH v3] scsi: target: fix iSCSI ISID use-after-free in REGISTER AND MOVE
Date: Wed, 10 Jun 2026 04:22:48 +0000	[thread overview]
Message-ID: <20260610042245.35473-1-hexlabsecurity@proton.me> (raw)

core_scsi3_emulate_pro_register_and_move() maps the PERSISTENT RESERVE OUT
parameter list with transport_kmap_data_sg() and parses the destination
TransportID with target_parse_pr_out_transport_id(). For an iSCSI
TransportID (FORMAT CODE 01b), iscsi_parse_pr_out_transport_id() returns
the ISID in iport_ptr as a raw pointer into that mapped buffer.

The function then unmaps the buffer with transport_kunmap_data_sg() before
dereferencing iport_ptr in strcmp(), __core_scsi3_locate_pr_reg() and
core_scsi3_alloc_registration(). When the parameter list spans more than
one page (PARAMETER LIST LENGTH > 4096), transport_kmap_data_sg() uses
vmap() and transport_kunmap_data_sg() does vunmap(), so the kernel virtual
address backing iport_ptr is torn down and every subsequent dereference is
a use-after-free read of the unmapped region.

Keep the parameter list mapped until iport_ptr is no longer needed: drop
the early transport_kunmap_data_sg() and unmap once on the success path,
right before returning. The error paths already unmap through the existing
"if (buf) transport_kunmap_data_sg(cmd)" at the out: label, which now runs
on every post-map error exit because buf is no longer cleared early. Only
reads of the mapping happen while spinlocks are held; the map and unmap
calls remain outside any lock. The sibling caller
core_scsi3_decode_spec_i_port() already uses the buffer before unmapping it
and is left unchanged.

Fixes: 4949314c7283 ("target: Allow control CDBs with data > 1 page")
Cc: stable@vger.kernel.org
Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me>
---
v3 (review of v2 by John Garry and James Bottomley):
  - Drop the parser-ownership approach. Rather than copy the ISID into an
    allocation that both callers must kfree() (v2), keep the PR-OUT
    parameter list mapped across the iport_ptr dereferences and move the
    single unmap to the success path. No allocation, no kfree, and
    target_core_fabric_lib.c / core_scsi3_decode_spec_i_port() are
    unchanged. This is the form John Garry asked for ("keep the mapping in
    place for longer, until the out: label") and removes the multiple
    kfree() paths v2 added.
  - Re: holding the mapping while spinlocks are held (raised on v2): only
    reads of the mapping occur under dev_reservation_lock; the kmap/kunmap
    calls are all outside any lock, so there is no sleep-in-atomic concern.

v2: https://lore.kernel.org/linux-scsi/20260609005858.17504-1-hexlabsecurity@proton.me/
v1: https://lore.kernel.org/linux-scsi/20260606015359.181724-1-hexlabsecurity@proton.me/

Class / impact: CWE-416 use-after-free (use-after-vunmap) in the LIO SCSI
target, reachable by an authenticated iSCSI initiator that is a current
Persistent Reservation registrant on the LUN. It sends PERSISTENT RESERVE
OUT / REGISTER AND MOVE with an iSCSI (FORMAT CODE 01b) TransportID and a
PARAMETER LIST LENGTH > 4096 so the parameter list spans >1 page and is
mapped with vmap(). After transport_kunmap_data_sg() vunmap()s that region,
the retained iport_ptr is dereferenced -> kernel read of an unmapped
vmalloc address (oops / DoS). Primarily a remotely reachable authenticated
denial of service. Present in all maintained trees since 4949314c7283
(v3.3, 2012), which introduced the multi-page vmap() path. Verified at
mainline v7.1-rc6 and stable v6.12.92.

Reproducer (authenticated iSCSI initiator, current PR reservation holder):
  1. PERSISTENT RESERVE OUT / REGISTER a key from the iSCSI nexus.
  2. PERSISTENT RESERVE OUT / REGISTER AND MOVE, FORMAT CODE 01b TransportID
     (IQN + ",i,0x" + 12-char ISID), RELATIVE TARGET PORT IDENTIFIER of an
     existing target port, with PARAMETER LIST LENGTH = 8192 (two pages ->
     vmap()/vunmap()), the inner ADDITIONAL LENGTH set so tid_len + 24 ==
     data_length, the remainder zero padding.

A/B verification (CONFIG_KASAN_VMALLOC=y, kasan.fault=report, x86-64,
6.12.90; same kernel for every arm; 64-bit and 32-bit initiator):
  - Without this patch (8192-byte, two-page request):
      BUG: KASAN: vmalloc-out-of-bounds in strcmp+0xa7/0xb0
        strcmp
        core_scsi3_emulate_pro_register_and_move [target_core]
        ? remove_vm_area
        target_scsi3_emulate_pr_out [target_core]
        __target_execute_cmd / iscsit_execute_cmd / iscsi_target_rx_thread
      followed by "unable to handle page fault" (PTE 0); the command never
      completes (the iSCSI rx kthread dies).
  - Control (128-byte, single-page request): no report (kunmap is a no-op
    on 64-bit !HIGHMEM, no vunmap).
  - With this patch (same 8192-byte request, 64-bit and 32-bit initiator):
    no report, the command completes.

 drivers/target/target_core_pr.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c
index 11790f2c5d80..7c5bb7d67947 100644
--- a/drivers/target/target_core_pr.c
+++ b/drivers/target/target_core_pr.c
@@ -3293,9 +3293,6 @@ core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key,
 		goto out;
 	}
 
-	transport_kunmap_data_sg(cmd);
-	buf = NULL;
-
 	pr_debug("SPC-3 PR [%s] Extracted initiator %s identifier: %s"
 		" %s\n", dest_tf_ops->fabric_name, (iport_ptr != NULL) ?
 		"port" : "device", initiator_str, (iport_ptr != NULL) ?
@@ -3532,6 +3529,11 @@ core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key,
 	core_scsi3_update_and_write_aptpl(cmd->se_dev, aptpl);
 
 	core_scsi3_put_pr_reg(dest_pr_reg);
+	/*
+	 * iport_ptr aliases the PR-OUT parameter list mapped above, so the
+	 * buffer is unmapped only here on success (and at out: on error).
+	 */
+	transport_kunmap_data_sg(cmd);
 	return 0;
 out:
 	if (buf)
-- 
2.43.0



             reply	other threads:[~2026-06-10  4:22 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-10  4:22 Bryam Vargas [this message]
2026-06-10  4:37 ` [PATCH v3] scsi: target: fix iSCSI ISID use-after-free in REGISTER AND MOVE sashiko-bot
2026-06-10  8:40 ` John Garry
2026-06-10 11:00 ` David Disseldorp

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260610042245.35473-1-hexlabsecurity@proton.me \
    --to=hexlabsecurity@proton.me \
    --cc=James.Bottomley@HansenPartnership.com \
    --cc=ddiss@suse.de \
    --cc=john.g.garry@oracle.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-scsi@vger.kernel.org \
    --cc=martin.petersen@oracle.com \
    --cc=michael.christie@oracle.com \
    --cc=mlombard@redhat.com \
    --cc=target-devel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox