Coverage Report

Created: 2022-12-01 06:42

/src/curl_fuzzer/curl_fuzzer_tlv.cc
Line
Count
Source (jump to first uncovered line)
1
/***************************************************************************
2
 *                                  _   _ ____  _
3
 *  Project                     ___| | | |  _ \| |
4
 *                             / __| | | | |_) | |
5
 *                            | (__| |_| |  _ <| |___
6
 *                             \___|\___/|_| \_\_____|
7
 *
8
 * Copyright (C) 2017, Max Dymond, <cmeister2@gmail.com>, et al.
9
 *
10
 * This software is licensed as described in the file COPYING, which
11
 * you should have received as part of this distribution. The terms
12
 * are also available at https://curl.se/docs/copyright.html.
13
 *
14
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15
 * copies of the Software, and permit persons to whom the Software is
16
 * furnished to do so, under the terms of the COPYING file.
17
 *
18
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19
 * KIND, either express or implied.
20
 *
21
 ***************************************************************************/
22
#include <stdlib.h>
23
#include <string.h>
24
#include <curl/curl.h>
25
#include "curl_fuzzer.h"
26
27
/**
28
 * TLV access function - gets the first TLV from a data stream.
29
 */
30
int fuzz_get_first_tlv(FUZZ_DATA *fuzz,
31
                       TLV *tlv)
32
1.74M
{
33
  /* Reset the cursor. */
34
1.74M
  fuzz->state.data_pos = 0;
35
1.74M
  return fuzz_get_tlv_comn(fuzz, tlv);
36
1.74M
}
37
38
/**
39
 * TLV access function - gets the next TLV from a data stream.
40
*/
41
int fuzz_get_next_tlv(FUZZ_DATA *fuzz,
42
                      TLV *tlv)
43
9.53M
{
44
  /* Advance the cursor by the full length of the previous TLV. */
45
9.53M
  fuzz->state.data_pos += sizeof(TLV_RAW) + tlv->length;
46
47
  /* Work out if there's a TLV's worth of data to read */
48
9.53M
  if(fuzz->state.data_pos + sizeof(TLV_RAW) > fuzz->state.data_len) {
49
    /* No more TLVs to parse */
50
1.60M
    return TLV_RC_NO_MORE_TLVS;
51
1.60M
  }
52
53
7.93M
  return fuzz_get_tlv_comn(fuzz, tlv);
54
9.53M
}
55
56
/**
57
 * Common TLV function for accessing TLVs in a data stream.
58
 */
59
int fuzz_get_tlv_comn(FUZZ_DATA *fuzz,
60
                      TLV *tlv)
61
9.67M
{
62
9.67M
  int rc = 0;
63
9.67M
  size_t data_offset;
64
9.67M
  TLV_RAW *raw;
65
66
  /* Start by casting the data stream to a TLV. */
67
9.67M
  raw = (TLV_RAW *)&fuzz->state.data[fuzz->state.data_pos];
68
9.67M
  data_offset = fuzz->state.data_pos + sizeof(TLV_RAW);
69
70
  /* Set the TLV values. */
71
9.67M
  tlv->type = to_u16(raw->raw_type);
72
9.67M
  tlv->length = to_u32(raw->raw_length);
73
9.67M
  tlv->value = &fuzz->state.data[data_offset];
74
75
9.67M
  FV_PRINTF(fuzz, "TLV: type %x length %u\n", tlv->type, tlv->length);
76
77
  /* Use uint64s to verify lengths of TLVs so that overflow problems don't
78
     matter. */
79
9.67M
  uint64_t check_length = data_offset;
80
9.67M
  check_length += tlv->length;
81
82
9.67M
  uint64_t remaining_len = fuzz->state.data_len;
83
9.67M
  FV_PRINTF(fuzz, "Check length of data: %lu \n", check_length);
84
9.67M
  FV_PRINTF(fuzz, "Remaining length of data: %lu \n", remaining_len);
85
86
  /* Sanity check that the TLV length is ok. */
87
9.67M
  if(check_length > remaining_len) {
88
90.7k
    FV_PRINTF(fuzz, "Returning TLV_RC_SIZE_ERROR\n");
89
90.7k
    rc = TLV_RC_SIZE_ERROR;
90
90.7k
  }
91
92
9.67M
  return rc;
93
9.67M
}
94
95
/**
96
 * Do different actions on the CURL handle for different received TLVs.
97
 */
