Skip to content

Commit

Permalink
Merge tag 'linux-can-fixes-for-5.10-20201115' of git://git.kernel.org…
Browse files Browse the repository at this point in the history
…/pub/scm/linux/kernel/git/mkl/linux-can

Marc Kleine-Budde says:

====================
pull-request: can 2020-11-15

Anant Thazhemadam contributed two patches for the AF_CAN that prevent potential
access of uninitialized member in can_rcv() and canfd_rcv().

The next patch is by Alejandro Concepcion Rodriguez and changes can_restart()
to use the correct function to push a skb into the networking stack from
process context.

Zhang Qilong's patch fixes a memory leak in the error path of the ti_hecc's
probe function.

A patch by me fixes mcba_usb_start_xmit() function in the mcba_usb driver, to
first fill the skb and then pass it to can_put_echo_skb().

Colin Ian King's patch fixes a potential integer overflow on shift in the
peak_usb driver.

The next two patches target the flexcan driver, a patch by me adds the missing
"req_bit" to the stop mode property comment (which was broken during net-next
for v5.10). Zhang Qilong's patch fixes the failure handling of
pm_runtime_get_sync().

The next seven patches target the m_can driver including the tcan4x5x spi
driver glue code. Enric Balletbo i Serra's patch for the tcan4x5x Kconfig fix
the REGMAP_SPI dependency handling. A patch by me for the tcan4x5x driver's
probe() function adds missing error handling to for devm_regmap_init(), and in
tcan4x5x_can_remove() the order of deregistration is fixed. Wu Bo's patch for
the m_can driver fixes the state change handling in
m_can_handle_state_change(). Two patches by Dan Murphy first introduce
m_can_class_free_dev() and then make use of it to fix the freeing of the can
device. A patch by Faiz Abbas add a missing shutdown of the CAN controller in
the m_can_stop() function.

* tag 'linux-can-fixes-for-5.10-20201115' of git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can:
  can: m_can: m_can_stop(): set device to software init mode before closing
  can: m_can: Fix freeing of can device from peripherials
  can: m_can: m_can_class_free_dev(): introduce new function
  can: m_can: m_can_handle_state_change(): fix state change
  can: tcan4x5x: tcan4x5x_can_remove(): fix order of deregistration
  can: tcan4x5x: tcan4x5x_can_probe(): add missing error checking for devm_regmap_init()
  can: tcan4x5x: replace depends on REGMAP_SPI with depends on SPI
  can: flexcan: fix failure handling of pm_runtime_get_sync()
  can: flexcan: flexcan_setup_stop_mode(): add missing "req_bit" to stop mode property comment
  can: peak_usb: fix potential integer overflow on shift of a int
  can: mcba_usb: mcba_usb_start_xmit(): first fill skb, then pass to can_put_echo_skb()
  can: ti_hecc: Fix memleak in ti_hecc_probe
  can: dev: can_restart(): post buffer from the right context
  can: af_can: prevent potential access of uninitialized member in canfd_rcv()
  can: af_can: prevent potential access of uninitialized member in can_rcv()
====================

Link: https://lore.kernel.org/r/20201115174131.2089251-1-mkl@pengutronix.de
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
  • Loading branch information
Jakub Kicinski committed Nov 16, 2020
2 parents 849920c + a584e9b commit e2142ef
Show file tree
Hide file tree
Showing 11 changed files with 100 additions and 46 deletions.
2 changes: 1 addition & 1 deletion drivers/net/can/dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,7 @@ static void can_restart(struct net_device *dev)

cf->can_id |= CAN_ERR_RESTARTED;

netif_rx(skb);
netif_rx_ni(skb);

