Skip to content

Commit f5e1673

Browse files
riptlripatel-fd
authored andcommitted
bundle: simple client fuzzer
1 parent 6d1e884 commit f5e1673

File tree

7 files changed

+291
-150
lines changed

7 files changed

+291
-150
lines changed

src/disco/bundle/Local.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ $(call add-objs,fd_bundle_auth fd_bundle_client,fd_disco)
99
ifdef FD_HAS_HOSTED
1010
ifdef FD_HAS_SSE
1111
$(call make-unit-test,test_bundle_client,test_bundle_client,fd_disco fd_waltz fd_flamenco fd_tango fd_ballet fd_util,$(OPENSSL_LIBS))
12+
$(call make-fuzz-test,fuzz_bundle_client,fuzz_bundle_client,fd_disco fd_waltz fd_flamenco fd_tango fd_ballet fd_util,$(OPENSSL_LIBS))
1213
endif
1314
$(call make-fuzz-test,fuzz_bundle_auth_resp,fuzz_bundle_auth_resp,fd_disco fd_waltz fd_flamenco fd_tango fd_ballet fd_util,$(OPENSSL_LIBS))
1415
endif

src/disco/bundle/fd_bundle_client.c

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@
2222

2323
#define FD_BUNDLE_CLIENT_REQUEST_TIMEOUT ((long)8e9) /* 8 seconds */
2424