98
int fuzz_parse_tlv(FUZZ_DATA *fuzz, TLV *tlv)
99
8.00M
{
100
8.00M
  int rc;
101
8.00M
  char *tmp = NULL;
102
8.00M
  uint32_t tmp_u32;
103
104
8.00M
  switch(tlv->type) {
105
    /* The pointers in response TLVs will always be valid as long as the fuzz
106
       data is in scope, which is the entirety of this file. */
107
24.3k
    FRESPONSETLV(&fuzz->sockman[0], TLV_TYPE_RESPONSE0, 0);
108
19.8k
    FRESPONSETLV(&fuzz->sockman[0], TLV_TYPE_RESPONSE1, 1);
109
7.86k
    FRESPONSETLV(&fuzz->sockman[0], TLV_TYPE_RESPONSE2, 2);
110
33.1k
    FRESPONSETLV(&fuzz->sockman[0], TLV_TYPE_RESPONSE3, 3);
111
8.52k
    FRESPONSETLV(&fuzz->sockman[0], TLV_TYPE_RESPONSE4, 4);
112
7.13k
    FRESPONSETLV(&fuzz->sockman[0], TLV_TYPE_RESPONSE5, 5);
113
7.19k
    FRESPONSETLV(&fuzz->sockman[0], TLV_TYPE_RESPONSE6, 6);
114
5.25k
    FRESPONSETLV(&fuzz->sockman[0], TLV_TYPE_RESPONSE7, 7);
115
4.15k
    FRESPONSETLV(&fuzz->sockman[0], TLV_TYPE_RESPONSE8, 8);
116
3.94k
    FRESPONSETLV(&fuzz->sockman[0], TLV_TYPE_RESPONSE9, 9);
117
4.41k
    FRESPONSETLV(&fuzz->sockman[0], TLV_TYPE_RESPONSE10, 10);
118
119
4.27k
    FRESPONSETLV(&fuzz->sockman[1], TLV_TYPE_SECOND_RESPONSE0, 0);
120
4.17k
    FRESPONSETLV(&fuzz->sockman[1], TLV_TYPE_SECOND_RESPONSE1, 1);
121
122
1.77k
    case TLV_TYPE_UPLOAD1:
123
      /* The pointers in the TLV will always be valid as long as the fuzz data
124
         is in scope, which is the entirety of this file. */
125
126
1.77k
      FCHECK_OPTION_UNSET(fuzz, CURLOPT_UPLOAD);
127
128
1.75k
      fuzz->upload1_data = tlv->value;
129
1.75k
      fuzz->upload1_data_len = tlv->length;
130
131
1.75k
      FSET_OPTION(fuzz, CURLOPT_UPLOAD, 1L);
132
1.75k
      FSET_OPTION(fuzz,
133
1.75k
                  CURLOPT_INFILESIZE_LARGE,
134
1.75k
                  (curl_off_t)fuzz->upload1_data_len);
135
1.75k
      break;
136
137
270k
    case TLV_TYPE_HEADER:
138
      /* Limit the number of headers that can be added to a message to prevent
139
         timeouts. */
140
270k
      if(fuzz->header_list_count >= TLV_MAX_NUM_CURLOPT_HEADER) {
141
19
        rc = 255;
142
19
        goto EXIT_LABEL;
143
19
      }
144
145
270k
      tmp = fuzz_tlv_to_string(tlv);
146
270k
      fuzz->header_list = curl_slist_append(fuzz->header_list, tmp);
147
270k
      fuzz->header_list_count++;
148
270k
      break;
149
150
197k
    case TLV_TYPE_MAIL_RECIPIENT:
151
197k
      tmp = fuzz_tlv_to_string(tlv);
152
197k
      fuzz->mail_recipients_list =
153
197k
                            curl_slist_append(fuzz->mail_recipients_list, tmp);
154
197k
      break;
155
156
7.29M
    case TLV_TYPE_MIME_PART:
157
7.29M
      if(fuzz->mime == NULL) {
158
6.92k
        fuzz->mime = curl_mime_init(fuzz->easy);
159
6.92k
      }
160
161
7.29M
      fuzz->part = curl_mime_addpart(fuzz->mime);
162
163
      /* This TLV may have sub TLVs. */
164
7.29M
      fuzz_add_mime_part(tlv, fuzz->part);
165
166
7.29M
      break;
167
168
1.03k
    case TLV_TYPE_POSTFIELDS:
169
1.03k
      FCHECK_OPTION_UNSET(fuzz, CURLOPT_POSTFIELDS);
170
1.01k
      fuzz->postfields = fuzz_tlv_to_string(tlv);
171
1.01k
      FSET_OPTION(fuzz, CURLOPT_POSTFIELDS, fuzz->postfields);
172
1.01k
      break;
173
174
707
    case TLV_TYPE_HTTPPOSTBODY:
175
707
      FCHECK_OPTION_UNSET(fuzz, CURLOPT_HTTPPOST);
176
688
      fuzz_setup_http_post(fuzz, tlv);
177
688
      FSET_OPTION(fuzz, CURLOPT_HTTPPOST, fuzz->httppost);
178
688
      break;
179
180
    /* Define a set of u32 options. */
181
12.4k
    FU32TLV(fuzz, TLV_TYPE_HTTPAUTH, CURLOPT_HTTPAUTH);
182
2.54k
    FU32TLV(fuzz, TLV_TYPE_OPTHEADER, CURLOPT_HEADER);
183
4.62k
    FU32TLV(fuzz, TLV_TYPE_NOBODY, CURLOPT_NOBODY);
184
7.73k
    FU32TLV(fuzz, TLV_TYPE_FOLLOWLOCATION, CURLOPT_FOLLOWLOCATION);
185
3.73k
    FU32TLV(fuzz, TLV_TYPE_WILDCARDMATCH, CURLOPT_WILDCARDMATCH);
186
7.24k
    FU32TLV(fuzz, TLV_TYPE_RTSP_REQUEST, CURLOPT_RTSP_REQUEST);
187
750
    FU32TLV(fuzz, TLV_TYPE_RTSP_CLIENT_CSEQ, CURLOPT_RTSP_CLIENT_CSEQ);
188
25.0k
    FU32TLV(fuzz, TLV_TYPE_HTTP_VERSION, CURLOPT_HTTP_VERSION);
189
3.20k
    FU32TLV(fuzz, TLV_TYPE_NETRC, CURLOPT_NETRC);
190
533
    FU32TLV(fuzz, TLV_TYPE_WS_OPTIONS, CURLOPT_WS_OPTIONS);
191
3.21k
    FU32TLV(fuzz, TLV_TYPE_CONNECT_ONLY, CURLOPT_CONNECT_ONLY);
192
1.89k
    FU32TLV(fuzz, TLV_TYPE_POST, CURLOPT_POST);
193
194
    /* Define a set of singleton TLVs - they can only have their value set once
195
       and all follow the same pattern. */
196
140k
    FSINGLETONTLV(fuzz, TLV_TYPE_URL, CURLOPT_URL);
197
322
    FSINGLETONTLV(fuzz, TLV_TYPE_DOH_URL, CURLOPT_DOH_URL);
198
4.68k
    FSINGLETONTLV(fuzz, TLV_TYPE_USERNAME, CURLOPT_USERNAME);
199
3.54k
    FSINGLETONTLV(fuzz, TLV_TYPE_PASSWORD, CURLOPT_PASSWORD);
200
844
    FSINGLETONTLV(fuzz, TLV_TYPE_COOKIE, CURLOPT_COOKIE);
201
1.77k
    FSINGLETONTLV(fuzz, TLV_TYPE_RANGE, CURLOPT_RANGE);
202
1.22k
    FSINGLETONTLV(fuzz, TLV_TYPE_CUSTOMREQUEST, CURLOPT_CUSTOMREQUEST);
203
847
    FSINGLETONTLV(fuzz, TLV_TYPE_MAIL_FROM, CURLOPT_MAIL_FROM);
204
976
    FSINGLETONTLV(fuzz, TLV_TYPE_ACCEPTENCODING, CURLOPT_ACCEPT_ENCODING);
205
299
    FSINGLETONTLV(fuzz, TLV_TYPE_RTSP_SESSION_ID, CURLOPT_RTSP_SESSION_ID);
206
316
    FSINGLETONTLV(fuzz, TLV_TYPE_RTSP_STREAM_URI, CURLOPT_RTSP_STREAM_URI);
207
287
    FSINGLETONTLV(fuzz, TLV_TYPE_RTSP_TRANSPORT, CURLOPT_RTSP_TRANSPORT);
208
373
    FSINGLETONTLV(fuzz, TLV_TYPE_MAIL_AUTH, CURLOPT_MAIL_AUTH);
209
300
    FSINGLETONTLV(fuzz, TLV_TYPE_LOGIN_OPTIONS, CURLOPT_LOGIN_OPTIONS);
210
465
    FSINGLETONTLV(fuzz, TLV_TYPE_XOAUTH2_BEARER, CURLOPT_XOAUTH2_BEARER);
211
1.31k
    FSINGLETONTLV(fuzz, TLV_TYPE_USERPWD, CURLOPT_USERPWD);
212
759
    FSINGLETONTLV(fuzz, TLV_TYPE_USERAGENT, CURLOPT_USERAGENT);
213
142
    FSINGLETONTLV(fuzz, TLV_TYPE_SSH_HOST_PUBLIC_KEY_SHA256, CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256);
214
215
118
    default:
216
      /* The fuzzer generates lots of unknown TLVs - we don't want these in the
217
         corpus so we reject any unknown TLVs. */
218
118
      rc = 127;
219
118
      goto EXIT_LABEL;
220
0
      break;
221
8.00M
  }
222
223
7.99M
  rc = 0;
224
225
8.00M
EXIT_LABEL:
226
227
8.00M
  fuzz_free((void **)&tmp);
228
229
8.00M
  return rc;
230
7.99M
}
231
232
/**
233
 * Converts a TLV data and length into an allocated string.
234
 */
