diff mbox series

[net,v3,4/6] usbnet: ipheth: use static NDP16 location in URB

Message ID 20241123235432.821220-4-forst@pen.gy
State New
Headers show
Series [net,v3,1/6] usbnet: ipheth: break up NCM header size computation | expand

Commit Message

Foster Snowhill Nov. 23, 2024, 11:54 p.m. UTC
Original code allowed for the start of NDP16 to be anywhere within the
URB based on the `wNdpIndex` value in NTH16. Only the start position of
NDP16 was checked, so it was possible for even the fixed-length part
of NDP16 to extend past the end of URB, leading to an out-of-bounds
read.

On iOS devices, the NDP16 header always directly follows NTH16. Rely on
and check for this specific format.

This, along with NCM-specific minimal URB length check that already
exists, will ensure that the fixed-length part of NDP16 plus a set
amount of DPEs fit within the URB.

Note that this commit alone does not fully address the OoB read.
The limit on the amount of DPEs needs to be enforced separately.

Fixes: a2d274c62e44 ("usbnet: ipheth: add CDC NCM support")
Signed-off-by: Foster Snowhill <forst@pen.gy>
---
v3:
    Split out from a monolithic patch in v2 as an atomic change.
v2: https://lore.kernel.org/netdev/20240912211817.1707844-1-forst@pen.gy/
    No code changes. Update commit message to further clarify that
    `ipheth` is not and does not aim to be a complete or spec-compliant
    CDC NCM implementation.
v1: https://lore.kernel.org/netdev/20240907230108.978355-1-forst@pen.gy/
---
 drivers/net/usb/ipheth.c | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

Comments

Foster Snowhill Dec. 1, 2024, 9:57 p.m. UTC | #1
Hello Paolo,

On 2024-11-28 10:08, Paolo Abeni wrote:
>> On iOS devices, the NDP16 header always directly follows NTH16. Rely on
>> and check for this specific format.
>> <snip>
> 
> This choice looks fragile. What if the next iOS version moves around
> such header?

This is a valid concern, and something I've been pondering myself
for a while. My thinking so far can be summed up as follows:

"iOS devices aren't fully compliant with NCM for regular tethering (missing
necessary descriptors, computer->phone not encapsulated at all), so it can't
be handled by the existing fully-featured spec-compliant `cdc_ncm` driver.
The `cdc_ncm` driver includes the functionality I need to parse incoming
NCM data, but I don't see an easy way for me to call that code from `ipheth`.
I don't want to mess with the `cdc_ncm` driver's code. So I'll approach this
by implementing the bare minimum of the NCM spec in `ipheth` just to parse
incoming NCM URBs, relying on the specific URB format that iOS devices have."

I didn't want to reimplement more than I absolutely had to, because that work
had already been done in `cdc_ncm`. I relied on the specific URB format of
iOS devices that hasn't changed since the NCM mode was introduced there.

You're right, the URB format can change, without warning. If/when that
happens, it would be a good time to re-think the approach, and maybe figure
out a way to make use of the parsing code in `cdc_ncm`.

For now I wanted to limit the scope of changes to "let's make it work with
the assumptions that hold to this day".

> I think you should add least validate the assumption in the actual URB
> payload.

This is already validated by checking that ncm0->dwSignature matches
the four-byte constant defined in the NCM 1.0 spec. However I think you're
right that it may not be enough, if by some random chance the initial four
bytes right after NTH16 are set to the desired constant, yet aren't part
of a real NDP16 header. The condition below should cover it:

	ncmh->wNdpIndex == cpu_to_le16(sizeof(struct usb_cdc_ncm_nth16))

I'll add it in v4.

>> diff --git a/drivers/net/usb/ipheth.c b/drivers/net/usb/ipheth.c
>> index 48c79e69bb7b..3f9ea6546720 100644
>> --- a/drivers/net/usb/ipheth.c
>> +++ b/drivers/net/usb/ipheth.c
>> @@ -236,16 +236,14 @@ static int ipheth_rcvbulk_callback_ncm(struct urb *urb)
>>  	}
>>
>>  	ncmh = urb->transfer_buffer;
>> -	if (ncmh->dwSignature != cpu_to_le32(USB_CDC_NCM_NTH16_SIGN) ||
>> -	    le16_to_cpu(ncmh->wNdpIndex) >= urb->actual_length) {
>> +	if (ncmh->dwSignature != cpu_to_le32(USB_CDC_NCM_NTH16_SIGN)) {
>>  		dev->net->stats.rx_errors++;
>>  		return retval;
>>  	}
> 
> The URB length is never checked, why it's safe to access (a lot of)
> bytes inside the URB without any check?

There is a length check right above this hunk, starting with:

	if (urb->actual_length < IPHETH_NCM_HEADER_SIZE) {

IPHETH_NCM_HEADER_SIZE is defined in such a way that it covers NTH16,
the fixed-size NDP16 part plus up to 22 DPEs. This is described in the
commit message.


Thank you,
Foster.
diff mbox series

Patch

diff --git a/drivers/net/usb/ipheth.c b/drivers/net/usb/ipheth.c
index 48c79e69bb7b..3f9ea6546720 100644
--- a/drivers/net/usb/ipheth.c
+++ b/drivers/net/usb/ipheth.c
@@ -236,16 +236,14 @@  static int ipheth_rcvbulk_callback_ncm(struct urb *urb)
 	}
 
 	ncmh = urb->transfer_buffer;
-	if (ncmh->dwSignature != cpu_to_le32(USB_CDC_NCM_NTH16_SIGN) ||
-	    le16_to_cpu(ncmh->wNdpIndex) >= urb->actual_length) {
+	if (ncmh->dwSignature != cpu_to_le32(USB_CDC_NCM_NTH16_SIGN)) {
 		dev->net->stats.rx_errors++;
 		return retval;
 	}
 
-	ncm0 = urb->transfer_buffer + le16_to_cpu(ncmh->wNdpIndex);
-	if (ncm0->dwSignature != cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN) ||
-	    le16_to_cpu(ncmh->wHeaderLength) + le16_to_cpu(ncm0->wLength) >=
-	    urb->actual_length) {
+	/* On iOS, NDP16 directly follows NTH16 */
+	ncm0 = urb->transfer_buffer + sizeof(struct usb_cdc_ncm_nth16);
+	if (ncm0->dwSignature != cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN)) {
 		dev->net->stats.rx_errors++;
 		return retval;
 	}