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 : }
|