Coverage Report

Created: 2023-12-08 06:48

/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.98k
{
33
  /* Reset the cursor. */
34
1.98k
  fuzz->state.data_pos = 0;
35
1.98k
  return fuzz_get_tlv_comn(fuzz, tlv);
36
1.98k
}
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
16.2k
{
44
  /* Advance the cursor by the full length of the previous TLV. */
45
16.2k
  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
16.2k
  if(fuzz->state.data_pos + sizeof(TLV_RAW) > fuzz->state.data_len) {
49
    /* No more TLVs to parse */
50
1.00k
    return TLV_RC_NO_MORE_TLVS;
51
1.00k
  }
52
53
15.2k
  return fuzz_get_tlv_comn(fuzz, tlv);
54
16.2k
}
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
17.2k
{
62
17.2k
  int rc = 0;
63
17.2k
  size_t data_offset;
64
17.2k
  TLV_RAW *raw;
65
66
  /* Start by casting the data stream to a TLV. */
67
17.2k
  raw = (TLV_RAW *)&fuzz->state.data[fuzz->state.data_pos];
68
17.2k
  data_offset = fuzz->state.data_pos + sizeof(TLV_RAW);
69
70
  /* Set the TLV values. */
71
17.2k
  tlv->type = to_u16(raw->raw_type);
72
17.2k
  tlv->length = to_u32(raw->raw_length);
73
17.2k
  tlv->value = &fuzz->state.data[data_offset];
74
75
17.2k
  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
17.2k
  uint64_t check_length = data_offset;
80
17.2k
  check_length += tlv->length;
81
82
17.2k
  uint64_t remaining_len = fuzz->state.data_len;
83
17.2k
  FV_PRINTF(fuzz, "Check length of data: %lu \n", check_length);
84
17.2k
  FV_PRINTF(fuzz, "Remaining length of data: %lu \n", remaining_len);
85
86
  /* Sanity check that the TLV length is ok. */
87
17.2k
  if(check_length > remaining_len) {
88
468
    FV_PRINTF(fuzz, "Returning TLV_RC_SIZE_ERROR\n");
89
468
    rc = TLV_RC_SIZE_ERROR;
90
468
  }
91
92
17.2k
  return rc;
93
17.2k
}
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
15.3k
{
100
15.3k
  int rc;
101
15.3k
  char *tmp = NULL;
102
15.3k
  uint32_t tmp_u32;
103
15.3k
  curl_slist *new_list;
104
105
15.3k
  switch(tlv->type) {
106
    /* The pointers in response TLVs will always be valid as long as the fuzz
107
       data is in scope, which is the entirety of this file. */
108
205
    FRESPONSETLV(&fuzz->sockman[0], TLV_TYPE_RESPONSE0, 0);
109
197
    FRESPONSETLV(&fuzz->sockman[0], TLV_TYPE_RESPONSE1, 1);
110
194
    FRESPONSETLV(&fuzz->sockman[0], TLV_TYPE_RESPONSE2, 2);
111
196
    FRESPONSETLV(&fuzz->sockman[0], TLV_TYPE_RESPONSE3, 3);
112
213
    FRESPONSETLV(&fuzz->sockman[0], TLV_TYPE_RESPONSE4, 4);
113
199
    FRESPONSETLV(&fuzz->sockman[0], TLV_TYPE_RESPONSE5, 5);
114
195
    FRESPONSETLV(&fuzz->sockman[0], TLV_TYPE_RESPONSE6, 6);
115
206
    FRESPONSETLV(&fuzz->sockman[0], TLV_TYPE_RESPONSE7, 7);
116
226
    FRESPONSETLV(&fuzz->sockman[0], TLV_TYPE_RESPONSE8, 8);
117
195
    FRESPONSETLV(&fuzz->sockman[0], TLV_TYPE_RESPONSE9, 9);
118
194
    FRESPONSETLV(&fuzz->sockman[0], TLV_TYPE_RESPONSE10, 10);
119
120
194
    FRESPONSETLV(&fuzz->sockman[1], TLV_TYPE_SECOND_RESPONSE0, 0);
121
194
    FRESPONSETLV(&fuzz->sockman[1], TLV_TYPE_SECOND_RESPONSE1, 1);
122
123
23
    case TLV_TYPE_UPLOAD1:
124
      /* The pointers in the TLV will always be valid as long as the fuzz data
125
         is in scope, which is the entirety of this file. */
126
127
23
      FCHECK_OPTION_UNSET(fuzz, CURLOPT_UPLOAD);
128
129
22
      fuzz->upload1_data = tlv->value;
130
22
      fuzz->upload1_data_len = tlv->length;
131
132
22
      FSET_OPTION(fuzz, CURLOPT_UPLOAD, 1L);
133
22
      FSET_OPTION(fuzz,
134
22
                  CURLOPT_INFILESIZE_LARGE,
135
22
                  (curl_off_t)fuzz->upload1_data_len);
136
22
      break;
137
138
8.61k
    case TLV_TYPE_HEADER:
139
      /* Limit the number of headers that can be added to a message to prevent
140
         timeouts. */
141
8.61k
      if(fuzz->header_list_count >= TLV_MAX_NUM_CURLOPT_HEADER) {
142
0
        rc = 255;
143
0
        goto EXIT_LABEL;
144
0
      }
145
146
8.61k
      tmp = fuzz_tlv_to_string(tlv);
147
8.61k
      if (tmp == NULL) {
148
        // keep on despite allocation failure
149
0
        break;
150
0
      }
151
8.61k
      new_list = curl_slist_append(fuzz->header_list, tmp);
152
8.61k
      if (new_list == NULL) {
153
0
        break;
154
0
      }
155
8.61k
      fuzz->header_list = new_list;
156
8.61k
      fuzz->header_list_count++;
157
8.61k
      break;
158
159
677
    case TLV_TYPE_MAIL_RECIPIENT:
160
      /* Limit the number of headers that can be added to a message to prevent
161
         timeouts. */
162
677
      if(fuzz->header_list_count >= TLV_MAX_NUM_CURLOPT_HEADER) {
163
0
        rc = 255;
164
0
        goto EXIT_LABEL;
165
0
      }
166
677
      tmp = fuzz_tlv_to_string(tlv);
167
677
      if (tmp == NULL) {
168
        // keep on despite allocation failure
169
0
        break;
170
0
      }
171
677
      new_list = curl_slist_append(fuzz->mail_recipients_list, tmp);
172
677
      if (new_list != NULL) {
173
677
        fuzz->mail_recipients_list = new_list;
174
677
        fuzz->header_list_count++;
175
677
      }
176
677
      break;
177
178
2.61k
    case TLV_TYPE_MIME_PART:
179
2.61k
      if(fuzz->mime == NULL) {
180
158
        fuzz->mime = curl_mime_init(fuzz->easy);
181
158
      }
182
183
2.61k
      fuzz->part = curl_mime_addpart(fuzz->mime);
184
185
      /* This TLV may have sub TLVs. */
186
2.61k
      fuzz_add_mime_part(tlv, fuzz->part);
187
188
2.61k
      break;
189
190
8
    case TLV_TYPE_POSTFIELDS:
191
8
      FCHECK_OPTION_UNSET(fuzz, CURLOPT_POSTFIELDS);
192
7
      fuzz->postfields = fuzz_tlv_to_string(tlv);
193
7
      FSET_OPTION(fuzz, CURLOPT_POSTFIELDS, fuzz->postfields);
194
7
      break;
195
196
30
    case TLV_TYPE_HTTPPOSTBODY:
197
30
      FCHECK_OPTION_UNSET(fuzz, CURLOPT_HTTPPOST);
198
29
      fuzz_setup_http_post(fuzz, tlv);
199
29
      FSET_OPTION(fuzz, CURLOPT_HTTPPOST, fuzz->httppost);
200
29
      break;
201
202
    /* Define a set of u32 options. */
203
142
    FU32TLV(fuzz, TLV_TYPE_HTTPAUTH, CURLOPT_HTTPAUTH);
204
132
    FU32TLV(fuzz, TLV_TYPE_OPTHEADER, CURLOPT_HEADER);
205
136
    FU32TLV(fuzz, TLV_TYPE_NOBODY, CURLOPT_NOBODY);
206
130
    FU32TLV(fuzz, TLV_TYPE_FOLLOWLOCATION, CURLOPT_FOLLOWLOCATION);
207
133
    FU32TLV(fuzz, TLV_TYPE_WILDCARDMATCH, CURLOPT_WILDCARDMATCH);
208
136
    FU32TLV(fuzz, TLV_TYPE_RTSP_REQUEST, CURLOPT_RTSP_REQUEST);
209
34
    FU32TLV(fuzz, TLV_TYPE_RTSP_CLIENT_CSEQ, CURLOPT_RTSP_CLIENT_CSEQ);
210
115
    FU32TLV(fuzz, TLV_TYPE_HTTP_VERSION, CURLOPT_HTTP_VERSION);
211
160
    FU32TLV(fuzz, TLV_TYPE_NETRC, CURLOPT_NETRC);
212
33
    FU32TLV(fuzz, TLV_TYPE_WS_OPTIONS, CURLOPT_WS_OPTIONS);
213
124
    FU32TLV(fuzz, TLV_TYPE_CONNECT_ONLY, CURLOPT_CONNECT_ONLY);
214
133
    FU32TLV(fuzz, TLV_TYPE_POST, CURLOPT_POST);
215
216
    /* Define a set of singleton TLVs - they can only have their value set once
217
       and all follow the same pattern. */
218
49
    FSINGLETONTLV(fuzz, TLV_TYPE_URL, CURLOPT_URL);
219
15
    FSINGLETONTLV(fuzz, TLV_TYPE_DOH_URL, CURLOPT_DOH_URL);
220
11
    FSINGLETONTLV(fuzz, TLV_TYPE_USERNAME, CURLOPT_USERNAME);
221
11
    FSINGLETONTLV(fuzz, TLV_TYPE_PASSWORD, CURLOPT_PASSWORD);
222
19
    FSINGLETONTLV(fuzz, TLV_TYPE_COOKIE, CURLOPT_COOKIE);
223
13
    FSINGLETONTLV(fuzz, TLV_TYPE_RANGE, CURLOPT_RANGE);
224
19
    FSINGLETONTLV(fuzz, TLV_TYPE_CUSTOMREQUEST, CURLOPT_CUSTOMREQUEST);
225
21
    FSINGLETONTLV(fuzz, TLV_TYPE_MAIL_FROM, CURLOPT_MAIL_FROM);
226
31
    FSINGLETONTLV(fuzz, TLV_TYPE_ACCEPTENCODING, CURLOPT_ACCEPT_ENCODING);
227
13
    FSINGLETONTLV(fuzz, TLV_TYPE_RTSP_SESSION_ID, CURLOPT_RTSP_SESSION_ID);
228
21
    FSINGLETONTLV(fuzz, TLV_TYPE_RTSP_STREAM_URI, CURLOPT_RTSP_STREAM_URI);
229
15
    FSINGLETONTLV(fuzz, TLV_TYPE_RTSP_TRANSPORT, CURLOPT_RTSP_TRANSPORT);
230
15
    FSINGLETONTLV(fuzz, TLV_TYPE_MAIL_AUTH, CURLOPT_MAIL_AUTH);
231
19
    FSINGLETONTLV(fuzz, TLV_TYPE_LOGIN_OPTIONS, CURLOPT_LOGIN_OPTIONS);
232
13
    FSINGLETONTLV(fuzz, TLV_TYPE_XOAUTH2_BEARER, CURLOPT_XOAUTH2_BEARER);
233
37
    FSINGLETONTLV(fuzz, TLV_TYPE_USERPWD, CURLOPT_USERPWD);
234
21
    FSINGLETONTLV(fuzz, TLV_TYPE_USERAGENT, CURLOPT_USERAGENT);
235
6
    FSINGLETONTLV(fuzz, TLV_TYPE_SSH_HOST_PUBLIC_KEY_SHA256, CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256);
236
237
3
    default:
238
      /* The fuzzer generates lots of unknown TLVs - we don't want these in the
239
         corpus so we reject any unknown TLVs. */
240
3
      rc = 127;
241
3
      goto EXIT_LABEL;
242
0
      break;
243
15.3k
  }
244
245
15.0k
  rc = 0;
246
247
15.3k
EXIT_LABEL:
248
249
15.3k
  fuzz_free((void **)&tmp);
250
251
15.3k
  return rc;
252
15.0k
}
253
254
/**
255
 * Converts a TLV data and length into an allocated string.
256
 */
