| From 0df6cd596b4fd706eec08ecd8d5e38007fb073e0 Mon Sep 17 00:00:00 2001 |
| From: Oliver Hartkopp <socketcan@hartkopp.net> |
| Date: Sat, 7 Dec 2019 19:34:18 +0100 |
| Subject: [PATCH] can: can_dropped_invalid_skb(): ensure an initialized |
| headroom in outgoing CAN sk_buffs |
| |
| commit e7153bf70c3496bac00e7e4f395bb8d8394ac0ea upstream. |
| |
| KMSAN sysbot detected a read access to an untinitialized value in the |
| headroom of an outgoing CAN related sk_buff. When using CAN sockets this |
| area is filled appropriately - but when using a packet socket this |
| initialization is missing. |
| |
| The problematic read access occurs in the CAN receive path which can |
| only be triggered when the sk_buff is sent through a (virtual) CAN |
| interface. So we check in the sending path whether we need to perform |
| the missing initializations. |
| |
| Fixes: d3b58c47d330d ("can: replace timestamp as unique skb attribute") |
| Reported-by: syzbot+b02ff0707a97e4e79ebb@syzkaller.appspotmail.com |
| Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net> |
| Tested-by: Oliver Hartkopp <socketcan@hartkopp.net> |
| Cc: linux-stable <stable@vger.kernel.org> # >= v4.1 |
| Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/include/linux/can/dev.h b/include/linux/can/dev.h |
| index f01623aef2f7..73199facd5a4 100644 |
| --- a/include/linux/can/dev.h |
| +++ b/include/linux/can/dev.h |
| @@ -18,6 +18,7 @@ |
| #include <linux/can/error.h> |
| #include <linux/can/led.h> |
| #include <linux/can/netlink.h> |
| +#include <linux/can/skb.h> |
| #include <linux/netdevice.h> |
| |
| /* |
| @@ -91,6 +92,36 @@ struct can_priv { |
| #define get_can_dlc(i) (min_t(__u8, (i), CAN_MAX_DLC)) |
| #define get_canfd_dlc(i) (min_t(__u8, (i), CANFD_MAX_DLC)) |
| |
| +/* Check for outgoing skbs that have not been created by the CAN subsystem */ |
| +static inline bool can_skb_headroom_valid(struct net_device *dev, |
| + struct sk_buff *skb) |
| +{ |
| + /* af_packet creates a headroom of HH_DATA_MOD bytes which is fine */ |
| + if (WARN_ON_ONCE(skb_headroom(skb) < sizeof(struct can_skb_priv))) |
| + return false; |
| + |
| + /* af_packet does not apply CAN skb specific settings */ |
| + if (skb->ip_summed == CHECKSUM_NONE) { |
| + /* init headroom */ |
| + can_skb_prv(skb)->ifindex = dev->ifindex; |
| + can_skb_prv(skb)->skbcnt = 0; |
| + |
| + skb->ip_summed = CHECKSUM_UNNECESSARY; |
| + |
| + /* preform proper loopback on capable devices */ |
| + if (dev->flags & IFF_ECHO) |
| + skb->pkt_type = PACKET_LOOPBACK; |
| + else |
| + skb->pkt_type = PACKET_HOST; |
| + |
| + skb_reset_mac_header(skb); |
| + skb_reset_network_header(skb); |
| + skb_reset_transport_header(skb); |
| + } |
| + |
| + return true; |
| +} |
| + |
| /* Drop a given socketbuffer if it does not contain a valid CAN frame. */ |
| static inline bool can_dropped_invalid_skb(struct net_device *dev, |
| struct sk_buff *skb) |
| @@ -108,6 +139,9 @@ static inline bool can_dropped_invalid_skb(struct net_device *dev, |
| } else |
| goto inval_skb; |
| |
| + if (!can_skb_headroom_valid(dev, skb)) |
| + goto inval_skb; |
| + |
| return false; |
| |
| inval_skb: |
| -- |
| 2.7.4 |
| |