From: Wang Hai <wanghai38(a)huawei.com>
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: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Wang Hai <wanghai38(a)huawei.com>
Reviewed-by: Wei Yongjun <weiyongjun1(a)huawei.com>
Reviewed-by: Xiu Jianfeng <xiujianfeng(a)huawei.com>
Signed-off-by: Zheng Zengkai <zhengzengkai(a)huawei.com>
---
drivers/usb/gadget/composite.c | 2 +-
drivers/usb/gadget/function/rndis.c | 37 +++++++++++++++++++++++++----
2 files changed, 34 insertions(+), 5 deletions(-)
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 1a556a628971..7f963bb1c59b 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -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;
diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c
index 64de9f1b874c..9ea94215e113 100644
--- a/drivers/usb/gadget/function/rndis.c
+++ b/drivers/usb/gadget/function/rndis.c
@@ -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,12 +617,25 @@ 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),
- r)) {
+ BufOffset + 8 + (u8 *)buf, BufLength,
+ r)) {
/* OID not supported */
resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED);
resp->MessageLength = cpu_to_le32(sizeof *resp);
@@ -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);
--
2.20.1