From: Xi Wang <xi.wang-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
To: Roland Dreier <roland-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>,
Sean Hefty <sean.hefty-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>,
Hal Rosenstock
<hal.rosenstock-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Cc: linux-rdma-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
security-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org,
Xi Wang <xi.wang-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Subject: IB/uverbs: multiple possible overflows
Date: Fri, 27 Apr 2012 05:13:50 -0400 [thread overview]
Message-ID: <4F9A634E.5020400@gmail.com> (raw)
Hi,
There are multiple integer overflows that may lead to buffer overflows
in drivers/infiniband/core/uverbs_cmd.c. I will explain how an exploit
might work and suggest some patches. Thanks for reviewing.
Possible exploit
================
Consider ib_uverbs_unmarshall_recv(). The function is called from
ib_uverbs_post_recv() and ib_uverbs_post_srq_recv(). In both cases,
its parameters wr_count, sge_count, and wqe_size are directly from
userspace via copy_from_user() and thus they can be any values.
Let's choose the following values, and see how to bypass the existing
checks and reach "next = kmalloc(...)". Assume 32-bit systems.
wr_count = 1
sge_count = 0x80000000
wqe_size = 16
1. NOT in_len < wqe_size * wr_count + sge_count * 16
if (in_len < wqe_size * wr_count +
sge_count * sizeof (struct ib_uverbs_sge))
return ERR_PTR(-EINVAL);
Here struct ib_uverbs_sge is 16 bytes.
Since the second multiplication 0x80000000 * 16 overflows and becomes
0, the check is basically in_len < 16, which can be easily bypassed.
2. NOT wqe_size < 16
if (wqe_size < sizeof (struct ib_uverbs_recv_wr))
return ERR_PTR(-EINVAL);
Here struct ib_uverbs_recv_wr is 16 bytes.
Since we choose wqe_size = 16, the check will be bypassed.
3. wqe_size <= KMALLOC_MAX_SIZE
user_wr = kmalloc(wqe_size, GFP_KERNEL);
if (!user_wr)
return ERR_PTR(-ENOMEM);
To make kmalloc() succeed, wqe_size shouldn't be large. Again,
since we choose wqe_size = 16, the check will be bypassed.
4. wr_count > 0
for (i = 0; i < wr_count; ++i) { ... }
Since we choose wr_count = 1, the loop will run once.
5. NOT num_sge + sg_ind > sge_count
if (user_wr->num_sge + sg_ind > sge_count) {
ret = -EINVAL;
goto err;
}
Here sg_ind is 0. Note that num_sge is also from userspace via
copy_from_user(). Let's choose num_sge = sge_count = 0x80000000,
so as to bypass the check.
Now we come to the allocation call.
next = kmalloc(ALIGN(sizeof *next, sizeof (struct ib_sge)) +
user_wr->num_sge * sizeof (struct ib_sge),
GFP_KERNEL);
The allocation size is basically 32 + num_sge * 16 = 32, which is
smaller than expected.
Also note that in
next->num_sg = user_wr->num_sge;
next->num_sge (ib_recv_wr::num_sge) is signed.
next->sg_list = (void *) next +
ALIGN(sizeof *next, sizeof (struct ib_sge));
Now next->sg_list points to the end of the allocated memory. Any
access to sg_list[i] would be out of bounds.
The subsequent copy_from_user(next->sg_list, ...) is a no-op, since
the size next->num_sge * sizeof (struct ib_sge) evaluates to 0.
Now ib_uverbs_unmarshall_recv() returns to its caller with a negative
wr->num_sge = 0x80000000 and wr->sg_list that points to a bad location.
Let's look at one of the callers, ib_uverbs_post_recv(). The function
then calls into a specific ->post_recv().
qp->device->post_recv(qp->real_qp, wr, &bad_wr);
We use iwch_post_receive() in drivers/infiniband/hw/cxgb3/iwch_qp.c as
an example.
if (wr->num_sge > T3_MAX_SGE) {
err = -EINVAL;
break;
}
...
if (num_wrs)
if (wr->sg_list[0].lkey)
err = build_rdma_recv(qhp, wqe, wr);
else
err = build_zero_stag_recv(qhp, wqe, wr);
else
err = -ENOMEM;
The check "wr->num_sge > T3_MAX_SGE" will be bypassed since wr->num_sge
is negative 0x80000000.
The access "wr->sg_list[0].lkey" is out of bounds. Not sure what value
it gets. We could have bigger trouble in build_rdma_recv(), because it
calls iwch_sgl2pbl_map() with wr->num_sge.
for (i = 0; i < num_sgle; i++) {
...
pbl_addr[i] = ...;
page_size[i] = ...;
}
This time num_sgle = 0x80000000 is interpreted as unsigned (u32), and
the loop may overwrite a lot of places.
Other overflows
===============
ib_uverbs_post_send() has similar code.
Suggested patches
=================
1. Change the types of the following struct fields to unsigned.
ib_recv_wr::num_sge
ib_send_wr::num_sge
This ensures that driver-specific checks like wr->num_sge > T3_MAX_SGE
won't be bypassed.
2. Fix ib_uverbs_unmarshall_recv() and ib_uverbs_post_send().
I am not sure if there's any well-known or reasonable constant limit
for every value from userspace:
wr_count
sge_count
wqe_size
num_sge
If we know such limits, define them as something like WR_COUNT_MAX
for checking.
If the limits do not exist, then it might be boring to fix the code.
For example, in addition to
if (in_len < wqe_size * wr_count +
sge_count * sizeof (struct ib_uverbs_sge))
return ERR_PTR(-EINVAL);
We might have to write many more overflow checks:
if (wr_count && wqe_size > UINT_MAX / wr_count)
return ERR_PTR(-EINVAL);
if (sge_count > (UINT_MAX - wqe_size * wr_count)
/ sizeof (struct ib_uverbs_sge))
return ERR_PTR(-EINVAL);
- xi
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
reply other threads:[~2012-04-27 9:13 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4F9A634E.5020400@gmail.com \
--to=xi.wang-re5jqeeqqe8avxtiumwx3w@public.gmane.org \
--cc=hal.rosenstock-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org \
--cc=linux-rdma-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
--cc=roland-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org \
--cc=sean.hefty-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org \
--cc=security-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox