FAILED: patch "[PATCH] scsi: target: Bound PR-OUT TransportID parsing to the" failed to apply to 5.10-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 5.10-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-5.10.y
git checkout FETCH_HEAD
git cherry-pick -x d04a179085c262c9ed577d0a4cbc6482ff1fd9a3
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026072010-thinly-purr-b2b4@gregkh' --subject-prefix 'PATCH 5.10.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From d04a179085c262c9ed577d0a4cbc6482ff1fd9a3 Mon Sep 17 00:00:00 2001
From: Bryam Vargas <[email protected]>
Date: Thu, 11 Jun 2026 13:42:26 -0500
Subject: [PATCH] scsi: target: Bound PR-OUT TransportID parsing to the
received buffer
core_scsi3_decode_spec_i_port() and core_scsi3_emulate_register_and_move() hand the raw PERSISTENT RESERVE OUT parameter buffer to target_parse_pr_out_transport_id() without telling it how many bytes are valid. For an iSCSI TransportID (FORMAT CODE 01b), iscsi_parse_pr_out_transport_id() locates the ",i,0x" ISID separator with an unbounded strstr() (and on the error path prints the name with a further unbounded "%s"). An initiator can submit a TransportID whose iSCSI name contains neither a ",i,0x" substring nor a NUL terminator, filling the parameter list to its end, so the scan runs off the end of the buffer.
When the parameter list spans more than one page the buffer is a multi-page vmap (transport_kmap_data_sg()), so the over-read walks into the trailing vmalloc guard page and oopses (KASAN: vmalloc-out-of-bounds in strstr). It is reachable by any fabric that delivers a PR OUT to a device exported through an iSCSI TPG, including a guest via vhost-scsi.
Pass the number of received bytes down to the parser and validate the iSCSI TransportID's own self-described length (ADDITIONAL LENGTH + 4) once, up
front: reject it if it is below the spc4r17 minimum or larger than the received buffer, then bound the separator search, the ISID walk and the name copy by that length. This is the length check the callers already perform after the parse (core_scsi3_decode_spec_i_port() compares tid_len against tpdl, core_scsi3_emulate_register_and_move() validates it against data_length), moved ahead of the scan. Also drop the unbounded "%s" of the unterminated name.
Add per-format explicit name-length checks before copying into i_str, rather than silently truncating with min_t: for FORMAT CODE 00b reject if the descriptor body (tid_len - 4 bytes) cannot fit in i_str[TRANSPORT_IQN_LEN]; for FORMAT CODE 01b reject if the name portion (from &buf[4] up to the separator) cannot fit. Both checks make the bounds intent explicit at each format branch.
While here, also reject a FORMAT CODE 01b TransportID whose ",i,0x" separator sits at the very end of the descriptor: that leaves an empty ISID and points the returned port nexus pointer at buf + tid_len, one past the descriptor, which the registration code (__core_scsi3_locate_pr_reg(), __core_scsi3_alloc_registration()) then dereferences as the ISID string -- the same over-read of the parameter buffer for a malformed descriptor.
drivers/target/target_core_fabric_lib.c +69 -20
@@ -290,13 +290,24 @@ static void sbp_parse_pr_out_transport_id(char *buf, char *i_str)static bool iscsi_parse_pr_out_transport_id (struct se_portal_group *se_tpg,char *buf, +u32 buf_len,u32 *out_tid_len,char **port_nexus_ptr,char *i_str) {char *p; +u32 tid_len;int i; -u8 format_code = (buf[0 ] &0xc0 ); +u8 format_code; + +/* +* The 4-byte iSCSI TransportID header (FORMAT CODE + 2-byte ADDITIONAL +* LENGTH) must be present before any of it can be parsed. +*/ +if (buf_len <4 ) +return false; + + format_code = buf[0 ] &0xc0 ;/* * Check for FORMAT CODE 00b or 01b from spc4r17, section 7.5.4.6: *
@@ -316,15 +327,17 @@ static bool iscsi_parse_pr_out_transport_id(return false; }/* -* If the caller wants the TransportID Length, we set that value for the -* entire iSCSI Tarnsport ID now. +* Reconstruct the self-described TransportID length from the ADDITIONAL +* LENGTH field plus the 4-byte header. Reject it if it is below the +* spc4r17 section 7.5.4.6 minimum (ADDITIONAL LENGTH shall be at least +* 20) or if it runs past the bytes actually received, so that every +* access below stays inside the TransportID. */ -if (out_tid_len) { -/* The shift works thanks to integer promotion rules */ - *out_tid_len =get_unaligned_be16 (&buf[2 ]); -/* Add four bytes for iSCSI Transport ID header */ - *out_tid_len +=4 ; - } + tid_len =get_unaligned_be16 (&buf[2 ]) +4 ; +if (tid_len <24 || tid_len > buf_len) +return false; +if (out_tid_len) + *out_tid_len = tid_len;/* * Check for ',i,0x' separator between iSCSI Name and iSCSI Initiator
@@ -332,16 +345,32 @@ static bool iscsi_parse_pr_out_transport_id(* format. */ if (format_code ==0x40 ) { - p =strstr (&buf[4 ],",i,0x" ); + p =strnstr (&buf[4 ],",i,0x" , tid_len -4 );if (!p) { -pr_err ("Unable to locate \",i,0x\" separator" -" for Initiator port identifier: %s\n" , - &buf[4 ]); +pr_err ("Unable to locate \",i,0x\" separator in iSCSI TransportID\n" ); +return false; + } +/* +* The iSCSI name runs from &buf[4] up to the separator; reject it +* if it cannot fit in i_str[TRANSPORT_IQN_LEN]. +*/ +if (p - &buf[4 ] >=TRANSPORT_IQN_LEN ) { +pr_err ("iSCSI Initiator port name too long in TransportID\n" );return false; } *p ='\0' ;/* Terminate iSCSI Name */ p +=5 ;/* Skip over ",i,0x" separator */ +/* +* The ISID must follow the separator. A ",i,0x" sitting at the +* very end of the TransportID leaves no ISID and would point the +* port nexus at buf + tid_len, i.e. past the descriptor, which +* the registration code then reads as the ISID string. +*/ +if (p >= buf + tid_len) { +pr_err ("Missing ISID in iSCSI Initiator port TransportID\n" ); +return false; + } *port_nexus_ptr = p;/* * Go ahead and do the lower case conversion of the received
@@ -349,7 +378,7 @@ static bool iscsi_parse_pr_out_transport_id(* for comparison against the running iSCSI session's ISID from * iscsi_target.c:lio_sess_get_initiator_sid() */ -for (i =0 ; i <12 ; i++) { +for (i =0 ; i <12 && p < buf + tid_len; i++) {/* * The first ISCSI INITIATOR SESSION ID field byte * containing an ASCII null character terminates the
@@ -367,10 +396,22 @@ static bool iscsi_parse_pr_out_transport_id( *p =tolower (*p); p++; } - }else +strscpy (i_str, &buf[4 ],TRANSPORT_IQN_LEN ); + }else { *port_nexus_ptr =NULL ; - -strscpy (i_str, &buf[4 ],TRANSPORT_IQN_LEN ); +/* +* FORMAT CODE 00b: the name occupies buf[4..tid_len-1]. The +* declared length tid_len - 4 must fit in i_str[TRANSPORT_IQN_LEN]. +* (For 01b the same tid_len bound would be over-restrictive: the +* descriptor also carries the separator and ISID, so a legal +* <=223-byte name gives tid_len up to 244.) +*/ +if (tid_len -4 >=TRANSPORT_IQN_LEN ) { +pr_err ("iSCSI Initiator port name too long in TransportID\n" ); +return false; + } +strscpy (i_str, &buf[4 ], tid_len -4 ); + }return true; }
@@ -420,8 +461,16 @@ int target_get_pr_transport_id(struct se_node_acl *nacl, }bool target_parse_pr_out_transport_id (struct se_portal_group *tpg, -char *buf,u32 *out_tid_len,char **port_nexus_ptr,char *i_str) +char *buf,u32 buf_len,u32 *out_tid_len, +char **port_nexus_ptr,char *i_str) { +/* +* The fixed-length SAS/SRP/FCP/SBP TransportIDs are 24 bytes; the iSCSI +* format is variable and bounds itself against buf_len below. +*/ +if (tpg->proto_id !=SCSI_PROTOCOL_ISCSI && buf_len <24 ) +return false; +switch (tpg->proto_id) {case SCSI_PROTOCOL_SAS :/*
@@ -440,8 +489,8 @@ bool target_parse_pr_out_transport_id(struct se_portal_group *tpg,sbp_parse_pr_out_transport_id (buf, i_str);break ;case SCSI_PROTOCOL_ISCSI : -return iscsi_parse_pr_out_transport_id (tpg, buf, out_tid_len, - port_nexus_ptr, i_str); +return iscsi_parse_pr_out_transport_id (tpg, buf, buf_len, + out_tid_len, port_nexus_ptr, i_str);default :pr_err ("Unknown proto_id: 0x%02x\n" , tpg->proto_id);return false;
drivers/target/target_core_internal.h +2 -1
@@ -104,7 +104,8 @@ int target_get_pr_transport_id(struct se_node_acl *nacl, struct t10_pr_registration *pr_reg, int *format_code, unsigned char *buf);bool target_parse_pr_out_transport_id (struct se_portal_group *tpg, -char *buf,u32 *out_tid_len,char **port_nexus_ptr,char *i_str); +char *buf,u32 buf_len,u32 *out_tid_len, +char **port_nexus_ptr,char *i_str);/* target_core_hba.c */ struct se_hba *core_alloc_hba (const char *,u32 ,u32 );
drivers/target/target_core_pr.c +2 -2
@@ -1573,7 +1573,7 @@ core_scsi3_decode_spec_i_port( iport_ptr =NULL ; tid_found =target_parse_pr_out_transport_id (tmp_tpg, - ptr, &tid_len, &iport_ptr, i_str); + ptr, tpdl, &tid_len, &iport_ptr, i_str);if (!tid_found)continue ;/*
@@ -3285,7 +3285,7 @@ core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key, goto out; } tid_found =target_parse_pr_out_transport_id (dest_se_tpg, - &buf[24 ], &tmp_tid_len, &iport_ptr, initiator_str); + &buf[24 ], tid_len, &tmp_tid_len, &iport_ptr, initiator_str);if (!tid_found) {pr_err ("SPC-3 PR REGISTER_AND_MOVE: Unable to locate" " initiator_str from Transport ID\n" );
Sasha Levin
maintainerPATCH 1/3 scsi: target: core: pr: Initialize arrays at declaration time
From: Chaitanya Kulkarni <[email protected]>
[ Upstream commit 4db6dfe62c5f76ce9eef28967c2e2000efde10d5 ]
Avoids calling memset().
Stable-dep-of: d04a179085c2 ("scsi: target: Bound PR-OUT TransportID parsing to the received buffer")
--- drivers/target/target_core_pr.c | 36 ++++++++++++--------------------- 1 file changed, 13 insertions(+), 23 deletions(-)
drivers/target/target_core_pr.c +13 -23
@@ -895,9 +895,8 @@ static void core_scsi3_aptpl_reserve(struct se_node_acl *node_acl, struct t10_pr_registration *pr_reg) { - char i_buf[PR_REG_ISID_ID_LEN]; + char i_buf[PR_REG_ISID_ID_LEN] = { }; -memset (i_buf,0 ,PR_REG_ISID_ID_LEN );core_pr_dump_initiator_port (pr_reg, i_buf,PR_REG_ISID_ID_LEN );spin_lock (&dev->dev_reservation_lock);
@@ -927,12 +926,10 @@ static int __core_scsi3_check_aptpl_registration( {struct t10_pr_registration *pr_reg, *pr_reg_tmp;struct t10_reservation *pr_tmpl = &dev->t10_pr; -unsigned char i_port[PR_APTPL_MAX_IPORT_LEN ]; -unsigned char t_port[PR_APTPL_MAX_TPORT_LEN ]; +unsigned char i_port[PR_APTPL_MAX_IPORT_LEN ] = { }; +unsigned char t_port[PR_APTPL_MAX_TPORT_LEN ] = { };u16 tpgt; -memset (i_port,0 ,PR_APTPL_MAX_IPORT_LEN ); -memset (t_port,0 ,PR_APTPL_MAX_TPORT_LEN );/* * Copy Initiator Port information from struct se_node_acl */
@@ -1022,9 +1019,8 @@ static void __core_scsi3_dump_registration( enum register_type register_type) {struct se_portal_group *se_tpg = nacl->se_tpg; -char i_buf[PR_REG_ISID_ID_LEN ]; +char i_buf[PR_REG_ISID_ID_LEN ] = { }; -memset (&i_buf[0 ],0 ,PR_REG_ISID_ID_LEN );core_pr_dump_initiator_port (pr_reg, i_buf,PR_REG_ISID_ID_LEN ); pr_debug("SPC-3 PR [%s] Service Action: REGISTER%s Initiator"
@@ -1203,10 +1199,10 @@ static struct t10_pr_registration *core_scsi3_locate_pr_reg( struct se_session *sess) {struct se_portal_group *tpg = nacl->se_tpg; -unsigned char buf[PR_REG_ISID_LEN ], *isid_ptr =NULL ; +unsigned char buf[PR_REG_ISID_LEN ] = { }; +unsigned char *isid_ptr =NULL ;if (tpg->se_tpg_tfo->sess_get_initiator_sid !=NULL ) { -memset (&buf[0 ],0 ,PR_REG_ISID_LEN ); tpg->se_tpg_tfo->sess_get_initiator_sid (sess, &buf[0 ],PR_REG_ISID_LEN ); isid_ptr = &buf[0 ];
@@ -1284,11 +1280,10 @@ static void __core_scsi3_free_registration(struct t10_reservation *pr_tmpl = &dev->t10_pr;struct se_node_acl *nacl = pr_reg->pr_reg_nacl;struct se_dev_entry *deve; -char i_buf[PR_REG_ISID_ID_LEN ]; +char i_buf[PR_REG_ISID_ID_LEN ] = { };lockdep_assert_held (&pr_tmpl->registration_lock); -memset (i_buf,0 ,PR_REG_ISID_ID_LEN );core_pr_dump_initiator_port (pr_reg, i_buf,PR_REG_ISID_ID_LEN );if (!list_empty (&pr_reg->pr_reg_list))
@@ -2060,7 +2055,8 @@ core_scsi3_emulate_pro_register(struct se_cmd *cmd, u64 res_key, u64 sa_res_key,struct se_portal_group *se_tpg;struct t10_pr_registration *pr_reg, *pr_reg_p, *pr_reg_tmp;struct t10_reservation *pr_tmpl = &dev->t10_pr; -unsigned char isid_buf[PR_REG_ISID_LEN ], *isid_ptr =NULL ; +unsigned char isid_buf[PR_REG_ISID_LEN ] = { }; +unsigned char *isid_ptr =NULL ;sense_reason_t ret =TCM_NO_SENSE ;int pr_holder =0 , type;
@@ -2071,7 +2067,6 @@ core_scsi3_emulate_pro_register(struct se_cmd *cmd, u64 res_key, u64 sa_res_key, se_tpg = se_sess->se_tpg;if (se_tpg->se_tpg_tfo->sess_get_initiator_sid) { -memset (&isid_buf[0 ],0 ,PR_REG_ISID_LEN ); se_tpg->se_tpg_tfo->sess_get_initiator_sid (se_sess, &isid_buf[0 ],PR_REG_ISID_LEN ); isid_ptr = &isid_buf[0 ];
@@ -2283,11 +2278,9 @@ core_scsi3_pro_reserve(struct se_cmd *cmd, int type, int scope, u64 res_key)struct se_lun *se_lun = cmd->se_lun;struct t10_pr_registration *pr_reg, *pr_res_holder;struct t10_reservation *pr_tmpl = &dev->t10_pr; -char i_buf[PR_REG_ISID_ID_LEN ]; +char i_buf[PR_REG_ISID_ID_LEN ] = { };sense_reason_t ret; -memset (i_buf,0 ,PR_REG_ISID_ID_LEN ); -if (!se_sess || !se_lun) {pr_err ("SPC-3 PR: se_sess || struct se_lun is NULL!\n" );return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE ;
@@ -2458,12 +2451,11 @@ static void __core_scsi3_complete_pro_release( int unreg) {const struct target_core_fabric_ops *tfo = se_nacl->se_tpg->se_tpg_tfo; -char i_buf[PR_REG_ISID_ID_LEN ]; +char i_buf[PR_REG_ISID_ID_LEN ] = { };int pr_res_type =0 , pr_res_scope =0 ;lockdep_assert_held (&dev->dev_reservation_lock); -memset (i_buf,0 ,PR_REG_ISID_ID_LEN );core_pr_dump_initiator_port (pr_reg, i_buf,PR_REG_ISID_ID_LEN );/* * Go ahead and release the current PR reservation holder.
@@ -2769,11 +2761,10 @@ static void __core_scsi3_complete_pro_preempt( {struct se_node_acl *nacl = pr_reg->pr_reg_nacl;const struct target_core_fabric_ops *tfo = nacl->se_tpg->se_tpg_tfo; -char i_buf[PR_REG_ISID_ID_LEN ]; +char i_buf[PR_REG_ISID_ID_LEN ] = { };lockdep_assert_held (&dev->dev_reservation_lock); -memset (i_buf,0 ,PR_REG_ISID_ID_LEN );core_pr_dump_initiator_port (pr_reg, i_buf,PR_REG_ISID_ID_LEN );/* * Do an implicit RELEASE of the existing reservation.
@@ -3159,7 +3150,7 @@ core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key,struct t10_reservation *pr_tmpl = &dev->t10_pr;unsigned char *buf;const unsigned char *initiator_str; -char *iport_ptr =NULL , i_buf[PR_REG_ISID_ID_LEN ]; +char *iport_ptr =NULL , i_buf[PR_REG_ISID_ID_LEN ] = { };u32 tid_len, tmp_tid_len;int new_reg =0 , type, scope, matching_iname;sense_reason_t ret;
@@ -3171,7 +3162,6 @@ core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key,return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE ; } -memset (i_buf,0 ,PR_REG_ISID_ID_LEN ); se_tpg = se_sess->se_tpg; tf_ops = se_tpg->se_tpg_tfo;/*
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/3 scsi: target: core: Generate correct identifiers for PR OUT transport IDs
From: Maurizio Lombardi <[email protected]>
[ Upstream commit 6e0f6aa44b68335df404a2df955055f416b5f2aa ]
Fix target_parse_pr_out_transport_id() to return a string representing the transport ID in a human-readable format (e.g., naa.xxxxxxxx...) for various SCSI protocol types (SAS, FCP, SRP, SBP).
Previously, the function returned a pointer to the raw binary buffer, which was incorrectly compared against human-readable strings, causing comparisons to fail. Now, the function writes a properly formatted string into a buffer provided by the caller. The output format depends on the transport protocol:
* SAS: 64-bit identifier, "naa." prefix.
* FCP: 64-bit identifier, colon separated values.
* SBP: 64-bit identifier, no prefix.
* SRP: 128-bit identifier, "0x" prefix.
* iSCSI: IQN string.
Stable-dep-of: d04a179085c2 ("scsi: target: Bound PR-OUT TransportID parsing to the received buffer")
--- drivers/target/target_core_fabric_lib.c | 63 +++++++++++++++++++------ drivers/target/target_core_internal.h | 4 +- drivers/target/target_core_pr.c | 18 +++---- 3 files changed, 60 insertions(+), 25 deletions(-)
drivers/target/target_core_fabric_lib.c +48 -15
@@ -257,11 +257,41 @@ static int iscsi_get_pr_transport_id_len(return len; } -static char *iscsi_parse_pr_out_transport_id ( +static void sas_parse_pr_out_transport_id (char *buf,char *i_str) +{ +char hex[17 ] = {}; + +bin2hex (hex, buf +4 ,8 ); +snprintf (i_str,TRANSPORT_IQN_LEN ,"naa.%s" , hex); +} + +static void srp_parse_pr_out_transport_id (char *buf,char *i_str) +{ +char hex[33 ] = {}; + +bin2hex (hex, buf +8 ,16 ); +snprintf (i_str,TRANSPORT_IQN_LEN ,"0x%s" , hex); +} + +static void fcp_parse_pr_out_transport_id (char *buf,char *i_str) +{ +snprintf (i_str,TRANSPORT_IQN_LEN ,"%8phC" , buf +8 ); +} + +static void sbp_parse_pr_out_transport_id (char *buf,char *i_str) +{ +char hex[17 ] = {}; + +bin2hex (hex, buf +8 ,8 ); +snprintf (i_str,TRANSPORT_IQN_LEN ,"%s" , hex); +} + +static bool iscsi_parse_pr_out_transport_id (struct se_portal_group *se_tpg,char *buf,u32 *out_tid_len, -char **port_nexus_ptr) +char **port_nexus_ptr, +char *i_str) {char *p;int i;
@@ -282,7 +312,7 @@ static char *iscsi_parse_pr_out_transport_id(if ((format_code !=0x00 ) && (format_code !=0x40 )) {pr_err ("Illegal format code: 0x%02x for iSCSI" " Initiator Transport ID\n" , format_code); -return NULL ; +return false; }/* * If the caller wants the TransportID Length, we set that value for the
@@ -306,7 +336,7 @@ static char *iscsi_parse_pr_out_transport_id(pr_err ("Unable to locate \",i,0x\" separator" " for Initiator port identifier: %s\n" , &buf[4 ]); -return NULL ; +return false; } *p ='\0' ;/* Terminate iSCSI Name */ p +=5 ;/* Skip over ",i,0x" separator */
@@ -339,7 +369,8 @@ static char *iscsi_parse_pr_out_transport_id( }else *port_nexus_ptr =NULL ; -return &buf[4 ]; +strscpy (i_str, &buf[4 ],TRANSPORT_IQN_LEN ); +return true; } int target_get_pr_transport_id_len(struct se_node_acl *nacl,
@@ -387,33 +418,35 @@ int target_get_pr_transport_id(struct se_node_acl *nacl, } } -const char *target_parse_pr_out_transport_id (struct se_portal_group *tpg, -char *buf,u32 *out_tid_len,char **port_nexus_ptr) +bool target_parse_pr_out_transport_id (struct se_portal_group *tpg, +char *buf,u32 *out_tid_len,char **port_nexus_ptr,char *i_str) { -u32 offset; -switch (tpg->proto_id) {case SCSI_PROTOCOL_SAS :/* * Assume the FORMAT CODE 00b from spc4r17, 7.5.4.7 TransportID * for initiator ports using SCSI over SAS Serial SCSI Protocol. */ - offset =4 ; +sas_parse_pr_out_transport_id (buf, i_str);break ; -case SCSI_PROTOCOL_SBP :case SCSI_PROTOCOL_SRP : +srp_parse_pr_out_transport_id (buf, i_str); +break ;case SCSI_PROTOCOL_FCP : - offset =8 ; +fcp_parse_pr_out_transport_id (buf, i_str); +break ; +case SCSI_PROTOCOL_SBP : +sbp_parse_pr_out_transport_id (buf, i_str);break ;case SCSI_PROTOCOL_ISCSI :return iscsi_parse_pr_out_transport_id (tpg, buf, out_tid_len, - port_nexus_ptr); + port_nexus_ptr, i_str);default :pr_err ("Unknown proto_id: 0x%02x\n" , tpg->proto_id); -return NULL ; +return false; } *port_nexus_ptr =NULL ; *out_tid_len =24 ; -return buf + offset; +return true; }
drivers/target/target_core_internal.h +2 -2
@@ -102,8 +102,8 @@ int target_get_pr_transport_id_len(struct se_node_acl *nacl,int target_get_pr_transport_id (struct se_node_acl *nacl,struct t10_pr_registration *pr_reg,int *format_code,unsigned char *buf); -const char *target_parse_pr_out_transport_id (struct se_portal_group *tpg, -char *buf,u32 *out_tid_len,char **port_nexus_ptr); +bool target_parse_pr_out_transport_id (struct se_portal_group *tpg, +char *buf,u32 *out_tid_len,char **port_nexus_ptr,char *i_str);/* target_core_hba.c */ struct se_hba *core_alloc_hba (const char *,u32 ,u32 );
drivers/target/target_core_pr.c +10 -8
@@ -1486,11 +1486,12 @@ core_scsi3_decode_spec_i_port(LIST_HEAD (tid_dest_list);struct pr_transport_id_holder *tidh_new, *tidh, *tidh_tmp;unsigned char *buf, *ptr, proto_ident; -const unsigned char *i_str =NULL ; +unsigned char i_str[TRANSPORT_IQN_LEN ];char *iport_ptr =NULL , i_buf[PR_REG_ISID_ID_LEN ];sense_reason_t ret;u32 tpdl, tid_len =0 ;u32 dest_rtpi =0 ; +bool tid_found;/* * Allocate a struct pr_transport_id_holder and setup the
@@ -1579,9 +1580,9 @@ core_scsi3_decode_spec_i_port( dest_rtpi = tmp_lun->lun_rtpi; iport_ptr =NULL ; - i_str =target_parse_pr_out_transport_id (tmp_tpg, - ptr, &tid_len, &iport_ptr); -if (!i_str) + tid_found =target_parse_pr_out_transport_id (tmp_tpg, + ptr, &tid_len, &iport_ptr, i_str); +if (!tid_found)continue ;/* * Determine if this SCSI device server requires that
@@ -3149,13 +3150,14 @@ core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key,struct t10_pr_registration *pr_reg, *pr_res_holder, *dest_pr_reg;struct t10_reservation *pr_tmpl = &dev->t10_pr;unsigned char *buf; -const unsigned char *initiator_str; +unsigned char initiator_str[TRANSPORT_IQN_LEN ];char *iport_ptr =NULL , i_buf[PR_REG_ISID_ID_LEN ] = { };u32 tid_len, tmp_tid_len;int new_reg =0 , type, scope, matching_iname;sense_reason_t ret;unsigned short rtpi;unsigned char proto_ident; +bool tid_found;if (!se_sess || !se_lun) {pr_err ("SPC-3 PR: se_sess || struct se_lun is NULL!\n" );
@@ -3274,9 +3276,9 @@ core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key, ret =TCM_INVALID_PARAMETER_LIST ; goto out; } - initiator_str =target_parse_pr_out_transport_id (dest_se_tpg, - &buf[24 ], &tmp_tid_len, &iport_ptr); -if (!initiator_str) { + tid_found =target_parse_pr_out_transport_id (dest_se_tpg, + &buf[24 ], &tmp_tid_len, &iport_ptr, initiator_str); +if (!tid_found) {pr_err ("SPC-3 PR REGISTER_AND_MOVE: Unable to locate" " initiator_str from Transport ID\n" ); ret =TCM_INVALID_PARAMETER_LIST ;
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 3/3 scsi: target: Bound PR-OUT TransportID parsing to the received buffer
In reply to Sasha LevinFrom: Bryam Vargas <[email protected]>
[ Upstream commit d04a179085c262c9ed577d0a4cbc6482ff1fd9a3 ]
core_scsi3_decode_spec_i_port() and core_scsi3_emulate_register_and_move() hand the raw PERSISTENT RESERVE OUT parameter buffer to target_parse_pr_out_transport_id() without telling it how many bytes are valid. For an iSCSI TransportID (FORMAT CODE 01b), iscsi_parse_pr_out_transport_id() locates the ",i,0x" ISID separator with an unbounded strstr() (and on the error path prints the name with a further unbounded "%s"). An initiator can submit a TransportID whose iSCSI name contains neither a ",i,0x" substring nor a NUL terminator, filling the parameter list to its end, so the scan runs off the end of the buffer.
When the parameter list spans more than one page the buffer is a multi-page vmap (transport_kmap_data_sg()), so the over-read walks into the trailing vmalloc guard page and oopses (KASAN: vmalloc-out-of-bounds in strstr). It is reachable by any fabric that delivers a PR OUT to a device exported through an iSCSI TPG, including a guest via vhost-scsi.
Pass the number of received bytes down to the parser and validate the iSCSI TransportID's own self-described length (ADDITIONAL LENGTH + 4) once, up
front: reject it if it is below the spc4r17 minimum or larger than the received buffer, then bound the separator search, the ISID walk and the name copy by that length. This is the length check the callers already perform after the parse (core_scsi3_decode_spec_i_port() compares tid_len against tpdl, core_scsi3_emulate_register_and_move() validates it against data_length), moved ahead of the scan. Also drop the unbounded "%s" of the unterminated name.
Add per-format explicit name-length checks before copying into i_str, rather than silently truncating with min_t: for FORMAT CODE 00b reject if the descriptor body (tid_len - 4 bytes) cannot fit in i_str[TRANSPORT_IQN_LEN]; for FORMAT CODE 01b reject if the name portion (from &buf[4] up to the separator) cannot fit. Both checks make the bounds intent explicit at each format branch.
While here, also reject a FORMAT CODE 01b TransportID whose ",i,0x" separator sits at the very end of the descriptor: that leaves an empty ISID and points the returned port nexus pointer at buf + tid_len, one past the descriptor, which the registration code (__core_scsi3_locate_pr_reg(), __core_scsi3_alloc_registration()) then dereferences as the ISID string -- the same over-read of the parameter buffer for a malformed descriptor.
--- drivers/target/target_core_fabric_lib.c | 89 +++++++++++++++++++------ drivers/target/target_core_internal.h | 3 +- drivers/target/target_core_pr.c | 4 +- 3 files changed, 73 insertions(+), 23 deletions(-)
drivers/target/target_core_fabric_lib.c +69 -20
@@ -289,13 +289,24 @@ static void sbp_parse_pr_out_transport_id(char *buf, char *i_str)static bool iscsi_parse_pr_out_transport_id (struct se_portal_group *se_tpg,char *buf, +u32 buf_len,u32 *out_tid_len,char **port_nexus_ptr,char *i_str) {char *p; +u32 tid_len;int i; -u8 format_code = (buf[0 ] &0xc0 ); +u8 format_code; + +/* +* The 4-byte iSCSI TransportID header (FORMAT CODE + 2-byte ADDITIONAL +* LENGTH) must be present before any of it can be parsed. +*/ +if (buf_len <4 ) +return false; + + format_code = buf[0 ] &0xc0 ;/* * Check for FORMAT CODE 00b or 01b from spc4r17, section 7.5.4.6: *
@@ -315,15 +326,17 @@ static bool iscsi_parse_pr_out_transport_id(return false; }/* -* If the caller wants the TransportID Length, we set that value for the -* entire iSCSI Tarnsport ID now. +* Reconstruct the self-described TransportID length from the ADDITIONAL +* LENGTH field plus the 4-byte header. Reject it if it is below the +* spc4r17 section 7.5.4.6 minimum (ADDITIONAL LENGTH shall be at least +* 20) or if it runs past the bytes actually received, so that every +* access below stays inside the TransportID. */ -if (out_tid_len) { -/* The shift works thanks to integer promotion rules */ - *out_tid_len =get_unaligned_be16 (&buf[2 ]); -/* Add four bytes for iSCSI Transport ID header */ - *out_tid_len +=4 ; - } + tid_len =get_unaligned_be16 (&buf[2 ]) +4 ; +if (tid_len <24 || tid_len > buf_len) +return false; +if (out_tid_len) + *out_tid_len = tid_len;/* * Check for ',i,0x' separator between iSCSI Name and iSCSI Initiator
@@ -331,16 +344,32 @@ static bool iscsi_parse_pr_out_transport_id(* format. */ if (format_code ==0x40 ) { - p =strstr (&buf[4 ],",i,0x" ); + p =strnstr (&buf[4 ],",i,0x" , tid_len -4 );if (!p) { -pr_err ("Unable to locate \",i,0x\" separator" -" for Initiator port identifier: %s\n" , - &buf[4 ]); +pr_err ("Unable to locate \",i,0x\" separator in iSCSI TransportID\n" ); +return false; + } +/* +* The iSCSI name runs from &buf[4] up to the separator; reject it +* if it cannot fit in i_str[TRANSPORT_IQN_LEN]. +*/ +if (p - &buf[4 ] >=TRANSPORT_IQN_LEN ) { +pr_err ("iSCSI Initiator port name too long in TransportID\n" );return false; } *p ='\0' ;/* Terminate iSCSI Name */ p +=5 ;/* Skip over ",i,0x" separator */ +/* +* The ISID must follow the separator. A ",i,0x" sitting at the +* very end of the TransportID leaves no ISID and would point the +* port nexus at buf + tid_len, i.e. past the descriptor, which +* the registration code then reads as the ISID string. +*/ +if (p >= buf + tid_len) { +pr_err ("Missing ISID in iSCSI Initiator port TransportID\n" ); +return false; + } *port_nexus_ptr = p;/* * Go ahead and do the lower case conversion of the received
@@ -348,7 +377,7 @@ static bool iscsi_parse_pr_out_transport_id(* for comparison against the running iSCSI session's ISID from * iscsi_target.c:lio_sess_get_initiator_sid() */ -for (i =0 ; i <12 ; i++) { +for (i =0 ; i <12 && p < buf + tid_len; i++) {/* * The first ISCSI INITIATOR SESSION ID field byte * containing an ASCII null character terminates the
@@ -366,10 +395,22 @@ static bool iscsi_parse_pr_out_transport_id( *p =tolower (*p); p++; } - }else +strscpy (i_str, &buf[4 ],TRANSPORT_IQN_LEN ); + }else { *port_nexus_ptr =NULL ; - -strscpy (i_str, &buf[4 ],TRANSPORT_IQN_LEN ); +/* +* FORMAT CODE 00b: the name occupies buf[4..tid_len-1]. The +* declared length tid_len - 4 must fit in i_str[TRANSPORT_IQN_LEN]. +* (For 01b the same tid_len bound would be over-restrictive: the +* descriptor also carries the separator and ISID, so a legal +* <=223-byte name gives tid_len up to 244.) +*/ +if (tid_len -4 >=TRANSPORT_IQN_LEN ) { +pr_err ("iSCSI Initiator port name too long in TransportID\n" ); +return false; + } +strscpy (i_str, &buf[4 ], tid_len -4 ); + }return true; }
@@ -419,8 +460,16 @@ int target_get_pr_transport_id(struct se_node_acl *nacl, }bool target_parse_pr_out_transport_id (struct se_portal_group *tpg, -char *buf,u32 *out_tid_len,char **port_nexus_ptr,char *i_str) +char *buf,u32 buf_len,u32 *out_tid_len, +char **port_nexus_ptr,char *i_str) { +/* +* The fixed-length SAS/SRP/FCP/SBP TransportIDs are 24 bytes; the iSCSI +* format is variable and bounds itself against buf_len below. +*/ +if (tpg->proto_id !=SCSI_PROTOCOL_ISCSI && buf_len <24 ) +return false; +switch (tpg->proto_id) {case SCSI_PROTOCOL_SAS :/*
@@ -439,8 +488,8 @@ bool target_parse_pr_out_transport_id(struct se_portal_group *tpg,sbp_parse_pr_out_transport_id (buf, i_str);break ;case SCSI_PROTOCOL_ISCSI : -return iscsi_parse_pr_out_transport_id (tpg, buf, out_tid_len, - port_nexus_ptr, i_str); +return iscsi_parse_pr_out_transport_id (tpg, buf, buf_len, + out_tid_len, port_nexus_ptr, i_str);default :pr_err ("Unknown proto_id: 0x%02x\n" , tpg->proto_id);return false;
drivers/target/target_core_internal.h +2 -1
@@ -103,7 +103,8 @@ int target_get_pr_transport_id(struct se_node_acl *nacl, struct t10_pr_registration *pr_reg, int *format_code, unsigned char *buf);bool target_parse_pr_out_transport_id (struct se_portal_group *tpg, -char *buf,u32 *out_tid_len,char **port_nexus_ptr,char *i_str); +char *buf,u32 buf_len,u32 *out_tid_len, +char **port_nexus_ptr,char *i_str);/* target_core_hba.c */ struct se_hba *core_alloc_hba (const char *,u32 ,u32 );
drivers/target/target_core_pr.c +2 -2
@@ -1581,7 +1581,7 @@ core_scsi3_decode_spec_i_port( iport_ptr =NULL ; tid_found =target_parse_pr_out_transport_id (tmp_tpg, - ptr, &tid_len, &iport_ptr, i_str); + ptr, tpdl, &tid_len, &iport_ptr, i_str);if (!tid_found)continue ;/*
@@ -3277,7 +3277,7 @@ core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key, goto out; } tid_found =target_parse_pr_out_transport_id (dest_se_tpg, - &buf[24 ], &tmp_tid_len, &iport_ptr, initiator_str); + &buf[24 ], tid_len, &tmp_tid_len, &iport_ptr, initiator_str);if (!tid_found) {pr_err ("SPC-3 PR REGISTER_AND_MOVE: Unable to locate" " initiator_str from Transport ID\n" );
Signature · 1 line
2.53.0