From 89180d6083a06b96d2e1250b333b91e7936517fc Mon Sep 17 00:00:00 2001 From: Sebastian Vater Date: Tue, 30 Sep 2025 12:29:21 +0200 Subject: Fixed crash in iSCSI server if a write command is sent. --- src/server/iscsi.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++------ src/server/iscsi.h | 4 ++- 2 files changed, 92 insertions(+), 11 deletions(-) diff --git a/src/server/iscsi.c b/src/server/iscsi.c index d833076..871f8bb 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -2781,7 +2781,8 @@ void iscsi_scsi_task_destroy(iscsi_scsi_task *scsi_task) { if ( (scsi_task != NULL) && (--scsi_task->ref == 0UL) ) { if ( scsi_task->buf != NULL ) { - free( scsi_task->buf ); + if ( (scsi_task->flags & ISCSI_SCSI_TASK_FLAGS_XFER_WRITE) == 0 ) + free( scsi_task->buf ); scsi_task->buf = NULL; } @@ -2856,7 +2857,7 @@ void iscsi_scsi_task_sense_data_build(iscsi_scsi_task *scsi_task, const uint8_t sense_data->cmd_spec_info = 0UL; // Zero does not require endianess conversion sense_data->asc = asc; sense_data->ascq = ascq; - sense_data->field_rep_unit_code = 0UL; + sense_data->field_rep_unit_code = 0U; sense_data->sense_key_spec_flags = 0U; sense_data->sense_key_spec = 0U; // Zero does not require endianess conversion @@ -4271,7 +4272,13 @@ static int iscsi_scsi_emu_block_read_write(dnbd3_image_t *image, iscsi_scsi_task } const uint32_t block_size = iscsi_scsi_emu_block_get_size( image ); - const uint32_t max_xfer_len = (iscsi_is_pow2( block_size ) ? (ISCSI_SCSI_EMU_MAX_XFER_LEN >> iscsi_scsi_emu_block_get_size_shift( image )) : (ISCSI_SCSI_EMU_MAX_XFER_LEN / block_size)); + const bool block_size_pow2 = iscsi_is_pow2( block_size ); + uint32_t block_size_shift; + + if ( block_size_pow2 ) + block_size_shift = iscsi_scsi_emu_block_get_size_shift( image ); + + const uint32_t max_xfer_len = (block_size_pow2 ? (ISCSI_SCSI_EMU_MAX_XFER_LEN >> block_size_shift) : (ISCSI_SCSI_EMU_MAX_XFER_LEN / block_size)); if ( xfer_len > max_xfer_len ) { iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ, ISCSI_SCSI_ASC_INVALID_FIELD_IN_CDB, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE ); @@ -4279,7 +4286,7 @@ static int iscsi_scsi_emu_block_read_write(dnbd3_image_t *image, iscsi_scsi_task return ISCSI_SCSI_TASK_RUN_COMPLETE; } - if ( ((flags & ISCSI_SCSI_EMU_BLOCK_FLAGS_WRITE) != 0) && ((xfer_len + block_size) > scsi_task->xfer_len) ) { + if ( ((flags & ISCSI_SCSI_EMU_BLOCK_FLAGS_WRITE) != 0) && ((block_size_pow2 ? (xfer_len << block_size_shift) : (xfer_len * block_size)) > scsi_task->xfer_len) ) { iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_NO_SENSE, ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE ); return ISCSI_SCSI_TASK_RUN_COMPLETE; @@ -10071,10 +10078,9 @@ static int iscsi_connection_pdu_header_handle_scsi_cmd(iscsi_connection *conn, i return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_INVALID_PDU_FIELD ); } - if ( (scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_READ) != 0 ) + if ( (scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_READ) != 0 ) { task->scsi_task.flags |= ISCSI_SCSI_TASK_FLAGS_XFER_READ; - - if ( (scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_WRITE) != 0 ) { + } else if ( (scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_WRITE) != 0 ) { task->scsi_task.flags |= ISCSI_SCSI_TASK_FLAGS_XFER_WRITE; if ( (conn->session->err_recovery_level > 0UL) && (iscsi_r2t_find_pdu_bhs( conn, pdu ) != NULL) ) { @@ -10111,7 +10117,6 @@ static int iscsi_connection_pdu_header_handle_scsi_cmd(iscsi_connection *conn, i } pdu->task = task; - pdu->task_ref_cnt++; return ISCSI_CONNECT_PDU_READ_OK; } @@ -10766,6 +10771,47 @@ static int iscsi_connection_pdu_data_handle_scsi_cmd_read(iscsi_connection *conn return iscsi_connection_handle_scsi_data_in_queued_tasks( conn ); } +/** + * @brief Creates and submits a sub task for writing. + * + * This function is also assigns the task with + * an iSCSI PDU. + * + * @param[in] conn Pointer to iSCSI connection which handles + * this task. May NOT be NULL, so be caureful. + * @param[in] task Pointer to iSCSI task which should be the + * parent of the new sub task. NULL if NOT + * allowed here, so take caution. + * @param[in] pdu Pointer to iSCSI PDU which contains + * the desired buffer length to write. NULL + * is a prohibited value here, take caution. + * @param[in] buf Pointer to buffer containing + * the write data and may NOT be NULL, so + * be careful. + * @return 0 on successful sub task submit or a + * negative error code otherwise. + */ +static int iscsi_task_sub_task_submit_write(iscsi_connection *conn, iscsi_task *task, iscsi_pdu *pdu, uint8_t *buf) +{ + iscsi_task *sub_task = iscsi_task_create( conn, task, iscsi_scsi_task_xfer_complete ); + + if ( sub_task == NULL ) + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + + sub_task->scsi_task.buf = buf; + sub_task->scsi_task.pos = task->pos; + sub_task->scsi_task.len = pdu->ds_len; + + pdu->task = sub_task; + pdu->ref++; + + task->pos += pdu->ds_len; + + iscsi_task_queue( conn, sub_task ); + + return ISCSI_CONNECT_PDU_READ_OK; +} + /** * @brief Handles an incoming iSCSI payload data SCSI write command request PDU. * @@ -10783,9 +10829,42 @@ static int iscsi_connection_pdu_data_handle_scsi_cmd_read(iscsi_connection *conn */ static int iscsi_connection_pdu_data_handle_scsi_cmd_write(iscsi_connection *conn, iscsi_task *task) { - // TODO: Implement SCSI command write (COW). + iscsi_pdu *pdu = task->pdu; + iscsi_scsi_cmd_packet *scsi_cmd_pkt = (iscsi_scsi_cmd_packet *) pdu->bhs_pkt; + const uint32_t xfer_len = task->scsi_task.xfer_len; - return 0; + if ( ((scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_FINAL) != 0) && (pdu->ds_len < xfer_len) ) { + int rc = iscsi_task_xfer_add( conn, task ); + + if ( rc < 0 ) { + iscsi_task_destroy( task ); + + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + } + + if ( pdu->ds_len != 0UL ) { + rc = iscsi_task_sub_task_submit_write( conn, task, pdu, (uint8_t *) pdu->ds_cmd_data ); + + if ( rc < 0 ) { + iscsi_task_destroy( task ); + + return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + } + } + + return ISCSI_CONNECT_PDU_READ_OK; + } + + if ( pdu->ds_len == xfer_len ) { + iscsi_scsi_task *scsi_task = &task->scsi_task; + + scsi_task->buf = (uint8_t *) pdu->ds_cmd_data; + scsi_task->len = xfer_len; + } + + iscsi_task_queue( conn, task ); + + return ISCSI_CONNECT_PDU_READ_OK; } /** diff --git a/src/server/iscsi.h b/src/server/iscsi.h index 4ee5dac..eff6f11 100644 --- a/src/server/iscsi.h +++ b/src/server/iscsi.h @@ -345,6 +345,7 @@ uint8_t *iscsi_vsprintf_alloc(const char *format, va_list args); // Allocates a uint8_t *iscsi_sprintf_alloc(const char *format, ... ); // Allocates a buffer and sprintf's it void iscsi_strcpy_pad(char *dst, const char *src, const size_t size, const int pad); // Copies a string with additional padding character to fill in a specified size + /// Shift factor for default capacity. #define ISCSI_HASHMAP_DEFAULT_CAPACITY_SHIFT 5UL @@ -764,6 +765,7 @@ uint iscsi_hashmap_size(const iscsi_hashmap *map); // Retrieves the number of el int iscsi_hashmap_iterate(iscsi_hashmap *map, iscsi_hashmap_callback callback, uint8_t *user_data); // Iterator with callback function invoked on each element + /* iSCSI protocol stuff (all WORD/DWORD/QWORD values are big endian by default unless specified otherwise). */ @@ -3735,7 +3737,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_sense_data_check_cond_packet { uint8_t ascq; /// Field replaceable unit code. - uint32_t field_rep_unit_code; + uint8_t field_rep_unit_code; /// Sense key specific. uint8_t sense_key_spec_flags; -- cgit v1.2.3-55-g7522