Commit 740612c7 authored by Wang Hai's avatar Wang Hai Committed by Zheng Zengkai
Browse files

usb: gadget: rndis: Fix info leak of rndis



hulk inclusion
category: bugfix
bugzilla: 172330
CVE: HWPSIRT-2021-84477

--------------------------------

We can construct some special USB packets that cause kernel
info leak by the following steps of rndis.

1. construct the packet to make rndis call gen_ndis_set_resp().

In gen_ndis_set_resp(), BufOffset comes from the USB packet and
it is not checked so that BufOffset can be any value. Therefore,
if OID is RNDIS_OID_GEN_CURRENT_PACKET_FILTER, then *params->filter
can get data at any address.

2. construct the packet to make rndis call rndis_query_response().

In rndis_query_response(), if OID is RNDIS_OID_GEN_CURRENT_PACKET_FILTER,
then the data of *params->filter is fetched and returned, resulting in
info leak.

Therefore, we need to check the BufOffset to prevent info leak. Here,
buf size is USB_COMP_EP0_BUFSIZ, as long as "8 + BufOffset + BufLength"
is less than USB_COMP_EP0_BUFSIZ, it will be considered legal.

Fixes: 1da177e4 ("Linux-2.6.12-rc2")
Signed-off-by: default avatarWang Hai <wanghai38@huawei.com>
Reviewed-by: default avatarWei Yongjun <weiyongjun1@huawei.com>
Reviewed-by: default avatarXiu Jianfeng <xiujianfeng@huawei.com>
Signed-off-by: default avatarZheng Zengkai <zhengzengkai@huawei.com>
parent 6b759618
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -2157,7 +2157,7 @@ int composite_dev_prepare(struct usb_composite_driver *composite,
	if (!cdev->req)
		return -ENOMEM;

	cdev->req->buf = kmalloc(USB_COMP_EP0_BUFSIZ, GFP_KERNEL);
	cdev->req->buf = kzalloc(USB_COMP_EP0_BUFSIZ, GFP_KERNEL);
	if (!cdev->req->buf)
		goto fail;

+33 −4
Original line number Diff line number Diff line
@@ -506,6 +506,10 @@ static int gen_ndis_set_resp(struct rndis_params *params, u32 OID,

	switch (OID) {
	case RNDIS_OID_GEN_CURRENT_PACKET_FILTER:
		if (buf_len < 2) {
			pr_err("%s:Not support for buf_len < 2\n", __func__);
			break;
		}

		/* these NDIS_PACKET_TYPE_* bitflags are shared with
		 * cdc_filter; it's not RNDIS-specific
@@ -592,6 +596,7 @@ static int rndis_query_response(struct rndis_params *params,
				rndis_query_msg_type *buf)
{
	rndis_query_cmplt_type *resp;
	u32 BufOffset, BufLength;
	rndis_resp_t *r;

	/* pr_debug("%s: OID = %08X\n", __func__, cpu_to_le32(buf->OID)); */
@@ -612,11 +617,24 @@ static int rndis_query_response(struct rndis_params *params,

	resp->MessageType = cpu_to_le32(RNDIS_MSG_QUERY_C);
	resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
	BufOffset = le32_to_cpu(buf->InformationBufferOffset);
	BufLength = le32_to_cpu(buf->InformationBufferLength);

	/*
	 * If the address of the buf to be accessed exceeds the valid
	 * range of the buf, then return RNDIS_STATUS_NOT_SUPPORTED.
	 */
	if (8 + BufOffset + BufLength >= USB_COMP_EP0_BUFSIZ) {
		resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED);
		resp->MessageLength = cpu_to_le32(sizeof(*resp));
		resp->InformationBufferLength = cpu_to_le32(0);
		resp->InformationBufferOffset = cpu_to_le32(0);
		params->resp_avail(params->v);
		return 0;
	}

	if (gen_ndis_query_resp(params, le32_to_cpu(buf->OID),
			le32_to_cpu(buf->InformationBufferOffset)
					+ 8 + (u8 *)buf,
			le32_to_cpu(buf->InformationBufferLength),
				BufOffset + 8 + (u8 *)buf, BufLength,
				r)) {
		/* OID not supported */
		resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED);
@@ -660,6 +678,17 @@ static int rndis_set_response(struct rndis_params *params,
	resp->MessageType = cpu_to_le32(RNDIS_MSG_SET_C);
	resp->MessageLength = cpu_to_le32(16);
	resp->RequestID = buf->RequestID; /* Still LE in msg buffer */

	/*
	 * If the address of the buf to be accessed exceeds the valid
	 * range of the buf, then return RNDIS_STATUS_NOT_SUPPORTED.
	 */
	if (8 + BufOffset + BufLength >= USB_COMP_EP0_BUFSIZ) {
		resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED);
		params->resp_avail(params->v);
		return 0;
	}

	if (gen_ndis_set_resp(params, le32_to_cpu(buf->OID),
			((u8 *)buf) + 8 + BufOffset, BufLength, r))
		resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED);