1
0
Fork 0
mirror of https://codeberg.org/demostf/parser.git synced 2026-06-03 10:14:06 +02:00

additional hardening against mallformed demos

This commit is contained in:
Robin Appelman 2022-12-13 22:06:07 +01:00
commit a7a46384ce
4 changed files with 45 additions and 9 deletions

View file

@ -297,6 +297,9 @@ impl Parse<'_> for PacketEntitiesMessage {
for _ in 0..updated_entries { for _ in 0..updated_entries {
let diff: u32 = read_bit_var(&mut data)?; let diff: u32 = read_bit_var(&mut data)?;
last_index = last_index.saturating_add(diff as i32).saturating_add(1); last_index = last_index.saturating_add(diff as i32).saturating_add(1);
if last_index >= 2048 {
return Err(ParseError::InvalidDemo("invalid entity index"));
}
let entity_index = EntityId::from(last_index as u32); let entity_index = EntityId::from(last_index as u32);
let update_type = data.read()?; let update_type = data.read()?;

View file

@ -414,7 +414,7 @@ pub fn parse_string_table_update<'a>(
for _ in 0..entry_count { for _ in 0..entry_count {
let index = if stream.read()? { let index = if stream.read()? {
(last_entry + 1) as u16 last_entry.saturating_add(1) as u16
} else { } else {
stream.read_sized(entry_bits as usize)? stream.read_sized(entry_bits as usize)?
}; };

View file

@ -54,7 +54,7 @@ impl Parse<'_> for TempEntitiesMessage {
let class_id = if stream.read()? { let class_id = if stream.read()? {
let bits = log_base2(state.server_classes.len()) + 1; let bits = log_base2(state.server_classes.len()) + 1;
(stream.read_sized::<u16>(bits as usize)? - 1).into() (stream.read_sized::<u16>(bits as usize)?.saturating_sub(1)).into()
} else { } else {
let last = events.last().ok_or(ParseError::InvalidDemo( let last = events.last().ok_or(ParseError::InvalidDemo(
"temp entity update without previous", "temp entity update without previous",

View file

@ -319,7 +319,12 @@ fn test_parse_send_table_roundtrip() {
impl ParseSendTable { impl ParseSendTable {
pub fn flatten_props(&self, tables: &[ParseSendTable]) -> Result<Vec<SendPropDefinition>> { pub fn flatten_props(&self, tables: &[ParseSendTable]) -> Result<Vec<SendPropDefinition>> {
let mut flat = Vec::with_capacity(32); let mut flat = Vec::with_capacity(32);
self.get_all_props(tables, &self.get_excludes(tables), &mut flat)?; self.get_all_props(
tables,
&self.get_excludes(tables, &mut Vec::with_capacity(8)),
&mut flat,
Vec::with_capacity(8),
)?;
// sort often changed props before the others // sort often changed props before the others
let mut start = 0; let mut start = 0;
@ -335,7 +340,12 @@ impl ParseSendTable {
Ok(flat) Ok(flat)
} }
fn get_excludes<'a>(&'a self, tables: &'a [ParseSendTable]) -> Vec<SendPropIdentifier> { fn get_excludes<'a>(
&'a self,
tables: &'a [ParseSendTable],
processed_tables: &mut Vec<SendTableName>,
) -> Vec<SendPropIdentifier> {
processed_tables.push(self.name.clone());
let mut excludes = Vec::with_capacity(8); let mut excludes = Vec::with_capacity(8);
for prop in self.props.iter() { for prop in self.props.iter() {
@ -345,7 +355,9 @@ impl ParseSendTable {
prop.name.as_str(), prop.name.as_str(),
)) ))
} else if let Some(table) = prop.get_data_table(tables) { } else if let Some(table) = prop.get_data_table(tables) {
excludes.extend_from_slice(&table.get_excludes(tables)); if !processed_tables.contains(&table.name) {
excludes.extend_from_slice(&table.get_excludes(tables, processed_tables));
}
} }
} }
@ -358,10 +370,17 @@ impl ParseSendTable {
tables: &[ParseSendTable], tables: &[ParseSendTable],
excludes: &[SendPropIdentifier], excludes: &[SendPropIdentifier],
props: &mut Vec<SendPropDefinition>, props: &mut Vec<SendPropDefinition>,
processed_tables: Vec<SendTableName>,
) -> Result<()> { ) -> Result<()> {
let mut local_props = Vec::new(); let mut local_props = Vec::new();
self.get_all_props_iterator_props(tables, excludes, &mut local_props, props)?; self.get_all_props_iterator_props(
tables,
excludes,
&mut local_props,
props,
processed_tables,
)?;
props.extend_from_slice(&local_props); props.extend_from_slice(&local_props);
Ok(()) Ok(())
} }
@ -372,17 +391,31 @@ impl ParseSendTable {
excludes: &[SendPropIdentifier], excludes: &[SendPropIdentifier],
local_props: &mut Vec<SendPropDefinition>, local_props: &mut Vec<SendPropDefinition>,
props: &mut Vec<SendPropDefinition>, props: &mut Vec<SendPropDefinition>,
processed_tables: Vec<SendTableName>,
) -> Result<()> { ) -> Result<()> {
let processed_tables = &processed_tables;
self.props self.props
.iter() .iter()
.filter(|prop| !prop.is_exclude()) .filter(|prop| !prop.is_exclude())
.filter(|prop| !excludes.iter().any(|exclude| *exclude == prop.identifier())) .filter(|prop| !excludes.iter().any(|exclude| *exclude == prop.identifier()))
.try_for_each(|prop| { .try_for_each(|prop| {
let mut child_processed_tables = processed_tables.clone();
child_processed_tables.push(self.name.clone());
if let Some(table) = prop.get_data_table(tables) { if let Some(table) = prop.get_data_table(tables) {
if !processed_tables.contains(&table.name) {
if prop.flags.contains(SendPropFlag::Collapsible) { if prop.flags.contains(SendPropFlag::Collapsible) {
table.get_all_props_iterator_props(tables, excludes, local_props, props)?; table.get_all_props_iterator_props(
tables,
excludes,
local_props,
props,
child_processed_tables,
)?;
} else { } else {
table.get_all_props(tables, excludes, props)?; table.get_all_props(tables, excludes, props, child_processed_tables)?;
}
} else {
dbg!(table.name.as_str());
} }
} else { } else {
local_props.push(SendPropDefinition::try_from(prop)?); local_props.push(SendPropDefinition::try_from(prop)?);