Skip to content

Commit e091043

Browse files
author
Paolo Abeni
committed
Merge branch 'usbnet-ipheth-prevent-oob-reads-of-ndp16'
Foster Snowhill says: ==================== usbnet: ipheth: prevent OoB reads of NDP16 iOS devices support two types of tethering over USB: regular, where the internet connection is shared from the phone to the attached computer, and reverse, where the internet connection is shared from the attached computer to the phone. The `ipheth` driver is responsible for regular tethering only. With this tethering type, iOS devices support two encapsulation modes on RX: legacy and NCM. In "NCM mode", the iOS device encapsulates RX (phone->computer) traffic in NCM Transfer Blocks (similarly to CDC NCM). However, unlike reverse tethering, regular tethering is not compliant with the CDC NCM spec: * Does not have the required CDC NCM descriptors * TX (computer->phone) is not NCM-encapsulated at all Thus `ipheth` implements a very limited subset of the spec with the sole purpose of parsing RX URBs. This driver does not aim to be a CDC NCM-compliant implementation and, in fact, can't be one because of the points above. For a complete spec-compliant CDC NCM implementation, there is already the `cdc_ncm` driver. This driver is used for reverse tethering on iOS devices. This patch series does not in any way change `cdc_ncm`. In the first iteration of the NCM mode implementation in `ipheth`, there were a few potential out of bounds reads when processing malformed URBs received from a connected device: * Only the start of NDP16 (wNdpIndex) was checked to fit in the URB buffer. * Datagram length check as part of DPEs could overflow. * DPEs could be read past the end of NDP16 and even end of URB buffer if a trailer DPE wasn't encountered. The above is not expected to happen in normal device operation. To address the above issues for iOS devices in NCM mode, rely on and check for a specific fixed format of incoming URBs expected from an iOS device: * 12-byte NTH16 * 96-byte NDP16, allowing up to 22 DPEs (up to 21 datagrams + trailer) On iOS, NDP16 directly follows NTH16, and its length is constant regardless of the DPE count. As the regular tethering implementation of iOS devices isn't compliant with CDC NCM, it's not possible to use the `cdc_ncm` driver to handle this functionality. Furthermore, while the logic required to properly parse URBs with NCM-encapsulated frames is already part of said driver, I haven't found a nice way to reuse the existing code without messing with the `cdc_ncm` driver itself. I didn't want to reimplement more of the spec than I absolutely had to, because that work had already been done in `cdc_ncm`. Instead, to limit the scope, I chose to rely on the specific URB format of iOS devices that hasn't changed since the NCM mode was introduced there. I tested each individual patch in the v5 series with iPhone 15 Pro Max, iOS 18.2.1: compiled cleanly, ran iperf3 between phone and computer, observed no errors in either kernel log or interface statistics. v4 was Reviewed-by Jakub Kicinski <[email protected]>. Compared to v4, v5 has no code changes. The two differences are: * Patch "usbnet: ipheth: break up NCM header size computation" moved later in the series, closer to a subsequent commit that makes use of the change. * In patch "usbnet: ipheth: refactor NCM datagram loop", removed a stray paragraph in commit msg. Above items are also noted in the changelogs of respective patches. ==================== Link: https://patch.msgid.link/[email protected] Signed-off-by: Paolo Abeni <[email protected]>
2 parents 19ae40f + be154b5 commit e091043

File tree

1 file changed

+45
-24
lines changed

1 file changed

+45
-24
lines changed

drivers/net/usb/ipheth.c

Lines changed: 45 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,18 @@
6161
#define IPHETH_USBINTF_PROTO 1
6262

6363
#define IPHETH_IP_ALIGN 2 /* padding at front of URB */
64-
#define IPHETH_NCM_HEADER_SIZE (12 + 96) /* NCMH + NCM0 */
64+
/* On iOS devices, NCM headers in RX have a fixed size regardless of DPE count:
65+
* - NTH16 (NCMH): 12 bytes, as per CDC NCM 1.0 spec
66+
* - NDP16 (NCM0): 96 bytes, of which
67+
* - NDP16 fixed header: 8 bytes
68+
* - maximum of 22 DPEs (21 datagrams + trailer), 4 bytes each
69+
*/
70+
#define IPHETH_NDP16_MAX_DPE 22
71+
#define IPHETH_NDP16_HEADER_SIZE (sizeof(struct usb_cdc_ncm_ndp16) + \
72+
IPHETH_NDP16_MAX_DPE * \
73+
sizeof(struct usb_cdc_ncm_dpe16))
74+
#define IPHETH_NCM_HEADER_SIZE (sizeof(struct usb_cdc_ncm_nth16) + \
75+
IPHETH_NDP16_HEADER_SIZE)
6576
#define IPHETH_TX_BUF_SIZE ETH_FRAME_LEN
6677
#define IPHETH_RX_BUF_SIZE_LEGACY (IPHETH_IP_ALIGN + ETH_FRAME_LEN)
6778
#define IPHETH_RX_BUF_SIZE_NCM 65536
@@ -207,15 +218,23 @@ static int ipheth_rcvbulk_callback_legacy(struct urb *urb)
207218
return ipheth_consume_skb(buf, len, dev);
208219
}
209220

