summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastian Vater2025-09-30 12:29:21 +0200
committerSebastian Vater2025-09-30 12:29:21 +0200
commit89180d6083a06b96d2e1250b333b91e7936517fc (patch)
treedc8d4844264af4c184a5b94e9ff197fd059ff61d
parentImages are now propagated as a read-only SCSI device in iSCSI implementation ... (diff)
downloaddnbd3-89180d6083a06b96d2e1250b333b91e7936517fc.tar.gz
dnbd3-89180d6083a06b96d2e1250b333b91e7936517fc.tar.xz
dnbd3-89180d6083a06b96d2e1250b333b91e7936517fc.zip
Fixed crash in iSCSI server if a write command is sent.
-rw-r--r--src/server/iscsi.c99
-rw-r--r--src/server/iscsi.h4
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;
}
@@ -10767,6 +10772,47 @@ static int iscsi_connection_pdu_data_handle_scsi_cmd_read(iscsi_connection *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.
*
* This function handles SCSI write command
@@ -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;