Commit 5bbed92d authored by Daniel Martensson's avatar Daniel Martensson Committed by David S. Miller

caif-hsi: Added sanity check for length of CAIF frames

Added sanity check for length of CAIF frames, and tear down of
CAIF link-layer device upon protocol error.
Signed-off-by: default avatarSjur Brændeland <sjur.brandeland@stericsson.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 28bd2049
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/if_arp.h> #include <linux/if_arp.h>
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/rtnetlink.h>
#include <net/caif/caif_layer.h> #include <net/caif/caif_layer.h>
#include <net/caif/caif_hsi.h> #include <net/caif/caif_hsi.h>
...@@ -348,8 +349,7 @@ static void cfhsi_tx_done_cb(struct cfhsi_drv *drv) ...@@ -348,8 +349,7 @@ static void cfhsi_tx_done_cb(struct cfhsi_drv *drv)
cfhsi_tx_done(cfhsi); cfhsi_tx_done(cfhsi);
} }
static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi, static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
bool *dump)
{ {
int xfer_sz = 0; int xfer_sz = 0;
int nfrms = 0; int nfrms = 0;
...@@ -360,8 +360,7 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi, ...@@ -360,8 +360,7 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi,
(desc->offset > CFHSI_MAX_EMB_FRM_SZ)) { (desc->offset > CFHSI_MAX_EMB_FRM_SZ)) {
dev_err(&cfhsi->ndev->dev, "%s: Invalid descriptor.\n", dev_err(&cfhsi->ndev->dev, "%s: Invalid descriptor.\n",
__func__); __func__);
*dump = true; return -EPROTO;
return 0;
} }
/* Check for embedded CAIF frame. */ /* Check for embedded CAIF frame. */
...@@ -379,6 +378,12 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi, ...@@ -379,6 +378,12 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi,
len |= ((*(pfrm+1)) << 8) & 0xFF00; len |= ((*(pfrm+1)) << 8) & 0xFF00;
len += 2; /* Add FCS fields. */ len += 2; /* Add FCS fields. */
/* Sanity check length of CAIF frame. */
if (unlikely(len > CFHSI_MAX_CAIF_FRAME_SZ)) {
dev_err(&cfhsi->ndev->dev, "%s: Invalid length.\n",
__func__);
return -EPROTO;
}
/* Allocate SKB (OK even in IRQ context). */ /* Allocate SKB (OK even in IRQ context). */
skb = alloc_skb(len + 1, GFP_ATOMIC); skb = alloc_skb(len + 1, GFP_ATOMIC);
...@@ -423,18 +428,16 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi, ...@@ -423,18 +428,16 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi,
if (desc->header & CFHSI_PIGGY_DESC) if (desc->header & CFHSI_PIGGY_DESC)
xfer_sz += CFHSI_DESC_SZ; xfer_sz += CFHSI_DESC_SZ;
if (xfer_sz % 4) { if ((xfer_sz % 4) || (xfer_sz > (CFHSI_BUF_SZ_RX - CFHSI_DESC_SZ))) {
dev_err(&cfhsi->ndev->dev, dev_err(&cfhsi->ndev->dev,
"%s: Invalid payload len: %d, ignored.\n", "%s: Invalid payload len: %d, ignored.\n",
__func__, xfer_sz); __func__, xfer_sz);
xfer_sz = 0; return -EPROTO;
*dump = true;
} }
return xfer_sz; return xfer_sz;
} }
static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi, static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
bool *dump)
{ {
int rx_sz = 0; int rx_sz = 0;
int nfrms = 0; int nfrms = 0;
...@@ -446,8 +449,7 @@ static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi, ...@@ -446,8 +449,7 @@ static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi,
(desc->offset > CFHSI_MAX_EMB_FRM_SZ))) { (desc->offset > CFHSI_MAX_EMB_FRM_SZ))) {
dev_err(&cfhsi->ndev->dev, "%s: Invalid descriptor.\n", dev_err(&cfhsi->ndev->dev, "%s: Invalid descriptor.\n",
__func__); __func__);
*dump = true; return -EPROTO;
return -EINVAL;
} }
/* Set frame pointer to start of payload. */ /* Set frame pointer to start of payload. */
...@@ -469,13 +471,6 @@ static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi, ...@@ -469,13 +471,6 @@ static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi,
u8 *pcffrm = NULL; u8 *pcffrm = NULL;
int len = 0; int len = 0;
if (WARN_ON(desc->cffrm_len[nfrms] > CFHSI_MAX_PAYLOAD_SZ)) {
dev_err(&cfhsi->ndev->dev, "%s: Invalid payload.\n",
__func__);
*dump = true;
return -EINVAL;
}
/* CAIF frame starts after head padding. */ /* CAIF frame starts after head padding. */
pcffrm = pfrm + *pfrm + 1; pcffrm = pfrm + *pfrm + 1;
...@@ -484,6 +479,13 @@ static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi, ...@@ -484,6 +479,13 @@ static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi,
len |= ((*(pcffrm + 1)) << 8) & 0xFF00; len |= ((*(pcffrm + 1)) << 8) & 0xFF00;
len += 2; /* Add FCS fields. */ len += 2; /* Add FCS fields. */
/* Sanity check length of CAIF frames. */
if (unlikely(len > CFHSI_MAX_CAIF_FRAME_SZ)) {
dev_err(&cfhsi->ndev->dev, "%s: Invalid length.\n",
__func__);
return -EPROTO;
}
/* Allocate SKB (OK even in IRQ context). */ /* Allocate SKB (OK even in IRQ context). */
skb = alloc_skb(len + 1, GFP_ATOMIC); skb = alloc_skb(len + 1, GFP_ATOMIC);
if (!skb) { if (!skb) {
...@@ -528,7 +530,6 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi) ...@@ -528,7 +530,6 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi)
int res; int res;
int desc_pld_len = 0; int desc_pld_len = 0;
struct cfhsi_desc *desc = NULL; struct cfhsi_desc *desc = NULL;
bool dump = false;
desc = (struct cfhsi_desc *)cfhsi->rx_buf; desc = (struct cfhsi_desc *)cfhsi->rx_buf;
...@@ -544,16 +545,20 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi) ...@@ -544,16 +545,20 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi)
spin_unlock_bh(&cfhsi->lock); spin_unlock_bh(&cfhsi->lock);
if (cfhsi->rx_state.state == CFHSI_RX_STATE_DESC) { if (cfhsi->rx_state.state == CFHSI_RX_STATE_DESC) {
desc_pld_len = cfhsi_rx_desc(desc, cfhsi, &dump); desc_pld_len = cfhsi_rx_desc(desc, cfhsi);
if (desc_pld_len == -ENOMEM) if (desc_pld_len == -ENOMEM)
goto restart; goto restart;
if (desc_pld_len == -EPROTO)
goto out_of_sync;
} else { } else {
int pld_len; int pld_len;
if (!cfhsi->rx_state.piggy_desc) { if (!cfhsi->rx_state.piggy_desc) {
pld_len = cfhsi_rx_pld(desc, cfhsi, &dump); pld_len = cfhsi_rx_pld(desc, cfhsi);
if (pld_len == -ENOMEM) if (pld_len == -ENOMEM)
goto restart; goto restart;
if (pld_len == -EPROTO)
goto out_of_sync;
cfhsi->rx_state.pld_len = pld_len; cfhsi->rx_state.pld_len = pld_len;
} else { } else {
pld_len = cfhsi->rx_state.pld_len; pld_len = cfhsi->rx_state.pld_len;
...@@ -567,7 +572,7 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi) ...@@ -567,7 +572,7 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi)
cfhsi->rx_state.piggy_desc = true; cfhsi->rx_state.piggy_desc = true;
/* Extract piggy-backed descriptor. */ /* Extract piggy-backed descriptor. */
desc_pld_len = cfhsi_rx_desc(piggy_desc, cfhsi, &dump); desc_pld_len = cfhsi_rx_desc(piggy_desc, cfhsi);
if (desc_pld_len == -ENOMEM) if (desc_pld_len == -ENOMEM)
goto restart; goto restart;
...@@ -577,15 +582,10 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi) ...@@ -577,15 +582,10 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi)
*/ */
memcpy((u8 *)desc, (u8 *)piggy_desc, memcpy((u8 *)desc, (u8 *)piggy_desc,
CFHSI_DESC_SHORT_SZ); CFHSI_DESC_SHORT_SZ);
}
}
if (unlikely(dump)) { if (desc_pld_len == -EPROTO)
size_t rx_offset = cfhsi->rx_ptr - cfhsi->rx_buf; goto out_of_sync;
dev_err(&cfhsi->ndev->dev, "%s: RX offset: %u.\n", }
__func__, (unsigned) rx_offset);
print_hex_dump_bytes("--> ", DUMP_PREFIX_NONE,
cfhsi->rx_buf, cfhsi->rx_len + rx_offset);
} }
memset(&cfhsi->rx_state, 0, sizeof(cfhsi->rx_state)); memset(&cfhsi->rx_state, 0, sizeof(cfhsi->rx_state));
...@@ -622,6 +622,13 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi) ...@@ -622,6 +622,13 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi)
BUG(); BUG();
} }
mod_timer(&cfhsi->rx_slowpath_timer, jiffies + 1); mod_timer(&cfhsi->rx_slowpath_timer, jiffies + 1);
return;
out_of_sync:
dev_err(&cfhsi->ndev->dev, "%s: Out of sync.\n", __func__);
print_hex_dump_bytes("--> ", DUMP_PREFIX_NONE,
cfhsi->rx_buf, CFHSI_DESC_SZ);
schedule_work(&cfhsi->out_of_sync_work);
} }
static void cfhsi_rx_slowpath(unsigned long arg) static void cfhsi_rx_slowpath(unsigned long arg)
...@@ -804,6 +811,17 @@ static void cfhsi_wake_down(struct work_struct *work) ...@@ -804,6 +811,17 @@ static void cfhsi_wake_down(struct work_struct *work)
} }
static void cfhsi_out_of_sync(struct work_struct *work)
{
struct cfhsi *cfhsi = NULL;
cfhsi = container_of(work, struct cfhsi, out_of_sync_work);
rtnl_lock();
dev_close(cfhsi->ndev);
rtnl_unlock();
}
static void cfhsi_wake_up_cb(struct cfhsi_drv *drv) static void cfhsi_wake_up_cb(struct cfhsi_drv *drv)
{ {
struct cfhsi *cfhsi = NULL; struct cfhsi *cfhsi = NULL;
...@@ -1023,6 +1041,7 @@ int cfhsi_probe(struct platform_device *pdev) ...@@ -1023,6 +1041,7 @@ int cfhsi_probe(struct platform_device *pdev)
/* Initialize the work queues. */ /* Initialize the work queues. */
INIT_WORK(&cfhsi->wake_up_work, cfhsi_wake_up); INIT_WORK(&cfhsi->wake_up_work, cfhsi_wake_up);
INIT_WORK(&cfhsi->wake_down_work, cfhsi_wake_down); INIT_WORK(&cfhsi->wake_down_work, cfhsi_wake_down);
INIT_WORK(&cfhsi->out_of_sync_work, cfhsi_out_of_sync);
/* Clear all bit fields. */ /* Clear all bit fields. */
clear_bit(CFHSI_WAKE_UP_ACK, &cfhsi->bits); clear_bit(CFHSI_WAKE_UP_ACK, &cfhsi->bits);
......
...@@ -52,8 +52,9 @@ struct cfhsi_desc { ...@@ -52,8 +52,9 @@ struct cfhsi_desc {
/* /*
* Maximum bytes transferred in one transfer. * Maximum bytes transferred in one transfer.
*/ */
/* TODO: 4096 is temporary... */ #define CFHSI_MAX_CAIF_FRAME_SZ 4096
#define CFHSI_MAX_PAYLOAD_SZ (CFHSI_MAX_PKTS * 4096)
#define CFHSI_MAX_PAYLOAD_SZ (CFHSI_MAX_PKTS * CFHSI_MAX_CAIF_FRAME_SZ)
/* Size of the complete HSI TX buffer. */ /* Size of the complete HSI TX buffer. */
#define CFHSI_BUF_SZ_TX (CFHSI_DESC_SZ + CFHSI_MAX_PAYLOAD_SZ) #define CFHSI_BUF_SZ_TX (CFHSI_DESC_SZ + CFHSI_MAX_PAYLOAD_SZ)
...@@ -143,6 +144,7 @@ struct cfhsi { ...@@ -143,6 +144,7 @@ struct cfhsi {
struct list_head list; struct list_head list;
struct work_struct wake_up_work; struct work_struct wake_up_work;
struct work_struct wake_down_work; struct work_struct wake_down_work;
struct work_struct out_of_sync_work;
struct workqueue_struct *wq; struct workqueue_struct *wq;
wait_queue_head_t wake_up_wait; wait_queue_head_t wake_up_wait;
wait_queue_head_t wake_down_wait; wait_queue_head_t wake_down_wait;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment