Re: CAN-XL frame to not CAN-XL enabled interface

Oliver Hartkopp <[email protected]> Fri, 31 Jul 2026 15:42:31 +0200
Newsgroups org.kernel.vger.linux-can
Message-ID <[email protected]>

On 31.07.26 15:28, Vincent Mailhol wrote:
> On 31/07/2026 at 14:55, Vincent Mailhol wrote:
>> On 31/07/2026 at 13:02, Marc Kleine-Budde wrote:
>>> Vincent,
>>>
>>> does the current code check if you send a CAN XL frame to a CAN XL
>>> enabled interface?
>>>
>>> On 31.07.2026 10:54:02, [email protected] wrote:
>>>> This is a pre-existing issue, but does this code properly handle CAN XL frames?
>>>>
>>>> If an ETH_P_CANXL frame is sent via AF_PACKET, can_dev_dropped_skb() currently
>>>> lacks a check to drop CAN XL frames for devices that don't support CAN XL.
>>>> When such a frame enters rkcanfd_start_xmit(), can_is_canfd_skb() returns
>>>> false, causing the driver to treat it as a Classic CAN frame.
>>>
>>>> Sashiko AI review ยท https://sashiko.dev/#/patchset/[email protected]?part=1
>>
>> The logic is for checking an ETH_P_CANXL frame is:
>>
>>    - When sending a packet (even with PF_PACKET) the only check which
>>      is performed is to check that the skb len is not above
>>      net_device->mtu. So, if the device is not CAN XL capable, anything
>>      about CANFD_MTU (72 bytes) is dropped.
>>
>>    - In the driver, can_dev_dropped_skb() calls
>>      can_dropped_invalid_skb() which then call can_is_canxl_skb() which
>>      validate that the skb length is within the range
>>
>>        [CANXL_HDR_SIZE + CANXL_MIN_DLEN, skb->len > CANXL_MTU]
>>
>>      that is to say 13 and 2060. But we already establish that the
>>      maximum length is 72 at this time.
>>
>> So, yes, it seems that an ETH_P_CANXL frame with a length between 13
>> and 72 will pass the can_dev_dropped_skb() check. And because
>> CANXL_XLF must be set at this point, the driver sees a can_frame->len
>> of at least 128 bytes (and up to 255).
>>
>> Until now, I was convinced in my mind that can_is_canxl_skb() rejected
>> frames with a length below CANXL_MIN_MTU which would have prevented
>> the issue. And I wrote the implementation under this false assumption
>> without double checking.
>>
>>
>> Here is a potential fix:
>>
>> ---8<---
>> diff --git a/include/linux/can/dev.h b/include/linux/can/dev.h
>> index 6d0710d6f571..88dec0f91d71 100644
>> --- a/include/linux/can/dev.h
>> +++ b/include/linux/can/dev.h
>> @@ -152,6 +152,7 @@ static inline bool can_dev_in_xl_only_mode(struct can_priv *priv)
>>   /* drop skb if it does not contain a valid CAN frame for sending */
>>   static inline bool can_dev_dropped_skb(struct net_device *dev, struct sk_buff *skb)
>>   {
>> +       const struct canxl_frame *cxl = (struct canxl_frame *)skb->data;
>>          struct can_priv *priv = netdev_priv(dev);
>>          u32 silent_mode = priv->ctrlmode & (CAN_CTRLMODE_LISTENONLY |
>>                                              CAN_CTRLMODE_RESTRICTED);
>> @@ -167,6 +168,11 @@ static inline bool can_dev_dropped_skb(struct net_device *dev, struct sk_buff *s
>>                  goto invalid_skb;
>>          }
>>   
>> +       if (!(priv->ctrlmode & CAN_CTRLMODE_XL) && cxl->flags & CANXL_XLF) {
>> +               netdev_info_once(dev, "CAN XL is disabled, dropping skb\n");
>> +               goto invalid_skb;
>> +       }
>> +
>>          if (can_dev_in_xl_only_mode(priv) && !can_is_canxl_skb(skb)) {
>>                  netdev_info_once(dev,
>>                                   "Error signaling is disabled, dropping skb\n");
>> ---8<---
> 
> On second thought, maybe this is an even better fix:
> 
> ---8<---
> diff --git a/drivers/net/can/dev/skb.c b/drivers/net/can/dev/skb.c
> index 95fcdc1026f8..9846989d4960 100644
> --- a/drivers/net/can/dev/skb.c
> +++ b/drivers/net/can/dev/skb.c
> @@ -384,7 +384,7 @@ bool can_dropped_invalid_skb(struct net_device *dev, struct sk_buff *skb)
>                  break;
>   
>          case ETH_P_CANXL:
> -               if (!can_is_canxl_skb(skb))
> +               if (!can_cap_enabled(dev, CAN_CAP_XL) || !can_is_canxl_skb(skb))
>                          goto inval_skb;
>                  break;
> ---8<---

Yes! I really like the can_cap stuff ;-)

> 
>> The cxl->flags & CANXL_XLF doesn't catch everything by itself, but
>> once we know that this flag is off, can_is_canxl_skb() will reject the
>> frame later on. So this should be the minimum fix
>> (can_dev_dropped_skb() is an inline function in the hot path, so any
>> trick is welcome here, I think).
>>
>> Does this make sense?
> 
> 
> Yours sincerely,
> Vincent Mailhol
> 
>