On Wed, Feb 28, 2024 at 10:59:04PM +0000, Justin Stitt wrote: > Replace 3 instances of strncpy in ql4_mbx.c > > No bugs exist in the current implementation as some care was taken to > ensure the write length was decreased by one to leave some space for a > NUL-byte. However, instead of using strncpy(dest, src, LEN-1) we can opt > for strscpy(dest, src, sizeof(dest)) which will result in > NUL-termination as well. It should be noted that the entire chap_table > is zero-allocated so the NUL-padding provided by strncpy is not needed. > > While here, I noticed that MIN_CHAP_SECRET_LEN was not used anywhere. > Since strscpy gives us the number of bytes copied into the destination > buffer (or an -E2BIG) we can check both for an error during copying and > also for a non-length compliant secret. Add a new jump label so we can > properly clean up our chap_table should we have to abort due to bad > secret. > > The third instance in this file involves some more peculiar handling of > strings: > | uint32_t mbox_cmd[MBOX_REG_COUNT]; > | ... > | memset(&mbox_cmd, 0, sizeof(mbox_cmd)); > | ... > | mbox_cmd[0] = MBOX_CMD_SET_PARAM; > | if (param == SET_DRVR_VERSION) { > | mbox_cmd[1] = SET_DRVR_VERSION; > | strncpy((char *)&mbox_cmd[2], QLA4XXX_DRIVER_VERSION, > | MAX_DRVR_VER_LEN - 1); > > mbox_cmd has a size of 8: > | #define MBOX_REG_COUNT 8 > ... and its type width is 4 bytes. Hence, we have 32 bytes to work with > here. The first 4 bytes are used as a flag for the MBOX_CMD_SET_PARAM. > The next 4 bytes are used for SET_DRVR_VERSION. We now have 32-8=24 > bytes remaining -- which thankfully is what MAX_DRVR_VER_LEN is equal to > | #define MAX_DRVR_VER_LEN 24 > > ... and the thing we're copying into this pseudo-string buffer is > | #define QLA4XXX_DRIVER_VERSION "5.04.00-k6" > > ... which is great because its less than 24 bytes (therefore we aren't > truncating the source). > > All to say, there's no bug in the existing implementation (yay!) but we > can clean the code up a bit by using strscpy(). > > In ql4_os.c, there aren't any strncpy() uses to replace but there are > some existing strscpy() calls that could be made more idiomatic. Where > possible, use strscpy(dest, src, sizeof(dest)). Note that > chap_rec->password has a size of ISCSI_CHAP_AUTH_SECRET_MAX_LEN > | #define ISCSI_CHAP_AUTH_SECRET_MAX_LEN 256 > ... while the current strscpy usage uses QL4_CHAP_MAX_SECRET_LEN > | #define QL4_CHAP_MAX_SECRET_LEN 100 > ... however since chap_table->secret was set and bounded properly in its > string assignment its probably safe here to switch over to sizeof(). > > | struct iscsi_chap_rec { > ... > | char username[ISCSI_CHAP_AUTH_NAME_MAX_LEN]; > | uint8_t password[ISCSI_CHAP_AUTH_SECRET_MAX_LEN]; > ... > | }; > > | strscpy(chap_rec->password, chap_table->secret, > | QL4_CHAP_MAX_SECRET_LEN); > > Signed-off-by: Justin Stitt <justinstitt@xxxxxxxxxx> > --- > drivers/scsi/qla4xxx/ql4_mbx.c | 17 ++++++++++++----- > drivers/scsi/qla4xxx/ql4_os.c | 14 +++++++------- > 2 files changed, 19 insertions(+), 12 deletions(-) > > diff --git a/drivers/scsi/qla4xxx/ql4_mbx.c b/drivers/scsi/qla4xxx/ql4_mbx.c > index 249f1d7021d4..75125d2021f5 100644 > --- a/drivers/scsi/qla4xxx/ql4_mbx.c > +++ b/drivers/scsi/qla4xxx/ql4_mbx.c > @@ -1641,6 +1641,7 @@ int qla4xxx_set_chap(struct scsi_qla_host *ha, char *username, char *password, > struct ql4_chap_table *chap_table; > uint32_t chap_size = 0; > dma_addr_t chap_dma; > + ssize_t secret_len; > > chap_table = dma_pool_zalloc(ha->chap_dma_pool, GFP_KERNEL, &chap_dma); > if (chap_table == NULL) { > @@ -1652,9 +1653,13 @@ int qla4xxx_set_chap(struct scsi_qla_host *ha, char *username, char *password, > chap_table->flags |= BIT_6; /* peer */ > else > chap_table->flags |= BIT_7; /* local */ > - chap_table->secret_len = strlen(password); > - strncpy(chap_table->secret, password, MAX_CHAP_SECRET_LEN - 1); > - strncpy(chap_table->name, username, MAX_CHAP_NAME_LEN - 1); > + > + secret_len = strscpy(chap_table->secret, password, > + sizeof(chap_table->secret)); > + if (secret_len < MIN_CHAP_SECRET_LEN) > + goto cleanup_chap_table; > + chap_table->secret_len = (uint8_t)secret_len; > + strscpy(chap_table->name, username, sizeof(chap_table->name)); > chap_table->cookie = cpu_to_le16(CHAP_VALID_COOKIE); > > if (is_qla40XX(ha)) { > @@ -1679,6 +1684,8 @@ int qla4xxx_set_chap(struct scsi_qla_host *ha, char *username, char *password, > memcpy((struct ql4_chap_table *)ha->chap_list + idx, > chap_table, sizeof(struct ql4_chap_table)); > } > + > +cleanup_chap_table: > dma_pool_free(ha->chap_dma_pool, chap_table, chap_dma); > if (rval != QLA_SUCCESS) > ret = -EINVAL; > @@ -2281,8 +2288,8 @@ int qla4_8xxx_set_param(struct scsi_qla_host *ha, int param) > mbox_cmd[0] = MBOX_CMD_SET_PARAM; > if (param == SET_DRVR_VERSION) { > mbox_cmd[1] = SET_DRVR_VERSION; > - strncpy((char *)&mbox_cmd[2], QLA4XXX_DRIVER_VERSION, > - MAX_DRVR_VER_LEN - 1); > + strscpy((char *)&mbox_cmd[2], QLA4XXX_DRIVER_VERSION, > + MAX_DRVR_VER_LEN); > } else { > ql4_printk(KERN_ERR, ha, "%s: invalid parameter 0x%x\n", > __func__, param); > diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c > index 675332e49a7b..17cccd14765f 100644 > --- a/drivers/scsi/qla4xxx/ql4_os.c > +++ b/drivers/scsi/qla4xxx/ql4_os.c > @@ -799,10 +799,10 @@ static int qla4xxx_get_chap_list(struct Scsi_Host *shost, uint16_t chap_tbl_idx, > > chap_rec->chap_tbl_idx = i; > strscpy(chap_rec->username, chap_table->name, > - ISCSI_CHAP_AUTH_NAME_MAX_LEN); > - strscpy(chap_rec->password, chap_table->secret, > - QL4_CHAP_MAX_SECRET_LEN); > - chap_rec->password_length = chap_table->secret_len; > + sizeof(chap_rec->username)); > + chap_rec->password_length = strscpy(chap_rec->password, > + chap_table->secret, > + sizeof(chap_rec->password)); This hunk took me a bit to convince myself it's safe, but yes, I agree. It all boils down to sizeof(chap_table->secret) being less than sizeof(chap_rec->password), so there's no behavioral change here. Reviewed-by: Kees Cook <keescook@xxxxxxxxxxxx> -- Kees Cook