Files
Pumpkin/crates/pumpkin-protocol/fuzz/fuzz_targets/decoder_bedrock.rs
Alexander Medvedev a2b92e6dc5 chore: some fuzz fixes
2026-08-15 13:35:14 +02:00

104 lines
3.4 KiB
Rust

#![no_main]
use libfuzzer_sys::fuzz_target;
use pumpkin_protocol::bedrock::packet_decoder::BedrockBatchDecoder;
use pumpkin_protocol::bedrock::server::{
SAnimate, SBlockPickRequest, SClientCacheBlobStatus, SClientCacheStatus, SCommandRequest,
SContainerClose, SEmote, SEmoteList, SInteraction, SInventoryTransaction, SItemStackRequest,
SLoadingScreen, SLogin, SMobEquipment, SModalFormResponse, SPacketViolationWarning,
SPlayerAction, SPlayerAuthInput, SPlayerHotbar, SRequestAbility, SRequestChunkRadius,
SRequestNetworkSettings, SResourcePackResponse, SRespawn, SSetLocalPlayerAsInitialized,
SSetPlayerInventoryOptions, SText,
};
use pumpkin_protocol::serial::{PacketRead, PacketReadSlice};
use std::io::Cursor;
// ---------------------------------------------------------------------------
// Helper: Run every Serverbound packet's read method.
// ---------------------------------------------------------------------------
fn fuzz_serverbound_packets(payload: &[u8]) {
let mut cursor = Cursor::new(payload);
macro_rules! run_read {
($($packet:ty),* $(,)?) => {
$(
cursor.set_position(0);
let _ = <$packet>::read(&mut cursor);
)*
};
}
macro_rules! run_read_slice {
($($packet:ty),* $(,)?) => {
$(
let mut slice = payload;
let _ = <$packet>::read_slice(&mut slice);
)*
};
}
// Standard Bedrock Serverbound Packets (PacketRead)
run_read!(
SAnimate,
SBlockPickRequest,
SClientCacheBlobStatus,
SClientCacheStatus,
SCommandRequest,
SContainerClose,
SEmote,
SEmoteList,
SInteraction,
SInventoryTransaction,
SItemStackRequest,
SLoadingScreen,
SLogin,
SMobEquipment,
SModalFormResponse,
SPacketViolationWarning,
SPlayerAction,
SPlayerAuthInput,
SPlayerHotbar,
SRequestAbility,
SRequestChunkRadius,
SRequestNetworkSettings,
SResourcePackResponse,
SRespawn,
SSetLocalPlayerAsInitialized,
SSetPlayerInventoryOptions,
SText,
);
// Bedrock Serverbound Packets supporting zero-copy PacketReadSlice
run_read_slice!(SCommandRequest, SEmote, SModalFormResponse, SText,);
}
// ---------------------------------------------------------------------------
// Fuzz Target
// ---------------------------------------------------------------------------
fuzz_target!(|data: &[u8]| {
if data.len() < 2 {
return;
}
// Split data for decoder configuration vs raw payload
let threshold_raw = data[0];
let stream_data = &data[1..];
let mut decoder = BedrockBatchDecoder::new();
// Setup Decoder
if threshold_raw > 0 {
// Assuming your CompressionThreshold is a wrapper around u32
decoder.set_compression((threshold_raw as u32).try_into().unwrap());
}
// 1. Fuzz the Decoder (Framing/VarInts/Bitmasks)
let mut decoder_cursor = Cursor::new(stream_data.to_vec());
if let Ok(raw_packet) = decoder.get_game_packet(&mut decoder_cursor) {
// If framed correctly, fuzz the internal payload
fuzz_serverbound_packets(&raw_packet.payload);
}
// 2. Shotgun Fuzz
// Pass raw data directly to all readers to catch panics in the parsers
fuzz_serverbound_packets(stream_data);
});