Coverage Report

Created: 2026-07-30 07:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/curl_fuzzer/curl_fuzzer_callback.cc
Line
Count
Source
1
/***************************************************************************
2
 *                                  _   _ ____  _
3
 *  Project                     ___| | | |  _ \| |
4
 *                             / __| | | | |_) | |
5
 *                            | (__| |_| |  _ <| |___
6
 *                             \___|\___/|_| \_\_____|
7
 *
8
 * Copyright (C) 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
23
#include <fcntl.h>
24
#include <string.h>
25
#include <unistd.h>
26
#include <sys/un.h>
27
#include <curl/curl.h>
28
#include "curl_fuzzer.h"
29
30
/**
31
 * Define a macro which checks to see that allocated file descriptors are
32
 * valid and won't cause issue with FD_SETs.  Taken from lib/select.h
33
 */
34
332k
#define FUZZ_VALID_SOCK(s) (((s) >= 0) && ((s) < FD_SETSIZE))
35
36
/**
37
 * Function for providing a socket to CURL already primed with data.
38
 */
39
curl_socket_t fuzz_open_socket(void *ptr,
40
                               curlsocktype purpose,
41
                               struct curl_sockaddr *address)
42
111k
{
43
111k
  FUZZ_DATA *fuzz = (FUZZ_DATA *)ptr;
44
111k
  int fds[2];
45
111k
  int flags;
46
111k
  int status;
47
111k
  const uint8_t *data;
48
111k
  size_t data_len;
49
111k
  struct sockaddr_un client_addr;
50
111k
  FUZZ_SOCKET_MANAGER *sman;
51
52
  /* Handle unused parameters */
53
111k
  (void)purpose;
54
111k
  (void)address;
55
56
111k
  if(fuzz->sockman[0].fd_state != FUZZ_SOCK_CLOSED &&
57
5.60k
     fuzz->sockman[1].fd_state != FUZZ_SOCK_CLOSED) {
58
    /* Both sockets have already been opened. */
59
377
    return CURL_SOCKET_BAD;
60
377
  }
61
110k
  else if(fuzz->sockman[0].fd_state != FUZZ_SOCK_CLOSED) {
62
5.22k
    sman = &fuzz->sockman[1];
63
5.22k
  }
64
105k
  else {
65
105k
    FV_PRINTF(fuzz, "FUZZ: Using socket manager 0 \n");
66
105k
    sman = &fuzz->sockman[0];
67
105k
  }
68
110k
  FV_PRINTF(fuzz, "FUZZ[%d]: Using socket manager %d \n",
69
110k
            sman->index,
70
110k
            sman->index);
71
72
110k
  if(socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) {
73
    /* Failed to create a pair of sockets. */
74
0
    return CURL_SOCKET_BAD;
75
0
  }
76
77
110k
  if(!FUZZ_VALID_SOCK(fds[0]) || !FUZZ_VALID_SOCK(fds[1])) {
78
    /* One or more of the file descriptors is too large to fit in an fd_set,
79
       so reject it here. Print out a message because this ought to be quite
80
       rare. */
81
0
    printf("FUZZ[%d]: Not using file descriptors %d,%d as FD_SETSIZE is %d\n",
82
0
           sman->index,
83
0
           fds[0],
84
0
           fds[1],
85
0
           FD_SETSIZE);
86
87
    /* Close the file descriptors so they don't leak. */
88
0
    close(fds[0]);
89
0
    close(fds[1]);
90
91
0
    return CURL_SOCKET_BAD;
92
0
  }
93
94
  /* Make both ends non-blocking. The curl end (fds[1]) must be non-blocking
95
     so that a large send() from the protocol layer (e.g. a 1MB DICT URL)
96
     cannot stall indefinitely when the kernel socket buffer fills up. */
97
110k
  flags = fcntl(fds[0], F_GETFL, 0);
98
110k
  status = fcntl(fds[0], F_SETFL, flags | O_NONBLOCK);
99
100
110k
  if(status == -1) {
101
0
    close(fds[0]);
102
0
    close(fds[1]);
103
0
    return CURL_SOCKET_BAD;
104
0
  }
105
106
110k
  flags = fcntl(fds[1], F_GETFL, 0);
107
110k
  status = fcntl(fds[1], F_SETFL, flags | O_NONBLOCK);
108
109
110k
  if(status == -1) {
110
0
    close(fds[0]);
111
0
    close(fds[1]);
112
0
    return CURL_SOCKET_BAD;
113
0
  }
114
115
  /* At this point, the file descriptors in hand should be good enough to
116
     work with. */
117
110k
  sman->fd = fds[0];
118
110k
  sman->fd_state = FUZZ_SOCK_OPEN;
119
120
  /* If the server should be sending data immediately, send it here. */
121
110k
  data = sman->responses[0].data;
122
110k
  data_len = sman->responses[0].data_len;
123
124
110k
  if(data != NULL) {
125
24.0k
    FV_PRINTF(fuzz, "FUZZ[%d]: Sending initial response \n", sman->index);
126
127
24.0k
    if(write(sman->fd, data, data_len) != (ssize_t)data_len) {
128
      /* Close the file descriptors so they don't leak. */
129
2
      close(sman->fd);
130
2
      sman->fd = -1;
131
132
2
      close(fds[1]);
133
134
      /* Failed to write all of the response data. */
135
2
      return CURL_SOCKET_BAD;
136
2
    }
137
24.0k
  }
138
139
  /* Check to see if the socket should be shut down immediately. */
140
110k
  if(sman->responses[1].data == NULL) {
141
85.5k
    FV_PRINTF(fuzz,
142
85.5k
              "FUZZ[%d]: Shutting down server socket: %d \n",
143
85.5k
              sman->index,
144
85.5k
              sman->fd);
145
85.5k
    shutdown(sman->fd, SHUT_WR);
146
85.5k
    sman->fd_state = FUZZ_SOCK_SHUTDOWN;
147
85.5k
  }
148
149
  /* Return the other half of the socket pair. */
150
110k
  return fds[1];
151
110k
}
152
153
/**
154
 * Callback function for setting socket options on the sockets created by
155
 * fuzz_open_socket. In our testbed the sockets are "already connected".
156
 */
