diff mbox series

[net] net: vertexcom: mse102x: Fix possible double free of TX skb

Message ID 20241022155242.33729-1-wahrenst@gmx.net (mailing list archive)
State Changes Requested
Delegated to: Netdev Maintainers
Headers show
Series [net] net: vertexcom: mse102x: Fix possible double free of TX skb | expand

Checks

Context Check Description
netdev/series_format success Single patches do not need cover letters
netdev/tree_selection success Clearly marked for net
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag present in non-next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 5 this patch: 5
netdev/build_tools success No tools touched, skip
netdev/cc_maintainers warning 1 maintainers not CCed: horms@kernel.org
netdev/build_clang success Errors and warnings before: 3 this patch: 3
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success Fixes tag looks correct
netdev/build_allmodconfig_warn success Errors and warnings before: 4 this patch: 4
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 78 lines checked
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0
netdev/contest success net-next-2024-10-23--12-00 (tests: 777)

Commit Message

Stefan Wahren Oct. 22, 2024, 3:52 p.m. UTC
The scope of the TX skb is wider than just mse102x_tx_frame_spi(),
so in case the TX skb room needs to be expanded, also its pointer
needs to be adjusted. Otherwise the already freed skb pointer would
be freed again in mse102x_tx_work(), which leads to crashes:

  Internal error: Oops: 0000000096000004 [#2] PREEMPT SMP
  CPU: 0 PID: 712 Comm: kworker/0:1 Tainted: G      D            6.6.23
  Hardware name: chargebyte Charge SOM DC-ONE (DT)
  Workqueue: events mse102x_tx_work [mse102x]
  pstate: 20400009 (nzCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
  pc : skb_release_data+0xb8/0x1d8
  lr : skb_release_data+0x1ac/0x1d8
  sp : ffff8000819a3cc0
  x29: ffff8000819a3cc0 x28: ffff0000046daa60 x27: ffff0000057f2dc0
  x26: ffff000005386c00 x25: 0000000000000002 x24: 00000000ffffffff
  x23: 0000000000000000 x22: 0000000000000001 x21: ffff0000057f2e50
  x20: 0000000000000006 x19: 0000000000000000 x18: ffff00003fdacfcc
  x17: e69ad452d0c49def x16: 84a005feff870102 x15: 0000000000000000
  x14: 000000000000024a x13: 0000000000000002 x12: 0000000000000000
  x11: 0000000000000400 x10: 0000000000000930 x9 : ffff00003fd913e8
  x8 : fffffc00001bc008
  x7 : 0000000000000000 x6 : 0000000000000008
  x5 : ffff00003fd91340 x4 : 0000000000000000 x3 : 0000000000000009
  x2 : 00000000fffffffe x1 : 0000000000000000 x0 : 0000000000000000
  Call trace:
   skb_release_data+0xb8/0x1d8
   kfree_skb_reason+0x48/0xb0
   mse102x_tx_work+0x164/0x35c [mse102x]
   process_one_work+0x138/0x260
   worker_thread+0x32c/0x438
   kthread+0x118/0x11c
   ret_from_fork+0x10/0x20
  Code: aa1303e0 97fffab6 72001c1f 54000141 (f9400660)

Cc: stable@vger.kernel.org
Fixes: 2f207cbf0dd4 ("net: vertexcom: Add MSE102x SPI support")
Signed-off-by: Stefan Wahren <wahrenst@gmx.net>
---
 drivers/net/ethernet/vertexcom/mse102x.c | 34 ++++++++++++------------
 1 file changed, 17 insertions(+), 17 deletions(-)

--
2.34.1

Comments

Simon Horman Oct. 24, 2024, 9:49 a.m. UTC | #1
On Tue, Oct 22, 2024 at 05:52:42PM +0200, Stefan Wahren wrote:
> The scope of the TX skb is wider than just mse102x_tx_frame_spi(),
> so in case the TX skb room needs to be expanded, also its pointer
> needs to be adjusted. Otherwise the already freed skb pointer would
> be freed again in mse102x_tx_work(), which leads to crashes:
> 
>   Internal error: Oops: 0000000096000004 [#2] PREEMPT SMP
>   CPU: 0 PID: 712 Comm: kworker/0:1 Tainted: G      D            6.6.23
>   Hardware name: chargebyte Charge SOM DC-ONE (DT)
>   Workqueue: events mse102x_tx_work [mse102x]
>   pstate: 20400009 (nzCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
>   pc : skb_release_data+0xb8/0x1d8
>   lr : skb_release_data+0x1ac/0x1d8
>   sp : ffff8000819a3cc0
>   x29: ffff8000819a3cc0 x28: ffff0000046daa60 x27: ffff0000057f2dc0
>   x26: ffff000005386c00 x25: 0000000000000002 x24: 00000000ffffffff
>   x23: 0000000000000000 x22: 0000000000000001 x21: ffff0000057f2e50
>   x20: 0000000000000006 x19: 0000000000000000 x18: ffff00003fdacfcc
>   x17: e69ad452d0c49def x16: 84a005feff870102 x15: 0000000000000000
>   x14: 000000000000024a x13: 0000000000000002 x12: 0000000000000000
>   x11: 0000000000000400 x10: 0000000000000930 x9 : ffff00003fd913e8
>   x8 : fffffc00001bc008
>   x7 : 0000000000000000 x6 : 0000000000000008
>   x5 : ffff00003fd91340 x4 : 0000000000000000 x3 : 0000000000000009
>   x2 : 00000000fffffffe x1 : 0000000000000000 x0 : 0000000000000000
>   Call trace:
>    skb_release_data+0xb8/0x1d8
>    kfree_skb_reason+0x48/0xb0
>    mse102x_tx_work+0x164/0x35c [mse102x]
>    process_one_work+0x138/0x260
>    worker_thread+0x32c/0x438
>    kthread+0x118/0x11c
>    ret_from_fork+0x10/0x20
>   Code: aa1303e0 97fffab6 72001c1f 54000141 (f9400660)
> 
> Cc: stable@vger.kernel.org
> Fixes: 2f207cbf0dd4 ("net: vertexcom: Add MSE102x SPI support")
> Signed-off-by: Stefan Wahren <wahrenst@gmx.net>

Reviewed-by: Simon Horman <horms@kernel.org>
Jakub Kicinski Oct. 29, 2024, 7:10 p.m. UTC | #2
On Tue, 22 Oct 2024 17:52:42 +0200 Stefan Wahren wrote:
> -static int mse102x_tx_frame_spi(struct mse102x_net *mse, struct sk_buff *txp,
> +static int mse102x_tx_frame_spi(struct mse102x_net *mse, struct sk_buff **txp,
>  				unsigned int pad)
>  {
>  	struct mse102x_net_spi *mses = to_mse102x_spi(mse);
> @@ -226,29 +226,29 @@ static int mse102x_tx_frame_spi(struct mse102x_net *mse, struct sk_buff *txp,
>  	int ret;
> 
>  	netif_dbg(mse, tx_queued, mse->ndev, "%s: skb %p, %d@%p\n",
> -		  __func__, txp, txp->len, txp->data);
> +		  __func__, *txp, (*txp)->len, (*txp)->data);
> 
> -	if ((skb_headroom(txp) < DET_SOF_LEN) ||
> -	    (skb_tailroom(txp) < DET_DFT_LEN + pad)) {
> -		tskb = skb_copy_expand(txp, DET_SOF_LEN, DET_DFT_LEN + pad,
> +	if ((skb_headroom(*txp) < DET_SOF_LEN) ||
> +	    (skb_tailroom(*txp) < DET_DFT_LEN + pad)) {
> +		tskb = skb_copy_expand(*txp, DET_SOF_LEN, DET_DFT_LEN + pad,
>  				       GFP_KERNEL);
>  		if (!tskb)
>  			return -ENOMEM;
> 
> -		dev_kfree_skb(txp);
> -		txp = tskb;
> +		dev_kfree_skb(*txp);
> +		*txp = tskb;
>  	}
> 
> -	mse102x_push_header(txp);
> +	mse102x_push_header(*txp);
> 
>  	if (pad)
> -		skb_put_zero(txp, pad);
> +		skb_put_zero(*txp, pad);
> 
> -	mse102x_put_footer(txp);
> +	mse102x_put_footer(*txp);
> 
> -	xfer->tx_buf = txp->data;
> +	xfer->tx_buf = (*txp)->data;
>  	xfer->rx_buf = NULL;
> -	xfer->len = txp->len;
> +	xfer->len = (*txp)->len;
> 
>  	ret = spi_sync(mses->spidev, msg);
>  	if (ret < 0) {
> @@ -368,7 +368,7 @@ static void mse102x_rx_pkt_spi(struct mse102x_net *mse)
>  	mse->ndev->stats.rx_bytes += rxlen;

Isn't it easier to change this function to free the copy rather than
the original? That way the original will remain valid for the callers.
Stefan Wahren Oct. 29, 2024, 9:15 p.m. UTC | #3
Hi Jakub,

Am 29.10.24 um 20:10 schrieb Jakub Kicinski:
> On Tue, 22 Oct 2024 17:52:42 +0200 Stefan Wahren wrote:
>> -static int mse102x_tx_frame_spi(struct mse102x_net *mse, struct sk_buff *txp,
>> +static int mse102x_tx_frame_spi(struct mse102x_net *mse, struct sk_buff **txp,
>>   				unsigned int pad)
>>   {
>>   	struct mse102x_net_spi *mses = to_mse102x_spi(mse);
>> @@ -226,29 +226,29 @@ static int mse102x_tx_frame_spi(struct mse102x_net *mse, struct sk_buff *txp,
>>   	int ret;
>>
>>   	netif_dbg(mse, tx_queued, mse->ndev, "%s: skb %p, %d@%p\n",
>> -		  __func__, txp, txp->len, txp->data);
>> +		  __func__, *txp, (*txp)->len, (*txp)->data);
>>
>> -	if ((skb_headroom(txp) < DET_SOF_LEN) ||
>> -	    (skb_tailroom(txp) < DET_DFT_LEN + pad)) {
>> -		tskb = skb_copy_expand(txp, DET_SOF_LEN, DET_DFT_LEN + pad,
>> +	if ((skb_headroom(*txp) < DET_SOF_LEN) ||
>> +	    (skb_tailroom(*txp) < DET_DFT_LEN + pad)) {
>> +		tskb = skb_copy_expand(*txp, DET_SOF_LEN, DET_DFT_LEN + pad,
>>   				       GFP_KERNEL);
>>   		if (!tskb)
>>   			return -ENOMEM;
>>
>> -		dev_kfree_skb(txp);
>> -		txp = tskb;
>> +		dev_kfree_skb(*txp);
>> +		*txp = tskb;
>>   	}
>>
>> -	mse102x_push_header(txp);
>> +	mse102x_push_header(*txp);
>>
>>   	if (pad)
>> -		skb_put_zero(txp, pad);
>> +		skb_put_zero(*txp, pad);
>>
>> -	mse102x_put_footer(txp);
>> +	mse102x_put_footer(*txp);
>>
>> -	xfer->tx_buf = txp->data;
>> +	xfer->tx_buf = (*txp)->data;
>>   	xfer->rx_buf = NULL;
>> -	xfer->len = txp->len;
>> +	xfer->len = (*txp)->len;
>>
>>   	ret = spi_sync(mses->spidev, msg);
>>   	if (ret < 0) {
>> @@ -368,7 +368,7 @@ static void mse102x_rx_pkt_spi(struct mse102x_net *mse)
>>   	mse->ndev->stats.rx_bytes += rxlen;
> Isn't it easier to change this function to free the copy rather than
> the original? That way the original will remain valid for the callers.
You mean something like this?

diff --git a/drivers/net/ethernet/vertexcom/mse102x.c
b/drivers/net/ethernet/vertexcom/mse102x.c
index a04d4073def9..2c37957478fb 100644
--- a/drivers/net/ethernet/vertexcom/mse102x.c
+++ b/drivers/net/ethernet/vertexcom/mse102x.c
@@ -222,7 +222,7 @@ static int mse102x_tx_frame_spi(struct mse102x_net
*mse, struct sk_buff *txp,
      struct mse102x_net_spi *mses = to_mse102x_spi(mse);
      struct spi_transfer *xfer = &mses->spi_xfer;
      struct spi_message *msg = &mses->spi_msg;
-    struct sk_buff *tskb;
+    struct sk_buff *tskb = NULL;
      int ret;

      netif_dbg(mse, tx_queued, mse->ndev, "%s: skb %p, %d@%p\n",
@@ -235,7 +235,6 @@ static int mse102x_tx_frame_spi(struct mse102x_net
*mse, struct sk_buff *txp,
          if (!tskb)
              return -ENOMEM;

-        dev_kfree_skb(txp);
          txp = tskb;
      }

@@ -257,6 +256,8 @@ static int mse102x_tx_frame_spi(struct mse102x_net
*mse, struct sk_buff *txp,
          mse->stats.xfer_err++;
      }

+    dev_kfree_skb(tskb);
+
      return ret;
  }
Jakub Kicinski Oct. 29, 2024, 10:01 p.m. UTC | #4
On Tue, 29 Oct 2024 22:15:15 +0100 Stefan Wahren wrote:
> > Isn't it easier to change this function to free the copy rather than
> > the original? That way the original will remain valid for the callers.  
> You mean something like this?
> 
> diff --git a/drivers/net/ethernet/vertexcom/mse102x.c
> b/drivers/net/ethernet/vertexcom/mse102x.c
> index a04d4073def9..2c37957478fb 100644
> --- a/drivers/net/ethernet/vertexcom/mse102x.c
> +++ b/drivers/net/ethernet/vertexcom/mse102x.c
> @@ -222,7 +222,7 @@ static int mse102x_tx_frame_spi(struct mse102x_net
> *mse, struct sk_buff *txp,
>       struct mse102x_net_spi *mses = to_mse102x_spi(mse);
>       struct spi_transfer *xfer = &mses->spi_xfer;
>       struct spi_message *msg = &mses->spi_msg;
> -    struct sk_buff *tskb;
> +    struct sk_buff *tskb = NULL;
>       int ret;
> 
>       netif_dbg(mse, tx_queued, mse->ndev, "%s: skb %p, %d@%p\n",
> @@ -235,7 +235,6 @@ static int mse102x_tx_frame_spi(struct mse102x_net
> *mse, struct sk_buff *txp,
>           if (!tskb)
>               return -ENOMEM;
> 
> -        dev_kfree_skb(txp);
>           txp = tskb;
>       }
> 
> @@ -257,6 +256,8 @@ static int mse102x_tx_frame_spi(struct mse102x_net
> *mse, struct sk_buff *txp,
>           mse->stats.xfer_err++;
>       }
> 
> +    dev_kfree_skb(tskb);
> +
>       return ret;
>   }

Exactly, I think it would work and it feels simpler.
Stefan Wahren Oct. 29, 2024, 11:06 p.m. UTC | #5
Am 29.10.24 um 23:01 schrieb Jakub Kicinski:
> On Tue, 29 Oct 2024 22:15:15 +0100 Stefan Wahren wrote:
>>> Isn't it easier to change this function to free the copy rather than
>>> the original? That way the original will remain valid for the callers.
>> You mean something like this?
>>
>> diff --git a/drivers/net/ethernet/vertexcom/mse102x.c
>> b/drivers/net/ethernet/vertexcom/mse102x.c
>> index a04d4073def9..2c37957478fb 100644
>> --- a/drivers/net/ethernet/vertexcom/mse102x.c
>> +++ b/drivers/net/ethernet/vertexcom/mse102x.c
>> @@ -222,7 +222,7 @@ static int mse102x_tx_frame_spi(struct mse102x_net
>> *mse, struct sk_buff *txp,
>>        struct mse102x_net_spi *mses = to_mse102x_spi(mse);
>>        struct spi_transfer *xfer = &mses->spi_xfer;
>>        struct spi_message *msg = &mses->spi_msg;
>> -    struct sk_buff *tskb;
>> +    struct sk_buff *tskb = NULL;
>>        int ret;
>>
>>        netif_dbg(mse, tx_queued, mse->ndev, "%s: skb %p, %d@%p\n",
>> @@ -235,7 +235,6 @@ static int mse102x_tx_frame_spi(struct mse102x_net
>> *mse, struct sk_buff *txp,
>>            if (!tskb)
>>                return -ENOMEM;
>>
>> -        dev_kfree_skb(txp);
>>            txp = tskb;
>>        }
>>
>> @@ -257,6 +256,8 @@ static int mse102x_tx_frame_spi(struct mse102x_net
>> *mse, struct sk_buff *txp,
>>            mse->stats.xfer_err++;
>>        }
>>
>> +    dev_kfree_skb(tskb);
>> +
>>        return ret;
>>    }
> Exactly, I think it would work and it feels simpler.
I didn't test it yet, i need access to evaluation board before. But this
change will behave differently regarding stats of tx_bytes [1]. The
first version will include the padding, while the second does not.

[1] -
https://elixir.bootlin.com/linux/v6.12-rc5/source/drivers/net/ethernet/vertexcom/mse102x.c#L445
Jakub Kicinski Oct. 30, 2024, 12:36 a.m. UTC | #6
On Wed, 30 Oct 2024 00:06:46 +0100 Stefan Wahren wrote:
> > Exactly, I think it would work and it feels simpler.  
> I didn't test it yet, i need access to evaluation board before. But this
> change will behave differently regarding stats of tx_bytes [1]. The
> first version will include the padding, while the second does not.

Good point! But I think we'd be moving in the right direction.
tx_bytes should count bytes sent to the network, not data+metadata
sent on an internal bus.

If you connect this board to a different controller directly the
rx_bytes of the other end should match the tx_bytes of the board 
with mse102x. The byte accounting would benefit from further massaging
in a separate patch.
diff mbox series

Patch

diff --git a/drivers/net/ethernet/vertexcom/mse102x.c b/drivers/net/ethernet/vertexcom/mse102x.c
index a04d4073def9..8b9e700a1e63 100644
--- a/drivers/net/ethernet/vertexcom/mse102x.c
+++ b/drivers/net/ethernet/vertexcom/mse102x.c
@@ -216,7 +216,7 @@  static inline void mse102x_put_footer(struct sk_buff *skb)
 	*footer = cpu_to_be16(DET_DFT);
 }

-static int mse102x_tx_frame_spi(struct mse102x_net *mse, struct sk_buff *txp,
+static int mse102x_tx_frame_spi(struct mse102x_net *mse, struct sk_buff **txp,
 				unsigned int pad)
 {
 	struct mse102x_net_spi *mses = to_mse102x_spi(mse);
@@ -226,29 +226,29 @@  static int mse102x_tx_frame_spi(struct mse102x_net *mse, struct sk_buff *txp,
 	int ret;

 	netif_dbg(mse, tx_queued, mse->ndev, "%s: skb %p, %d@%p\n",
-		  __func__, txp, txp->len, txp->data);
+		  __func__, *txp, (*txp)->len, (*txp)->data);

-	if ((skb_headroom(txp) < DET_SOF_LEN) ||
-	    (skb_tailroom(txp) < DET_DFT_LEN + pad)) {
-		tskb = skb_copy_expand(txp, DET_SOF_LEN, DET_DFT_LEN + pad,
+	if ((skb_headroom(*txp) < DET_SOF_LEN) ||
+	    (skb_tailroom(*txp) < DET_DFT_LEN + pad)) {
+		tskb = skb_copy_expand(*txp, DET_SOF_LEN, DET_DFT_LEN + pad,
 				       GFP_KERNEL);
 		if (!tskb)
 			return -ENOMEM;

-		dev_kfree_skb(txp);
-		txp = tskb;
+		dev_kfree_skb(*txp);
+		*txp = tskb;
 	}

-	mse102x_push_header(txp);
+	mse102x_push_header(*txp);

 	if (pad)
-		skb_put_zero(txp, pad);
+		skb_put_zero(*txp, pad);

-	mse102x_put_footer(txp);
+	mse102x_put_footer(*txp);

-	xfer->tx_buf = txp->data;
+	xfer->tx_buf = (*txp)->data;
 	xfer->rx_buf = NULL;
-	xfer->len = txp->len;
+	xfer->len = (*txp)->len;

 	ret = spi_sync(mses->spidev, msg);
 	if (ret < 0) {
@@ -368,7 +368,7 @@  static void mse102x_rx_pkt_spi(struct mse102x_net *mse)
 	mse->ndev->stats.rx_bytes += rxlen;
 }

-static int mse102x_tx_pkt_spi(struct mse102x_net *mse, struct sk_buff *txb,
+static int mse102x_tx_pkt_spi(struct mse102x_net *mse, struct sk_buff **txb,
 			      unsigned long work_timeout)
 {
 	unsigned int pad = 0;
@@ -377,11 +377,11 @@  static int mse102x_tx_pkt_spi(struct mse102x_net *mse, struct sk_buff *txb,
 	int ret;
 	bool first = true;

-	if (txb->len < ETH_ZLEN)
-		pad = ETH_ZLEN - txb->len;
+	if ((*txb)->len < ETH_ZLEN)
+		pad = ETH_ZLEN - (*txb)->len;

 	while (1) {
-		mse102x_tx_cmd_spi(mse, CMD_RTS | (txb->len + pad));
+		mse102x_tx_cmd_spi(mse, CMD_RTS | ((*txb)->len + pad));
 		ret = mse102x_rx_cmd_spi(mse, (u8 *)&rx);
 		cmd_resp = be16_to_cpu(rx);

@@ -437,7 +437,7 @@  static void mse102x_tx_work(struct work_struct *work)

 	while ((txb = skb_dequeue(&mse->txq))) {
 		mutex_lock(&mses->lock);
-		ret = mse102x_tx_pkt_spi(mse, txb, work_timeout);
+		ret = mse102x_tx_pkt_spi(mse, &txb, work_timeout);
 		mutex_unlock(&mses->lock);
 		if (ret) {
 			mse->ndev->stats.tx_dropped++;