Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+49 −30
Original line number Diff line number Diff line
@@ -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>

@@ -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;
@@ -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. */
@@ -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);
@@ -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;
@@ -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. */
@@ -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;

@@ -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) {
@@ -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;

@@ -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;
@@ -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;

@@ -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));
@@ -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)
@@ -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;
@@ -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);
+4 −2
Original line number Diff line number Diff line
@@ -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)
@@ -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;