Skip to content

Commit eac25fe

Browse files
authored
Rollup merge of #77528 - tamird:avoid-cast-net-parser, r=dtolnay
Avoid unchecked casts in net parser Once this and #77426 are in, I'll send another PR adding scope id parsing. r? @dtolnay
2 parents d7123c2 + f78a7ad commit eac25fe

File tree

1 file changed

+48
-36
lines changed

1 file changed

+48
-36
lines changed

library/std/src/net/parser.rs

+48-36
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,34 @@
66
#[cfg(test)]
77
mod tests;
88

9+
use crate::convert::TryInto as _;
910
use crate::error::Error;
1011
use crate::fmt;
1112
use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
1213
use crate::str::FromStr;
1314

15+
trait ReadNumberHelper: crate::marker::Sized {
16+
const ZERO: Self;
17+
fn checked_mul(&self, other: u32) -> Option<Self>;
18+
fn checked_add(&self, other: u32) -> Option<Self>;
19+
}
20+
21+
macro_rules! impl_helper {
22+
($($t:ty)*) => ($(impl ReadNumberHelper for $t {
23+
const ZERO: Self = 0;
24+
#[inline]
25+
fn checked_mul(&self, other: u32) -> Option<Self> {
26+
Self::checked_mul(*self, other.try_into().ok()?)
27+
}
28+
#[inline]
29+
fn checked_add(&self, other: u32) -> Option<Self> {
30+
Self::checked_add(*self, other.try_into().ok()?)
31+
}
32+
})*)
33+
}
34+
35+
impl_helper! { u8 u16 }
36+
1437
struct Parser<'a> {
1538
// parsing as ASCII, so can use byte array
1639
state: &'a [u8],
@@ -21,10 +44,6 @@ impl<'a> Parser<'a> {
2144
Parser { state: input.as_bytes() }
2245
}
2346

24-
fn is_eof(&self) -> bool {
25-
self.state.is_empty()
26-
}
27-
2847
/// Run a parser, and restore the pre-parse state if it fails
2948
fn read_atomically<T, F>(&mut self, inner: F) -> Option<T>
3049
where
@@ -40,26 +59,19 @@ impl<'a> Parser<'a> {
4059

4160
/// Run a parser, but fail if the entire input wasn't consumed.
4261
/// Doesn't run atomically.
43-
fn read_till_eof<T, F>(&mut self, inner: F) -> Option<T>
44-
where
45-
F: FnOnce(&mut Parser<'_>) -> Option<T>,
46-
{
47-
inner(self).filter(|_| self.is_eof())
48-
}
49-
50-
/// Same as read_till_eof, but returns a Result<AddrParseError> on failure
5162
fn parse_with<T, F>(&mut self, inner: F) -> Result<T, AddrParseError>
5263
where
5364
F: FnOnce(&mut Parser<'_>) -> Option<T>,
5465
{
55-
self.read_till_eof(inner).ok_or(AddrParseError(()))
66+
let result = inner(self);
67+
if self.state.is_empty() { result } else { None }.ok_or(AddrParseError(()))
5668
}
5769

5870
/// Read the next character from the input
5971
fn read_char(&mut self) -> Option<char> {
6072
self.state.split_first().map(|(&b, tail)| {
6173
self.state = tail;
62-
b as char
74+
char::from(b)
6375
})
6476
}
6577

@@ -84,25 +96,26 @@ impl<'a> Parser<'a> {
8496
})
8597
}
8698

87-
// Read a single digit in the given radix. For instance, 0-9 in radix 10;
88-
// 0-9A-F in radix 16.
89-
fn read_digit(&mut self, radix: u32) -> Option<u32> {
90-
self.read_atomically(move |p| p.read_char()?.to_digit(radix))
91-
}
92-
9399
// Read a number off the front of the input in the given radix, stopping
94100
// at the first non-digit character or eof. Fails if the number has more
95-
// digits than max_digits, or the value is >= upto, or if there is no number.
96-
fn read_number(&mut self, radix: u32, max_digits: u32, upto: u32) -> Option<u32> {
101+
// digits than max_digits or if there is no number.
102+
fn read_number<T: ReadNumberHelper>(
103+
&mut self,
104+
radix: u32,
105+
max_digits: Option<usize>,
106+
) -> Option<T> {
97107
self.read_atomically(move |p| {
98-
let mut result = 0;
108+
let mut result = T::ZERO;
99109
let mut digit_count = 0;
100110

101-
while let Some(digit) = p.read_digit(radix) {
102-
result = (result * radix) + digit;
111+
while let Some(digit) = p.read_atomically(|p| p.read_char()?.to_digit(radix)) {
112+
result = result.checked_mul(radix)?;
113+
result = result.checked_add(digit)?;
103114
digit_count += 1;
104-
if digit_count > max_digits || result >= upto {
105-
return None;
115+
if let Some(max_digits) = max_digits {
116+
if digit_count > max_digits {
117+
return None;
118+
}
106119
}
107120
}
108121

@@ -116,7 +129,7 @@ impl<'a> Parser<'a> {
116129
let mut groups = [0; 4];
117130

118131
for (i, slot) in groups.iter_mut().enumerate() {
119-
*slot = p.read_separator('.', i, |p| p.read_number(10, 3, 0x100))? as u8;
132+
*slot = p.read_separator('.', i, |p| p.read_number(10, None))?;
120133
}
121134

122135
Some(groups.into())
@@ -140,17 +153,17 @@ impl<'a> Parser<'a> {
140153
let ipv4 = p.read_separator(':', i, |p| p.read_ipv4_addr());
141154

142155
if let Some(v4_addr) = ipv4 {
143-
let octets = v4_addr.octets();
144-
groups[i + 0] = ((octets[0] as u16) << 8) | (octets[1] as u16);
145-
groups[i + 1] = ((octets[2] as u16) << 8) | (octets[3] as u16);
156+
let [one, two, three, four] = v4_addr.octets();
157+
groups[i + 0] = u16::from_be_bytes([one, two]);
158+
groups[i + 1] = u16::from_be_bytes([three, four]);
146159
return (i + 2, true);
147160
}
148161
}
149162

150-
let group = p.read_separator(':', i, |p| p.read_number(16, 4, 0x10000));
163+
let group = p.read_separator(':', i, |p| p.read_number(16, Some(4)));
151164

152165
match group {
153-
Some(g) => *slot = g as u16,
166+
Some(g) => *slot = g,
154167
None => return (i, false),
155168
}
156169
}
@@ -195,12 +208,11 @@ impl<'a> Parser<'a> {
195208
self.read_ipv4_addr().map(IpAddr::V4).or_else(move || self.read_ipv6_addr().map(IpAddr::V6))
196209
}
197210

198-
/// Read a : followed by a port in base 10
211+
/// Read a : followed by a port in base 10.
199212
fn read_port(&mut self) -> Option<u16> {
200213
self.read_atomically(|p| {
201214
let _ = p.read_given_char(':')?;
202-
let port = p.read_number(10, 5, 0x10000)?;
203-
Some(port as u16)
215+
p.read_number(10, None)
204216
})
205217
}
206218

0 commit comments

Comments
 (0)