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

Generated by: LCOV version 1.14