235
char *fuzz_tlv_to_string(TLV *tlv)
236
2.06M
{
237
2.06M
  char *tlvstr;
238
239
  /* Allocate enough space, plus a null terminator */
240
2.06M
  tlvstr = (char *)malloc(tlv->length + 1);
241
242
2.06M
  if(tlvstr != NULL) {
243
2.06M
    memcpy(tlvstr, tlv->value, tlv->length);
244
2.06M
    tlvstr[tlv->length] = 0;
245
2.06M
  }
246
247
2.06M
  return tlvstr;
248
2.06M
}
249
250
/* set up for CURLOPT_HTTPPOST, an alternative API to CURLOPT_MIMEPOST */
251
void fuzz_setup_http_post(FUZZ_DATA *fuzz, TLV *tlv)
252
688
{
253
688
  if (fuzz->httppost == NULL) {
254
688
    struct curl_httppost *post = NULL;
255
688
    struct curl_httppost *last = NULL;
256
257
688
    fuzz->post_body = fuzz_tlv_to_string(tlv);
258
259
    /* This is just one of several possible entrypoints to
260
     * the HTTPPOST API. see https://curl.se/libcurl/c/curl_formadd.html
261
     * for lots of others which could be added here.
262
     */
263
688
    curl_formadd(&post, &last,
264
688
     CURLFORM_COPYNAME, FUZZ_HTTPPOST_NAME,
265
688
     CURLFORM_PTRCONTENTS, fuzz->post_body,
266
688
     CURLFORM_CONTENTLEN, (curl_off_t) strlen(fuzz->post_body),
267
688
     CURLFORM_END);
268
269
688
    fuzz->last_post_part = last;
270
688
    fuzz->httppost = post;
271
688
  }
272
273
688
  return;
274
688
}
275
276
/**
277
 * Extract the values from the TLV.
278
 */
279
int fuzz_add_mime_part(TLV *src_tlv, curl_mimepart *part)
280
7.29M
{
281
7.29M
  FUZZ_DATA part_fuzz;
282
7.29M
  TLV tlv;
283
7.29M
  int rc = 0;
284
7.29M
  int tlv_rc;
285
286
7.29M
  memset(&part_fuzz, 0, sizeof(FUZZ_DATA));
287
288
7.29M
  if(src_tlv->length < sizeof(TLV_RAW)) {
289
    /* Not enough data for a single TLV - don't continue */
290
5.63M
    goto EXIT_LABEL;
291
5.63M
  }
292
293
  /* Set up the state parser */
294
1.65M
  part_fuzz.state.data = src_tlv->value;
295
1.65M
  part_fuzz.state.data_len = src_tlv->length;
296
297
1.65M
  for(tlv_rc = fuzz_get_first_tlv(&part_fuzz, &tlv);
298
3.19M
      tlv_rc == 0;
299
1.65M
      tlv_rc = fuzz_get_next_tlv(&part_fuzz, &tlv)) {
300
301
    /* Have the TLV in hand. Parse the TLV. */
302
1.58M
    rc = fuzz_parse_mime_tlv(part, &tlv);
303
304
1.58M
    if(rc != 0) {
305
      /* Failed to parse the TLV. Can't continue. */
306
40.4k
      goto EXIT_LABEL;
307
40.4k
    }
308
1.58M
  }
309
310
1.61M
  if(tlv_rc != TLV_RC_NO_MORE_TLVS) {
311
    /* A TLV call failed. Can't continue. */
312
88.9k
    goto EXIT_LABEL;
313
88.9k
  }
314
315
7.29M
EXIT_LABEL:
316
317
7.29M
  return(rc);
318
1.61M
}
319
320
/**
321
 * Do different actions on the mime part for different received TLVs.
322
 */
323
int fuzz_parse_mime_tlv(curl_mimepart *part, TLV *tlv)
324
1.58M
{
325
1.58M
  int rc;
326
1.58M
  char *tmp;
327
328
1.58M
  switch(tlv->type) {
329
1.52M
    case TLV_TYPE_MIME_PART_NAME:
330
1.52M
      tmp = fuzz_tlv_to_string(tlv);
331
1.52M
      curl_mime_name(part, tmp);
332
1.52M
      fuzz_free((void **)&tmp);
333
1.52M
      break;
334
335
21.7k
    case TLV_TYPE_MIME_PART_DATA:
336
21.7k
      curl_mime_data(part, (const char *)tlv->value, tlv->length);
337
21.7k
      break;
338
339
40.4k
    default:
340
      /* The fuzzer generates lots of unknown TLVs - we don't want these in the
341
         corpus so we reject any unknown TLVs. */
342
40.4k
      rc = 255;
343
40.4k
      goto EXIT_LABEL;
344
0
      break;
345
1.58M
  }
346
347
1.54M
  rc = 0;
348
349
1.58M
EXIT_LABEL:
350
351
1.58M
  return rc;
352
1.54M
}