LCOV - code coverage report
Current view: top level - flamenco/snapshot - fuzz_snapshot_http.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 87 108 80.6 %
Date: 2025-01-08 12:08:44 Functions: 4 4 100.0 %

          Line data    Source code
       1             : #if !FD_HAS_HOSTED || !FD_HAS_THREADS
       2             : #error "This target requires FD_HAS_HOSTED and FD_HAS_THREADS"
       3             : #endif
       4             : 
       5             : /* fuzz_snapshot_http.c uses auto-generated fuzz inputs to mock the
       6             :    server-side of the snapshot downloader.  Communication runs over an
       7             :    unnamed AF_UNIX socket pair. */
       8             : 
       9             : #include <assert.h>
      10             : #include <errno.h>
      11             : #include <poll.h>
      12             : #include <sched.h>
      13             : #include <stdio.h>
      14             : #include <stdlib.h>
      15             : #include <sys/socket.h>
      16             : #include <threads.h>
      17             : #include <unistd.h>
      18             : 
      19             : #include "../../util/sanitize/fd_fuzz.h"
      20             : #include "fd_snapshot_http.h"
      21             : 
      22             : struct shared_state {
      23             :   int          client_sock;
      24             :   int volatile done_sending;
      25             : };
      26             : 
      27             : int
      28             : LLVMFuzzerInitialize( int  *   argc,
      29          18 :                       char *** argv ) {
      30             :   /* Set up shell without signal handlers */
      31          18 :   putenv( "FD_LOG_BACKTRACE=0" );
      32          18 :   fd_boot( argc, argv );
      33          18 :   atexit( fd_halt );
      34          18 :   fd_log_level_core_set(3); /* crash on warning log */
      35             :   /* Don't print warning log */
      36          18 :   fd_log_level_logfile_set( 4 );
      37          18 :   fd_log_level_stderr_set( 4 );
      38          18 :   return 0;
      39          18 : }
      40             : 
      41             : static int
      42           3 : target_task( void * ctx ) {
      43           3 :   struct shared_state * st = ctx;
      44             : 
      45           3 :   int socket = st->client_sock;
      46             : 
      47           3 :   fd_snapshot_name_t name[1] = {{0}};
      48           3 :   fd_snapshot_http_t  _http[1];
      49           3 :   fd_snapshot_http_t * http = fd_snapshot_http_new( _http, "localhost:80", FD_IP4_ADDR( 127, 0, 0, 1 ), 80, name );
      50             : 
      51             :   /* Hijack the HTTP state and make it think there is a successful connection */
      52           3 :   assert( http->socket_fd == -1 );
      53           0 :   http->socket_fd    = socket;
      54           3 :   http->state        = FD_SNAPSHOT_HTTP_STATE_REQ;
      55           3 :   http->req_timeout  = 1e9L;  /* 1s */
      56           3 :   http->req_deadline = fd_log_wallclock() + http->req_timeout;
      57             : 
      58           6 :   for(;;) {
      59           6 :     int stop = 0;
      60           6 :     switch( http->state ) {
      61           3 :     case FD_SNAPSHOT_HTTP_STATE_RESP:
      62           3 :     case FD_SNAPSHOT_HTTP_STATE_DL:
      63           3 :       if( st->done_sending ) {
      64           3 :         FD_FUZZ_MUST_BE_COVERED;
      65             :         /* Did we consume all bytes? */
      66           3 :         struct pollfd pfd[1] = {{.fd=socket, .events=(short)POLLIN}};
      67           3 :         poll( pfd, 1, 0 );
      68           3 :         stop = pfd[0].revents==0;
      69           3 :       }
      70           3 :       break;
      71           6 :     }
      72           6 :     if( stop ) break;
      73             : 
      74           6 :     uchar buf[1024];
      75           6 :     ulong out_sz;
      76           6 :     int ok = fd_io_istream_snapshot_http_read( http, buf, sizeof(buf), &out_sz );
      77           6 :     if( ok!=0 ) {
      78           3 :       FD_FUZZ_MUST_BE_COVERED;
      79           3 :       break;
      80           3 :     }
      81             : 
      82           3 :     FD_FUZZ_MUST_BE_COVERED;
      83           3 :   }
      84             : 
      85           3 :   fd_snapshot_http_delete( http );
      86           3 :   close( socket );
      87           3 :   return 0;
      88           3 : }
      89             : 
      90             : static void
      91             : io_task( int            sock,
      92             :          int volatile * done_sending,
      93             :          uchar const *  data,
      94           3 :          ulong          data_sz ) {
      95             : 
      96           3 :   uchar const * data_end       = data + data_sz;
      97           3 :   int           event_interest = POLLIN|POLLOUT;
      98             : 
      99             :   /* Do the I/O */
     100             : 
     101        4384 :   for(;;) {
     102        4384 :     struct pollfd pfd[1] = {{.fd=sock, .events=(short)event_interest}};
     103        4384 :     if( FD_UNLIKELY( poll( pfd, 1, 0 )<0 ) ) {
     104           0 :       if( errno!=EINTR )
     105           0 :       FD_LOG_ERR(( "poll() failed (%d-%s)", errno, fd_io_strerror( errno ) ));
     106           0 :         break;
     107           0 :     }
     108             : 
     109        4384 :     if( pfd[0].revents==0 )
     110        4375 :       sched_yield();
     111             : 
     112             :     /* Send a fragment of input data */
     113             : 
     114        4384 :     if( pfd[0].revents & POLLOUT ) {
     115           3 :       if( FD_LIKELY( data<data_end ) ) {
     116           3 :         long n = send( sock, data, (ulong)(data_end - data), MSG_DONTWAIT|MSG_NOSIGNAL );
     117           3 :         if( n<=0 ) {
     118           0 :           if( FD_LIKELY( (errno==ECONNRESET) | (errno==EPIPE) ) ) break;
     119           0 :           if( FD_UNLIKELY( errno!=EAGAIN && errno!=EINTR ) ) {  /* TODO use EWOULDBLOCK instead? */
     120           0 :             FD_LOG_CRIT(( "send() to target failed (%d-%s)", errno, fd_io_strerror( errno ) ));
     121           0 :             break;
     122           0 :           }
     123           0 :           continue;
     124           0 :         }
     125           3 :         data += (ulong)n;
     126           3 :       }
     127           3 :       if( data==data_end ) {
     128           3 :         event_interest &= ~POLLOUT;
     129           3 :         *done_sending   = 1;
     130           3 :       }
     131           3 :     }
     132             : 
     133             :     /* Discard any incoming data, for as long as the client keeps the
     134             :        connection open. */
     135             : 
     136        4384 :     if( pfd[0].revents & POLLIN ) {
     137           6 :       char buf[1024];
     138           6 :       long n = recv( sock, buf, sizeof(buf), MSG_DONTWAIT );
     139           6 :       if( n<0 ) {
     140           0 :         if( FD_LIKELY( (errno==ECONNRESET) | (errno==EPIPE) ) ) break;
     141           0 :         if( FD_UNLIKELY( errno!=EAGAIN && errno!=EINTR ) ) {  /* TODO use EWOULDBLOCK instead? */
     142           0 :           FD_LOG_CRIT(( "recv() from target failed (%d-%s)", errno, fd_io_strerror( errno ) ));
     143           0 :           break;
     144           0 :         }
     145           6 :       } else if( n==0 ) {
     146           3 :         break;  /* socket closed */
     147           3 :       }
     148           6 :     }
     149             : 
     150        4384 :   }
     151             : 
     152           3 : }
     153             : 
     154             : int
     155             : LLVMFuzzerTestOneInput( uchar const * data,
     156           3 :                         ulong         data_sz ) {
     157             : 
     158           3 :   int sockets[2] = {-1, -1};
     159           3 :   if( FD_UNLIKELY( 0!=socketpair( AF_UNIX, SOCK_STREAM, 0, sockets ) ) ) {
     160           0 :     FD_LOG_ERR(( "socketpair(AF_UNIX,SOCK_STREAM) failed (%d-%s)", errno, fd_io_strerror( errno ) ));
     161           0 :     return 0;
     162           0 :   }
     163             : 
     164           3 :   FD_FUZZ_MUST_BE_COVERED;
     165             : 
     166           3 :   struct shared_state st = { .client_sock = sockets[1] };
     167             : 
     168             :   /* Launch a thread that does the HTTP client side */
     169           3 :   thrd_t thr;
     170           3 :   assert( thrd_create( &thr, target_task, &st )==thrd_success );
     171           0 :   fd_msan_unpoison( &thr, sizeof(thrd_t) );
     172             : 
     173             :   /* Do the server side I/O */
     174           3 :   io_task( sockets[0], &st.done_sending, data, data_sz );
     175           3 :   close( sockets[0] );
     176             : 
     177           3 :   thrd_join( thr, NULL );
     178           3 :   return 0;
     179           3 : }

Generated by: LCOV version 1.14