EV-Kafka
view release on metacpan or search on metacpan
xt/fuzz_structured.t view on Meta::CPAN
use strict;
use warnings;
use Test::More;
use EV::Kafka;
# Structured/mutation parser fuzzing. xt/fuzz_decode.t feeds UNIFORM
# RANDOM bytes to the parsers â that can never build a valid response
# envelope around a poisoned length field, which is exactly why the
# fetch-response int32 overflow (batch_length 0x7FFFFFF9, a one-packet
# remote segfault) survived it at 20k iterations.
#
# Here we build VALID response envelopes (same byte-builder style as the
# t/18/t/21 goldens), record the offset of every length/count/varint
# field while building, then splice hostile values into those fields:
# negative, 0, INT32_MAX, INT32_MIN, off-by-one vs the remaining byte
# count, and huge/overlong uvarints. The parser must return undef or a
# hashref (arrayref for the batch decoder) and never crash, panic, or
# die.
plan skip_all => 'set RELEASE_TESTING' unless $ENV{RELEASE_TESTING};
my $iters = $ENV{FUZZ_ITERS} || 2_000;
plan tests => 11;
sub i16 { pack 'n', $_[0] }
sub i32 { pack 'N', $_[0] }
sub i64 { pack 'q>', $_[0] }
sub kstr { my $s = shift; i16(length $s) . $s }
# --- Template builders ---------------------------------------------------
# Each returns ($bytes, \@sites) where a site is [offset, kind, width]:
# kind 'i32'/'i16' overwrite in place, kind 'uvar' splices width bytes.
# Metadata v1: brokers, topics, partitions, replica/isr arrays.
sub tpl_metadata_v1 {
my @sites;
my $b = '';
my $at = sub { push @sites, [length($b), $_[0], $_[1]] };
$at->('i32', 4); $b .= i32(1); # brokers count
$b .= i32(0); # node_id
$at->('i16', 2); $b .= kstr('host1'); # host
$b .= i32(9092); # port
$at->('i16', 2); $b .= i16(-1); # rack: null
$b .= i32(0); # controller_id
$at->('i32', 4); $b .= i32(1); # topics count
$b .= i16(0); # topic error
$at->('i16', 2); $b .= kstr('t1'); # topic name
$b .= chr(0); # is_internal
$at->('i32', 4); $b .= i32(1); # partitions count
$b .= i16(0) . i32(0) . i32(0); # err, pid, leader
$at->('i32', 4); $b .= i32(1) . i32(0); # replicas: [0]
$at->('i32', 4); $b .= i32(1) . i32(0); # isr: [0]
return ($b, \@sites);
}
# Metadata v9 (flexible): compact arrays/strings â uvarint length fields.
sub tpl_metadata_v9 {
my @sites;
my $b = '';
my $at = sub { push @sites, [length($b), 'uvar', $_[0]] };
$b .= i32(0); # throttle
$at->(1); $b .= "\x02"; # brokers: 1
$b .= i32(0); # node_id
$at->(1); $b .= "\x06" . 'host1'; # host: compact
$b .= i32(9092); # port
$at->(1); $b .= "\x00"; # rack: null
$b .= "\x00"; # tagged fields
$at->(1); $b .= "\x00"; # cluster_id: null
$b .= i32(0); # controller_id
$at->(1); $b .= "\x01"; # topics: 0
( run in 0.517 second using v1.01-cache-2.11-cpan-4ab04211f4c )