Skip to content

Commit df1d38e

Browse files
committed
Linux USB: avoid 32-bit unsigned integer wraparound.
When adding values whose sum might overflow an unsigned integer, first check whether the sum *would* overflow an unsigned integer and, if so, clamp the sum at UINT_MAX. Do the same for a multiplication. This should fix #1134, as well as the issue in #1205.
1 parent 764ff9b commit df1d38e

File tree

1 file changed

+66
-7
lines changed

1 file changed

+66
-7
lines changed

pcap-usb-linux-common.c

Lines changed: 66 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,23 @@
2222
* deal with Linux USB captures.
2323
*/
2424

25+
#include <limits.h> /* for UINT_MAX */
26+
2527
#include "pcap/pcap.h"
2628
#include "pcap/usb.h"
2729

2830
#include "pcap-usb-linux-common.h"
2931

32+
/*
33+
* Return the sum of the two u_int arguments if that sum fits in a u_int,
34+
* and return UINT_MAX otherwise.
35+
*/
36+
static inline u_int
37+
u_int_sum(u_int a, u_int b)
38+
{
39+
return (((b) <= UINT_MAX - (b)) ? (a) + (b) : UINT_MAX);
40+
}
41+
3042
/*
3143
* Compute, from the data provided by the Linux USB memory-mapped capture
3244
* mechanism, the amount of packet data that would have been provided
@@ -55,7 +67,10 @@ fix_linux_usb_mmapped_length(struct pcap_pkthdr *pkth, const u_char *bp)
5567
pkth->len == sizeof(pcap_usb_header_mmapped) +
5668
(hdr->ndesc * sizeof (usb_isodesc)) + hdr->urb_len) {
5769
usb_isodesc *descs;
58-
u_int pre_truncation_data_len, pre_truncation_len;
70+
u_int pre_truncation_descriptors_len;
71+
u_int pre_truncation_header_len;
72+
u_int pre_truncation_data_len;
73+
u_int pre_truncation_len;
5974

6075
descs = (usb_isodesc *) (bp + sizeof(pcap_usb_header_mmapped));
6176

@@ -82,7 +97,15 @@ fix_linux_usb_mmapped_length(struct pcap_pkthdr *pkth, const u_char *bp)
8297
u_int desc_end;
8398

8499
if (descs[desc].len != 0) {
85-
desc_end = descs[desc].offset + descs[desc].len;
100+
/*
101+
* Compute the end offset of the data
102+
* for this descriptor, i.e. the offset
103+
* of the byte after te data. Clamp
104+
* the sum at UINT_MAX, so that it fits
105+
* in a u_int.
106+
*/
107+
desc_end = u_int_sum(descs[desc].offset,
108+
descs[desc].len);
86109
if (desc_end > pre_truncation_data_len)
87110
pre_truncation_data_len = desc_end;
88111
}
@@ -91,14 +114,50 @@ fix_linux_usb_mmapped_length(struct pcap_pkthdr *pkth, const u_char *bp)
91114
/*
92115
* Now calculate the total length based on that data
93116
* length.
117+
*
118+
* First, make sure the total length of the ISO
119+
* descriptors fits in an unsigned int. We know
120+
* that sizeof (usb_isodesc) is a small power-of-2
121+
* integer (16 bytes), so we just check whether
122+
* hdr->ndesc < (UINT_MAX + (uint64_t)1) / sizeof (usb_isodesc),
123+
* as that would mean that hdr->ndesc * sizeof (usb_isodesc)
124+
* is < (UINT_MAX + (uint64_t)1) and thus <= UINT_MAX.
125+
* ((UINT_MAX + (uint64_t)1) will probably be computed
126+
* at compile time with most C compilers.)
127+
*/
128+
if (hdr->ndesc < (UINT_MAX + (uint64_t)1) / sizeof (usb_isodesc)) {
129+
/*
130+
* It fits.
131+
*/
132+
pre_truncation_descriptors_len =
133+
hdr->ndesc * sizeof (usb_isodesc);
134+
} else {
135+
/*
136+
* It doesn't fit.
137+
*/
138+
pre_truncation_descriptors_len = UINT_MAX;
139+
}
140+
141+
/*
142+
* Now, add the length of the memory-mapped header and
143+
* the length of the ISO descriptors, clamping the
144+
* result at UINT_MAX.
145+
*/
146+
pre_truncation_header_len = u_int_sum(sizeof(pcap_usb_header_mmapped),
147+
pre_truncation_descriptors_len);
148+
149+
/*
150+
* Now, add the total header length (memory-mapped header
151+
* and ISO descriptors) and the data length, clamping
152+
* the result at UINT_MAX.
94153
*/
95-
pre_truncation_len = sizeof(pcap_usb_header_mmapped) +
96-
(hdr->ndesc * sizeof (usb_isodesc)) +
97-
pre_truncation_data_len;
154+
pre_truncation_len = u_int_sum(pre_truncation_header_len,
155+
pre_truncation_data_len);
98156

99157
/*
100-
* If that's greater than or equal to the captured length,
101-
* use that as the length.
158+
* pre_truncation_len is now the smaller of
159+
* UINT_MAX and the total header plus data length.
160+
* That's guaranteed to fit in a UINT_MAX.
102161
*/
103162
if (pre_truncation_len >= pkth->caplen)
104163
pkth->len = pre_truncation_len;

0 commit comments

Comments
 (0)