257
char *fuzz_tlv_to_string(TLV *tlv)
258
9.80k
{
259
9.80k
  char *tlvstr;
260
261
  /* Allocate enough space, plus a null terminator */
262
9.80k
  tlvstr = (char *)malloc(tlv->length + 1);
263
264
9.80k
  if(tlvstr != NULL) {
265
9.80k
    memcpy(tlvstr, tlv->value, tlv->length);
266
9.80k
    tlvstr[tlv->length] = 0;
267
9.80k
  }
268
269
9.80k
  return tlvstr;
270
9.80k
}
271
272
/* set up for CURLOPT_HTTPPOST, an alternative API to CURLOPT_MIMEPOST */
273
void fuzz_setup_http_post(FUZZ_DATA *fuzz, TLV *tlv)
274
29
{
275
29
  if (fuzz->httppost == NULL) {
276
29
    struct curl_httppost *post = NULL;
277
29
    struct curl_httppost *last = NULL;
278
279
29
    fuzz->post_body = fuzz_tlv_to_string(tlv);
280
29
    if (fuzz->post_body == NULL) {
281
0
      return;
282
0
    }
283
284
    /* This is just one of several possible entrypoints to
285
     * the HTTPPOST API. see https://curl.se/libcurl/c/curl_formadd.html
286
     * for lots of others which could be added here.
287
     */
288
29
    curl_formadd(&post, &last,
289
29
     CURLFORM_COPYNAME, FUZZ_HTTPPOST_NAME,
290
29
     CURLFORM_PTRCONTENTS, fuzz->post_body,
291
29
     CURLFORM_CONTENTLEN, (curl_off_t) strlen(fuzz->post_body),
292
29
     CURLFORM_END);
293
294
29
    fuzz->last_post_part = last;
295
29
    fuzz->httppost = post;
296
29
  }
297
298
29
  return;
299
29
}
300
301
/**
302
 * Extract the values from the TLV.
303
 */