221+
/* In "NCM mode", the iOS device encapsulates RX (phone->computer) traffic
222+
* in NCM Transfer Blocks (similarly to CDC NCM). However, unlike reverse
223+
* tethering (handled by the `cdc_ncm` driver), regular tethering is not
224+
* compliant with the CDC NCM spec, as the device is missing the necessary
225+
* descriptors, and TX (computer->phone) traffic is not encapsulated
226+
* at all. Thus `ipheth` implements a very limited subset of the spec with
227+
* the sole purpose of parsing RX URBs.
228+
*/
210229
static int ipheth_rcvbulk_callback_ncm(struct urb *urb)
211230
{
212231
struct usb_cdc_ncm_nth16 *ncmh;
213232
struct usb_cdc_ncm_ndp16 *ncm0;
214233
struct usb_cdc_ncm_dpe16 *dpe;
215234
struct ipheth_device *dev;
235+
u16 dg_idx, dg_len;
216236
int retval = -EINVAL;
217237
char *buf;
218-
int len;
219238

220239
dev = urb->context;
221240

@@ -226,40 +245,42 @@ static int ipheth_rcvbulk_callback_ncm(struct urb *urb)
226245

227246
ncmh = urb->transfer_buffer;
228247
if (ncmh->dwSignature != cpu_to_le32(USB_CDC_NCM_NTH16_SIGN) ||
229-
le16_to_cpu(ncmh->wNdpIndex) >= urb->actual_length) {
230-
dev->net->stats.rx_errors++;
231-
return retval;
232-
}
248+
/* On iOS, NDP16 directly follows NTH16 */
249+
ncmh->wNdpIndex != cpu_to_le16(sizeof(struct usb_cdc_ncm_nth16)))
250+
goto rx_error;
233251

234-
ncm0 = urb->transfer_buffer + le16_to_cpu(ncmh->wNdpIndex);
235-
if (ncm0->dwSignature != cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN) ||
236-
le16_to_cpu(ncmh->wHeaderLength) + le16_to_cpu(ncm0->wLength) >=
237-
urb->actual_length) {
238-
dev->net->stats.rx_errors++;
239-
return retval;
240-
}
252+
ncm0 = urb->transfer_buffer + sizeof(struct usb_cdc_ncm_nth16);
253+
if (ncm0->dwSignature != cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN))
254+
goto rx_error;
241255

242256
dpe = ncm0->dpe16;
243-
while (le16_to_cpu(dpe->wDatagramIndex) != 0 &&
244-
le16_to_cpu(dpe->wDatagramLength) != 0) {
245-
if (le16_to_cpu(dpe->wDatagramIndex) >= urb->actual_length ||
246-
le16_to_cpu(dpe->wDatagramIndex) +
247-
le16_to_cpu(dpe->wDatagramLength) > urb->actual_length) {
257+
for (int dpe_i = 0; dpe_i < IPHETH_NDP16_MAX_DPE; ++dpe_i, ++dpe) {
258+
dg_idx = le16_to_cpu(dpe->wDatagramIndex);
259+
dg_len = le16_to_cpu(dpe->wDatagramLength);
260+
261+
/* Null DPE must be present after last datagram pointer entry
262+
* (3.3.1 USB CDC NCM spec v1.0)
263+
*/
264+
if (dg_idx == 0 && dg_len == 0)
265+
return 0;
266+
267+
if (dg_idx < IPHETH_NCM_HEADER_SIZE ||
268+
dg_idx >= urb->actual_length ||
269+
dg_len > urb->actual_length - dg_idx) {
248270
dev->net->stats.rx_length_errors++;
249271
return retval;
250272
}
251273

252-
buf = urb->transfer_buffer + le16_to_cpu(dpe->wDatagramIndex);
253-
len = le16_to_cpu(dpe->wDatagramLength);
274+
buf = urb->transfer_buffer + dg_idx;
254275

255-
retval = ipheth_consume_skb(buf, len, dev);
276+
retval = ipheth_consume_skb(buf, dg_len, dev);
256277
if (retval != 0)
257278
return retval;
258-
259-
dpe++;
260279
}
261280

262-
return 0;
281+
rx_error:
282+
dev->net->stats.rx_errors++;
283+
return retval;
263284
}
264285

265286
static void ipheth_rcvbulk_callback(struct urb *urb)

0 commit comments

Comments
 (0)