25+
__attribute__((weak)) long
26+
fd_bundle_tickcount( void ) {
27+
return fd_tickcount();
28+
}
29+
2530
void
2631
fd_bundle_client_reset( fd_bundle_tile_t * ctx ) {
2732
if( FD_UNLIKELY( ctx->tcp_sock >= 0 ) ) {
@@ -49,7 +54,7 @@ fd_bundle_client_reset( fd_bundle_tile_t * ctx ) {
4954
}
5055
# endif
5156

52-
fd_bundle_tile_backoff( ctx, fd_tickcount() );
57+
fd_bundle_tile_backoff( ctx, fd_bundle_tickcount() );
5358

5459
fd_bundle_auther_reset( &ctx->auther );
5560
fd_grpc_client_reset( ctx->grpc_client );
@@ -154,7 +159,7 @@ fd_bundle_client_create_conn( fd_bundle_tile_t * ctx ) {
154159
# endif /* FD_HAS_OPENSSL */
155160

156161
fd_grpc_client_reset( ctx->grpc_client );
157-
ctx->last_ping_tx_ticks = fd_tickcount();
162+
ctx->last_ping_tx_ticks = fd_bundle_tickcount();
158163
ctx->last_ping_tx_nanos = fd_log_wallclock();
159164
}
160165

@@ -245,7 +250,7 @@ fd_bundle_client_send_ping( fd_bundle_tile_t * ctx ) {
245250
fd_h2_rbuf_t * rbuf_tx = fd_grpc_client_rbuf_tx( ctx->grpc_client );
246251

247252
if( FD_LIKELY( fd_h2_tx_ping( conn, rbuf_tx ) ) ) {
248-
ctx->last_ping_tx_ticks = fd_tickcount();
253+
ctx->last_ping_tx_ticks = fd_bundle_tickcount();
249254
ctx->last_ping_tx_nanos = fd_log_wallclock();
250255
ctx->ping_randomize = fd_rng_ulong( ctx->rng );
251256
}
@@ -338,7 +343,7 @@ fd_bundle_client_step1( fd_bundle_tile_t * ctx,
338343
/* gRPC conn died? */
339344
if( FD_UNLIKELY( !ctx->grpc_client ) ) {
340345
reconnect:
341-
if( FD_UNLIKELY( fd_bundle_tile_should_stall( ctx, fd_tickcount() ) ) ) {
346+
if( FD_UNLIKELY( fd_bundle_tile_should_stall( ctx, fd_bundle_tickcount() ) ) ) {
342347
return;
343348
}
344349
fd_bundle_client_create_conn( ctx );
@@ -347,7 +352,7 @@ fd_bundle_client_step1( fd_bundle_tile_t * ctx,
347352
}
348353

349354
/* Did a HTTP/2 PING time out */
350-
long check_ts = fd_tickcount();
355+
long check_ts = fd_bundle_tickcount();
351356
if( FD_UNLIKELY( fd_bundle_client_ping_is_timeout( ctx, check_ts ) ) ) {
352357
FD_LOG_WARNING(( "Bundle gRPC timed out (HTTP/2 PING went unanswered for %.2f seconds)",
353358
( (double)ctx->ping_deadline_ticks / fd_tempo_tick_per_ns( NULL ) )*1e-9 ));
@@ -367,7 +372,7 @@ fd_bundle_client_step1( fd_bundle_tile_t * ctx,
367372

368373
/* Are we ready to issue a new request? */
369374
if( FD_UNLIKELY( fd_grpc_client_request_is_blocked( ctx->grpc_client ) ) ) return;
370-
long io_ts = fd_tickcount();
375+
long io_ts = fd_bundle_tickcount();
371376
if( FD_UNLIKELY( fd_bundle_tile_should_stall( ctx, io_ts ) ) ) return;
372377

373378
*charge_busy |= fd_bundle_client_step_reconnect( ctx, io_ts );
@@ -471,7 +476,7 @@ fd_bundle_tile_publish_bundle_txn(
471476
FD_LOG_CRIT(( "ctx->stem not set. This is a bug." ));
472477
}
473478

474-
ulong tspub = (ulong)fd_frag_meta_ts_comp( fd_tickcount() );
479+
ulong tspub = (ulong)fd_frag_meta_ts_comp( fd_bundle_tickcount() );
475480
fd_stem_publish( ctx->stem, ctx->verify_out.idx, sig, ctx->verify_out.chunk, sz, 0UL, 0UL, tspub );
476481
ctx->verify_out.chunk = fd_dcache_compact_next( ctx->verify_out.chunk, sz, ctx->verify_out.chunk0, ctx->verify_out.wmark );
477482
ctx->metrics.txn_received_cnt++;
@@ -506,7 +511,7 @@ fd_bundle_tile_publish_txn(
506511
FD_LOG_CRIT(( "ctx->stem not set. This is a bug." ));
507512
}
508513

509-
ulong tspub = (ulong)fd_frag_meta_ts_comp( fd_tickcount() );
514+
ulong tspub = (ulong)fd_frag_meta_ts_comp( fd_bundle_tickcount() );
510515
fd_stem_publish( ctx->stem, ctx->verify_out.idx, sig, ctx->verify_out.chunk, sz, 0UL, 0UL, tspub );
511516
ctx->verify_out.chunk = fd_dcache_compact_next( ctx->verify_out.chunk, sz, ctx->verify_out.chunk0, ctx->verify_out.wmark );
512517
ctx->metrics.txn_received_cnt++;
@@ -716,7 +721,7 @@ fd_bundle_client_handle_builder_fee_info(
716721

717722
long validity_duration_ticks = (long)( fd_tempo_tick_per_ns( NULL ) * (60e9 * 5.) ); /* 5 minutes */
718723
ctx->builder_info_avail = 1;
719-
ctx->builder_info_valid_until_ticks = fd_tickcount() + validity_duration_ticks;
724+
ctx->builder_info_valid_until_ticks = fd_bundle_tickcount() + validity_duration_ticks;
720725
}
721726

722727
static void
@@ -758,13 +763,13 @@ fd_bundle_client_grpc_rx_msg(
758763
case FD_BUNDLE_CLIENT_REQ_Auth_GenerateAuthChallenge:
759764
if( FD_UNLIKELY( !fd_bundle_auther_handle_challenge_resp( &ctx->auther, protobuf, protobuf_sz ) ) ) {
760765
ctx->metrics.decode_fail_cnt++;
761-
fd_bundle_tile_backoff( ctx, fd_tickcount() );
766+
fd_bundle_tile_backoff( ctx, fd_bundle_tickcount() );
762767
}
763768
break;
764769
case FD_BUNDLE_CLIENT_REQ_Auth_GenerateAuthTokens:
765770
if( FD_UNLIKELY( !fd_bundle_auther_handle_tokens_resp( &ctx->auther, protobuf, protobuf_sz ) ) ) {
766771
ctx->metrics.decode_fail_cnt++;
767-
fd_bundle_tile_backoff( ctx, fd_tickcount() );
772+
fd_bundle_tile_backoff( ctx, fd_bundle_tickcount() );
768773
}
769774
break;
770775
case FD_BUNDLE_CLIENT_REQ_Bundle_SubscribeBundles:
@@ -784,7 +789,7 @@ fd_bundle_client_grpc_rx_msg(
784789
static void
785790
fd_bundle_client_request_failed( fd_bundle_tile_t * ctx,
786791
ulong request_ctx ) {
787-
fd_bundle_tile_backoff( ctx, fd_tickcount() );
792+
fd_bundle_tile_backoff( ctx, fd_bundle_tickcount() );
788793
switch( request_ctx ) {
789794
case FD_BUNDLE_CLIENT_REQ_Auth_GenerateAuthChallenge:
790795
case FD_BUNDLE_CLIENT_REQ_Auth_GenerateAuthTokens:
@@ -816,15 +821,15 @@ fd_bundle_client_grpc_rx_end(
816821
case FD_BUNDLE_CLIENT_REQ_Bundle_SubscribePackets:
817822
ctx->packet_subscription_live = 0;
818823
ctx->packet_subscription_wait = 0;
819-
fd_bundle_tile_backoff( ctx, fd_tickcount() );
824+
fd_bundle_tile_backoff( ctx, fd_bundle_tickcount() );
820825
ctx->defer_reset = 1;
821826
FD_LOG_INFO(( "SubscribePackets stream failed (gRPC status %u-%s). Reconnecting ...",
822827
resp->grpc_status, fd_grpc_status_cstr( resp->grpc_status ) ));
823828
return;
824829
case FD_BUNDLE_CLIENT_REQ_Bundle_SubscribeBundles:
825830
ctx->bundle_subscription_live = 0;
826831
ctx->bundle_subscription_wait = 0;
827-
fd_bundle_tile_backoff( ctx, fd_tickcount() );
832+
fd_bundle_tile_backoff( ctx, fd_bundle_tickcount() );
828833
ctx->defer_reset = 1;
829834
FD_LOG_INFO(( "SubscribeBundles stream failed (gRPC status %u-%s). Reconnecting ...",
830835
resp->grpc_status, fd_grpc_status_cstr( resp->grpc_status ) ));
@@ -864,7 +869,7 @@ fd_bundle_client_grpc_rx_timeout(
864869
static void
865870
fd_bundle_client_grpc_ping_ack( void * app_ctx ) {
866871
fd_bundle_tile_t * ctx = app_ctx;
867-
ctx->last_ping_rx_ticks = fd_tickcount();
872+
ctx->last_ping_rx_ticks = fd_bundle_tickcount();
868873
ctx->metrics.ping_ack_cnt++;
869874
long rtt_sample = fd_log_wallclock() - ctx->last_ping_tx_nanos;
870875
fd_rtt_sample( ctx->rtt, (float)rtt_sample, 0 );
@@ -921,7 +926,7 @@ fd_bundle_client_status( fd_bundle_tile_t const * ctx ) {
921926
return CONNECTING; /* not fully connected */
922927
}
923928

924-
if( FD_UNLIKELY( fd_bundle_client_ping_is_timeout( ctx, fd_tickcount() ) ) ) {
929+
if( FD_UNLIKELY( fd_bundle_client_ping_is_timeout( ctx, fd_bundle_tickcount() ) ) ) {
925930
return DISCONNECTED; /* possible timeout */
926931
}
927932

src/disco/bundle/fd_bundle_tile.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ fd_bundle_tile_publish_block_engine_update(
115115

116116
update->status = (uchar)ctx->bundle_status_recent;
117117

118-
ulong tspub = (ulong)fd_frag_meta_ts_comp( fd_tickcount() );
118+
ulong tspub = (ulong)fd_frag_meta_ts_comp( fd_bundle_tickcount() );
119119
fd_stem_publish(
120120
stem,
121121
ctx->plugin_out.idx,

src/disco/bundle/fd_bundle_tile_private.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,13 @@ typedef struct fd_bundle_tile fd_bundle_tile_t;
144144

145145
FD_PROTOTYPES_BEGIN
146146
147+
/* fd_bundle_tickcount is an externally linked function wrapping
148+
fd_tickcount(). This is backed by a weak symbol, allowing tests to
149+
override the clock source. */
150+
151+
long
152+
fd_bundle_tickcount( void );
153+
147154
/* fd_bundle_client_grpc_callbacks provides callbacks for grpc_client. */
148155

149156
extern fd_grpc_client_callbacks_t fd_bundle_client_grpc_callbacks;

src/disco/bundle/fuzz_bundle_client.c

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/* fuzz_bundle_client injects HTTP/2 frames into a bundle tile state. */
2+
3+
#include "test_bundle_common.c"
4+
#include "fd_bundle_tile_private.h"
5+
#include <errno.h>
6+
#include <stdlib.h>
7+
8+
/* Override the clock source weak symbol.
9+
For now, this fuzzer does not support timeouts. */
10+
11+
long
12+
fd_bundle_tickcount( void ) {
13+
return 2UL;
14+
}
15+
16+
static fd_wksp_t * g_wksp;
17+
18+
int
19+
LLVMFuzzerInitialize( int * pargc,
20+
char *** pargv ) {
21+
putenv( "FD_LOG_BACKTRACE=0" );
22+
23+
fd_boot( pargc, pargv );
24+
25+
ulong cpu_idx = fd_tile_cpu_id( fd_tile_idx() );
26+
if( cpu_idx>fd_shmem_cpu_cnt() ) cpu_idx = 0UL;
27+
char const * _page_sz = fd_env_strip_cmdline_cstr ( pargc, pargv, "--page-sz", NULL, "normal" );
28+
ulong page_cnt = fd_env_strip_cmdline_ulong( pargc, pargv, "--page-cnt", NULL, 256UL );
29+
ulong numa_idx = fd_env_strip_cmdline_ulong( pargc, pargv, "--numa-idx", NULL, fd_shmem_numa_idx( cpu_idx ) );
30+
fd_wksp_t * wksp = fd_wksp_new_anonymous( fd_cstr_to_shmem_page_sz( _page_sz ), page_cnt, fd_shmem_cpu_idx( numa_idx ), "wksp", 16UL );
31+
FD_TEST( wksp );
32+
g_wksp = wksp;
33+
34+
atexit( fd_halt );
35+
36+
fd_log_level_core_set( 4 );
37+
fd_log_level_stderr_set( 4 );
38+
fd_log_level_logfile_set( 4 );
39+
40+
return 0;
41+
}
42+
43+
int
44+
LLVMFuzzerTestOneInput( uchar const * data,
45+
ulong size ) {
46+
fd_wksp_t * const wksp = g_wksp;
47+
if( size<8UL ) return -1;
48+
49+
test_bundle_env_t env[1];
50+
test_bundle_env_create( env, wksp );
51+
test_bundle_env_mock_conn_empty( env );
52+
53+
fd_bundle_tile_t * const ctx = env->state;
54+
fd_h2_rbuf_t * const frame_rx = ctx->grpc_client->frame_rx;
55+
fd_h2_rbuf_t * const frame_tx = ctx->grpc_client->frame_tx;
56+
57+
ctx->grpc_client->conn->ping_tx = 2; /* allow PING ACKs */
58+
59+
ulong const seed = fd_ulong_hash( FD_LOAD( ulong, data+size-8 ) );
60+
if( seed & 1 ) test_bundle_env_mock_h2_hs( env->state );
61+
if( seed & 2 ) test_bundle_env_mock_builder_info( env->state );
62+
if( seed & 4 ) test_bundle_env_mock_bundle_stream( env->state );
63+
if( seed & 8 ) test_bundle_env_mock_packet_stream( env->state );
64+
if( seed & 16 ) test_bundle_env_mock_builder_info_req( env->state );
65+
66+
while( size ) {
67+
// ulong chunk_sz = 1UL;
68+
ulong const chunk_sz = fd_ulong_min( size, fd_h2_rbuf_free_sz( frame_rx ) );
69+
fd_h2_rbuf_push( frame_rx, data, chunk_sz );
70+
data += chunk_sz;
71+
size -= chunk_sz;
72+
int charge_busy = 0;
73+
fd_bundle_client_step( ctx, &charge_busy );
74+
fd_h2_rbuf_skip( frame_tx, fd_h2_rbuf_used_sz( frame_tx ) );
75+
if( ctx->defer_reset ) break;
76+
}
77+
78+
test_bundle_env_destroy( env );
79+
80+
/* Check for memory leaks */
81+
fd_wksp_usage_t wksp_usage;
82+
FD_TEST( fd_wksp_usage( wksp, NULL, 0UL, &wksp_usage ) );
83+
FD_TEST( wksp_usage.free_cnt==wksp_usage.total_cnt );
84+
85+
return 0;
86+
}

0 commit comments

Comments
 (0)