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

Generated by: LCOV version 1.14