Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

can: skb: alloc_can{,fd}_skb(): set "cf" to NULL if skb allocation fails

The handling of CAN bus errors typically consist of allocating a CAN
error SKB using alloc_can_err_skb() followed by stats handling and
filling the error details in the newly allocated CAN error SKB. Even
if the allocation of the SKB fails the stats handling should not be
skipped.

The common pattern in CAN drivers is to allocate the skb and work on
the struct can_frame pointer "cf", if it has been assigned by
alloc_can_err_skb().

| skb = alloc_can_err_skb(priv->ndev, &cf);
|
| /* RX errors */
| if (bdiag1 & (MCP251XFD_REG_BDIAG1_DCRCERR |
| MCP251XFD_REG_BDIAG1_NCRCERR)) {
| netdev_dbg(priv->ndev, "CRC error\n");
|
| stats->rx_errors++;
| if (cf)
| cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ;
| }

In case of an OOM alloc_can_err_skb() returns NULL, but doesn't set
"cf" to NULL as well. For the above pattern to work the "cf" has to be
initialized to NULL, which is easily forgotten.

To solve this kind of problems, set "cf" to NULL if
alloc_can_err_skb() returns NULL.

Link: https://lore.kernel.org/r/20210402102245.1512583-1-mkl@pengutronix.de
Suggested-by: Vincent MAILHOL <mailhol.vincent@wanadoo.fr>
Reviewed-by: Vincent Mailhol <mailhol.vincent@wanadoo.fr>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>

+8 -2
+8 -2
drivers/net/can/dev/skb.c
··· 183 183 184 184 skb = netdev_alloc_skb(dev, sizeof(struct can_skb_priv) + 185 185 sizeof(struct can_frame)); 186 - if (unlikely(!skb)) 186 + if (unlikely(!skb)) { 187 + *cf = NULL; 188 + 187 189 return NULL; 190 + } 188 191 189 192 skb->protocol = htons(ETH_P_CAN); 190 193 skb->pkt_type = PACKET_BROADCAST; ··· 214 211 215 212 skb = netdev_alloc_skb(dev, sizeof(struct can_skb_priv) + 216 213 sizeof(struct canfd_frame)); 217 - if (unlikely(!skb)) 214 + if (unlikely(!skb)) { 215 + *cfd = NULL; 216 + 218 217 return NULL; 218 + } 219 219 220 220 skb->protocol = htons(ETH_P_CANFD); 221 221 skb->pkt_type = PACKET_BROADCAST;