stats->rx_packets++;
stats->rx_bytes += cf->can_dlc;
Expand Down
10 changes: 7 additions & 3 deletions drivers/net/can/flexcan.c
Original file line number Diff line number Diff line change
Expand Up @@ -728,8 +728,10 @@ static int flexcan_get_berr_counter(const struct net_device *dev,
int err;

err = pm_runtime_get_sync(priv->dev);
if (err < 0)
if (err < 0) {
pm_runtime_put_noidle(priv->dev);
return err;
}

err = __flexcan_get_berr_counter(dev, bec);

Expand Down Expand Up @@ -1654,8 +1656,10 @@ static int flexcan_open(struct net_device *dev)
}

err = pm_runtime_get_sync(priv->dev);
if (err < 0)
if (err < 0) {
pm_runtime_put_noidle(priv->dev);
return err;
}

err = open_candev(dev);
if (err)
Expand Down Expand Up @@ -1852,7 +1856,7 @@ static int flexcan_setup_stop_mode(struct platform_device *pdev)
return -EINVAL;

/* stop mode property format is:
* <&gpr req_gpr>.
* <&gpr req_gpr req_bit>.
*/
ret = of_property_read_u32_array(np, "fsl,stop-mode", out_val,
ARRAY_SIZE(out_val));
Expand Down
3 changes: 2 additions & 1 deletion drivers/net/can/m_can/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ config CAN_M_CAN_PLATFORM

config CAN_M_CAN_TCAN4X5X
depends on CAN_M_CAN
depends on REGMAP_SPI
depends on SPI
select REGMAP_SPI
tristate "TCAN4X5X M_CAN device"
help
Say Y here if you want support for Texas Instruments TCAN4x5x
Expand Down
16 changes: 11 additions & 5 deletions drivers/net/can/m_can/m_can.c
Original file line number Diff line number Diff line change
Expand Up @@ -665,7 +665,7 @@ static int m_can_handle_state_change(struct net_device *dev,
unsigned int ecr;

switch (new_state) {
case CAN_STATE_ERROR_ACTIVE:
case CAN_STATE_ERROR_WARNING:
/* error warning state */
cdev->can.can_stats.error_warning++;
cdev->can.state = CAN_STATE_ERROR_WARNING;
Expand Down Expand Up @@ -694,7 +694,7 @@ static int m_can_handle_state_change(struct net_device *dev,
__m_can_get_berr_counter(dev, &bec);

switch (new_state) {
case CAN_STATE_ERROR_ACTIVE:
case CAN_STATE_ERROR_WARNING:
/* error warning state */
cf->can_id |= CAN_ERR_CRTL;
cf->data[1] = (bec.txerr > bec.rxerr) ?
Expand Down Expand Up @@ -1414,6 +1414,9 @@ static void m_can_stop(struct net_device *dev)
/* disable all interrupts */
m_can_disable_all_interrupts(cdev);

/* Set init mode to disengage from the network */
m_can_config_endisable(cdev, true);

/* set the state as STOPPED */
cdev->can.state = CAN_STATE_STOPPED;
}
Expand Down Expand Up @@ -1812,6 +1815,12 @@ struct m_can_classdev *m_can_class_allocate_dev(struct device *dev)
}
EXPORT_SYMBOL_GPL(m_can_class_allocate_dev);

void m_can_class_free_dev(struct net_device *net)
{
free_candev(net);
}
EXPORT_SYMBOL_GPL(m_can_class_free_dev);

int m_can_class_register(struct m_can_classdev *m_can_dev)
{
int ret;
Expand Down Expand Up @@ -1850,7 +1859,6 @@ int m_can_class_register(struct m_can_classdev *m_can_dev)
if (ret) {
if (m_can_dev->pm_clock_support)
pm_runtime_disable(m_can_dev->dev);
free_candev(m_can_dev->net);
}

return ret;
Expand Down Expand Up @@ -1908,8 +1916,6 @@ void m_can_class_unregister(struct m_can_classdev *m_can_dev)
unregister_candev(m_can_dev->net);

m_can_clk_stop(m_can_dev);

free_candev(m_can_dev->net);
}
EXPORT_SYMBOL_GPL(m_can_class_unregister);

Expand Down
1 change: 1 addition & 0 deletions drivers/net/can/m_can/m_can.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ struct m_can_classdev {
};

struct m_can_classdev *m_can_class_allocate_dev(struct device *dev);
void m_can_class_free_dev(struct net_device *net);
int m_can_class_register(struct m_can_classdev *cdev);
void m_can_class_unregister(struct m_can_classdev *cdev);
int m_can_class_get_clocks(struct m_can_classdev *cdev);
Expand Down
23 changes: 15 additions & 8 deletions drivers/net/can/m_can/m_can_platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,32 +67,36 @@ static int m_can_plat_probe(struct platform_device *pdev)
return -ENOMEM;

priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
if (!priv) {
ret = -ENOMEM;
goto probe_fail;
}

mcan_class->device_data = priv;

m_can_class_get_clocks(mcan_class);
ret = m_can_class_get_clocks(mcan_class);
if (ret)
goto probe_fail;

res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "m_can");
addr = devm_ioremap_resource(&pdev->dev, res);
irq = platform_get_irq_byname(pdev, "int0");
if (IS_ERR(addr) || irq < 0) {
ret = -EINVAL;
goto failed_ret;
goto probe_fail;
}

/* message ram could be shared */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "message_ram");
if (!res) {
ret = -ENODEV;
goto failed_ret;
goto probe_fail;
}

