TESTS: Add checks for CDB sanity to REPORT SUPPORTED OPCODES
Read all individual opcodes and check that CDB length > 0 and that CDB[0] Usage Data is 0xFF
This commit is contained in:
@@ -760,6 +760,16 @@ struct scsi_report_supported_op_codes {
|
|||||||
struct scsi_command_descriptor descriptors[0];
|
struct scsi_command_descriptor descriptors[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct scsi_report_supported_op_codes_one_command {
|
||||||
|
uint8_t ctdp;
|
||||||
|
uint8_t support;
|
||||||
|
uint8_t cdb_length;
|
||||||
|
uint8_t cdb_usage_data[16];
|
||||||
|
|
||||||
|
/* only present if CTDP==1 */
|
||||||
|
struct scsi_op_timeout_descriptor to;
|
||||||
|
};
|
||||||
|
|
||||||
struct scsi_persistent_reserve_in_read_keys {
|
struct scsi_persistent_reserve_in_read_keys {
|
||||||
uint32_t prgeneration;
|
uint32_t prgeneration;
|
||||||
uint32_t additional_length;
|
uint32_t additional_length;
|
||||||
|
|||||||
@@ -812,6 +812,12 @@ scsi_maintenancein_sa(const struct scsi_task *task)
|
|||||||
return task->cdb[1];
|
return task->cdb[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline uint8_t
|
||||||
|
scsi_report_supported_opcodes_options(const struct scsi_task *task)
|
||||||
|
{
|
||||||
|
return task->cdb[2] & 0x07;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* parse the data in blob and calculate the size of a full maintenancein
|
* parse the data in blob and calculate the size of a full maintenancein
|
||||||
* datain structure
|
* datain structure
|
||||||
@@ -822,7 +828,15 @@ scsi_maintenancein_datain_getfullsize(struct scsi_task *task)
|
|||||||
|
|
||||||
switch (scsi_maintenancein_sa(task)) {
|
switch (scsi_maintenancein_sa(task)) {
|
||||||
case SCSI_REPORT_SUPPORTED_OP_CODES:
|
case SCSI_REPORT_SUPPORTED_OP_CODES:
|
||||||
return task_get_uint32(task, 0) + 4;
|
switch (scsi_report_supported_opcodes_options(task)) {
|
||||||
|
case SCSI_REPORT_SUPPORTING_OPS_ALL:
|
||||||
|
return task_get_uint32(task, 0) + 4;
|
||||||
|
case SCSI_REPORT_SUPPORTING_OPCODE:
|
||||||
|
case SCSI_REPORT_SUPPORTING_SERVICEACTION:
|
||||||
|
return 4 +
|
||||||
|
(task_get_uint8(task, 1) & 0x80) ? 12 : 0 +
|
||||||
|
task_get_uint16(task, 2);
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -835,64 +849,104 @@ static void *
|
|||||||
scsi_maintenancein_datain_unmarshall(struct scsi_task *task)
|
scsi_maintenancein_datain_unmarshall(struct scsi_task *task)
|
||||||
{
|
{
|
||||||
struct scsi_report_supported_op_codes *rsoc;
|
struct scsi_report_supported_op_codes *rsoc;
|
||||||
|
struct scsi_report_supported_op_codes_one_command *rsoc_one;
|
||||||
int len, i;
|
int len, i;
|
||||||
|
|
||||||
switch (scsi_maintenancein_sa(task)) {
|
switch (scsi_maintenancein_sa(task)) {
|
||||||
case SCSI_REPORT_SUPPORTED_OP_CODES:
|
case SCSI_REPORT_SUPPORTED_OP_CODES:
|
||||||
if (task->datain.size < 4) {
|
switch (scsi_report_supported_opcodes_options(task)) {
|
||||||
return NULL;
|
case SCSI_REPORT_SUPPORTING_OPS_ALL:
|
||||||
}
|
if (task->datain.size < 4) {
|
||||||
|
return NULL;
|
||||||
len = task_get_uint32(task, 0);
|
|
||||||
/* len / 8 is not always correct since if CTDP==1 then the
|
|
||||||
* descriptor is 20 bytes in size intead of 8.
|
|
||||||
* It doesnt matter here though since it just means we would
|
|
||||||
* allocate more descriptors at the end of the structure than
|
|
||||||
* we strictly need. This avoids having to traverse the
|
|
||||||
* datain buffer twice.
|
|
||||||
*/
|
|
||||||
rsoc = scsi_malloc(task,
|
|
||||||
offsetof(struct scsi_report_supported_op_codes,
|
|
||||||
descriptors) +
|
|
||||||
len / 8 * sizeof(struct scsi_command_descriptor));
|
|
||||||
if (rsoc == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
rsoc->num_descriptors = 0;
|
|
||||||
i = 4;
|
|
||||||
while (len >= 8) {
|
|
||||||
struct scsi_command_descriptor *desc;
|
|
||||||
|
|
||||||
desc = &rsoc->descriptors[rsoc->num_descriptors++];
|
|
||||||
desc->opcode = task_get_uint8(task, i);
|
|
||||||
desc->sa = task_get_uint16(task, i + 2);
|
|
||||||
desc->ctdp = !!(task_get_uint8(task, i + 5) & 0x02);
|
|
||||||
desc->servactv = !!(task_get_uint8(task, i + 5) & 0x01);
|
|
||||||
desc->cdb_len = task_get_uint16(task, i + 6);
|
|
||||||
|
|
||||||
len -= 8;
|
|
||||||
i += 8;
|
|
||||||
|
|
||||||
/* No tiemout description */
|
|
||||||
if (!desc->ctdp) {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
desc->to.descriptor_length =
|
len = task_get_uint32(task, 0);
|
||||||
task_get_uint16(task, i);
|
/* len / 8 is not always correct since if CTDP==1 then
|
||||||
desc->to.command_specific =
|
* the descriptor is 20 bytes in size intead of 8.
|
||||||
task_get_uint8(task, i + 3);
|
* It doesnt matter here though since it just means
|
||||||
desc->to.nominal_processing_timeout =
|
* we would allocate more descriptors at the end of
|
||||||
task_get_uint32(task, i + 4);
|
* the structure than we strictly need. This avoids
|
||||||
desc->to.recommended_timeout =
|
* having to traverse the datain buffer twice.
|
||||||
task_get_uint32(task, i + 8);
|
*/
|
||||||
|
rsoc = scsi_malloc(task,
|
||||||
|
offsetof(struct scsi_report_supported_op_codes,
|
||||||
|
descriptors) +
|
||||||
|
len / 8 * sizeof(struct scsi_command_descriptor));
|
||||||
|
if (rsoc == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
len -= desc->to.descriptor_length + 2;
|
rsoc->num_descriptors = 0;
|
||||||
i += desc->to.descriptor_length + 2;
|
i = 4;
|
||||||
|
while (len >= 8) {
|
||||||
|
struct scsi_command_descriptor *desc;
|
||||||
|
|
||||||
|
desc = &rsoc->descriptors[rsoc->num_descriptors++];
|
||||||
|
desc->opcode =
|
||||||
|
task_get_uint8(task, i);
|
||||||
|
desc->sa =
|
||||||
|
task_get_uint16(task, i + 2);
|
||||||
|
desc->ctdp =
|
||||||
|
!!(task_get_uint8(task, i + 5) & 0x02);
|
||||||
|
desc->servactv =
|
||||||
|
!!(task_get_uint8(task, i + 5) & 0x01);
|
||||||
|
desc->cdb_len =
|
||||||
|
task_get_uint16(task, i + 6);
|
||||||
|
|
||||||
|
len -= 8;
|
||||||
|
i += 8;
|
||||||
|
|
||||||
|
/* No tiemout description */
|
||||||
|
if (!desc->ctdp) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
desc->to.descriptor_length =
|
||||||
|
task_get_uint16(task, i);
|
||||||
|
desc->to.command_specific =
|
||||||
|
task_get_uint8(task, i + 3);
|
||||||
|
desc->to.nominal_processing_timeout =
|
||||||
|
task_get_uint32(task, i + 4);
|
||||||
|
desc->to.recommended_timeout =
|
||||||
|
task_get_uint32(task, i + 8);
|
||||||
|
|
||||||
|
len -= desc->to.descriptor_length + 2;
|
||||||
|
i += desc->to.descriptor_length + 2;
|
||||||
|
}
|
||||||
|
return rsoc;
|
||||||
|
case SCSI_REPORT_SUPPORTING_OPCODE:
|
||||||
|
case SCSI_REPORT_SUPPORTING_SERVICEACTION:
|
||||||
|
rsoc_one = scsi_malloc(task, sizeof(struct scsi_report_supported_op_codes_one_command));
|
||||||
|
if (rsoc_one == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
rsoc_one->ctdp =
|
||||||
|
!!(task_get_uint8(task, 1) & 0x80);
|
||||||
|
rsoc_one->support =
|
||||||
|
task_get_uint8(task, 1) & 0x07;
|
||||||
|
rsoc_one->cdb_length =
|
||||||
|
task_get_uint16(task, 2);
|
||||||
|
if(rsoc_one->cdb_length <= 16) {
|
||||||
|
memcpy(rsoc_one->cdb_usage_data,
|
||||||
|
&task->datain.data[4],
|
||||||
|
rsoc_one->cdb_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rsoc_one->ctdp) {
|
||||||
|
i = 4 + rsoc_one->cdb_length;
|
||||||
|
|
||||||
|
rsoc_one->to.descriptor_length =
|
||||||
|
task_get_uint16(task, i);
|
||||||
|
rsoc_one->to.command_specific =
|
||||||
|
task_get_uint8(task, i + 3);
|
||||||
|
rsoc_one->to.nominal_processing_timeout =
|
||||||
|
task_get_uint32(task, i + 4);
|
||||||
|
rsoc_one->to.recommended_timeout =
|
||||||
|
task_get_uint32(task, i + 8);
|
||||||
|
}
|
||||||
|
return rsoc_one;
|
||||||
}
|
}
|
||||||
|
|
||||||
return rsoc;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|||||||
@@ -30,7 +30,9 @@ test_report_supported_opcodes_one_command(void)
|
|||||||
{
|
{
|
||||||
int i, ret;
|
int i, ret;
|
||||||
struct scsi_task *rso_task;
|
struct scsi_task *rso_task;
|
||||||
|
struct scsi_task *one_task;
|
||||||
struct scsi_report_supported_op_codes *rsoc;
|
struct scsi_report_supported_op_codes *rsoc;
|
||||||
|
struct scsi_report_supported_op_codes_one_command *rsoc_one;
|
||||||
|
|
||||||
logging(LOG_VERBOSE, LOG_BLANK_LINE);
|
logging(LOG_VERBOSE, LOG_BLANK_LINE);
|
||||||
logging(LOG_VERBOSE, "Test READ_SUPPORTED_OPCODES reading one-command");
|
logging(LOG_VERBOSE, "Test READ_SUPPORTED_OPCODES reading one-command");
|
||||||
@@ -56,7 +58,7 @@ test_report_supported_opcodes_one_command(void)
|
|||||||
CU_ASSERT_NOT_EQUAL(rsoc, NULL);
|
CU_ASSERT_NOT_EQUAL(rsoc, NULL);
|
||||||
|
|
||||||
|
|
||||||
logging(LOG_VERBOSE, "Verify read one-command for all supported "
|
logging(LOG_VERBOSE, "Verify read one-command works for all supported "
|
||||||
"opcodes");
|
"opcodes");
|
||||||
for (i = 0; i < rsoc->num_descriptors; i++) {
|
for (i = 0; i < rsoc->num_descriptors; i++) {
|
||||||
logging(LOG_VERBOSE, "Check opcode:0x%02x ServiceAction:0x%02x",
|
logging(LOG_VERBOSE, "Check opcode:0x%02x ServiceAction:0x%02x",
|
||||||
@@ -107,5 +109,41 @@ test_report_supported_opcodes_one_command(void)
|
|||||||
CU_ASSERT_EQUAL(ret, 0);
|
CU_ASSERT_EQUAL(ret, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
logging(LOG_VERBOSE, "Verify read one-command CDB looks sane");
|
||||||
|
for (i = 0; i < rsoc->num_descriptors; i++) {
|
||||||
|
logging(LOG_VERBOSE, "Check CDB for opcode:0x%02x "
|
||||||
|
"ServiceAction:0x%02x",
|
||||||
|
rsoc->descriptors[i].opcode,
|
||||||
|
rsoc->descriptors[i].sa);
|
||||||
|
ret = report_supported_opcodes(
|
||||||
|
iscsic, tgt_lun,
|
||||||
|
0,
|
||||||
|
rsoc->descriptors[i].servactv ?
|
||||||
|
SCSI_REPORT_SUPPORTING_SERVICEACTION :
|
||||||
|
SCSI_REPORT_SUPPORTING_OPCODE,
|
||||||
|
rsoc->descriptors[i].opcode,
|
||||||
|
rsoc->descriptors[i].sa,
|
||||||
|
65535, &one_task);
|
||||||
|
|
||||||
|
logging(LOG_VERBOSE, "Unmarshall the DATA-IN buffer");
|
||||||
|
rsoc_one = scsi_datain_unmarshall(one_task);
|
||||||
|
CU_ASSERT_NOT_EQUAL(rsoc_one, NULL);
|
||||||
|
|
||||||
|
logging(LOG_VERBOSE, "Verify CDB length is not 0");
|
||||||
|
CU_ASSERT_NOT_EQUAL(rsoc_one->cdb_length, 0);
|
||||||
|
if (rsoc_one->cdb_length != 0) {
|
||||||
|
logging(LOG_NORMAL, "[FAILED] CDB length is 0");
|
||||||
|
}
|
||||||
|
|
||||||
|
logging(LOG_VERBOSE, "Verify CDB[0] Usage Data is 0xFF");
|
||||||
|
CU_ASSERT_EQUAL(rsoc_one->cdb_usage_data[0], 0xff);
|
||||||
|
if (rsoc_one->cdb_usage_data[0] != 0xff) {
|
||||||
|
logging(LOG_NORMAL, "[FAILED] CDB[0] Usage Data is "
|
||||||
|
"not 0xFF");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
scsi_free_scsi_task(rso_task);
|
scsi_free_scsi_task(rso_task);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user