* [PATCH v2] scsi: target: compare all six ISID bytes when identifying a nexus
2026-11-09 13:12 [PATCH] scsi: target: compare all six ISID bytes when identifying a nexus Ciro Iriarte
@ 2026-11-09 14:31 ` Ciro Iriarte
0 siblings, 0 replies; 2+ messages in thread
From: Ciro Iriarte @ 2026-11-09 14:31 UTC (permalink / raw)
To: Martin K. Petersen; +Cc: target-devel, linux-scsi, linux-kernel, stable
lio_sess_get_initiator_sid() reports the ISID as twelve hex digits, and
iSCSI TransportIDs carry the same digits. __transport_register_session(),
__core_scsi3_do_alloc_registration() and
core_scsi3_alloc_aptpl_registration() turn that text into
sess_bin_isid and pr_reg_bin_isid with get_unaligned_be64(), which takes
the first eight characters, i.e. the first four ISID bytes.
open-iscsi ISIDs start with 00023d00 and differ only in the qualifier in
the last two bytes, so every session of one initiator gets the same
binary ISID. target_scsi2_reservation_check() and
target_scsi3_pr_reservation_check() then treat any session of the
reservation holder's initiator as the holder:
- after RESERVE(6) on one session, another session of the same
initiator can still write;
- with a Write Exclusive persistent reservation, another session that
is not registered, or registered but not the holder, can still write.
Reproduced with two open-iscsi sessions (ifaces default and if2) to a
ramdisk LUN: on v6.12.113 every TEST UNIT READY, READ(10) and WRITE(10)
the test sent from the second session completed with GOOD, under
RESERVE(6) and under each persistent reservation type, whether or not
that session was registered.
Convert all six bytes. Text that is not exactly twelve hex digits keeps
the old conversion of its first eight characters, now copied into a
zeroed buffer so that a shorter string, such as an initiator_sid written
to res_aptpl_metadata, is not read past its end.
An all-zero ISID now converts to 0, which
target_scsi2_reservation_reserve() took to mean "no ISID". Set
DRF_SPC2_RESERVATIONS_WITH_ISID when the fabric reports ISIDs instead.
Also update the TransportID parser comment that referred to the
get_unaligned_be64() read.
With distinct ISIDs, target_scsi3_pr_reservation_check() now sets
isid_mismatch for the holder initiator's other sessions, and
core_scsi3_pr_seq_non_holder() treated every such session as
unregistered: DEF_PR_REG_ACTIVE is kept per se_dev_entry, which all
sessions of the initiator share, so it cannot tell them apart. A
session that registered its own key would then get RESERVATION
CONFLICT for writes under a Registrants Only or All Registrants
reservation, and for PREEMPT, CLEAR and RELEASE. Look up that session's
own registration with core_scsi3_locate_pr_reg() instead.
With this patch, TEST UNIT READY and WRITE(10) from the second session
under RESERVE(6) return RESERVATION CONFLICT. Under Write Exclusive,
WRITE(10) from the second session conflicts whether or not it is
registered, and READ(10) is allowed. Under the Registrants Only and All
Registrants types, WRITE(10) from the second session conflicts until it
registers, as does READ(10) under the Exclusive Access types; once
registered it has access and can PREEMPT the holder.
Initiators that relied on the old behaviour, reserving through one
session and writing through another without registering it, will now
see RESERVATION CONFLICT, as SPC requires.
Fixes: c66ac9db8d4a ("[SCSI] target: Add LIO target core v4.0.0-rc6")
Cc: stable@vger.kernel.org
Signed-off-by: Ciro Iriarte <ciro.iriarte+software@gmail.com>
---
v2:
- core_scsi3_pr_seq_non_holder(): on an ISID mismatch, look up the
session's own registration instead of treating it as unregistered.
v1 made that path reachable for the holder initiator's other sessions,
so a registered second session got RESERVATION CONFLICT under the
Registrants Only / All Registrants types and for PREEMPT, CLEAR and
RELEASE (Sashiko review). Re-tested on v6.12.113 with types 1, 5-8
and PREEMPT/RELEASE/CLEAR from the second session.
- Fixes: now points at c66ac9db8d4a, which added the get_unaligned_be64()
conversion; e48354ce078c only made it reachable (Sashiko review).
- Reworded the TransportID parser comment.
v1: https://lore.kernel.org/r/591ac67a-fa1e-4395-9573-2d9405cfe0b6@gmail.com
drivers/target/target_core_fabric_lib.c | 9 ++---
drivers/target/target_core_pr.c | 46 ++++++++++++++++++++++---
drivers/target/target_core_pr.h | 1 +
drivers/target/target_core_transport.c | 2 +-
4 files changed, 49 insertions(+), 9 deletions(-)
diff --git a/drivers/target/target_core_fabric_lib.c b/drivers/target/target_core_fabric_lib.c
index ad024009d..ccfeba645 100644
--- a/drivers/target/target_core_fabric_lib.c
+++ b/drivers/target/target_core_fabric_lib.c
@@ -372,10 +372,11 @@ static bool iscsi_parse_pr_out_transport_id(
return false;
}
/*
- * The registration code reads the ISID as a C string,
- * and its first 8 bytes with get_unaligned_be64(), so
- * require 8 bytes after the separator and a NUL inside
- * the rest of the descriptor.
+ * The registration code reads the ISID as a C string, so
+ * require a NUL inside the rest of the descriptor. Keep the
+ * existing minimum of 8 bytes after the separator: a valid
+ * ISID is twelve hex digits, and target_isid_to_bin() falls
+ * back to the first eight characters of anything else.
*/
if (buf + tid_len - p < 8 || !memchr(p, '\0', buf + tid_len - p)) {
pr_err("Incomplete or unterminated ISID in iSCSI Initiator port TransportID\n");
diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c
index ca1a2802a..0f2ff9d42 100644
--- a/drivers/target/target_core_pr.c
+++ b/drivers/target/target_core_pr.c
@@ -19,6 +19,7 @@
#include <linux/fcntl.h>
#include <linux/fs.h>
#include <linux/fs_struct.h>
+#include <linux/hex.h>
#include <scsi/scsi_proto.h>
#include <linux/unaligned.h>
@@ -30,6 +31,28 @@
#include "target_core_pr.h"
#include "target_core_ua.h"
+/*
+ * lio_sess_get_initiator_sid() prints the six ISID bytes as twelve hex
+ * digits, and iSCSI TransportIDs carry the same digits. Convert all six
+ * bytes: reading the first eight digits as a big-endian u64 keeps only the
+ * first four ISID bytes, so two sessions of one initiator that differ only
+ * in the qualifier, the usual case, would compare equal. Text in any other
+ * format keeps the old conversion of its first eight characters, without
+ * reading past the end of a shorter string.
+ */
+u64 target_isid_to_bin(const char *isid)
+{
+ u8 bin[6];
+ char raw[8] = {};
+
+ if (strnlen(isid, 2 * sizeof(bin) + 1) == 2 * sizeof(bin) &&
+ !hex2bin(bin, isid, sizeof(bin)))
+ return get_unaligned_be48(bin);
+
+ memcpy(raw, isid, strnlen(isid, sizeof(raw)));
+ return get_unaligned_be64(raw);
+}
+
/*
* Used for Specify Initiator Ports Capable Bit (SPEC_I_PT)
*/
@@ -285,7 +308,11 @@ target_scsi2_reservation_reserve(struct se_cmd *cmd)
dev->reservation_holder = sess;
dev->dev_reservation_flags |= DRF_SPC2_RESERVATIONS;
- if (sess->sess_bin_isid != 0) {
+ /*
+ * An all-zero ISID converts to 0, so test whether the fabric has
+ * ISIDs rather than the value.
+ */
+ if (tpg->se_tpg_tfo->sess_get_initiator_sid) {
dev->dev_res_bin_isid = sess->sess_bin_isid;
dev->dev_reservation_flags |= DRF_SPC2_RESERVATIONS_WITH_ISID;
}
@@ -323,7 +350,18 @@ static int core_scsi3_pr_seq_non_holder(struct se_cmd *cmd, u32 pr_reg_type,
* RESERVATION CONFLICT on some CDBs */
if (isid_mismatch) {
- registered_nexus = 0;
+ struct t10_pr_registration *pr_reg;
+
+ /*
+ * Another I_T nexus of the holder's initiator. DEF_PR_REG_ACTIVE
+ * is shared by all of its sessions, so look for a registration
+ * made through this one.
+ */
+ pr_reg = core_scsi3_locate_pr_reg(cmd->se_dev, nacl, se_sess);
+ if (pr_reg) {
+ registered_nexus = 1;
+ core_scsi3_put_pr_reg(pr_reg);
+ }
} else {
struct se_dev_entry *se_deve;
@@ -674,7 +712,7 @@ static struct t10_pr_registration *__core_scsi3_do_alloc_registration(
* save it to the registration now.
*/
if (isid != NULL) {
- pr_reg->pr_reg_bin_isid = get_unaligned_be64(isid);
+ pr_reg->pr_reg_bin_isid = target_isid_to_bin(isid);
snprintf(pr_reg->pr_reg_isid, PR_REG_ISID_LEN, "%s", isid);
pr_reg->isid_present_at_reg = 1;
}
@@ -866,7 +904,7 @@ int core_scsi3_alloc_aptpl_registration(
* SCSI Initiator Port, restore it now.
*/
if (isid != NULL) {
- pr_reg->pr_reg_bin_isid = get_unaligned_be64(isid);
+ pr_reg->pr_reg_bin_isid = target_isid_to_bin(isid);
snprintf(pr_reg->pr_reg_isid, PR_REG_ISID_LEN, "%s", isid);
pr_reg->isid_present_at_reg = 1;
}
diff --git a/drivers/target/target_core_pr.h b/drivers/target/target_core_pr.h
index b793c9963..4debfe4be 100644
--- a/drivers/target/target_core_pr.h
+++ b/drivers/target/target_core_pr.h
@@ -72,6 +72,7 @@ extern void core_scsi3_free_pr_reg_from_nacl(struct se_device *,
struct se_node_acl *);
extern void core_scsi3_free_all_registrations(struct se_device *);
extern unsigned char *core_scsi3_pr_dump_type(int);
+u64 target_isid_to_bin(const char *isid);
extern sense_reason_t target_scsi3_emulate_pr_in(struct se_cmd *);
extern sense_reason_t target_scsi3_emulate_pr_out(struct se_cmd *);
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index 927861c30..4629c9343 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -419,7 +419,7 @@ void __transport_register_session(
memset(&buf[0], 0, PR_REG_ISID_LEN);
se_tpg->se_tpg_tfo->sess_get_initiator_sid(se_sess,
&buf[0], PR_REG_ISID_LEN);
- se_sess->sess_bin_isid = get_unaligned_be64(&buf[0]);
+ se_sess->sess_bin_isid = target_isid_to_bin(buf);
}
spin_lock_irqsave(&se_nacl->nacl_sess_lock, flags);
--
2.55.0
^ permalink raw reply [flat|nested] 2+ messages in thread