304
int fuzz_add_mime_part(TLV *src_tlv, curl_mimepart *part)
305
2.61k
{
306
2.61k
  FUZZ_DATA part_fuzz;
307
2.61k
  TLV tlv;
308
2.61k
  int rc = 0;
309
2.61k
  int tlv_rc;
310
311
2.61k
  memset(&part_fuzz, 0, sizeof(FUZZ_DATA));
312
313
2.61k
  if(src_tlv->length < sizeof(TLV_RAW)) {
314
    /* Not enough data for a single TLV - don't continue */
315
1.67k
    goto EXIT_LABEL;
316
1.67k
  }
317
318
  /* Set up the state parser */
319
936
  part_fuzz.state.data = src_tlv->value;
320
936
  part_fuzz.state.data_len = src_tlv->length;
321
322
936
  for(tlv_rc = fuzz_get_first_tlv(&part_fuzz, &tlv);
323
2.19k
      tlv_rc == 0;
324
1.45k
      tlv_rc = fuzz_get_next_tlv(&part_fuzz, &tlv)) {
325
326
    /* Have the TLV in hand. Parse the TLV. */
327
1.45k
    rc = fuzz_parse_mime_tlv(part, &tlv);
328
329
1.45k
    if(rc != 0) {
330
      /* Failed to parse the TLV. Can't continue. */
331
200
      goto EXIT_LABEL;
332
200
    }
333
1.45k
  }
334
335
736
  if(tlv_rc != TLV_RC_NO_MORE_TLVS) {
336
    /* A TLV call failed. Can't continue. */
337
391
    goto EXIT_LABEL;
338
391
  }
339
340
2.61k
EXIT_LABEL:
341
342
2.61k
  return(rc);
343
736
}
344
345
/**
346
 * Do different actions on the mime part for different received TLVs.
347
 */
348
int fuzz_parse_mime_tlv(curl_mimepart *part, TLV *tlv)
349
1.45k
{
350
1.45k
  int rc;
351
1.45k
  char *tmp;
352
353
1.45k
  switch(tlv->type) {
354
304
    case TLV_TYPE_MIME_PART_NAME:
355
304
      tmp = fuzz_tlv_to_string(tlv);
356
304
      curl_mime_name(part, tmp);
357
304
      fuzz_free((void **)&tmp);
358
304
      break;
359
360
955
    case TLV_TYPE_MIME_PART_DATA:
361
955
      curl_mime_data(part, (const char *)tlv->value, tlv->length);
362
955
      break;
363
364
200
    default:
365
      /* The fuzzer generates lots of unknown TLVs - we don't want these in the
366
         corpus so we reject any unknown TLVs. */
367
200
      rc = 255;
368
200
      goto EXIT_LABEL;
369
0
      break;
370
1.45k
  }
371
372
1.25k
  rc = 0;
373
374
1.45k
EXIT_LABEL:
375
376
1.45k
  return rc;
377
1.25k
}