On Fri, Feb 23, 2024 at 10:23:09PM +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 <[email protected]>
> ---
>  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));

I'm genuinely not sure what to do here, but I suspect your approach is
safest.

I can't see where chap_table->secret is getting set, but if it was
longer than 100 bytes, there was a bug here, as strncpy() would truncate
but chap_table->secret_len would continue to have the original length.
However, it looks like the buf_len passed to iscsi_set_param() is
ignored. O_o

>       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);

As you mentioned in the commit log, this is a weird destination, but
is a legit calculation.

>       } 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));
>  
>               if (chap_table->flags & BIT_7) /* local */
>                       chap_rec->chap_type = CHAP_TYPE_OUT;
> @@ -6291,8 +6291,8 @@ static void qla4xxx_get_param_ddb(struct ddb_entry 
> *ddb_entry,
>  
>       tddb->tpgt = sess->tpgt;
>       tddb->port = conn->persistent_port;
> -     strscpy(tddb->iscsi_name, sess->targetname, ISCSI_NAME_SIZE);
> -     strscpy(tddb->ip_addr, conn->persistent_address, DDB_IPADDR_LEN);
> +     strscpy(tddb->iscsi_name, sess->targetname, sizeof(tddb->iscsi_name));
> +     strscpy(tddb->ip_addr, conn->persistent_address, sizeof(tddb->ip_addr));
>  }
>  
>  static void qla4xxx_convert_param_ddb(struct dev_db_entry *fw_ddb_entry,
> @@ -7792,7 +7792,7 @@ static int qla4xxx_sysfs_ddb_logout(struct 
> iscsi_bus_flash_session *fnode_sess,
>       }
>  
>       strscpy(flash_tddb->iscsi_name, fnode_sess->targetname,
> -             ISCSI_NAME_SIZE);
> +             sizeof(flash_tddb->iscsi_name));
>  
>       if (!strncmp(fnode_sess->portal_type, PORTAL_TYPE_IPV6, 4))
>               sprintf(flash_tddb->ip_addr, "%pI6", fnode_conn->ipaddress);
> 
> -- 
> 2.44.0.rc0.258.g7320e95886-goog
> 

Reviewed-by: Kees Cook <[email protected]>

-- 
Kees Cook

Reply via email to