Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions fuzz/fuzz_read_packet.c
Original file line number Diff line number Diff line change
Expand Up @@ -207,14 +207,19 @@ static void fuzz_round_trip_packet( struct fuzz_input_t * in )

if ( !corrupt || corrupt_xor == 0 )
{
// an uncorrupted packet written by netcode_write_packet must read back
// with the same type and sequence
// Connection requests have no packet sequence. Other encrypted
// packets round-trip unless they carry the reserved sequence.

FUZZ_CHECK( read );
FUZZ_CHECK( ( (uint8_t*) read )[0] == packet_type );
if ( packet_type != NETCODE_CONNECTION_REQUEST_PACKET )
if ( packet_type != NETCODE_CONNECTION_REQUEST_PACKET && sequence == UINT64_MAX )
{
FUZZ_CHECK( read_sequence == sequence );
FUZZ_CHECK( !read );
}
else
{
FUZZ_CHECK( read );
FUZZ_CHECK( ( (uint8_t*) read )[0] == packet_type );
if ( packet_type != NETCODE_CONNECTION_REQUEST_PACKET )
FUZZ_CHECK( read_sequence == sequence );
}
}

Expand Down
58 changes: 58 additions & 0 deletions netcode.c
Original file line number Diff line number Diff line change
Expand Up @@ -1870,6 +1870,11 @@ int netcode_replay_protection_already_received( struct netcode_replay_protection
{
netcode_assert( replay_protection );

// The maximum value is reserved for an empty receive slot.

if ( sequence == UINT64_MAX )
return 1;

// written so it cannot overflow: "sequence + BUFFER_SIZE <= most_recent" wraps for
// sequence values near UINT64_MAX and falsely rejects them as replays

Expand All @@ -1892,6 +1897,9 @@ void netcode_replay_protection_advance_sequence( struct netcode_replay_protectio
{
netcode_assert( replay_protection );

if ( sequence == UINT64_MAX )
return;

if ( sequence > replay_protection->most_recent_sequence )
replay_protection->most_recent_sequence = sequence;

Expand Down Expand Up @@ -2090,6 +2098,14 @@ void * netcode_read_packet( uint8_t * buffer,
(*sequence) |= ( uint64_t) ( value ) << ( 8 * i );
}

// The maximum value is reserved by replay protection.

if ( *sequence == UINT64_MAX )
{
netcode_printf( NETCODE_LOG_LEVEL_DEBUG, "ignored encrypted packet. sequence is invalid\n" );
return NULL;
}

// ignore the packet if it has already been received

if ( replay_protection && packet_type >= NETCODE_CONNECTION_KEEP_ALIVE_PACKET )
Expand Down Expand Up @@ -7862,6 +7878,29 @@ void test_replay_protection()
// while packets that fell out of the window are rejected as before

check( netcode_replay_protection_already_received( &replay_protection, UINT64_MAX - 1 - NETCODE_REPLAY_PROTECTION_BUFFER_SIZE ) == 1 );

// The reserved sequence cannot be accepted or move the replay window.

netcode_replay_protection_reset( &replay_protection );
check( netcode_replay_protection_already_received( &replay_protection, UINT64_MAX - 1 ) == 0 );
netcode_replay_protection_advance_sequence( &replay_protection, UINT64_MAX - 1 );
check( netcode_replay_protection_already_received( &replay_protection, UINT64_MAX ) == 1 );
netcode_replay_protection_advance_sequence( &replay_protection, UINT64_MAX );
check( replay_protection.most_recent_sequence == UINT64_MAX - 1 );
check( netcode_replay_protection_already_received( &replay_protection, UINT64_MAX ) == 1 );
check( netcode_replay_protection_already_received( &replay_protection, UINT64_MAX - 2 ) == 0 );

// Refusing the reserved value leaves an ordinary receive window usable.

netcode_replay_protection_reset( &replay_protection );
check( netcode_replay_protection_already_received( &replay_protection, 10 ) == 0 );
netcode_replay_protection_advance_sequence( &replay_protection, 10 );
check( netcode_replay_protection_already_received( &replay_protection, UINT64_MAX ) == 1 );
netcode_replay_protection_advance_sequence( &replay_protection, UINT64_MAX );
check( replay_protection.most_recent_sequence == 10 );
check( netcode_replay_protection_already_received( &replay_protection, 11 ) == 0 );
netcode_replay_protection_advance_sequence( &replay_protection, 11 );
check( replay_protection.most_recent_sequence == 11 );
}

void test_read_packet_replay_protection()
Expand Down Expand Up @@ -7936,6 +7975,25 @@ void test_read_packet_replay_protection()
check( replay_protection.most_recent_sequence == packet_sequence );
free( output_packet );
}

// The packet reader refuses the reserved sequence even without a replay window.

{
struct netcode_connection_denied_packet_t denied_packet;
denied_packet.packet_type = NETCODE_CONNECTION_DENIED_PACKET;
packet_bytes = netcode_write_packet( &denied_packet, packet_data, sizeof( packet_data ), UINT64_MAX, packet_key, TEST_PROTOCOL_ID );
check( packet_bytes > 1 + NETCODE_MAC_BYTES );
memcpy( buffer, packet_data, (size_t) packet_bytes );
check( netcode_read_packet( buffer, packet_bytes, &sequence, packet_key, TEST_PROTOCOL_ID, 1000, 0, NULL, allowed_packet_types, NULL, NULL, NULL ) == NULL );

packet_bytes = netcode_write_packet( &denied_packet, packet_data, sizeof( packet_data ), UINT64_MAX - 1, packet_key, TEST_PROTOCOL_ID );
check( packet_bytes > 1 + NETCODE_MAC_BYTES );
memcpy( buffer, packet_data, (size_t) packet_bytes );
void * output_packet = netcode_read_packet( buffer, packet_bytes, &sequence, packet_key, TEST_PROTOCOL_ID, 1000, 0, NULL, allowed_packet_types, NULL, NULL, NULL );
check( output_packet );
check( sequence == UINT64_MAX - 1 );
free( output_packet );
}
}

static int num_ignored_asserts = 0;
Expand Down
Loading