2020-09-22 04:01:29

by Kiran K

[permalink] [raw]
Subject: [PATCH v1] Bluetooth: btintel: Fix issue reported by static analysis tool

Smatch tool reported the below issue:

drivers/bluetooth/btintel.c:490 btintel_read_version_tlv()
error: 'tlv->len' from user is not capped properly

Additional details in the below link
https://www.spinics.net/lists/linux-bluetooth/msg87786.html

Signed-off-by: Kiran K <[email protected]>
---
drivers/bluetooth/btintel.c | 43 ++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 42 insertions(+), 1 deletion(-)

diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c
index 88ce5f0..47f2b3d 100644
--- a/drivers/bluetooth/btintel.c
+++ b/drivers/bluetooth/btintel.c
@@ -431,62 +431,99 @@ int btintel_read_version_tlv(struct hci_dev *hdev, struct intel_version_tlv *ver
* version field like hw_platform, hw_variant, and fw_variant
* to keep the existing setup flow
*/
- while (skb->len) {
+ while (skb->len >= sizeof(struct intel_tlv)) {
struct intel_tlv *tlv;

tlv = (struct intel_tlv *)skb->data;
+ if (struct_size(tlv, val, tlv->len) > skb->len)
+ goto failed;
+
switch (tlv->type) {
case INTEL_TLV_CNVI_TOP:
+ if (tlv->len < sizeof(u32))
+ goto failed;
version->cnvi_top = get_unaligned_le32(tlv->val);
break;
case INTEL_TLV_CNVR_TOP:
+ if (tlv->len < sizeof(u32))
+ goto failed;
version->cnvr_top = get_unaligned_le32(tlv->val);
break;
case INTEL_TLV_CNVI_BT:
+ if (tlv->len < sizeof(u32))
+ goto failed;
version->cnvi_bt = get_unaligned_le32(tlv->val);
break;
case INTEL_TLV_CNVR_BT:
+ if (tlv->len < sizeof(u32))
+ goto failed;
version->cnvr_bt = get_unaligned_le32(tlv->val);
break;
case INTEL_TLV_DEV_REV_ID:
+ if (tlv->len < sizeof(u16))
+ goto failed;
version->dev_rev_id = get_unaligned_le16(tlv->val);
break;
case INTEL_TLV_IMAGE_TYPE:
+ if (tlv->len < sizeof(u8))
+ goto failed;
version->img_type = tlv->val[0];
break;
case INTEL_TLV_TIME_STAMP:
+ if (tlv->len < sizeof(u16))
+ goto failed;
version->timestamp = get_unaligned_le16(tlv->val);
break;
case INTEL_TLV_BUILD_TYPE:
+ if (tlv->len < sizeof(u8))
+ goto failed;
version->build_type = tlv->val[0];
break;
case INTEL_TLV_BUILD_NUM:
+ if (tlv->len < sizeof(u32))
+ goto failed;
version->build_num = get_unaligned_le32(tlv->val);
break;
case INTEL_TLV_SECURE_BOOT:
+ if (tlv->len < sizeof(u8))
+ goto failed;
version->secure_boot = tlv->val[0];
break;
case INTEL_TLV_OTP_LOCK:
+ if (tlv->len < sizeof(u8))
+ goto failed;
version->otp_lock = tlv->val[0];
break;
case INTEL_TLV_API_LOCK:
+ if (tlv->len < sizeof(u8))
+ goto failed;
version->api_lock = tlv->val[0];
break;
case INTEL_TLV_DEBUG_LOCK:
+ if (tlv->len < sizeof(u8))
+ goto failed;
version->debug_lock = tlv->val[0];
break;
case INTEL_TLV_MIN_FW:
+ if (tlv->len < 3)
+ goto failed;
version->min_fw_build_nn = tlv->val[0];
version->min_fw_build_cw = tlv->val[1];
version->min_fw_build_yy = tlv->val[2];
break;
case INTEL_TLV_LIMITED_CCE:
+ if (tlv->len < sizeof(u8))
+ goto failed;
version->limited_cce = tlv->val[0];
break;
case INTEL_TLV_SBE_TYPE:
+ if (tlv->len < sizeof(u8))
+ goto failed;
version->sbe_type = tlv->val[0];
break;
case INTEL_TLV_OTP_BDADDR:
+ if (tlv->len != sizeof(version->otp_bd_addr))
+ goto failed;
memcpy(&version->otp_bd_addr, tlv->val, tlv->len);
break;
default:
@@ -499,6 +536,10 @@ int btintel_read_version_tlv(struct hci_dev *hdev, struct intel_version_tlv *ver

kfree_skb(skb);
return 0;
+
+failed:
+ kfree_skb(skb);
+ return -EINVAL;
}
EXPORT_SYMBOL_GPL(btintel_read_version_tlv);

--
2.7.4


2020-09-22 05:32:41

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [PATCH v1] Bluetooth: btintel: Fix issue reported by static analysis tool

Hi Kiran,

On Mon, Sep 21, 2020 at 9:03 PM Kiran K <[email protected]> wrote:
>
> Smatch tool reported the below issue:
>
> drivers/bluetooth/btintel.c:490 btintel_read_version_tlv()
> error: 'tlv->len' from user is not capped properly
>
> Additional details in the below link
> https://www.spinics.net/lists/linux-bluetooth/msg87786.html
>
> Signed-off-by: Kiran K <[email protected]>
> ---
> drivers/bluetooth/btintel.c | 43 ++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 42 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c
> index 88ce5f0..47f2b3d 100644
> --- a/drivers/bluetooth/btintel.c
> +++ b/drivers/bluetooth/btintel.c
> @@ -431,62 +431,99 @@ int btintel_read_version_tlv(struct hci_dev *hdev, struct intel_version_tlv *ver
> * version field like hw_platform, hw_variant, and fw_variant
> * to keep the existing setup flow
> */
> - while (skb->len) {
> + while (skb->len >= sizeof(struct intel_tlv)) {
> struct intel_tlv *tlv;
>
> tlv = (struct intel_tlv *)skb->data;
> + if (struct_size(tlv, val, tlv->len) > skb->len)
> + goto failed;
> +
> switch (tlv->type) {
> case INTEL_TLV_CNVI_TOP:
> + if (tlv->len < sizeof(u32))
> + goto failed;
> version->cnvi_top = get_unaligned_le32(tlv->val);
> break;
> case INTEL_TLV_CNVR_TOP:
> + if (tlv->len < sizeof(u32))
> + goto failed;
> version->cnvr_top = get_unaligned_le32(tlv->val);
> break;
> case INTEL_TLV_CNVI_BT:
> + if (tlv->len < sizeof(u32))
> + goto failed;
> version->cnvi_bt = get_unaligned_le32(tlv->val);
> break;
> case INTEL_TLV_CNVR_BT:
> + if (tlv->len < sizeof(u32))
> + goto failed;
> version->cnvr_bt = get_unaligned_le32(tlv->val);
> break;
> case INTEL_TLV_DEV_REV_ID:
> + if (tlv->len < sizeof(u16))
> + goto failed;
> version->dev_rev_id = get_unaligned_le16(tlv->val);
> break;
> case INTEL_TLV_IMAGE_TYPE:
> + if (tlv->len < sizeof(u8))
> + goto failed;
> version->img_type = tlv->val[0];
> break;
> case INTEL_TLV_TIME_STAMP:
> + if (tlv->len < sizeof(u16))
> + goto failed;
> version->timestamp = get_unaligned_le16(tlv->val);
> break;
> case INTEL_TLV_BUILD_TYPE:
> + if (tlv->len < sizeof(u8))
> + goto failed;
> version->build_type = tlv->val[0];
> break;
> case INTEL_TLV_BUILD_NUM:
> + if (tlv->len < sizeof(u32))
> + goto failed;
> version->build_num = get_unaligned_le32(tlv->val);
> break;
> case INTEL_TLV_SECURE_BOOT:
> + if (tlv->len < sizeof(u8))
> + goto failed;
> version->secure_boot = tlv->val[0];
> break;
> case INTEL_TLV_OTP_LOCK:
> + if (tlv->len < sizeof(u8))
> + goto failed;
> version->otp_lock = tlv->val[0];
> break;
> case INTEL_TLV_API_LOCK:
> + if (tlv->len < sizeof(u8))
> + goto failed;
> version->api_lock = tlv->val[0];
> break;
> case INTEL_TLV_DEBUG_LOCK:
> + if (tlv->len < sizeof(u8))
> + goto failed;
> version->debug_lock = tlv->val[0];
> break;
> case INTEL_TLV_MIN_FW:
> + if (tlv->len < 3)
> + goto failed;
> version->min_fw_build_nn = tlv->val[0];
> version->min_fw_build_cw = tlv->val[1];
> version->min_fw_build_yy = tlv->val[2];
> break;
> case INTEL_TLV_LIMITED_CCE:
> + if (tlv->len < sizeof(u8))
> + goto failed;
> version->limited_cce = tlv->val[0];
> break;
> case INTEL_TLV_SBE_TYPE:
> + if (tlv->len < sizeof(u8))
> + goto failed;
> version->sbe_type = tlv->val[0];
> break;
> case INTEL_TLV_OTP_BDADDR:
> + if (tlv->len != sizeof(version->otp_bd_addr))
> + goto failed;

Do we really want to fail here? The advantage of using a TLV is that
we can skip if the type is not understood or is malformed but with
this checks the length becomes useless since the types will always
have a fixed value, also we cannot extend the types later on since it
would not be backward compatible if we maintain such strict checks.

> memcpy(&version->otp_bd_addr, tlv->val, tlv->len);
> break;
> default:
> @@ -499,6 +536,10 @@ int btintel_read_version_tlv(struct hci_dev *hdev, struct intel_version_tlv *ver
>
> kfree_skb(skb);
> return 0;
> +
> +failed:
> + kfree_skb(skb);
> + return -EINVAL;
> }
> EXPORT_SYMBOL_GPL(btintel_read_version_tlv);
>
> --
> 2.7.4
>


--
Luiz Augusto von Dentz

2020-09-22 06:03:50

by Kiran K

[permalink] [raw]
Subject: RE: [PATCH v1] Bluetooth: btintel: Fix issue reported by static analysis tool

Hi Luiz,

> -----Original Message-----
> From: Luiz Augusto von Dentz <[email protected]>
> Sent: Tuesday, September 22, 2020 10:47 AM
> To: Kiran K <[email protected]>
> Cc: [email protected]; Tumkur Narayan, Chethan
> <[email protected]>; Srivatsa, Ravishankar
> <[email protected]>; [email protected]; K, Kiran
> <[email protected]>
> Subject: Re: [PATCH v1] Bluetooth: btintel: Fix issue reported by static
> analysis tool
>
> Hi Kiran,
>
> On Mon, Sep 21, 2020 at 9:03 PM Kiran K <[email protected]> wrote:
> >
> > Smatch tool reported the below issue:
> >
> > drivers/bluetooth/btintel.c:490 btintel_read_version_tlv()
> > error: 'tlv->len' from user is not capped properly
> >
> > Additional details in the below link
> > https://www.spinics.net/lists/linux-bluetooth/msg87786.html
> >
> > Signed-off-by: Kiran K <[email protected]>
> > ---
> > drivers/bluetooth/btintel.c | 43
> > ++++++++++++++++++++++++++++++++++++++++++-
> > 1 file changed, 42 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c
> > index 88ce5f0..47f2b3d 100644
> > --- a/drivers/bluetooth/btintel.c
> > +++ b/drivers/bluetooth/btintel.c
> > @@ -431,62 +431,99 @@ int btintel_read_version_tlv(struct hci_dev
> *hdev, struct intel_version_tlv *ver
> > * version field like hw_platform, hw_variant, and fw_variant
> > * to keep the existing setup flow
> > */
> > - while (skb->len) {
> > + while (skb->len >= sizeof(struct intel_tlv)) {
> > struct intel_tlv *tlv;
> >
> > tlv = (struct intel_tlv *)skb->data;
> > + if (struct_size(tlv, val, tlv->len) > skb->len)
> > + goto failed;
> > +
> > switch (tlv->type) {
> > case INTEL_TLV_CNVI_TOP:
> > + if (tlv->len < sizeof(u32))
> > + goto failed;
> > version->cnvi_top = get_unaligned_le32(tlv->val);
> > break;
> > case INTEL_TLV_CNVR_TOP:
> > + if (tlv->len < sizeof(u32))
> > + goto failed;
> > version->cnvr_top = get_unaligned_le32(tlv->val);
> > break;
> > case INTEL_TLV_CNVI_BT:
> > + if (tlv->len < sizeof(u32))
> > + goto failed;
> > version->cnvi_bt = get_unaligned_le32(tlv->val);
> > break;
> > case INTEL_TLV_CNVR_BT:
> > + if (tlv->len < sizeof(u32))
> > + goto failed;
> > version->cnvr_bt = get_unaligned_le32(tlv->val);
> > break;
> > case INTEL_TLV_DEV_REV_ID:
> > + if (tlv->len < sizeof(u16))
> > + goto failed;
> > version->dev_rev_id = get_unaligned_le16(tlv->val);
> > break;
> > case INTEL_TLV_IMAGE_TYPE:
> > + if (tlv->len < sizeof(u8))
> > + goto failed;
> > version->img_type = tlv->val[0];
> > break;
> > case INTEL_TLV_TIME_STAMP:
> > + if (tlv->len < sizeof(u16))
> > + goto failed;
> > version->timestamp = get_unaligned_le16(tlv->val);
> > break;
> > case INTEL_TLV_BUILD_TYPE:
> > + if (tlv->len < sizeof(u8))
> > + goto failed;
> > version->build_type = tlv->val[0];
> > break;
> > case INTEL_TLV_BUILD_NUM:
> > + if (tlv->len < sizeof(u32))
> > + goto failed;
> > version->build_num = get_unaligned_le32(tlv->val);
> > break;
> > case INTEL_TLV_SECURE_BOOT:
> > + if (tlv->len < sizeof(u8))
> > + goto failed;
> > version->secure_boot = tlv->val[0];
> > break;
> > case INTEL_TLV_OTP_LOCK:
> > + if (tlv->len < sizeof(u8))
> > + goto failed;
> > version->otp_lock = tlv->val[0];
> > break;
> > case INTEL_TLV_API_LOCK:
> > + if (tlv->len < sizeof(u8))
> > + goto failed;
> > version->api_lock = tlv->val[0];
> > break;
> > case INTEL_TLV_DEBUG_LOCK:
> > + if (tlv->len < sizeof(u8))
> > + goto failed;
> > version->debug_lock = tlv->val[0];
> > break;
> > case INTEL_TLV_MIN_FW:
> > + if (tlv->len < 3)
> > + goto failed;
> > version->min_fw_build_nn = tlv->val[0];
> > version->min_fw_build_cw = tlv->val[1];
> > version->min_fw_build_yy = tlv->val[2];
> > break;
> > case INTEL_TLV_LIMITED_CCE:
> > + if (tlv->len < sizeof(u8))
> > + goto failed;
> > version->limited_cce = tlv->val[0];
> > break;
> > case INTEL_TLV_SBE_TYPE:
> > + if (tlv->len < sizeof(u8))
> > + goto failed;
> > version->sbe_type = tlv->val[0];
> > break;
> > case INTEL_TLV_OTP_BDADDR:
> > + if (tlv->len != sizeof(version->otp_bd_addr))
> > + goto failed;
>
> Do we really want to fail here? The advantage of using a TLV is that we can
> skip if the type is not understood or is malformed but with this checks the
> length becomes useless since the types will always have a fixed value, also

Agree that the types are fixed here. But if due to some reason if controller is not honoring the same, then driver might end up reading unwanted data. The check is more about driver being defensive rather than believing what comes on wire.

> we cannot extend the types later on since it would not be backward
> compatible if we maintain such strict checks.

I didn’t get this part. Could you please be more specific ?

>
> > memcpy(&version->otp_bd_addr, tlv->val, tlv->len);
> > break;
> > default:
> > @@ -499,6 +536,10 @@ int btintel_read_version_tlv(struct hci_dev
> > *hdev, struct intel_version_tlv *ver
> >
> > kfree_skb(skb);
> > return 0;
> > +
> > +failed:
> > + kfree_skb(skb);
> > + return -EINVAL;
> > }
> > EXPORT_SYMBOL_GPL(btintel_read_version_tlv);
> >
> > --
> > 2.7.4
> >
>
>
> --
> Luiz Augusto von Dentz

Thanks,
Kiran

2020-09-25 16:41:11

by Marcel Holtmann

[permalink] [raw]
Subject: Re: [PATCH v1] Bluetooth: btintel: Fix issue reported by static analysis tool

Hi Kiran,

>>> Smatch tool reported the below issue:
>>>
>>> drivers/bluetooth/btintel.c:490 btintel_read_version_tlv()
>>> error: 'tlv->len' from user is not capped properly
>>>
>>> Additional details in the below link
>>> https://www.spinics.net/lists/linux-bluetooth/msg87786.html
>>>
>>> Signed-off-by: Kiran K <[email protected]>
>>> ---
>>> drivers/bluetooth/btintel.c | 43
>>> ++++++++++++++++++++++++++++++++++++++++++-
>>> 1 file changed, 42 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c
>>> index 88ce5f0..47f2b3d 100644
>>> --- a/drivers/bluetooth/btintel.c
>>> +++ b/drivers/bluetooth/btintel.c
>>> @@ -431,62 +431,99 @@ int btintel_read_version_tlv(struct hci_dev
>> *hdev, struct intel_version_tlv *ver
>>> * version field like hw_platform, hw_variant, and fw_variant
>>> * to keep the existing setup flow
>>> */
>>> - while (skb->len) {
>>> + while (skb->len >= sizeof(struct intel_tlv)) {
>>> struct intel_tlv *tlv;
>>>
>>> tlv = (struct intel_tlv *)skb->data;
>>> + if (struct_size(tlv, val, tlv->len) > skb->len)
>>> + goto failed;
>>> +
>>> switch (tlv->type) {
>>> case INTEL_TLV_CNVI_TOP:
>>> + if (tlv->len < sizeof(u32))
>>> + goto failed;
>>> version->cnvi_top = get_unaligned_le32(tlv->val);
>>> break;
>>> case INTEL_TLV_CNVR_TOP:
>>> + if (tlv->len < sizeof(u32))
>>> + goto failed;
>>> version->cnvr_top = get_unaligned_le32(tlv->val);
>>> break;
>>> case INTEL_TLV_CNVI_BT:
>>> + if (tlv->len < sizeof(u32))
>>> + goto failed;
>>> version->cnvi_bt = get_unaligned_le32(tlv->val);
>>> break;
>>> case INTEL_TLV_CNVR_BT:
>>> + if (tlv->len < sizeof(u32))
>>> + goto failed;
>>> version->cnvr_bt = get_unaligned_le32(tlv->val);
>>> break;
>>> case INTEL_TLV_DEV_REV_ID:
>>> + if (tlv->len < sizeof(u16))
>>> + goto failed;
>>> version->dev_rev_id = get_unaligned_le16(tlv->val);
>>> break;
>>> case INTEL_TLV_IMAGE_TYPE:
>>> + if (tlv->len < sizeof(u8))
>>> + goto failed;
>>> version->img_type = tlv->val[0];
>>> break;
>>> case INTEL_TLV_TIME_STAMP:
>>> + if (tlv->len < sizeof(u16))
>>> + goto failed;
>>> version->timestamp = get_unaligned_le16(tlv->val);
>>> break;
>>> case INTEL_TLV_BUILD_TYPE:
>>> + if (tlv->len < sizeof(u8))
>>> + goto failed;
>>> version->build_type = tlv->val[0];
>>> break;
>>> case INTEL_TLV_BUILD_NUM:
>>> + if (tlv->len < sizeof(u32))
>>> + goto failed;
>>> version->build_num = get_unaligned_le32(tlv->val);
>>> break;
>>> case INTEL_TLV_SECURE_BOOT:
>>> + if (tlv->len < sizeof(u8))
>>> + goto failed;
>>> version->secure_boot = tlv->val[0];
>>> break;
>>> case INTEL_TLV_OTP_LOCK:
>>> + if (tlv->len < sizeof(u8))
>>> + goto failed;
>>> version->otp_lock = tlv->val[0];
>>> break;
>>> case INTEL_TLV_API_LOCK:
>>> + if (tlv->len < sizeof(u8))
>>> + goto failed;
>>> version->api_lock = tlv->val[0];
>>> break;
>>> case INTEL_TLV_DEBUG_LOCK:
>>> + if (tlv->len < sizeof(u8))
>>> + goto failed;
>>> version->debug_lock = tlv->val[0];
>>> break;
>>> case INTEL_TLV_MIN_FW:
>>> + if (tlv->len < 3)
>>> + goto failed;
>>> version->min_fw_build_nn = tlv->val[0];
>>> version->min_fw_build_cw = tlv->val[1];
>>> version->min_fw_build_yy = tlv->val[2];
>>> break;
>>> case INTEL_TLV_LIMITED_CCE:
>>> + if (tlv->len < sizeof(u8))
>>> + goto failed;
>>> version->limited_cce = tlv->val[0];
>>> break;
>>> case INTEL_TLV_SBE_TYPE:
>>> + if (tlv->len < sizeof(u8))
>>> + goto failed;
>>> version->sbe_type = tlv->val[0];
>>> break;
>>> case INTEL_TLV_OTP_BDADDR:
>>> + if (tlv->len != sizeof(version->otp_bd_addr))
>>> + goto failed;
>>
>> Do we really want to fail here? The advantage of using a TLV is that we can
>> skip if the type is not understood or is malformed but with this checks the
>> length becomes useless since the types will always have a fixed value, also
>
> Agree that the types are fixed here. But if due to some reason if controller is not honoring the same, then driver might end up reading unwanted data. The check is more about driver being defensive rather than believing what comes on wire.
>
>> we cannot extend the types later on since it would not be backward
>> compatible if we maintain such strict checks.
>
> I didn’t get this part. Could you please be more specific ?

please change this that you check the size of the TLV data part against the expected size of the field and only then assign it.

Regards

Marcel