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

Commit f4a87ca1 authored by Mustafa Ismail's avatar Mustafa Ismail Committed by Doug Ledford
Browse files

i40iw: Fix double free of QP



A QP can be double freed if i40iw_cm_disconn() is
called while it is currently being freed by
i40iw_rem_ref(). The fix in i40iw_cm_disconn() will
first check if the QP is already freed before
making another request for the QP to be freed.

Signed-off-by: default avatarMustafa Ismail <mustafa.ismail@intel.com>
Signed-off-by: default avatarShiraz Saleem <shiraz.saleem@intel.com>
Signed-off-by: default avatarHenry Orosco <henry.orosco@intel.com>
Signed-off-by: default avatarDoug Ledford <dledford@redhat.com>
parent 91c42b72
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -512,7 +512,7 @@ u32 i40iw_initialize_hw_resources(struct i40iw_device *iwdev);

int i40iw_register_rdma_device(struct i40iw_device *iwdev);
void i40iw_port_ibevent(struct i40iw_device *iwdev);
int i40iw_cm_disconn(struct i40iw_qp *);
void i40iw_cm_disconn(struct i40iw_qp *iwqp);
void i40iw_cm_disconn_worker(void *);
int mini_cm_recv_pkt(struct i40iw_cm_core *, struct i40iw_device *,
		     struct sk_buff *);
+16 −4
Original line number Diff line number Diff line
@@ -3359,21 +3359,33 @@ static void i40iw_cm_init_tsa_conn(struct i40iw_qp *iwqp,
 * i40iw_cm_disconn - when a connection is being closed
 * @iwqp: associate qp for the connection
 */
int i40iw_cm_disconn(struct i40iw_qp *iwqp)
void i40iw_cm_disconn(struct i40iw_qp *iwqp)
{
	struct disconn_work *work;
	struct i40iw_device *iwdev = iwqp->iwdev;
	struct i40iw_cm_core *cm_core = &iwdev->cm_core;
	unsigned long flags;

	work = kzalloc(sizeof(*work), GFP_ATOMIC);
	if (!work)
		return -ENOMEM;	/* Timer will clean up */
		return;	/* Timer will clean up */

	spin_lock_irqsave(&iwdev->qptable_lock, flags);
	if (!iwdev->qp_table[iwqp->ibqp.qp_num]) {
		spin_unlock_irqrestore(&iwdev->qptable_lock, flags);
		i40iw_debug(&iwdev->sc_dev, I40IW_DEBUG_CM,
			    "%s qp_id %d is already freed\n",
			     __func__, iwqp->ibqp.qp_num);
		kfree(work);
		return;
	}
	i40iw_add_ref(&iwqp->ibqp);
	spin_unlock_irqrestore(&iwdev->qptable_lock, flags);

	work->iwqp = iwqp;
	INIT_WORK(&work->work, i40iw_disconnect_worker);
	queue_work(cm_core->disconn_wq, &work->work);
	return 0;
	return;
}

/**
+3 −1
Original line number Diff line number Diff line
@@ -308,7 +308,9 @@ void i40iw_process_aeq(struct i40iw_device *iwdev)
			iwqp = iwdev->qp_table[info->qp_cq_id];
			if (!iwqp) {
				spin_unlock_irqrestore(&iwdev->qptable_lock, flags);
				i40iw_pr_err("qp_id %d is already freed\n", info->qp_cq_id);
				i40iw_debug(dev, I40IW_DEBUG_AEQ,
					    "%s qp_id %d is already freed\n",
					    __func__, info->qp_cq_id);
				continue;
			}
			i40iw_add_ref(&iwqp->ibqp);