Skip to content

Commit c49e3ac

Browse files
Jeff-A-Martincathay4t
authored andcommitted
Avoid panic in TcU32Selector parsing
`TcU32SelectorBuffer::new_checked` returns an error when the underlying buffer is not large enough to contain `nkeys`. This avoids an index-out-of-bounds panic in `TcU32Selector::parse`, when extracting keys from the underlying buffer.
1 parent 666edbc commit c49e3ac

File tree

4 files changed

+54
-2
lines changed

4 files changed

+54
-2
lines changed

src/tc/filters/cls_u32.rs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ pub struct TcU32Selector {
177177
pub keys: Vec<TcU32Key>,
178178
}
179179

180-
buffer!(TcU32SelectorBuffer(TC_U32_SEL_BUF_LEN) {
180+
buffer!(TcU32SelectorBuffer {
181181
flags: (u8, 0),
182182
offshift: (u8, 1),
183183
nkeys: (u8, 2),
@@ -190,6 +190,35 @@ buffer!(TcU32SelectorBuffer(TC_U32_SEL_BUF_LEN) {
190190
keys: (slice, TC_U32_SEL_BUF_LEN..),
191191
});
192192

193+
impl<T: AsRef<[u8]>> TcU32SelectorBuffer<T> {
194+
pub fn new_checked(buffer: T) -> Result<Self, DecodeError> {
195+
let packet = Self::new(buffer);
196+
packet.check_buffer_length()?;
197+
Ok(packet)
198+
}
199+
200+
fn check_buffer_length(&self) -> Result<(), DecodeError> {
201+
let len = self.buffer.as_ref().len();
202+
if len < TC_U32_SEL_BUF_LEN {
203+
return Err(format!(
204+
"invalid TcU32SelectorBuffer: length {len} < {TC_U32_SEL_BUF_LEN}"
205+
)
206+
.into());
207+
}
208+
// Expect the buffer to be large enough to hold `nkeys`.
209+
let expected_len =
210+
((self.nkeys() as usize) * TC_U32_KEY_BUF_LEN) + TC_U32_SEL_BUF_LEN;
211+
if len < expected_len {
212+
return Err(format!(
213+
"invalid RouteNextHopBuffer: length {} < {}",
214+
len, expected_len,
215+
)
216+
.into());
217+
}
218+
Ok(())
219+
}
220+
}
221+
193222
impl Emitable for TcU32Selector {
194223
fn buffer_len(&self) -> usize {
195224
TC_U32_SEL_BUF_LEN + (self.nkeys as usize * TC_U32_KEY_BUF_LEN)

src/tc/filters/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ mod u32_flags;
66

77
pub use self::cls_u32::{
88
TcFilterU32, TcFilterU32Option, TcU32Key, TcU32Selector,
9+
TcU32SelectorBuffer,
910
};
1011
pub use self::matchall::{TcFilterMatchAll, TcFilterMatchAllOption};
1112
pub use u32_flags::{TcU32OptionFlags, TcU32SelectorFlags};

src/tc/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ pub use self::actions::{
2020
pub use self::attribute::TcAttribute;
2121
pub use self::filters::{
2222
TcFilterMatchAll, TcFilterMatchAllOption, TcFilterU32, TcFilterU32Option,
23-
TcU32Key, TcU32OptionFlags, TcU32Selector, TcU32SelectorFlags,
23+
TcU32Key, TcU32OptionFlags, TcU32Selector, TcU32SelectorBuffer,
24+
TcU32SelectorFlags,
2425
};
2526
pub use self::header::{TcHandle, TcHeader, TcMessageBuffer};
2627
pub use self::message::TcMessage;

src/tc/tests/filter_u32.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use crate::{
99
filters::{TcU32OptionFlags, TcU32SelectorFlags},
1010
TcAttribute, TcFilterU32Option, TcHandle, TcHeader, TcMessage,
1111
TcMessageBuffer, TcOption, TcU32Key, TcU32Selector,
12+
TcU32SelectorBuffer,
1213
},
1314
AddressFamily,
1415
};
@@ -112,3 +113,23 @@ fn test_get_filter_u32() {
112113

113114
assert_eq!(buf, raw);
114115
}
116+
117+
// Verify that [`TcU32Selector`] fails to parse a buffer with an
118+
// invalid number of keys.
119+
#[test]
120+
fn test_tcu32_selector_invalid_nkeys() {
121+
// TC u32 selector buffer layout:
122+
// |byte0|byte1|byte2|byte3|byte4|byte5|byte6|byte7|
123+
// |-----|-----|-----|-----|-----|-----|-----|-----|
124+
// |flags|shift|nkeys|pad | offmask | off |
125+
// |-----|-----|-----|-----|-----|-----|-----|-----|
126+
// | offoff | hoff | hmask |
127+
// |-----|-----|-----|-----|-----|-----|-----|-----|
128+
// | keys |
129+
// | ... |
130+
let buffer = [
131+
0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
132+
0x00, 0x00, 0x00, 0x00,
133+
];
134+
assert!(TcU32SelectorBuffer::new_checked(buffer).is_err());
135+
}

0 commit comments

Comments
 (0)