mram_addr = devm_ioremap(&pdev->dev, res->start, resource_size(res));
if (!mram_addr) {
ret = -ENOMEM;
goto failed_ret;
goto probe_fail;
}

priv->base = addr;
Expand All @@ -111,9 +115,10 @@ static int m_can_plat_probe(struct platform_device *pdev)

m_can_init_ram(mcan_class);

ret = m_can_class_register(mcan_class);
return m_can_class_register(mcan_class);

failed_ret:
probe_fail:
m_can_class_free_dev(mcan_class->net);
return ret;
}

Expand All @@ -134,6 +139,8 @@ static int m_can_plat_remove(struct platform_device *pdev)

m_can_class_unregister(mcan_class);

m_can_class_free_dev(mcan_class->net);

platform_set_drvdata(pdev, NULL);

return 0;
Expand Down
32 changes: 23 additions & 9 deletions drivers/net/can/m_can/tcan4x5x.c
Original file line number Diff line number Diff line change
Expand Up @@ -440,14 +440,18 @@ static int tcan4x5x_can_probe(struct spi_device *spi)
return -ENOMEM;

priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
if (!priv) {
ret = -ENOMEM;
goto out_m_can_class_free_dev;
}

priv->power = devm_regulator_get_optional(&spi->dev, "vsup");
if (PTR_ERR(priv->power) == -EPROBE_DEFER)
return -EPROBE_DEFER;
else
if (PTR_ERR(priv->power) == -EPROBE_DEFER) {
ret = -EPROBE_DEFER;
goto out_m_can_class_free_dev;
} else {
priv->power = NULL;
}

mcan_class->device_data = priv;

Expand All @@ -460,8 +464,10 @@ static int tcan4x5x_can_probe(struct spi_device *spi)
}

/* Sanity check */
if (freq < 20000000 || freq > TCAN4X5X_EXT_CLK_DEF)
return -ERANGE;
if (freq < 20000000 || freq > TCAN4X5X_EXT_CLK_DEF) {
ret = -ERANGE;
goto out_m_can_class_free_dev;
}

priv->reg_offset = TCAN4X5X_MCAN_OFFSET;
priv->mram_start = TCAN4X5X_MRAM_START;
Expand All @@ -487,6 +493,10 @@ static int tcan4x5x_can_probe(struct spi_device *spi)

