Coverage Report

Created: 2026-07-16 06:34

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
315k
#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
105k
{
43
105k
  FUZZ_DATA *fuzz = (FUZZ_DATA *)ptr;
44
105k
  int fds[2];
45
105k
  int flags;
46
105k
  int status;
47
105k
  const uint8_t *data;
48
105k
  size_t data_len;
49
105k
  struct sockaddr_un client_addr;
50
105k
  FUZZ_SOCKET_MANAGER *sman;
51
52
  /* Handle unused parameters */
53
105k
  (void)purpose;
54
105k
  (void)address;
55
56
105k
  if(fuzz->sockman[0].fd_state != FUZZ_SOCK_CLOSED &&
57
5.21k
     fuzz->sockman[1].fd_state != FUZZ_SOCK_CLOSED) {
58
    /* Both sockets have already been opened. */
59
360
    return CURL_SOCKET_BAD;
60
360
  }
61
105k
  else if(fuzz->sockman[0].fd_state != FUZZ_SOCK_CLOSED) {
62
4.85k
    sman = &fuzz->sockman[1];
63
4.85k
  }
64
100k
  else {
65
100k
    FV_PRINTF(fuzz, "FUZZ: Using socket manager 0 \n");
66
100k
    sman = &fuzz->sockman[0];
67
100k
  }
68
105k
  FV_PRINTF(fuzz, "FUZZ[%d]: Using socket manager %d \n",
69
105k
            sman->index,
70
105k
            sman->index);
71
72
105k
  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
105k
  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
105k
  flags = fcntl(fds[0], F_GETFL, 0);
98
105k
  status = fcntl(fds[0], F_SETFL, flags | O_NONBLOCK);
99
100
105k
  if(status == -1) {
101
0
    close(fds[0]);
102
0
    close(fds[1]);
103
0
    return CURL_SOCKET_BAD;
104
0
  }
105
106
105k
  flags = fcntl(fds[1], F_GETFL, 0);
107
105k
  status = fcntl(fds[1], F_SETFL, flags | O_NONBLOCK);
108
109
105k
  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
105k
  sman->fd = fds[0];
118
105k
  sman->fd_state = FUZZ_SOCK_OPEN;
119
120
  /* If the server should be sending data immediately, send it here. */
121
105k
  data = sman->responses[0].data;
122
105k
  data_len = sman->responses[0].data_len;
123
124
105k
  if(data != NULL) {
125
21.7k
    FV_PRINTF(fuzz, "FUZZ[%d]: Sending initial response \n", sman->index);
126
127
21.7k
    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
21.7k
  }
138
139
  /* Check to see if the socket should be shut down immediately. */
140
105k
  if(sman->responses[1].data == NULL) {
141
81.2k
    FV_PRINTF(fuzz,
142
81.2k
              "FUZZ[%d]: Shutting down server socket: %d \n",
143
81.2k
              sman->index,
144
81.2k
              sman->fd);
145
81.2k
    shutdown(sman->fd, SHUT_WR);
146
81.2k
    sman->fd_state = FUZZ_SOCK_SHUTDOWN;
147
81.2k
  }
148
149
  /* Return the other half of the socket pair. */
150
105k
  return fds[1];
151
105k
}
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
105k
{
161
105k
  (void)ptr;
162
105k
  (void)curlfd;
163
105k
  (void)purpose;
164
165
105k
  return CURL_SOCKOPT_ALREADY_CONNECTED;
166
105k
}
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
654
{
176
654
  FUZZ_DATA *fuzz = (FUZZ_DATA *)ptr;
177
654
  size_t remaining_data;
178
654
  size_t buffer_size = size * nitems;
179
180
  /* If no upload data has been specified, then return an error code. */
181
654
  if(fuzz->upload1_data_len == 0) {
182
    /* No data to upload */
183
60
    return CURL_READFUNC_ABORT;
184
60
  }
185
186
  /* Work out how much data is remaining to upload. */
187
594
  remaining_data = fuzz->upload1_data_len - fuzz->upload1_data_written;
188
189
  /* Respect the buffer size that libcurl is giving us! */
190
594
  if(remaining_data > buffer_size) {
191
64
    remaining_data = buffer_size;
192
64
  }
193
194
594
  if(remaining_data > 0) {
195
490
    FV_PRINTF(fuzz,
196
490
              "FUZZ: Uploading %zu bytes from position %zu \n",
197
490
              remaining_data,
198
490
              fuzz->upload1_data_written);
199
200
    /* Send the upload data. */
201
490
    memcpy(buffer,
202
490
           &fuzz->upload1_data[fuzz->upload1_data_written],
203
490
           remaining_data);
204
205
    /* Increase the count of written data */
206
490
    fuzz->upload1_data_written += remaining_data;
207
490
  }
208
209
594
  return remaining_data;
210
654
}
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
438k
{
220
438k
  size_t total = size * nmemb;
221
438k
  FUZZ_DATA *fuzz = (FUZZ_DATA *)ptr;
222
438k
  size_t copy_len = total;
223
224
  /* Restrict copy_len to at most TEMP_WRITE_ARRAY_SIZE. */
225
438k
  if(copy_len > TEMP_WRITE_ARRAY_SIZE) {
226
64.9k
    copy_len = TEMP_WRITE_ARRAY_SIZE;
227
64.9k
  }
228
229
  /* Copy bytes to the temp store just to ensure the parameters are
230
     exercised. */
231
438k
  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
438k
  fuzz->written_data += total;
236
237
438k
  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
438k
  return total;
245
438k
}