[PATCH RFC net] ps3/gelic: Fix possible NULL pointer dereference

From: Simon Horman
Date: Wed Feb 21 2024 - 11:57:08 EST


Fix possible NULL pointer dereference in gelic_card_release_tx_chain()

The cited commit introduced a netdev variable to
gelic_card_release_tx_chain() which is set unconditionally on each
iteration of a for loop.

It is set to the value of tx_chain->tail->skb->dev. However, in some
cases it is assumed that tx_chain->tail->skb may be NULL. And if that
occurs, setting netdev will cause a NULl pointer dereference.

Given the age of this code I do wonder if this can occur in practice.
But to be on the safe side this patch assumes that it can and aims to
avoid the dereference in the case where tx_chain->tail->skb may be NULL.

Flagged by Smatch.
Compile tested only.

Fixes: 589866f9f1cb ("PS3: gelic: Add support for dual network interface")
Signed-off-by: Simon Horman <horms@xxxxxxxxxx>
---
drivers/net/ethernet/toshiba/ps3_gelic_net.c | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/drivers/net/ethernet/toshiba/ps3_gelic_net.c b/drivers/net/ethernet/toshiba/ps3_gelic_net.c
index d5b75af163d3..f03489799f5d 100644
--- a/drivers/net/ethernet/toshiba/ps3_gelic_net.c
+++ b/drivers/net/ethernet/toshiba/ps3_gelic_net.c
@@ -549,14 +549,13 @@ static void gelic_card_release_tx_chain(struct gelic_card *card, int stop)
{
struct gelic_descr_chain *tx_chain;
enum gelic_descr_dma_status status;
- struct net_device *netdev;
int release = 0;

for (tx_chain = &card->tx_chain;
tx_chain->head != tx_chain->tail && tx_chain->tail;
tx_chain->tail = tx_chain->tail->next) {
status = gelic_descr_get_status(tx_chain->tail);
- netdev = tx_chain->tail->skb->dev;
+
switch (status) {
case GELIC_DESCR_DMA_RESPONSE_ERROR:
case GELIC_DESCR_DMA_PROTECTION_ERROR:
@@ -566,11 +565,14 @@ static void gelic_card_release_tx_chain(struct gelic_card *card, int stop)
"%s: forcing end of tx descriptor " \
"with status %x\n",
__func__, status);
- netdev->stats.tx_dropped++;
+ tx_chain->tail->skb->dev->stats.tx_dropped++;
break;

case GELIC_DESCR_DMA_COMPLETE:
if (tx_chain->tail->skb) {
+ struct net_device *netdev;
+
+ netdev = tx_chain->tail->skb->dev;
netdev->stats.tx_packets++;
netdev->stats.tx_bytes +=
tx_chain->tail->skb->len;