priv->regmap = devm_regmap_init(&spi->dev, &tcan4x5x_bus,
&spi->dev, &tcan4x5x_regmap);
if (IS_ERR(priv->regmap)) {
ret = PTR_ERR(priv->regmap);
goto out_clk;
}

ret = tcan4x5x_power_enable(priv->power, 1);
if (ret)
Expand Down Expand Up @@ -514,18 +524,22 @@ static int tcan4x5x_can_probe(struct spi_device *spi)
clk_disable_unprepare(mcan_class->cclk);
clk_disable_unprepare(mcan_class->hclk);
}

out_m_can_class_free_dev:
m_can_class_free_dev(mcan_class->net);
dev_err(&spi->dev, "Probe failed, err=%d\n", ret);

return ret;
}

static int tcan4x5x_can_remove(struct spi_device *spi)
{
struct tcan4x5x_priv *priv = spi_get_drvdata(spi);

m_can_class_unregister(priv->mcan_dev);

tcan4x5x_power_enable(priv->power, 0);

m_can_class_unregister(priv->mcan_dev);
m_can_class_free_dev(priv->mcan_dev->net);

return 0;
}
Expand Down
13 changes: 8 additions & 5 deletions drivers/net/can/ti_hecc.c
Original file line number Diff line number Diff line change
Expand Up @@ -881,28 +881,31 @@ static int ti_hecc_probe(struct platform_device *pdev)
priv->base = devm_platform_ioremap_resource_byname(pdev, "hecc");
if (IS_ERR(priv->base)) {
dev_err(&pdev->dev, "hecc ioremap failed\n");
return PTR_ERR(priv->base);
err = PTR_ERR(priv->base);
goto probe_exit_candev;
}

/* handle hecc-ram memory */
priv->hecc_ram = devm_platform_ioremap_resource_byname(pdev,
"hecc-ram");
if (IS_ERR(priv->hecc_ram)) {
dev_err(&pdev->dev, "hecc-ram ioremap failed\n");
return PTR_ERR(priv->hecc_ram);
err = PTR_ERR(priv->hecc_ram);
goto probe_exit_candev;
}

/* handle mbx memory */
priv->mbx = devm_platform_ioremap_resource_byname(pdev, "mbx");
if (IS_ERR(priv->mbx)) {
dev_err(&pdev->dev, "mbx ioremap failed\n");
return PTR_ERR(priv->mbx);
err = PTR_ERR(priv->mbx);
goto probe_exit_candev;
}

irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!irq) {
dev_err(&pdev->dev, "No irq resource\n");
goto probe_exit;
goto probe_exit_candev;
}

priv->ndev = ndev;
Expand Down Expand Up @@ -966,7 +969,7 @@ static int ti_hecc_probe(struct platform_device *pdev)
clk_put(priv->clk);
probe_exit_candev:
free_candev(ndev);
probe_exit:

return err;
}

