From: Wang Hai wanghai38@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@huawei.com Reviewed-by: Wei Yongjun weiyongjun1@huawei.com Reviewed-by: Xiu Jianfeng xiujianfeng@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@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);