On 1/27/21 9:53 PM, Bob Pearson wrote: > On 1/27/21 9:50 PM, Zhu Yanjun wrote: >> On Thu, Jan 28, 2021 at 9:12 AM Bob Pearson <rpearsonhpe@xxxxxxxxx> wrote: >>> >>> rxe_rcv_mcast_pkt() in rxe_recv.c can leak SKBs in error path >>> code. The loop over the QPs attached to a multicast group >>> creates new cloned SKBs for all but the last QP in the list >>> and passes the SKB and its clones to rxe_rcv_pkt() for further >>> processing. Any QPs that do not pass some checks are skipped. >>> If the last QP in the list fails the tests the SKB is leaked. >>> This patch checks if the SKB for the last QP was used and if >>> not frees it. Also removes a redundant loop invariant assignment. >>> >>> Fixes: 8700e3e7c4857 ("Soft RoCE driver") >>> Fixes: 71abf20b28ff8 ("RDMA/rxe: Handle skb_clone() failure in rxe_recv.c") >>> Signed-off-by: Bob Pearson <rpearson@xxxxxxx> >>> --- >>> drivers/infiniband/sw/rxe/rxe_recv.c | 18 +++++++++++------- >>> 1 file changed, 11 insertions(+), 7 deletions(-) >>> >>> diff --git a/drivers/infiniband/sw/rxe/rxe_recv.c b/drivers/infiniband/sw/rxe/rxe_recv.c >>> index c9984a28eecc..57cc25e3b4ad 100644 >>> --- a/drivers/infiniband/sw/rxe/rxe_recv.c >>> +++ b/drivers/infiniband/sw/rxe/rxe_recv.c >>> @@ -252,7 +252,6 @@ static void rxe_rcv_mcast_pkt(struct rxe_dev *rxe, struct sk_buff *skb) >>> >>> list_for_each_entry(mce, &mcg->qp_list, qp_list) { >>> qp = mce->qp; >>> - pkt = SKB_TO_PKT(skb); >>> >>> /* validate qp for incoming packet */ >>> err = check_type_state(rxe, pkt, qp); >>> @@ -264,12 +263,18 @@ static void rxe_rcv_mcast_pkt(struct rxe_dev *rxe, struct sk_buff *skb) >>> continue; >>> >>> /* for all but the last qp create a new clone of the >>> - * skb and pass to the qp. >>> + * skb and pass to the qp. If an error occurs in the >>> + * checks for the last qp in the list we need to >>> + * free the skb since it hasn't been passed on to >>> + * rxe_rcv_pkt() which would free it later. >>> */ >>> - if (mce->qp_list.next != &mcg->qp_list) >>> + if (mce->qp_list.next != &mcg->qp_list) { >>> per_qp_skb = skb_clone(skb, GFP_ATOMIC); >>> - else >>> + } else { >>> per_qp_skb = skb; >>> + /* show we have consumed the skb */ >>> + skb = NULL; >>> + } >>> >>> if (unlikely(!per_qp_skb)) >>> continue; >>> @@ -284,10 +289,9 @@ static void rxe_rcv_mcast_pkt(struct rxe_dev *rxe, struct sk_buff *skb) >>> >>> rxe_drop_ref(mcg); /* drop ref from rxe_pool_get_key. */ >>> >>> - return; >>> - >>> err1: >>> - kfree_skb(skb); >>> + if (skb) >>> + kfree_skb(skb); >> >> "if (skb)" is not needed here. >> >> The implemetation of kfree_skb: >> >> void kfree_skb(struct sk_buff *skb) >> { >> if (unlikely(!skb)) >> return; >> if (likely(atomic_read(&skb->users) == 1)) >> smp_rmb(); >> else if (likely(!atomic_dec_and_test(&skb->users))) >> return; >> trace_kfree_skb(skb, __builtin_return_address(0)); >> __kfree_skb(skb); >> } >> >> Zhu Yanjun >>> } >>> >>> /** >>> -- >>> 2.27.0 >>> > Agreed but the reason I wrote that was to make it obvious why I set skb to NULL above. But as long as it is clear without it I can remove the test. > Actually I should have written if (unlikely(skb)) kfree_skb(skb);