Blob Blame History Raw
From: Julian Wiedmann <jwi@linux.ibm.com>
Subject: net/af_iucv: fix skb handling on HiperTransport xmit error
Patch-mainline: v4.19-rc4
Git-commit: b2f543949acd1ba64313fdad9e672ef47550d773
References: bnc#1113501, LTC#172679

Description:  net/af_iucv: fix skb leaks for HiperTransport
Symptom:      Memory leaks and/or double-freed network packets.
Problem:      Inbound packets may have any combination of flag bits set in
              their iucv header. Current code only handles certain
              combinations, and ignores (ie. leaks) all packets with other
              flags.
              On Transmit, current code is inconsistent about whether the error
              paths need to free the skb. Depending on which error path is
              taken, it may either get freed twice, or leak.
Solution:     On receive, drop any skb with an unexpected combination of iucv
              Header flags.
              On transmit, be consistent in all error paths about free'ing the
              skb.
Reproduction: -

Upstream-Description:

              net/af_iucv: fix skb handling on HiperTransport xmit error

              When sending an skb, afiucv_hs_send() bails out on various error
              conditions. But currently the caller has no way of telling whether the
              skb was freed or not - resulting in potentially either
              a) leaked skbs from iucv_send_ctrl(), or
              b) double-free's from iucv_sock_sendmsg().

              As dev_queue_xmit() will always consume the skb (even on error), be
              consistent and also free the skb from all other error paths. This way
              callers no longer need to care about managing the skb.

              Signed-off-by: Julian Wiedmann <jwi@linux.ibm.com>
              Reviewed-by: Ursula Braun <ubraun@linux.ibm.com>
              Signed-off-by: David S. Miller <davem@davemloft.net>


Signed-off-by: Julian Wiedmann <jwi@linux.ibm.com>
Acked-by: Petr Tesarik <ptesarik@suse.com>
---
 net/iucv/af_iucv.c |   34 +++++++++++++++++++++++-----------
 1 file changed, 23 insertions(+), 11 deletions(-)

--- a/net/iucv/af_iucv.c
+++ b/net/iucv/af_iucv.c
@@ -352,20 +352,28 @@ static int afiucv_hs_send(struct iucv_me
 		memcpy(&phs_hdr->iucv_hdr, imsg, sizeof(struct iucv_message));
 
 	skb->dev = iucv->hs_dev;
-	if (!skb->dev)
-		return -ENODEV;
-	if (!(skb->dev->flags & IFF_UP) || !netif_carrier_ok(skb->dev))
-		return -ENETDOWN;
+	if (!skb->dev) {
+		err = -ENODEV;
+		goto err_free;
+	}
+	if (!(skb->dev->flags & IFF_UP) || !netif_carrier_ok(skb->dev)) {
+		err = -ENETDOWN;
+		goto err_free;
+	}
 	if (skb->len > skb->dev->mtu) {
-		if (sock->sk_type == SOCK_SEQPACKET)
-			return -EMSGSIZE;
-		else
-			skb_trim(skb, skb->dev->mtu);
+		if (sock->sk_type == SOCK_SEQPACKET) {
+			err = -EMSGSIZE;
+			goto err_free;
+		}
+		skb_trim(skb, skb->dev->mtu);
 	}
 	skb->protocol = ETH_P_AF_IUCV;
 	nskb = skb_clone(skb, GFP_ATOMIC);
-	if (!nskb)
-		return -ENOMEM;
+	if (!nskb) {
+		err = -ENOMEM;
+		goto err_free;
+	}
+
 	skb_queue_tail(&iucv->send_skb_q, nskb);
 	err = dev_queue_xmit(skb);
 	if (net_xmit_eval(err)) {
@@ -376,6 +384,10 @@ static int afiucv_hs_send(struct iucv_me
 		WARN_ON(atomic_read(&iucv->msg_recv) < 0);
 	}
 	return net_xmit_eval(err);
+
+err_free:
+	kfree_skb(skb);
+	return err;
 }
 
 static struct sock *__iucv_get_sock_by_name(char *nm)
@@ -1171,7 +1183,7 @@ static int iucv_sock_sendmsg(struct sock
 		err = afiucv_hs_send(&txmsg, sk, skb, 0);
 		if (err) {
 			atomic_dec(&iucv->msg_sent);
-			goto fail;
+			goto out;
 		}
 	} else { /* Classic VM IUCV transport */
 		skb_queue_tail(&iucv->send_skb_q, skb);