Skip to content

Commit

Permalink
caif-hsi: Added sanity check for length of CAIF frames
Browse files Browse the repository at this point in the history
Added sanity check for length of CAIF frames, and tear down of
CAIF link-layer device upon protocol error.

Signed-off-by: Sjur Brændeland <sjur.brandeland@stericsson.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Daniel Martensson authored and David S. Miller committed Oct 19, 2011
1 parent 28bd204 commit 5bbed92
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 32 deletions.
79 changes: 49 additions & 30 deletions drivers/net/caif/caif_hsi.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <linux/sched.h>
#include <linux/if_arp.h>
#include <linux/timer.h>
#include <linux/rtnetlink.h>
#include <net/caif/caif_layer.h>
#include <net/caif/caif_hsi.h>

Expand Down Expand Up @@ -348,8 +349,7 @@ static void cfhsi_tx_done_cb(struct cfhsi_drv *drv)
cfhsi_tx_done(cfhsi);
}

static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi,
bool *dump)
static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
{
int xfer_sz = 0;
int nfrms = 0;
Expand All @@ -360,8 +360,7 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi,
(desc->offset > CFHSI_MAX_EMB_FRM_SZ)) {
dev_err(&cfhsi->ndev->dev, "%s: Invalid descriptor.\n",
__func__);
*dump = true;
return 0;
return -EPROTO;
}

/* Check for embedded CAIF frame. */
Expand All @@ -379,6 +378,12 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi,
len |= ((*(pfrm+1)) << 8) & 0xFF00;
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). */
skb = alloc_skb(len + 1, GFP_ATOMIC);
Expand Down Expand Up @@ -423,18 +428,16 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi,
if (desc->header & CFHSI_PIGGY_DESC)
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,
"%s: Invalid payload len: %d, ignored.\n",
__func__, xfer_sz);
xfer_sz = 0;
*dump = true;
return -EPROTO;
}
return xfer_sz;
}

static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi,
bool *dump)
static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
{
int rx_sz = 0;
int nfrms = 0;
Expand All @@ -446,8 +449,7 @@ static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi,
(desc->offset > CFHSI_MAX_EMB_FRM_SZ))) {
dev_err(&cfhsi->ndev->dev, "%s: Invalid descriptor.\n",
__func__);
*dump = true;
return -EINVAL;
return -EPROTO;
}

/* Set frame pointer to start of payload. */
Expand All @@ -469,13 +471,6 @@ static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi,
u8 *pcffrm = NULL;
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. */
pcffrm = pfrm + *pfrm + 1;

Expand All @@ -484,6 +479,13 @@ static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi,
len |= ((*(pcffrm + 1)) << 8) & 0xFF00;
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). */
skb = alloc_skb(len + 1, GFP_ATOMIC);
if (!skb) {
Expand Down Expand Up @@ -528,7 +530,6 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi)
int res;
int desc_pld_len = 0;
struct cfhsi_desc *desc = NULL;
bool dump = false;

desc = (struct cfhsi_desc *)cfhsi->rx_buf;

Expand All @@ -544,16 +545,20 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi)
spin_unlock_bh(&cfhsi->lock);

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)
goto restart;
if (desc_pld_len == -EPROTO)
goto out_of_sync;
} else {
int pld_len;

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)
goto restart;
if (pld_len == -EPROTO)
goto out_of_sync;
cfhsi->rx_state.pld_len = pld_len;
} else {
pld_len = cfhsi->rx_state.pld_len;
Expand All @@ -567,7 +572,7 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi)
cfhsi->rx_state.piggy_desc = true;

/* 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)
goto restart;

Expand All @@ -577,15 +582,10 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi)
*/
memcpy((u8 *)desc, (u8 *)piggy_desc,
CFHSI_DESC_SHORT_SZ);
}
}

if (unlikely(dump)) {
size_t rx_offset = cfhsi->rx_ptr - cfhsi->rx_buf;
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);
if (desc_pld_len == -EPROTO)
goto out_of_sync;
}
}

memset(&cfhsi->rx_state, 0, sizeof(cfhsi->rx_state));
Expand Down Expand Up @@ -622,6 +622,13 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi)
BUG();
}
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)
Expand Down Expand Up @@ -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)
{
struct cfhsi *cfhsi = NULL;
Expand Down Expand Up @@ -1023,6 +1041,7 @@ int cfhsi_probe(struct platform_device *pdev)
/* Initialize the work queues. */
INIT_WORK(&cfhsi->wake_up_work, cfhsi_wake_up);
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_bit(CFHSI_WAKE_UP_ACK, &cfhsi->bits);
Expand Down
6 changes: 4 additions & 2 deletions include/net/caif/caif_hsi.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,9 @@ struct cfhsi_desc {
/*
* Maximum bytes transferred in one transfer.
*/
/* TODO: 4096 is temporary... */
#define CFHSI_MAX_PAYLOAD_SZ (CFHSI_MAX_PKTS * 4096)
#define CFHSI_MAX_CAIF_FRAME_SZ 4096

#define CFHSI_MAX_PAYLOAD_SZ (CFHSI_MAX_PKTS * CFHSI_MAX_CAIF_FRAME_SZ)

/* Size of the complete HSI TX buffer. */
#define CFHSI_BUF_SZ_TX (CFHSI_DESC_SZ + CFHSI_MAX_PAYLOAD_SZ)
Expand Down Expand Up @@ -143,6 +144,7 @@ struct cfhsi {
struct list_head list;
struct work_struct wake_up_work;
struct work_struct wake_down_work;
struct work_struct out_of_sync_work;
struct workqueue_struct *wq;
wait_queue_head_t wake_up_wait;
wait_queue_head_t wake_down_wait;
Expand Down

0 comments on commit 5bbed92

Please sign in to comment.