diff options
author | Oliver Hartkopp <oliver@hartkopp.net> | 2009-12-11 23:13:21 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-12-13 22:47:42 -0500 |
commit | c7cd606f60e7679c7f9eee7010f02a6f000209c1 (patch) | |
tree | 31c19fd7617ede807757b0ae5c29d218587dc43f | |
parent | d90a909e1f3e006a1d57fe11fd417173b6494701 (diff) |
can: Fix data length code handling in rx path
A valid CAN dataframe can have a data length code (DLC) of 0 .. 8 data bytes.
When reading the CAN controllers register the 4-bit value may contain values
from 0 .. 15 which may exceed the reserved space in the socket buffer!
The ISO 11898-1 Chapter 8.4.2.3 (DLC field) says that register values > 8
should be reduced to 8 without any error reporting or frame drop.
This patch introduces a new helper macro to cast a given 4-bit data length
code (dlc) to __u8 and ensure the DLC value to be max. 8 bytes.
The different handlings in the rx path of the CAN netdevice drivers are fixed.
Signed-off-by: Oliver Hartkopp <oliver@hartkopp.net>
Signed-off-by: Wolfgang Grandegger <wg@grandegger.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/can/at91_can.c | 2 | ||||
-rw-r--r-- | drivers/net/can/bfin_can.c | 2 | ||||
-rw-r--r-- | drivers/net/can/mcp251x.c | 13 | ||||
-rw-r--r-- | drivers/net/can/mscan/mscan.c | 3 | ||||
-rw-r--r-- | drivers/net/can/sja1000/sja1000.c | 18 | ||||
-rw-r--r-- | drivers/net/can/ti_hecc.c | 2 | ||||
-rw-r--r-- | drivers/net/can/usb/ems_usb.c | 2 | ||||
-rw-r--r-- | include/linux/can/dev.h | 9 |
8 files changed, 26 insertions, 25 deletions
diff --git a/drivers/net/can/at91_can.c b/drivers/net/can/at91_can.c index cbe3fce53e3b..d0ec17878ffc 100644 --- a/drivers/net/can/at91_can.c +++ b/drivers/net/can/at91_can.c | |||
@@ -474,7 +474,7 @@ static void at91_read_mb(struct net_device *dev, unsigned int mb, | |||
474 | reg_msr = at91_read(priv, AT91_MSR(mb)); | 474 | reg_msr = at91_read(priv, AT91_MSR(mb)); |
475 | if (reg_msr & AT91_MSR_MRTR) | 475 | if (reg_msr & AT91_MSR_MRTR) |
476 | cf->can_id |= CAN_RTR_FLAG; | 476 | cf->can_id |= CAN_RTR_FLAG; |
477 | cf->can_dlc = min_t(__u8, (reg_msr >> 16) & 0xf, 8); | 477 | cf->can_dlc = get_can_dlc((reg_msr >> 16) & 0xf); |
478 | 478 | ||
479 | *(u32 *)(cf->data + 0) = at91_read(priv, AT91_MDL(mb)); | 479 | *(u32 *)(cf->data + 0) = at91_read(priv, AT91_MDL(mb)); |
480 | *(u32 *)(cf->data + 4) = at91_read(priv, AT91_MDH(mb)); | 480 | *(u32 *)(cf->data + 4) = at91_read(priv, AT91_MDH(mb)); |
diff --git a/drivers/net/can/bfin_can.c b/drivers/net/can/bfin_can.c index c7fc1de28173..0ec1524523cc 100644 --- a/drivers/net/can/bfin_can.c +++ b/drivers/net/can/bfin_can.c | |||
@@ -392,7 +392,7 @@ static void bfin_can_rx(struct net_device *dev, u16 isrc) | |||
392 | cf->can_id |= CAN_RTR_FLAG; | 392 | cf->can_id |= CAN_RTR_FLAG; |
393 | 393 | ||
394 | /* get data length code */ | 394 | /* get data length code */ |
395 | cf->can_dlc = bfin_read16(®->chl[obj].dlc); | 395 | cf->can_dlc = get_can_dlc(bfin_read16(®->chl[obj].dlc) & 0xF); |
396 | 396 | ||
397 | /* get payload */ | 397 | /* get payload */ |
398 | for (i = 0; i < 8; i += 2) { | 398 | for (i = 0; i < 8; i += 2) { |
diff --git a/drivers/net/can/mcp251x.c b/drivers/net/can/mcp251x.c index 78b1b69b2921..9c5a1537939c 100644 --- a/drivers/net/can/mcp251x.c +++ b/drivers/net/can/mcp251x.c | |||
@@ -403,9 +403,8 @@ static void mcp251x_hw_rx_frame(struct spi_device *spi, u8 *buf, | |||
403 | 403 | ||
404 | for (i = 1; i < RXBDAT_OFF; i++) | 404 | for (i = 1; i < RXBDAT_OFF; i++) |
405 | buf[i] = mcp251x_read_reg(spi, RXBCTRL(buf_idx) + i); | 405 | buf[i] = mcp251x_read_reg(spi, RXBCTRL(buf_idx) + i); |
406 | len = buf[RXBDLC_OFF] & RXBDLC_LEN_MASK; | 406 | |
407 | if (len > 8) | 407 | len = get_can_dlc(buf[RXBDLC_OFF] & RXBDLC_LEN_MASK); |
408 | len = 8; | ||
409 | for (; i < (RXBDAT_OFF + len); i++) | 408 | for (; i < (RXBDAT_OFF + len); i++) |
410 | buf[i] = mcp251x_read_reg(spi, RXBCTRL(buf_idx) + i); | 409 | buf[i] = mcp251x_read_reg(spi, RXBCTRL(buf_idx) + i); |
411 | } else { | 410 | } else { |
@@ -455,13 +454,7 @@ static void mcp251x_hw_rx(struct spi_device *spi, int buf_idx) | |||
455 | (buf[RXBSIDL_OFF] >> RXBSIDL_SHIFT); | 454 | (buf[RXBSIDL_OFF] >> RXBSIDL_SHIFT); |
456 | } | 455 | } |
457 | /* Data length */ | 456 | /* Data length */ |
458 | frame->can_dlc = buf[RXBDLC_OFF] & RXBDLC_LEN_MASK; | 457 | frame->can_dlc = get_can_dlc(buf[RXBDLC_OFF] & RXBDLC_LEN_MASK); |
459 | if (frame->can_dlc > 8) { | ||
460 | dev_warn(&spi->dev, "invalid frame recevied\n"); | ||
461 | priv->net->stats.rx_errors++; | ||
462 | dev_kfree_skb(skb); | ||
463 | return; | ||
464 | } | ||
465 | memcpy(frame->data, buf + RXBDAT_OFF, frame->can_dlc); | 458 | memcpy(frame->data, buf + RXBDAT_OFF, frame->can_dlc); |
466 | 459 | ||
467 | priv->net->stats.rx_packets++; | 460 | priv->net->stats.rx_packets++; |
diff --git a/drivers/net/can/mscan/mscan.c b/drivers/net/can/mscan/mscan.c index bb06dfb58f25..07346f880ca6 100644 --- a/drivers/net/can/mscan/mscan.c +++ b/drivers/net/can/mscan/mscan.c | |||
@@ -297,7 +297,8 @@ static void mscan_get_rx_frame(struct net_device *dev, struct can_frame *frame) | |||
297 | frame->can_id |= can_id >> 1; | 297 | frame->can_id |= can_id >> 1; |
298 | if (can_id & 1) | 298 | if (can_id & 1) |
299 | frame->can_id |= CAN_RTR_FLAG; | 299 | frame->can_id |= CAN_RTR_FLAG; |
300 | frame->can_dlc = in_8(®s->rx.dlr) & 0xf; | 300 | |
301 | frame->can_dlc = get_can_dlc(in_8(®s->rx.dlr) & 0xf); | ||
301 | 302 | ||
302 | if (!(frame->can_id & CAN_RTR_FLAG)) { | 303 | if (!(frame->can_id & CAN_RTR_FLAG)) { |
303 | void __iomem *data = ®s->rx.dsr1_0; | 304 | void __iomem *data = ®s->rx.dsr1_0; |
diff --git a/drivers/net/can/sja1000/sja1000.c b/drivers/net/can/sja1000/sja1000.c index b4ba88a31075..542a4f7255b4 100644 --- a/drivers/net/can/sja1000/sja1000.c +++ b/drivers/net/can/sja1000/sja1000.c | |||
@@ -293,15 +293,14 @@ static void sja1000_rx(struct net_device *dev) | |||
293 | uint8_t fi; | 293 | uint8_t fi; |
294 | uint8_t dreg; | 294 | uint8_t dreg; |
295 | canid_t id; | 295 | canid_t id; |
296 | uint8_t dlc; | ||
297 | int i; | 296 | int i; |
298 | 297 | ||
298 | /* create zero'ed CAN frame buffer */ | ||
299 | skb = alloc_can_skb(dev, &cf); | 299 | skb = alloc_can_skb(dev, &cf); |
300 | if (skb == NULL) | 300 | if (skb == NULL) |
301 | return; | 301 | return; |
302 | 302 | ||
303 | fi = priv->read_reg(priv, REG_FI); | 303 | fi = priv->read_reg(priv, REG_FI); |
304 | dlc = fi & 0x0F; | ||
305 | 304 | ||
306 | if (fi & FI_FF) { | 305 | if (fi & FI_FF) { |
307 | /* extended frame format (EFF) */ | 306 | /* extended frame format (EFF) */ |
@@ -318,16 +317,15 @@ static void sja1000_rx(struct net_device *dev) | |||
318 | | (priv->read_reg(priv, REG_ID2) >> 5); | 317 | | (priv->read_reg(priv, REG_ID2) >> 5); |
319 | } | 318 | } |
320 | 319 | ||
321 | if (fi & FI_RTR) | 320 | if (fi & FI_RTR) { |
322 | id |= CAN_RTR_FLAG; | 321 | id |= CAN_RTR_FLAG; |
322 | } else { | ||
323 | cf->can_dlc = get_can_dlc(fi & 0x0F); | ||
324 | for (i = 0; i < cf->can_dlc; i++) | ||
325 | cf->data[i] = priv->read_reg(priv, dreg++); | ||
326 | } | ||
323 | 327 | ||
324 | cf->can_id = id; | 328 | cf->can_id = id; |
325 | cf->can_dlc = dlc; | ||
326 | for (i = 0; i < dlc; i++) | ||
327 | cf->data[i] = priv->read_reg(priv, dreg++); | ||
328 | |||
329 | while (i < 8) | ||
330 | cf->data[i++] = 0; | ||
331 | 329 | ||
332 | /* release receive buffer */ | 330 | /* release receive buffer */ |
333 | priv->write_reg(priv, REG_CMR, CMD_RRB); | 331 | priv->write_reg(priv, REG_CMR, CMD_RRB); |
@@ -335,7 +333,7 @@ static void sja1000_rx(struct net_device *dev) | |||
335 | netif_rx(skb); | 333 | netif_rx(skb); |
336 | 334 | ||
337 | stats->rx_packets++; | 335 | stats->rx_packets++; |
338 | stats->rx_bytes += dlc; | 336 | stats->rx_bytes += cf->can_dlc; |
339 | } | 337 | } |
340 | 338 | ||
341 | static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status) | 339 | static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status) |
diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c index 07e8016b17ec..5c993c2da528 100644 --- a/drivers/net/can/ti_hecc.c +++ b/drivers/net/can/ti_hecc.c | |||
@@ -552,7 +552,7 @@ static int ti_hecc_rx_pkt(struct ti_hecc_priv *priv, int mbxno) | |||
552 | data = hecc_read_mbx(priv, mbxno, HECC_CANMCF); | 552 | data = hecc_read_mbx(priv, mbxno, HECC_CANMCF); |
553 | if (data & HECC_CANMCF_RTR) | 553 | if (data & HECC_CANMCF_RTR) |
554 | cf->can_id |= CAN_RTR_FLAG; | 554 | cf->can_id |= CAN_RTR_FLAG; |
555 | cf->can_dlc = data & 0xF; | 555 | cf->can_dlc = get_can_dlc(data & 0xF); |
556 | data = hecc_read_mbx(priv, mbxno, HECC_CANMDL); | 556 | data = hecc_read_mbx(priv, mbxno, HECC_CANMDL); |
557 | *(u32 *)(cf->data) = cpu_to_be32(data); | 557 | *(u32 *)(cf->data) = cpu_to_be32(data); |
558 | if (cf->can_dlc > 4) { | 558 | if (cf->can_dlc > 4) { |
diff --git a/drivers/net/can/usb/ems_usb.c b/drivers/net/can/usb/ems_usb.c index 591eb0eb1c2b..efbb05c71bf4 100644 --- a/drivers/net/can/usb/ems_usb.c +++ b/drivers/net/can/usb/ems_usb.c | |||
@@ -316,7 +316,7 @@ static void ems_usb_rx_can_msg(struct ems_usb *dev, struct ems_cpc_msg *msg) | |||
316 | return; | 316 | return; |
317 | 317 | ||
318 | cf->can_id = le32_to_cpu(msg->msg.can_msg.id); | 318 | cf->can_id = le32_to_cpu(msg->msg.can_msg.id); |
319 | cf->can_dlc = min_t(u8, msg->msg.can_msg.length, 8); | 319 | cf->can_dlc = get_can_dlc(msg->msg.can_msg.length & 0xF); |
320 | 320 | ||
321 | if (msg->type == CPC_MSG_TYPE_EXT_CAN_FRAME || | 321 | if (msg->type == CPC_MSG_TYPE_EXT_CAN_FRAME || |
322 | msg->type == CPC_MSG_TYPE_EXT_RTR_FRAME) | 322 | msg->type == CPC_MSG_TYPE_EXT_RTR_FRAME) |
diff --git a/include/linux/can/dev.h b/include/linux/can/dev.h index 1ed2a5cc03f5..3db7767d2a17 100644 --- a/include/linux/can/dev.h +++ b/include/linux/can/dev.h | |||
@@ -51,6 +51,15 @@ struct can_priv { | |||
51 | struct sk_buff **echo_skb; | 51 | struct sk_buff **echo_skb; |
52 | }; | 52 | }; |
53 | 53 | ||
54 | /* | ||
55 | * get_can_dlc(value) - helper macro to cast a given data length code (dlc) | ||
56 | * to __u8 and ensure the dlc value to be max. 8 bytes. | ||
57 | * | ||
58 | * To be used in the CAN netdriver receive path to ensure conformance with | ||
59 | * ISO 11898-1 Chapter 8.4.2.3 (DLC field) | ||
60 | */ | ||
61 | #define get_can_dlc(i) (min_t(__u8, (i), 8)) | ||
62 | |||
54 | struct net_device *alloc_candev(int sizeof_priv, unsigned int echo_skb_max); | 63 | struct net_device *alloc_candev(int sizeof_priv, unsigned int echo_skb_max); |
55 | void free_candev(struct net_device *dev); | 64 | void free_candev(struct net_device *dev); |
56 | 65 | ||