genesis: improve client fuzzer#8481
Conversation
There was a problem hiding this comment.
Pull request overview
This PR significantly enhances the genesis client fuzzer by adding comprehensive fuzzing capabilities including multi-peer support, structured input generation, custom mutation logic, and multiple test scenarios covering various HTTP response edge cases.
Changes:
- Expanded the fuzzer from a simple single-peer test to support up to 4 concurrent peers with configurable scenarios
- Added a custom mutator (LLVMFuzzerCustomMutator) that generates structured fuzzing inputs with awareness of the protocol being tested
- Implemented 12 distinct test scenarios covering various edge cases like incomplete headers, malformed responses, missing content-length, overflow conditions, and timeout scenarios
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| fuzz_mut_ensure( data, &data_sz, cursor+1UL, max_sz, &rng ); | ||
| ulong step_budget = 16UL + fuzz_mut_roll( &rng, FUZZ_MAX_STEPS-15UL ); | ||
| data[ cursor++ ] = (uchar)(step_budget - 16UL); |
There was a problem hiding this comment.
Potential buffer overflow: After the loop ending at line 481, cursor could equal or exceed max_sz. Lines 483-485 unconditionally execute fuzz_mut_ensure and write data[cursor++], which would result in an out-of-bounds write when cursor >= max_sz. Add a bounds check before this write: if(cursor<max_sz) or ensure the loop leaves cursor less than max_sz.
| fuzz_mut_ensure( data, &data_sz, cursor+1UL, max_sz, &rng ); | |
| ulong step_budget = 16UL + fuzz_mut_roll( &rng, FUZZ_MAX_STEPS-15UL ); | |
| data[ cursor++ ] = (uchar)(step_budget - 16UL); | |
| if( cursor<max_sz ) { | |
| fuzz_mut_ensure( data, &data_sz, cursor+1UL, max_sz, &rng ); | |
| ulong step_budget = 16UL + fuzz_mut_roll( &rng, FUZZ_MAX_STEPS-15UL ); | |
| data[ cursor++ ] = (uchar)(step_budget - 16UL); | |
| } |
| ulong step_budget = 16UL + fuzz_mut_roll( &rng, FUZZ_MAX_STEPS-15UL ); | ||
| data[ cursor++ ] = (uchar)(step_budget - 16UL); | ||
|
|
||
| ulong steps_to_emit = fd_ulong_min( step_budget, 40UL + fuzz_mut_roll( &rng, 24UL ) ); | ||
| for( ulong step=0UL; step<steps_to_emit && cursor<max_sz; step++ ) { | ||
| fuzz_mut_ensure( data, &data_sz, cursor+2UL, max_sz, &rng ); | ||
| if( FD_UNLIKELY( cursor>=data_sz ) ) break; | ||
| data[ cursor++ ] = (uchar)fuzz_mut_roll( &rng, peer_cnt ); | ||
| uchar action = (uchar)fuzz_mut_roll( &rng, 8UL ); | ||
| if( (action==7U) && (0UL==fuzz_mut_roll( &rng, 2UL )) ) action |= 0x40U; | ||
| if( 0UL==fuzz_mut_roll( &rng, 9UL ) ) action |= 0x80U; | ||
| data[ cursor++ ] = action; | ||
| if( (action & 7U)==0U ) { | ||
| fuzz_mut_ensure( data, &data_sz, cursor+1UL, max_sz, &rng ); |
There was a problem hiding this comment.
Potential buffer overflow: After line 491 increments cursor, cursor could equal max_sz. Line 495 then writes data[cursor++] without checking bounds, resulting in an out-of-bounds write. Similarly, if cursor equals max_sz after line 495, lines 497-498 could also write out of bounds. Add bounds checks or modify the loop to ensure cursor never reaches max_sz before writes.
| ulong step_budget = 16UL + fuzz_mut_roll( &rng, FUZZ_MAX_STEPS-15UL ); | |
| data[ cursor++ ] = (uchar)(step_budget - 16UL); | |
| ulong steps_to_emit = fd_ulong_min( step_budget, 40UL + fuzz_mut_roll( &rng, 24UL ) ); | |
| for( ulong step=0UL; step<steps_to_emit && cursor<max_sz; step++ ) { | |
| fuzz_mut_ensure( data, &data_sz, cursor+2UL, max_sz, &rng ); | |
| if( FD_UNLIKELY( cursor>=data_sz ) ) break; | |
| data[ cursor++ ] = (uchar)fuzz_mut_roll( &rng, peer_cnt ); | |
| uchar action = (uchar)fuzz_mut_roll( &rng, 8UL ); | |
| if( (action==7U) && (0UL==fuzz_mut_roll( &rng, 2UL )) ) action |= 0x40U; | |
| if( 0UL==fuzz_mut_roll( &rng, 9UL ) ) action |= 0x80U; | |
| data[ cursor++ ] = action; | |
| if( (action & 7U)==0U ) { | |
| fuzz_mut_ensure( data, &data_sz, cursor+1UL, max_sz, &rng ); | |
| ulong step_budget = 16UL + fuzz_mut_roll( &rng, FUZZ_MAX_STEPS-15UL ); | |
| if( FD_UNLIKELY( cursor>=max_sz ) ) return data_sz; | |
| data[ cursor++ ] = (uchar)(step_budget - 16UL); | |
| ulong steps_to_emit = fd_ulong_min( step_budget, 40UL + fuzz_mut_roll( &rng, 24UL ) ); | |
| for( ulong step=0UL; step<steps_to_emit && cursor<max_sz; step++ ) { | |
| fuzz_mut_ensure( data, &data_sz, cursor+2UL, max_sz, &rng ); | |
| if( FD_UNLIKELY( cursor>=data_sz ) ) break; | |
| if( FD_UNLIKELY( cursor>=max_sz ) ) break; | |
| data[ cursor++ ] = (uchar)fuzz_mut_roll( &rng, peer_cnt ); | |
| uchar action = (uchar)fuzz_mut_roll( &rng, 8UL ); | |
| if( (action==7U) && (0UL==fuzz_mut_roll( &rng, 2UL )) ) action |= 0x40U; | |
| if( 0UL==fuzz_mut_roll( &rng, 9UL ) ) action |= 0x80U; | |
| if( FD_UNLIKELY( cursor>=max_sz ) ) break; | |
| data[ cursor++ ] = action; | |
| if( (action & 7U)==0U ) { | |
| fuzz_mut_ensure( data, &data_sz, cursor+1UL, max_sz, &rng ); | |
| if( FD_UNLIKELY( cursor>=max_sz ) ) break; |
| if( mode>=8UL ) { | ||
| fuzz_mut_ensure( data, &data_sz, cursor+1UL, max_sz, &rng ); | ||
| data[ cursor++ ] = fuzz_mut_u8( &rng ); | ||
| } | ||
|
|
||
| fuzz_mut_ensure( data, &data_sz, cursor+1UL, max_sz, &rng ); | ||
| data[ cursor++ ] = (uchar)fuzz_mut_roll( &rng, 3UL ); |
There was a problem hiding this comment.
Potential buffer overflow: After line 462 sets cursor to min(max_sz, cursor+body_sz), cursor could equal max_sz. If mode is greater than or equal to 8, the code calls fuzz_mut_ensure which will clamp data_sz to max_sz, but then line 466 writes data[cursor++] where cursor could be max_sz, resulting in an out-of-bounds write. Add a check: if(cursor<max_sz) before the write or change the condition to if(mode>=8UL && cursor<max_sz).
| if( mode>=8UL ) { | |
| fuzz_mut_ensure( data, &data_sz, cursor+1UL, max_sz, &rng ); | |
| data[ cursor++ ] = fuzz_mut_u8( &rng ); | |
| } | |
| fuzz_mut_ensure( data, &data_sz, cursor+1UL, max_sz, &rng ); | |
| data[ cursor++ ] = (uchar)fuzz_mut_roll( &rng, 3UL ); | |
| if( mode>=8UL && cursor<max_sz ) { | |
| fuzz_mut_ensure( data, &data_sz, cursor+1UL, max_sz, &rng ); | |
| data[ cursor++ ] = fuzz_mut_u8( &rng ); | |
| } | |
| if( cursor<max_sz ) { | |
| fuzz_mut_ensure( data, &data_sz, cursor+1UL, max_sz, &rng ); | |
| data[ cursor++ ] = (uchar)fuzz_mut_roll( &rng, 3UL ); | |
| } |
| fuzz_mut_ensure( data, &data_sz, cursor+1UL, max_sz, &rng ); | ||
| data[ cursor++ ] = (uchar)fuzz_mut_roll( &rng, 3UL ); |
There was a problem hiding this comment.
Potential buffer overflow: After line 462 sets cursor to min(max_sz, cursor+body_sz), cursor could equal max_sz. Lines 469-470 unconditionally execute fuzz_mut_ensure and write data[cursor++], which would result in an out-of-bounds write when cursor equals max_sz. The loop condition cursor<max_sz on line 447 is only checked at the start of each iteration, not before these writes. Add a check before this write or break the loop if cursor >= max_sz after line 467.
| fuzz_mut_ensure( data, &data_sz, cursor+1UL, max_sz, &rng ); | ||
| uchar send_now = (uchar)(fuzz_mut_roll( &rng, 4UL ) ? 1U : 0U); | ||
| data[ cursor++ ] = send_now; | ||
| if( send_now ) { |
There was a problem hiding this comment.
Potential buffer overflow: After line 476 increments cursor, cursor could equal max_sz. If send_now is true, lines 478-479 execute fuzz_mut_ensure and write data[cursor++], which would result in an out-of-bounds write when cursor equals max_sz. Add a check: change line 477 to if(send_now && cursor<max_sz) to prevent the out-of-bounds write.
| if( send_now ) { | |
| if( send_now && cursor<max_sz ) { |
No description provided.