ACK: [PATCH] IB/uverbs: Handle large number of entries in poll CQ CVE-2010-4649

Tim Gardner tim.gardner at canonical.com
Tue Jul 5 17:52:12 UTC 2011


On 07/05/2011 10:45 AM, Paolo Pisati wrote:
> From: Dan Carpenter<error27 at gmail.com>
>
> BugLink: http://bugs.launchpad.net/bugs/805512
>
> commit upstream 7182afea8d1afd432a17c18162cc3fd441d0da93
>
> In ib_uverbs_poll_cq() code there is a potential integer overflow if
> userspace passes in a large cmd.ne.  The calls to kmalloc() would
> allocate smaller buffers than intended, leading to memory corruption.
> There iss also an information leak if resp wasn't all used.
> Unprivileged userspace may call this function, although only if an
> RDMA device that uses this function is present.
>
> Fix this by copying CQ entries one at a time, which avoids the
> allocation entirely, and also by moving this copying into a function
> that makes sure to initialize all memory copied to userspace.
>
> Special thanks to Jason Gunthorpe<jgunthorpe at obsidianresearch.com>
> for his help and advice.
>
> Cc:<stable at kernel.org>
> Signed-off-by: Dan Carpenter<error27 at gmail.com>
>
> [ Monkey around with things a bit to avoid bad code generation by gcc
>    when designated initializers are used.  - Roland ]
>
> Signed-off-by: Roland Dreier<rolandd at cisco.com>
>
> Signed-off-by: Paolo Pisati<paolo.pisati at canonical.com>
> ---
>   drivers/infiniband/core/uverbs_cmd.c |  101 +++++++++++++++++++---------------
>   1 files changed, 57 insertions(+), 44 deletions(-)
>
> diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c
> index 495c803..3f9d3fe 100644
> --- a/drivers/infiniband/core/uverbs_cmd.c
> +++ b/drivers/infiniband/core/uverbs_cmd.c
> @@ -877,68 +877,81 @@ out:
>   	return ret ? ret : in_len;
>   }
>
> +static int copy_wc_to_user(void __user *dest, struct ib_wc *wc)
> +{
> +	struct ib_uverbs_wc tmp;
> +
> +	tmp.wr_id		= wc->wr_id;
> +	tmp.status		= wc->status;
> +	tmp.opcode		= wc->opcode;
> +	tmp.vendor_err		= wc->vendor_err;
> +	tmp.byte_len		= wc->byte_len;
> +	tmp.imm_data		= (__u32 __force) wc->imm_data;
> +	tmp.qp_num		= wc->qp->qp_num;
> +	tmp.src_qp		= wc->src_qp;
> +	tmp.wc_flags		= wc->wc_flags;
> +	tmp.pkey_index		= wc->pkey_index;
> +	tmp.slid		= wc->slid;
> +	tmp.sl			= wc->sl;
> +	tmp.dlid_path_bits	= wc->dlid_path_bits;
> +	tmp.port_num		= wc->port_num;
> +	tmp.reserved		= 0;
> +
> +	if (copy_to_user(dest,&tmp, sizeof tmp))
> +		return -EFAULT;
> +
> +	return 0;
> +}
> +
>   ssize_t ib_uverbs_poll_cq(struct ib_uverbs_file *file,
>   			  const char __user *buf, int in_len,
>   			  int out_len)
>   {
>   	struct ib_uverbs_poll_cq       cmd;
> -	struct ib_uverbs_poll_cq_resp *resp;
> +	struct ib_uverbs_poll_cq_resp  resp;
> +	u8 __user                     *header_ptr;
> +	u8 __user                     *data_ptr;
>   	struct ib_cq                  *cq;
> -	struct ib_wc                  *wc;
> -	int                            ret = 0;
> -	int                            i;
> -	int                            rsize;
> +	struct ib_wc                   wc;
> +	int                            ret;
>
>   	if (copy_from_user(&cmd, buf, sizeof cmd))
>   		return -EFAULT;
>
> -	wc = kmalloc(cmd.ne * sizeof *wc, GFP_KERNEL);
> -	if (!wc)
> -		return -ENOMEM;
> -
> -	rsize = sizeof *resp + cmd.ne * sizeof(struct ib_uverbs_wc);
> -	resp = kmalloc(rsize, GFP_KERNEL);
> -	if (!resp) {
> -		ret = -ENOMEM;
> -		goto out_wc;
> -	}
> -
>   	cq = idr_read_cq(cmd.cq_handle, file->ucontext, 0);
> -	if (!cq) {
> -		ret = -EINVAL;
> -		goto out;
> -	}
> +	if (!cq)
> +		return -EINVAL;
>
> -	resp->count = ib_poll_cq(cq, cmd.ne, wc);
> +	/* we copy a struct ib_uverbs_poll_cq_resp to user space */
> +	header_ptr = (void __user *)(unsigned long) cmd.response;
> +	data_ptr = header_ptr + sizeof resp;
>
> -	put_cq_read(cq);
> +	memset(&resp, 0, sizeof resp);
> +	while (resp.count<  cmd.ne) {
> +		ret = ib_poll_cq(cq, 1,&wc);
> +		if (ret<  0)
> +			goto out_put;
> +		if (!ret)
> +			break;
> +
> +		ret = copy_wc_to_user(data_ptr,&wc);
> +		if (ret)
> +			goto out_put;
>
> -	for (i = 0; i<  resp->count; i++) {
> -		resp->wc[i].wr_id 	   = wc[i].wr_id;
> -		resp->wc[i].status 	   = wc[i].status;
> -		resp->wc[i].opcode 	   = wc[i].opcode;
> -		resp->wc[i].vendor_err 	   = wc[i].vendor_err;
> -		resp->wc[i].byte_len 	   = wc[i].byte_len;
> -		resp->wc[i].imm_data 	   = (__u32 __force) wc[i].imm_data;
> -		resp->wc[i].qp_num 	   = wc[i].qp->qp_num;
> -		resp->wc[i].src_qp 	   = wc[i].src_qp;
> -		resp->wc[i].wc_flags 	   = wc[i].wc_flags;
> -		resp->wc[i].pkey_index 	   = wc[i].pkey_index;
> -		resp->wc[i].slid 	   = wc[i].slid;
> -		resp->wc[i].sl 		   = wc[i].sl;
> -		resp->wc[i].dlid_path_bits = wc[i].dlid_path_bits;
> -		resp->wc[i].port_num 	   = wc[i].port_num;
> +		data_ptr += sizeof(struct ib_uverbs_wc);
> +		++resp.count;
>   	}
>
> -	if (copy_to_user((void __user *) (unsigned long) cmd.response, resp, rsize))
> +	if (copy_to_user(header_ptr,&resp, sizeof resp)) {
>   		ret = -EFAULT;
> +		goto out_put;
> +	}
>
> -out:
> -	kfree(resp);
> +	ret = in_len;
>
> -out_wc:
> -	kfree(wc);
> -	return ret ? ret : in_len;
> +out_put:
> +	put_cq_read(cq);
> +	return ret;
>   }
>
>   ssize_t ib_uverbs_req_notify_cq(struct ib_uverbs_file *file,

Acked-by: Tim Gardner <tim.gardner at canonical.com>
-- 
Tim Gardner tim.gardner at canonical.com




More information about the kernel-team mailing list