lib/scsi: fix SCSI_PERSISTENT_RESERVE_READ_KEYS handling
When unmarshalling a SCSI_PERSISTENT_RESERVE_READ_KEYS response, scsi_persistentreservein_datain_unmarshall() assumes that the ADDITIONAL LENGTH field represents the number of keys packed in the key array. This is incorrect as key array data buffer may be truncated while ADDITIONAL LENGTH is left in tact, as per SPC5r17 4.2.5.6: If the information being transferred to the Data-In Buffer includes fields containing counts ..., then the contents of these fields shall not be altered to reflect the truncation, if any, that results from an insufficient ALLOCATION LENGTH value, unless the standard that describes the Data-In Buffer format states otherwise. Determine the number of keys returned based on the minimum of the data-in length and the ADDITIONAL LENGTH value. Signed-off-by: David Disseldorp <ddiss@suse.de>
This commit is contained in:
@@ -962,7 +962,9 @@ scsi_receivecopyresults_datain_unmarshall(struct scsi_task *task)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifndef MIN /* instead of including all of iscsi-private.h */
|
||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
static void *
|
||||
scsi_persistentreservein_datain_unmarshall(struct scsi_task *task)
|
||||
{
|
||||
@@ -972,21 +974,44 @@ scsi_persistentreservein_datain_unmarshall(struct scsi_task *task)
|
||||
int i;
|
||||
|
||||
switch (scsi_persistentreservein_sa(task)) {
|
||||
case SCSI_PERSISTENT_RESERVE_READ_KEYS:
|
||||
i = task_get_uint32(task, 4);
|
||||
case SCSI_PERSISTENT_RESERVE_READ_KEYS: {
|
||||
uint32_t cdb_keys_len;
|
||||
uint32_t data_keys_len;
|
||||
uint32_t keys_len;
|
||||
|
||||
rk = scsi_malloc(task, offsetof(struct scsi_persistent_reserve_in_read_keys, keys) + i);
|
||||
if (task->datain.size < 8) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* SPC5r17: 6.16.2 READ KEYS service action
|
||||
* The ADDITIONAL LENGTH field indicates the number of bytes in
|
||||
* the Reservation key list. The contents of the ADDITIONAL
|
||||
* LENGTH field are not altered based on the allocation length.
|
||||
*/
|
||||
cdb_keys_len = task_get_uint32(task, 4);
|
||||
data_keys_len = task->datain.size - 8;
|
||||
/*
|
||||
* Only process as many keys as permitted by the given
|
||||
* ADDITIONAL LENGTH and data-in size limits.
|
||||
*/
|
||||
keys_len = MIN(cdb_keys_len, data_keys_len);
|
||||
|
||||
rk = scsi_malloc(task,
|
||||
offsetof(struct scsi_persistent_reserve_in_read_keys,
|
||||
keys) + keys_len);
|
||||
if (rk == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
rk->prgeneration = task_get_uint32(task, 0);
|
||||
rk->additional_length = task_get_uint32(task, 4);
|
||||
rk->additional_length = cdb_keys_len;
|
||||
|
||||
rk->num_keys = rk->additional_length / 8;
|
||||
rk->num_keys = keys_len / 8;
|
||||
for (i = 0; i < (int)rk->num_keys; i++) {
|
||||
rk->keys[i] = task_get_uint64(task, 8 + i * 8);
|
||||
}
|
||||
return rk;
|
||||
}
|
||||
case SCSI_PERSISTENT_RESERVE_READ_RESERVATION: {
|
||||
size_t alloc_sz;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user