157
int fuzz_sockopt_callback(void *ptr,
158
                          curl_socket_t curlfd,
159
                          curlsocktype purpose)
160
110k
{
161
110k
  (void)ptr;
162
110k
  (void)curlfd;
163
110k
  (void)purpose;
164
165
110k
  return CURL_SOCKOPT_ALREADY_CONNECTED;
166
110k
}
167
168
/**
169
 * Callback function for doing data uploads.
170
 */
171
size_t fuzz_read_callback(char *buffer,
172
                          size_t size,
173
                          size_t nitems,
174
                          void *ptr)
175
762
{
176
762
  FUZZ_DATA *fuzz = (FUZZ_DATA *)ptr;
177
762
  size_t remaining_data;
178
762
  size_t buffer_size = size * nitems;
179
180
  /* If no upload data has been specified, then return an error code. */
181
762
  if(fuzz->upload1_data_len == 0) {
182
    /* No data to upload */
183
77
    return CURL_READFUNC_ABORT;
184
77
  }
185
186
  /* Work out how much data is remaining to upload. */
187
685
  remaining_data = fuzz->upload1_data_len - fuzz->upload1_data_written;
188
189
  /* Respect the buffer size that libcurl is giving us! */
190
685
  if(remaining_data > buffer_size) {
191
98
    remaining_data = buffer_size;
192
98
  }
193
194
685
  if(remaining_data > 0) {
195
572
    FV_PRINTF(fuzz,
196
572
              "FUZZ: Uploading %zu bytes from position %zu \n",
197
572
              remaining_data,
198
572
              fuzz->upload1_data_written);
199
200
    /* Send the upload data. */
201
572
    memcpy(buffer,
202
572
           &fuzz->upload1_data[fuzz->upload1_data_written],
203
572
           remaining_data);
204
205
    /* Increase the count of written data */
206
572
    fuzz->upload1_data_written += remaining_data;
207
572
  }
208
209
685
  return remaining_data;
210
762
}
211
212
/**
213
 * Callback function for handling data output quietly.
214
 */
215
size_t fuzz_write_callback(void *contents,
216
                           size_t size,
217
                           size_t nmemb,
218
                           void *ptr)
219
744k
{
220
744k
  size_t total = size * nmemb;
221
744k
  FUZZ_DATA *fuzz = (FUZZ_DATA *)ptr;
222
744k
  size_t copy_len = total;
223
224
  /* Restrict copy_len to at most TEMP_WRITE_ARRAY_SIZE. */
225
744k
  if(copy_len > TEMP_WRITE_ARRAY_SIZE) {
226
360k
    copy_len = TEMP_WRITE_ARRAY_SIZE;
227
360k
  }
228
229
  /* Copy bytes to the temp store just to ensure the parameters are
230
     exercised. */
231
744k
  memcpy(fuzz->write_array, contents, copy_len);
232
233
  /* Add on the total to the count. If it exceeds the maximum then return
234
     zero to the caller so that the transfer is terminated early. */
235
744k
  fuzz->written_data += total;
236
237
744k
  if(fuzz->written_data > MAXIMUM_WRITE_LENGTH) {
238
0
    FV_PRINTF(fuzz,
239
0
              "FUZZ: Exceeded maximum write length (%zu) \n",
240
0
              fuzz->written_data);
241
0
    total = 0;
242
0
  }
243
244
744k
  return total;
245
744k
}