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