Line data Source code
1 : #if !FD_HAS_HOSTED
2 : #error "This target requires FD_HAS_HOSTED"
3 : #endif
4 :
5 : #include <assert.h>
6 : #include <stdio.h>
7 : #include <stdlib.h>
8 : #include <unistd.h>
9 :
10 : #include "../../util/fd_util.h"
11 : #include "../../util/sanitize/fd_fuzz.h"
12 : #include "picohttpparser.h"
13 :
14 : int
15 : LLVMFuzzerInitialize( int * argc,
16 18 : char *** argv ) {
17 : /* Set up shell without signal handlers */
18 18 : putenv( "FD_LOG_BACKTRACE=0" );
19 18 : fd_boot( argc, argv );
20 18 : atexit( fd_halt );
21 :
22 : /* Disable parsing error logging */
23 18 : fd_log_level_stderr_set(4);
24 18 : return 0;
25 18 : }
26 :
27 0 : #define HEADER_CAP (32UL)
28 :
29 0 : void fuzz_request(uchar const * data, ulong size) {
30 0 : if (size >= sizeof(size_t)) {
31 0 : size -= sizeof(size_t);
32 0 : size_t last_len = *(size_t *)data;
33 0 : if (last_len > 0) {
34 0 : if (size == 0) {
35 0 : last_len = 0;
36 0 : } else {
37 0 : last_len %= size;
38 0 : }
39 0 : }
40 0 : data += sizeof(size_t);
41 :
42 0 : do {
43 0 : char const * method;
44 0 : ulong method_len;
45 0 : char const * path;
46 0 : ulong path_len;
47 0 : int minor_version;
48 0 : struct phr_header headers[ HEADER_CAP ];
49 0 : ulong header_cnt = HEADER_CAP;
50 :
51 0 : int res = phr_parse_request(
52 0 : (char const *)data, size,
53 0 : &method, &method_len,
54 0 : &path, &path_len,
55 0 : &minor_version,
56 0 : headers, &header_cnt, last_len );
57 :
58 0 : if( res==0 ) {
59 0 : FD_FUZZ_MUST_BE_COVERED;
60 0 : assert( method_len < size );
61 0 : assert( path_len < size );
62 0 : assert( header_cnt <= HEADER_CAP );
63 0 : for( ulong i=0UL; i<header_cnt; i++ ) {
64 0 : assert( headers[i].name_len < size );
65 0 : assert( headers[i].value_len < size );
66 0 : }
67 0 : } else if ( res > 0 ) {
68 0 : assert( (ulong) res <= size) ;
69 0 : } else {
70 0 : FD_FUZZ_MUST_BE_COVERED;
71 0 : }
72 0 : } while(0);
73 :
74 : /* parse request byte by byte */
75 :
76 0 : do {
77 0 : char const * method;
78 0 : ulong method_len;
79 0 : char const * path;
80 0 : ulong path_len;
81 0 : int minor_version;
82 0 : struct phr_header headers[ HEADER_CAP ];
83 0 : ulong header_cnt = HEADER_CAP;
84 0 : int ok = 0;
85 :
86 0 : for( ulong cursor=0UL; cursor<size; cursor++ ) {
87 0 : FD_FUZZ_MUST_BE_COVERED;
88 0 : int res = phr_parse_request(
89 0 : (char const *)data + cursor, 1UL,
90 0 : &method, &method_len,
91 0 : &path, &path_len,
92 0 : &minor_version,
93 0 : headers, &header_cnt, 0 );
94 0 : if( res>0 ) {
95 0 : ok = 1;
96 0 : break;
97 0 : }
98 0 : if( res==-1 ) break;
99 0 : assert( res==-2 );
100 0 : }
101 :
102 0 : if( ok ) {
103 0 : FD_FUZZ_MUST_BE_COVERED;
104 0 : assert( method_len < size );
105 0 : assert( path_len < size );
106 0 : assert( header_cnt <= HEADER_CAP );
107 0 : for( ulong i=0UL; i<header_cnt; i++ ) {
108 0 : assert( headers[i].name_len < size );
109 0 : assert( headers[i].value_len < size );
110 0 : }
111 0 : } else {
112 0 : FD_FUZZ_MUST_BE_COVERED;
113 0 : }
114 0 : } while(0);
115 0 : }
116 0 : }
117 :
118 0 : void fuzz_response(uchar const * data, ulong size) {
119 0 : if (size >= sizeof(size_t)) {
120 0 : size -= sizeof(size_t);
121 0 : size_t last_len = *(size_t *)data;
122 0 : if (last_len > 0) {
123 0 : if (size == 0) {
124 0 : last_len = 0;
125 0 : } else {
126 0 : last_len %= size;
127 0 : }
128 0 : }
129 0 : data += sizeof(size_t);
130 :
131 0 : do {
132 0 : int minor_version;
133 0 : int status;
134 0 : const char * message;
135 0 : ulong message_len;
136 0 : struct phr_header headers[ HEADER_CAP ];
137 0 : ulong num_headers = HEADER_CAP;
138 :
139 0 : int res = phr_parse_response(
140 0 : (char const *)data, size,
141 0 : &minor_version, &status, &message, &message_len,
142 0 : headers, &num_headers, last_len );
143 0 : if ( res > 0 ) {
144 0 : assert( (ulong) res <= size) ;
145 0 : }
146 0 : } while(0);
147 0 : }
148 0 : }
149 :
150 0 : void fuzz_headers(uchar const * data, ulong size) {
151 0 : if (size >= sizeof(size_t)) {
152 0 : size -= sizeof(size_t);
153 0 : size_t last_len = *(size_t *)data;
154 0 : if (last_len > 0) {
155 0 : if (size == 0) {
156 0 : last_len = 0;
157 0 : } else {
158 0 : last_len %= size;
159 0 : }
160 0 : }
161 0 : data += sizeof(size_t);
162 :
163 0 : do {
164 0 : struct phr_header headers[ HEADER_CAP ];
165 0 : ulong num_headers = HEADER_CAP;
166 :
167 0 : int res = phr_parse_headers(
168 0 : (char const *)data, size,
169 0 : headers, &num_headers, last_len );
170 0 : if ( res > 0 ) {
171 0 : assert( (ulong) res <= size) ;
172 0 : }
173 0 : } while(0);
174 0 : }
175 0 : }
176 :
177 3 : void fuzz_phr_decode_chunked(uchar const * data, ulong size) {
178 3 : if (size >= 2) {
179 3 : struct phr_chunked_decoder decoder;
180 3 : memset(&decoder, 0, sizeof(struct phr_chunked_decoder));
181 3 : decoder._state = (char) (data[0] % 6);
182 3 : decoder.consume_trailer = (char) data[1];
183 :
184 3 : size_t buf_sz = size - 2;
185 3 : if (buf_sz > 0) {
186 3 : char *buf = malloc(buf_sz);
187 3 : memcpy(buf, data + 2, buf_sz);
188 :
189 3 : do {
190 3 : phr_decode_chunked(&decoder, buf, &buf_sz);
191 3 : } while(0);
192 :
193 3 : free(buf);
194 3 : }
195 3 : }
196 3 : }
197 :
198 : int
199 : LLVMFuzzerTestOneInput( uchar const * data,
200 3 : ulong size ) {
201 :
202 : /* parse request in one go */
203 :
204 3 : if (size >= 1) {
205 3 : uchar action = data[0] % 4;
206 3 : switch(action) {
207 0 : case 0:
208 0 : fuzz_request(data + 1, size - 1);
209 0 : break;
210 0 : case 1:
211 0 : fuzz_response(data + 1, size - 1);
212 0 : break;
213 0 : case 2:
214 0 : fuzz_headers(data + 1, size - 1);
215 0 : break;
216 3 : case 3:
217 3 : fuzz_phr_decode_chunked(data + 1, size - 1);
218 3 : break;
219 3 : }
220 3 : }
221 :
222 :
223 :
224 3 : FD_FUZZ_MUST_BE_COVERED;
225 3 : return 0;
226 3 : }
|