Expand Down
4 changes: 2 additions & 2 deletions drivers/net/can/usb/mcba_usb.c
Original file line number Diff line number Diff line change
Expand Up @@ -326,8 +326,6 @@ static netdev_tx_t mcba_usb_start_xmit(struct sk_buff *skb,
if (!ctx)
return NETDEV_TX_BUSY;

can_put_echo_skb(skb, priv->netdev, ctx->ndx);

if (cf->can_id & CAN_EFF_FLAG) {
/* SIDH | SIDL | EIDH | EIDL
* 28 - 21 | 20 19 18 x x x 17 16 | 15 - 8 | 7 - 0
Expand Down Expand Up @@ -357,6 +355,8 @@ static netdev_tx_t mcba_usb_start_xmit(struct sk_buff *skb,
if (cf->can_id & CAN_RTR_FLAG)
usb_msg.dlc |= MCBA_DLC_RTR_MASK;

can_put_echo_skb(skb, priv->netdev, ctx->ndx);

err = mcba_usb_xmit(priv, (struct mcba_usb_msg *)&usb_msg, ctx);
if (err)
goto xmit_failed;
Expand Down
4 changes: 2 additions & 2 deletions drivers/net/can/usb/peak_usb/pcan_usb_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ void peak_usb_get_ts_time(struct peak_time_ref *time_ref, u32 ts, ktime_t *time)
if (time_ref->ts_dev_1 < time_ref->ts_dev_2) {
/* case when event time (tsw) wraps */
if (ts < time_ref->ts_dev_1)
delta_ts = 1 << time_ref->adapter->ts_used_bits;
delta_ts = BIT_ULL(time_ref->adapter->ts_used_bits);

/* Otherwise, sync time counter (ts_dev_2) has wrapped:
* handle case when event time (tsn) hasn't.
Expand All @@ -168,7 +168,7 @@ void peak_usb_get_ts_time(struct peak_time_ref *time_ref, u32 ts, ktime_t *time)
* tsn ts
*/
} else if (time_ref->ts_dev_1 < ts) {
delta_ts = -(1 << time_ref->adapter->ts_used_bits);
delta_ts = -BIT_ULL(time_ref->adapter->ts_used_bits);
}

/* add delay between last sync and event timestamps */
Expand Down
38 changes: 28 additions & 10 deletions net/can/af_can.c
Original file line number Diff line number Diff line change
Expand Up @@ -677,33 +677,51 @@ static int can_rcv(struct sk_buff *skb, struct net_device *dev,
{
struct canfd_frame *cfd = (struct canfd_frame *)skb->data;

if (unlikely(dev->type != ARPHRD_CAN || skb->len != CAN_MTU ||
cfd->len > CAN_MAX_DLEN)) {
pr_warn_once("PF_CAN: dropped non conform CAN skbuf: dev type %d, len %d, datalen %d\n",
if (unlikely(dev->type != ARPHRD_CAN || skb->len != CAN_MTU)) {
pr_warn_once("PF_CAN: dropped non conform CAN skbuff: dev type %d, len %d\n",
dev->type, skb->len);
goto free_skb;
}

/* This check is made separately since cfd->len would be uninitialized if skb->len = 0. */
if (unlikely(cfd->len > CAN_MAX_DLEN)) {
pr_warn_once("PF_CAN: dropped non conform CAN skbuff: dev type %d, len %d, datalen %d\n",
dev->type, skb->len, cfd->len);
kfree_skb(skb);
return NET_RX_DROP;
goto free_skb;
}

can_receive(skb, dev);
return NET_RX_SUCCESS;

free_skb:
kfree_skb(skb);
return NET_RX_DROP;
}

static int canfd_rcv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *pt, struct net_device *orig_dev)
{
struct canfd_frame *cfd = (struct canfd_frame *)skb->data;

if (unlikely(dev->type != ARPHRD_CAN || skb->len != CANFD_MTU ||
cfd->len > CANFD_MAX_DLEN)) {
pr_warn_once("PF_CAN: dropped non conform CAN FD skbuf: dev type %d, len %d, datalen %d\n",
if (unlikely(dev->type != ARPHRD_CAN || skb->len != CANFD_MTU)) {
pr_warn_once("PF_CAN: dropped non conform CAN FD skbuff: dev type %d, len %d\n",
dev->type, skb->len);
goto free_skb;
}

/* This check is made separately since cfd->len would be uninitialized if skb->len = 0. */
if (unlikely(cfd->len > CANFD_MAX_DLEN)) {
pr_warn_once("PF_CAN: dropped non conform CAN FD skbuff: dev type %d, len %d, datalen %d\n",
dev->type, skb->len, cfd->len);
kfree_skb(skb);
return NET_RX_DROP;
goto free_skb;
}

can_receive(skb, dev);
return NET_RX_SUCCESS;

free_skb:
kfree_skb(skb);
return NET_RX_DROP;
}

/* af_can protocol functions */
Expand Down

0 comments on commit e2142ef

Please sign in to comment.