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