mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-07 22:43:47 -10:00
Protocol: don't panic on malformed chunk sections, compressed frames or legacy pings
A server sending a palette wider than 64 bits divided by zero in storage_len (and 33-64 bits overflowed shifts); SimpleBitStorage allows at most 32. A non-canonical data-length VarInt in a compressed frame was misread. An empty first read in the legacy ping handler indexed past the end. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
effb29e571
commit
2812cf201d
3 files changed
+43
-5
No files matched your search
@@ -381,7 +381,7 @@ fn serve_legacy(mut stream: TcpStream, shared: &Shared) -> Result<(), String> {
|
||||
let body = if data.len() == 1 {
|
||||
format!("{motd}\u{a7}{online}\u{a7}{max}")
|
||||
} else {
|
||||
if data[1] != 1 {
|
||||
if data.get(1) != Some(&1) {
|
||||
return Ok(());
|
||||
}
|
||||
if data.len() > 2 {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//! serialized LevelChunkSections with their PalettedContainers, block
|
||||
//! entities) and ClientboundLightUpdatePacketData (sky/block DataLayers).
|
||||
use crate::{
|
||||
Result,
|
||||
Error, Result,
|
||||
buf::{BitSet, Bytes, Codec, Longs, Reader, VarInt, Writer},
|
||||
packet,
|
||||
};
|
||||
@@ -83,6 +83,11 @@ impl Paletted {
|
||||
pub fn read(r: &mut Reader, strategy: Strategy, global_bits: u8) -> Result<Self> {
|
||||
let wire = r.u8()?;
|
||||
let (has_palette, bits) = strategy.configuration(wire, global_bits);
|
||||
// SimpleBitStorage holds 1 to 32 bits; wider wire values would
|
||||
// divide by zero or overflow a shift.
|
||||
if bits > 32 {
|
||||
return Err(Error::new(format!("{bits} bits per entry, more than 32")));
|
||||
}
|
||||
let palette = if !has_palette {
|
||||
Vec::new()
|
||||
} else if bits == 0 {
|
||||
@@ -425,6 +430,23 @@ mod tests {
|
||||
assert_eq!(g.get(4095), 4095 * 7);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wire_bits_wider_than_storage_are_rejected() {
|
||||
for wire in [33u8, 200] {
|
||||
let data = [wire];
|
||||
let mut r = Reader::new(&data);
|
||||
assert!(Paletted::read(&mut r, Strategy::Blocks, 16).is_err());
|
||||
}
|
||||
// 15- and 16-bit global palettes still read (1024 longs of zeroes).
|
||||
for (wire, global) in [(15u8, 15u8), (16, 16)] {
|
||||
let mut data = vec![wire];
|
||||
data.resize(1 + 1024 * 8, 0);
|
||||
let mut r = Reader::new(&data);
|
||||
let p = Paletted::read(&mut r, Strategy::Blocks, global).unwrap();
|
||||
assert_eq!(p.bits, wire);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chunk_data_round_trips() {
|
||||
let mut states = vec![0; BLOCKS];
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//! zlib compression above a threshold (CompressionEncoder / CompressionDecoder).
|
||||
use crate::{
|
||||
Error, Result,
|
||||
buf::{Reader, varint_size, write_varint},
|
||||
buf::{Reader, write_varint},
|
||||
};
|
||||
use flate2::{Compression, read::ZlibDecoder, write::ZlibEncoder};
|
||||
use std::io::{Read, Write};
|
||||
@@ -85,7 +85,7 @@ pub fn decompress(frame: Vec<u8>, threshold: Option<usize>) -> Result<Vec<u8>> {
|
||||
let mut r = Reader::new(&frame);
|
||||
let size = r.varint()?;
|
||||
if size == 0 {
|
||||
return Ok(frame[1..].to_vec());
|
||||
return Ok(frame[r.position()..].to_vec());
|
||||
}
|
||||
let size = usize::try_from(size).map_err(|_| Error::new("negative uncompressed length"))?;
|
||||
if size < threshold {
|
||||
@@ -98,7 +98,7 @@ pub fn decompress(frame: Vec<u8>, threshold: Option<usize>) -> Result<Vec<u8>> {
|
||||
"Badly compressed packet - size of {size} is larger than protocol maximum"
|
||||
)));
|
||||
}
|
||||
let start = varint_size(size as i32);
|
||||
let start = r.position();
|
||||
let mut out = Vec::with_capacity(size);
|
||||
ZlibDecoder::new(&frame[start..])
|
||||
.take(size as u64 + 1)
|
||||
@@ -163,4 +163,20 @@ mod tests {
|
||||
frame.extend(z.finish().unwrap());
|
||||
assert!(decompress(frame, Some(256)).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_noncanonical_length_prefix_still_reads() {
|
||||
// The data-length VarInt is read, not measured: zero as `0x80 0x00`.
|
||||
assert_eq!(
|
||||
decompress(vec![0x80, 0x00, 7, 1, 2], Some(256)).unwrap(),
|
||||
[7, 1, 2]
|
||||
);
|
||||
// And a compressed packet whose size was written wide: 300.
|
||||
let body = vec![9; 300];
|
||||
let mut frame = vec![0xAC, 0x82, 0x00];
|
||||
let mut z = ZlibEncoder::new(Vec::new(), Compression::default());
|
||||
z.write_all(&body).unwrap();
|
||||
frame.extend(z.finish().unwrap());
|
||||
assert_eq!(decompress(frame, Some(256)).unwrap(), body);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user