Coverage Report

Created: 2023-03-26 06:11

/src/curl_fuzzer/curl_fuzzer.cc
Line
Count
Source (jump to first uncovered line)
1
/***************************************************************************
2
 *                                  _   _ ____  _
3
 *  Project                     ___| | | |  _ \| |
4
 *                             / __| | | | |_) | |
5
 *                            | (__| |_| |  _ <| |___
6
 *                             \___|\___/|_| \_\_____|
7
 *
8
 * Copyright (C) 2017 - 2022, 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 <stdlib.h>
24
#include <signal.h>
25
#include <string.h>
26
#include <unistd.h>
27
#include <curl/curl.h>
28
#include "curl_fuzzer.h"
29
30
/**
31
 * Fuzzing entry point. This function is passed a buffer containing a test
32
 * case.  This test case should drive the CURL API into making a request.
33
 */
34
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
35
1.15k
{
36
1.15k
  int rc = 0;
37
1.15k
  int tlv_rc;
38
1.15k
  FUZZ_DATA fuzz;
39
1.15k
  TLV tlv;
40
41
  /* Ignore SIGPIPE errors. We'll handle the errors ourselves. */
42
1.15k
  signal(SIGPIPE, SIG_IGN);
43
44
  /* Have to set all fields to zero before getting to the terminate function */
45
1.15k
  memset(&fuzz, 0, sizeof(FUZZ_DATA));
46
47
1.15k
  if(size < sizeof(TLV_RAW)) {
48
    /* Not enough data for a single TLV - don't continue */
49
4
    goto EXIT_LABEL;
50
4
  }
51
52
  /* Try to initialize the fuzz data */
53
1.15k
  FTRY(fuzz_initialize_fuzz_data(&fuzz, data, size));
54
55
1.15k
  for(tlv_rc = fuzz_get_first_tlv(&fuzz, &tlv);
56
31.2k
      tlv_rc == 0;
57
30.4k
      tlv_rc = fuzz_get_next_tlv(&fuzz, &tlv)) {
58
59
    /* Have the TLV in hand. Parse the TLV. */
60
30.4k
    rc = fuzz_parse_tlv(&fuzz, &tlv);
61
62
30.4k
    if(rc != 0) {
63
      /* Failed to parse the TLV. Can't continue. */
64
370
      goto EXIT_LABEL;
65
370
    }
66
30.4k
  }
67
68
785
  if(tlv_rc != TLV_RC_NO_MORE_TLVS) {
69
    /* A TLV call failed. Can't continue. */
70
96
    goto EXIT_LABEL;
71
96
  }
72
73
  /* Set up the standard easy options. */
74
689
  FTRY(fuzz_set_easy_options(&fuzz));
75
76
  /**
77
   * Add in more curl options that have been accumulated over possibly
78
   * multiple TLVs.
79
   */
80
0
  if(fuzz.header_list != NULL) {
81
0
    curl_easy_setopt(fuzz.easy, CURLOPT_HTTPHEADER, fuzz.header_list);
82
0
  }
83
84
0
  if(fuzz.mail_recipients_list != NULL) {
85
0
    curl_easy_setopt(fuzz.easy, CURLOPT_MAIL_RCPT, fuzz.mail_recipients_list);
86
0
  }
87
88
0
  if(fuzz.mime != NULL) {
89
0
    curl_easy_setopt(fuzz.easy, CURLOPT_MIMEPOST, fuzz.mime);
90
0
  }
91
92
0
  if (fuzz.httppost != NULL) {
93
0
    curl_easy_setopt(fuzz.easy, CURLOPT_HTTPPOST, fuzz.httppost);
94
0
  }
95
96
  /* Run the transfer. */
97
0
  fuzz_handle_transfer(&fuzz);
98
99
1.15k
EXIT_LABEL:
100
101
1.15k
  fuzz_terminate_fuzz_data(&fuzz);
102
103
  /* This function must always return 0. Non-zero codes are reserved. */
104
1.15k
  return 0;
105
0
}
106
107
/**
108
 * Utility function to convert 4 bytes to a u32 predictably.
109
 */
110
uint32_t to_u32(const uint8_t b[4])
111
32.6k
{
112
32.6k
  uint32_t u;
113
32.6k
  u = (b[0] << 24) + (b[1] << 16) + (b[2] << 8) + b[3];
114
32.6k
  return u;
115
32.6k
}
116
117
/**
118
 * Utility function to convert 2 bytes to a u16 predictably.
119
 */
120
uint16_t to_u16(const uint8_t b[2])
121
32.2k
{
122
32.2k
  uint16_t u;
123
32.2k
  u = (b[0] << 8) + b[1];
124
32.2k
  return u;
125
32.2k
}
126
127
/**
128
 * Initialize the local fuzz data structure.
129
 */
130
int fuzz_initialize_fuzz_data(FUZZ_DATA *fuzz,
131
                              const uint8_t *data,
132
                              size_t data_len)
133
1.15k
{
134
1.15k
  int rc = 0;
135
1.15k
  int ii;
136
137
  /* Initialize the fuzz data. */
138
1.15k
  memset(fuzz, 0, sizeof(FUZZ_DATA));
139
140
  /* Create an easy handle. This will have all of the settings configured on
141
     it. */
142
1.15k
  fuzz->easy = curl_easy_init();
143
1.15k
  FCHECK(fuzz->easy != NULL);
144
145
  /* Set up the state parser */
146
1.15k
  fuzz->state.data = data;
147
1.15k
  fuzz->state.data_len = data_len;
148
149
  /* Set up the state of the server sockets. */
150
3.46k
  for(ii = 0; ii < FUZZ_NUM_CONNECTIONS; ii++) {
151
2.31k
    fuzz->sockman[ii].index = ii;
152
2.31k
    fuzz->sockman[ii].fd_state = FUZZ_SOCK_CLOSED;
153
2.31k
  }
154
155
  /* Check for verbose mode. */
156
1.15k
  fuzz->verbose = (getenv("FUZZ_VERBOSE") != NULL);
157
158
1.15k
  FCHECK(setenv("CURL_HSTS_HTTP", "1", 0) == 0);
159
1.15k
  FCHECK(setenv("CURL_ALTSVC_HTTP", "1", 0) == 0);
160
161
1.15k
EXIT_LABEL:
162
163
1.15k
  return rc;
164
1.15k
}
165
166
/**
167
 * Set standard options on the curl easy.
168
 */
169
int fuzz_set_easy_options(FUZZ_DATA *fuzz)
170
689
{
171
689
  int rc = 0;
172
689
  unsigned long allowed_protocols;
173
174
  /* Set some standard options on the CURL easy handle. We need to override the
175
     socket function so that we create our own sockets to present to CURL. */
176
689
  FTRY(curl_easy_setopt(fuzz->easy,
177
689
                        CURLOPT_OPENSOCKETFUNCTION,
178
689
                        fuzz_open_socket));
179
689
  FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_OPENSOCKETDATA, fuzz));
180
181
  /* In case something tries to set a socket option, intercept this. */
182
689
  FTRY(curl_easy_setopt(fuzz->easy,
183
689
                        CURLOPT_SOCKOPTFUNCTION,
184
689
                        fuzz_sockopt_callback));
185
186
  /* Set the standard read function callback. */
187
689
  FTRY(curl_easy_setopt(fuzz->easy,
188
689
                        CURLOPT_READFUNCTION,
189
689
                        fuzz_read_callback));
190
689
  FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_READDATA, fuzz));
191
192
  /* Set the standard write function callback. */
193
689
  FTRY(curl_easy_setopt(fuzz->easy,
194
689
                        CURLOPT_WRITEFUNCTION,
195
689
                        fuzz_write_callback));
196
689
  FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_WRITEDATA, fuzz));
197
198
  /* Set the writable cookie jar path so cookies are tested. */
199
689
  FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_COOKIEJAR, FUZZ_COOKIE_JAR_PATH));
200
201
  /* Set the RO cookie file path so cookies are tested. */
202
689
  FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_COOKIEFILE, FUZZ_RO_COOKIE_FILE_PATH));
203
204
  /* Set altsvc header cache filepath so that it can be fuzzed. */
205
689
  FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_ALTSVC, FUZZ_ALT_SVC_HEADER_CACHE_PATH));
206
207
  /* Set the hsts header cache filepath so that it can be fuzzed. */
208
689
  FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_HSTS, FUZZ_HSTS_HEADER_CACHE_PATH));
209
210
  /* Set the Certificate Revocation List file path so it can be fuzzed */
211
0
  FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_CRLFILE, FUZZ_CRL_FILE_PATH));
212
213
  /* Set the .netrc file path so it can be fuzzed */
214
0
  FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_NETRC_FILE, FUZZ_NETRC_FILE_PATH));
215
216
  /* Time out requests quickly. */
217
0
  FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_TIMEOUT_MS, 200L));
218
0
  FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_SERVER_RESPONSE_TIMEOUT, 1L));
219
220
  /* Can enable verbose mode by having the environment variable FUZZ_VERBOSE. */
221
0
  if(fuzz->verbose) {
222
0
    FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_VERBOSE, 1L));
223
0
  }
224
225
  /* Force resolution of all addresses to a specific IP address. */
226
0
  fuzz->connect_to_list = curl_slist_append(NULL, "::127.0.1.127:");
227
0
  FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_CONNECT_TO, fuzz->connect_to_list));
228
229
  /* Limit the protocols in use by this fuzzer. */
230
0
  FTRY(fuzz_set_allowed_protocols(fuzz));
231
232
689
EXIT_LABEL:
233
234
689
  return rc;
235
0
}
236
237
/**
238
 * Terminate the fuzz data structure, including freeing any allocated memory.
239
 */
240
void fuzz_terminate_fuzz_data(FUZZ_DATA *fuzz)
241
1.15k
{
242
1.15k
  int ii;
243
244
1.15k
  fuzz_free((void **)&fuzz->postfields);
245
246
3.47k
  for(ii = 0; ii < FUZZ_NUM_CONNECTIONS; ii++) {
247
2.31k
    if(fuzz->sockman[ii].fd_state != FUZZ_SOCK_CLOSED) {
248
0
      close(fuzz->sockman[ii].fd);
249
0
      fuzz->sockman[ii].fd_state = FUZZ_SOCK_CLOSED;
250
0
    }
251
2.31k
  }
252
253
1.15k
  if(fuzz->connect_to_list != NULL) {
254
0
    curl_slist_free_all(fuzz->connect_to_list);
255
0
    fuzz->connect_to_list = NULL;
256
0
  }
257
258
1.15k
  if(fuzz->header_list != NULL) {
259
56
    curl_slist_free_all(fuzz->header_list);
260
56
    fuzz->header_list = NULL;
261
56
  }
262
263
1.15k
  if(fuzz->mail_recipients_list != NULL) {
264
30
    curl_slist_free_all(fuzz->mail_recipients_list);
265
30
    fuzz->mail_recipients_list = NULL;
266
30
  }
267
268
1.15k
  if(fuzz->mime != NULL) {
269
169
    curl_mime_free(fuzz->mime);
270
169
    fuzz->mime = NULL;
271
169
  }
272
273
1.15k
  if(fuzz->easy != NULL) {
274
1.15k
    curl_easy_cleanup(fuzz->easy);
275
1.15k
    fuzz->easy = NULL;
276
1.15k
  }
277
278
  /* When you have passed the struct curl_httppost pointer to curl_easy_setopt
279
   * (using the CURLOPT_HTTPPOST option), you must not free the list until after
280
   *  you have called curl_easy_cleanup for the curl handle.
281
   *  https://curl.se/libcurl/c/curl_formadd.html */
282
1.15k
  if (fuzz->httppost != NULL) {
283
32
    curl_formfree(fuzz->httppost);
284
32
    fuzz->httppost = NULL;
285
32
  }
286
287
  // free after httppost and last_post_part.
288
1.15k
  if (fuzz->post_body != NULL) {
289
32
    fuzz_free((void **)&fuzz->post_body);
290
32
  }
291
1.15k
}
292
293
/**
294
 * If a pointer has been allocated, free that pointer.
295
 */
296
void fuzz_free(void **ptr)
297
31.8k
{
298
31.8k
  if(*ptr != NULL) {
299
23.7k
    free(*ptr);
300
23.7k
    *ptr = NULL;
301
23.7k
  }
302
31.8k
}
303
304
/**
305
 * Function for handling the fuzz transfer, including sending responses to
306
 * requests.
307
 */
308
int fuzz_handle_transfer(FUZZ_DATA *fuzz)
309
0
{
310
0
  int rc = 0;
311
0
  CURLM *multi_handle;
312
0
  int still_running; /* keep number of running handles */
313
0
  CURLMsg *msg; /* for picking up messages with the transfer status */
314
0
  int msgs_left; /* how many messages are left */
315
0
  int double_timeout = 0;
316
0
  fd_set fdread;
317
0
  fd_set fdwrite;
318
0
  fd_set fdexcep;
319
0
  struct timeval timeout;
320
0
  int select_rc;
321
0
  CURLMcode mc;
322
0
  int maxfd = -1;
323
0
  long curl_timeo = -1;
324
0
  int ii;
325
0
  FUZZ_SOCKET_MANAGER *sman[FUZZ_NUM_CONNECTIONS];
326
327
0
  for(ii = 0; ii < FUZZ_NUM_CONNECTIONS; ii++) {
328
0
    sman[ii] = &fuzz->sockman[ii];
329
330
    /* Set up the starting index for responses. */
331
0
    sman[ii]->response_index = 1;
332
0
  }
333
334
  /* init a multi stack */
335
0
  multi_handle = curl_multi_init();
336
337
  /* add the individual transfers */
338
0
  curl_multi_add_handle(multi_handle, fuzz->easy);
339
340
  /* Do an initial process. This might end the transfer immediately. */
341
0
  curl_multi_perform(multi_handle, &still_running);
342
0
  FV_PRINTF(fuzz,
343
0
            "FUZZ: Initial perform; still running? %d \n",
344
0
            still_running);
345
346
0
  while(still_running) {
347
    /* Reset the sets of file descriptors. */
348
0
    FD_ZERO(&fdread);
349
0
    FD_ZERO(&fdwrite);
350
0
    FD_ZERO(&fdexcep);
351
352
    /* Set a timeout of 10ms. This is lower than recommended by the multi guide
353
       but we're not going to any remote servers, so everything should complete
354
       very quickly. */
355
0
    timeout.tv_sec = 0;
356
0
    timeout.tv_usec = 10000;
357
358
    /* get file descriptors from the transfers */
359
0
    mc = curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);
360
0
    if(mc != CURLM_OK) {
361
0
      fprintf(stderr, "curl_multi_fdset() failed, code %d.\n", mc);
362
0
      rc = -1;
363
0
      break;
364
0
    }
365
366
0
    for(ii = 0; ii < FUZZ_NUM_CONNECTIONS; ii++) {
367
      /* Add the socket FD into the readable set if connected. */
368
0
      if(sman[ii]->fd_state == FUZZ_SOCK_OPEN) {
369
0
        FD_SET(sman[ii]->fd, &fdread);
370
371
        /* Work out the maximum FD between the cURL file descriptors and the
372
           server FD. */
373
0
        maxfd = FUZZ_MAX(sman[ii]->fd, maxfd);
374
0
      }
375
0
    }
376
377
    /* Work out what file descriptors need work. */
378
0
    rc = fuzz_select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout);
379
380
0
    if(rc == -1) {
381
      /* Had an issue while selecting a file descriptor. Let's just exit. */
382
0
      FV_PRINTF(fuzz, "FUZZ: select failed, exiting \n");
383
0
      break;
384
0
    }
385
0
    else if(rc == 0) {
386
0
      FV_PRINTF(fuzz,
387
0
                "FUZZ: Timed out; double timeout? %d \n",
388
0
                double_timeout);
389
390
      /* Timed out. */
391
0
      if(double_timeout == 1) {
392
        /* We don't expect multiple timeouts in a row. If there are double
393
           timeouts then exit. */
394
0
        break;
395
0
      }
396
0
      else {
397
        /* Set the timeout flag for the next time we select(). */
398
0
        double_timeout = 1;
399
0
      }
400
0
    }
401
0
    else {
402
      /* There's an active file descriptor. Reset the timeout flag. */
403
0
      double_timeout = 0;
404
0
    }
405
406
    /* Check to see if a server file descriptor is readable. If it is,
407
       then send the next response from the fuzzing data. */
408
0
    for(ii = 0; ii < FUZZ_NUM_CONNECTIONS; ii++) {
409
0
      if(sman[ii]->fd_state == FUZZ_SOCK_OPEN &&
410
0
         FD_ISSET(sman[ii]->fd, &fdread)) {
411
0
        rc = fuzz_send_next_response(fuzz, sman[ii]);
412
0
        if(rc != 0) {
413
          /* Failed to send a response. Break out here. */
414
0
          break;
415
0
        }
416
0
      }
417
0
    }
418
419
0
    curl_multi_perform(multi_handle, &still_running);
420
0
  }
421
422
  /* Remove the easy handle from the multi stack. */
423
0
  curl_multi_remove_handle(multi_handle, fuzz->easy);
424
425
  /* Clean up the multi handle - the top level function will handle the easy
426
     handle. */
427
0
  curl_multi_cleanup(multi_handle);
428
429
0
  return(rc);
430
0
}
431
432
/**
433
 * Sends the next fuzzing response to the server file descriptor.
434
 */
435
int fuzz_send_next_response(FUZZ_DATA *fuzz, FUZZ_SOCKET_MANAGER *sman)
436
0
{
437
0
  int rc = 0;
438
0
  ssize_t ret_in;
439
0
  ssize_t ret_out;
440
0
  char buffer[8192];
441
0
  const uint8_t *data;
442
0
  size_t data_len;
443
444
  /* Need to read all data sent by the client so the file descriptor becomes
445
     unreadable. Because the file descriptor is non-blocking we won't just
446
     hang here. */
447
0
  do {
448
0
    ret_in = read(sman->fd, buffer, sizeof(buffer));
449
0
    if(fuzz->verbose && ret_in > 0) {
450
0
      printf("FUZZ[%d]: Received %zu bytes \n==>\n", sman->index, ret_in);
451
0
      fwrite(buffer, ret_in, 1, stdout);
452
0
      printf("\n<==\n");
453
0
    }
454
0
  } while (ret_in > 0);
455
456
  /* Now send a response to the request that the client just made. */
457
0
  FV_PRINTF(fuzz,
458
0
            "FUZZ[%d]: Sending next response: %d \n",
459
0
            sman->index,
460
0
            sman->response_index);
461
0
  data = sman->responses[sman->response_index].data;
462
0
  data_len = sman->responses[sman->response_index].data_len;
463
464
0
  if(data != NULL) {
465
0
    if(write(sman->fd, data, data_len) != (ssize_t)data_len) {
466
      /* Failed to write the data back to the client. Prevent any further
467
         testing. */
468
0
      rc = -1;
469
0
    }
470
0
  }
471
472
  /* Work out if there are any more responses. If not, then shut down the
473
     server. */
474
0
  sman->response_index++;
475
476
0
  if(sman->response_index >= TLV_MAX_NUM_RESPONSES ||
477
0
     sman->responses[sman->response_index].data == NULL) {
478
0
    FV_PRINTF(fuzz,
479
0
              "FUZZ[%d]: Shutting down server socket: %d \n",
480
0
              sman->index,
481
0
              sman->fd);
482
0
    shutdown(sman->fd, SHUT_WR);
483
0
    sman->fd_state = FUZZ_SOCK_SHUTDOWN;
484
0
  }
485
486
0
  return(rc);
487
0
}
488
489
/**
490
 * Wrapper for select() so profiling can track it.
491
 */
492
int fuzz_select(int nfds,
493
                fd_set *readfds,
494
                fd_set *writefds,
495
                fd_set *exceptfds,
496
0
                struct timeval *timeout) {
497
0
  return select(nfds, readfds, writefds, exceptfds, timeout);
498
0
}
499
500
/**
501
 * Set allowed protocols based on the compile options.
502
 *
503
 * Note that it can only use ONE of the FUZZ_PROTOCOLS_* defines.a
504
 */
505
int fuzz_set_allowed_protocols(FUZZ_DATA *fuzz)
506
0
{
507
0
  int rc = 0;
508
0
  const char *allowed_protocols = "";
509
510
#ifdef FUZZ_PROTOCOLS_ALL
511
  /* Do not allow telnet currently as it accepts input from stdin. */
512
  allowed_protocols =
513
    "dict,file,ftp,ftps,gopher,gophers,http,https,imap,imaps,"
514
    "ldap,ldaps,mqtt,pop3,pop3s,rtmp,rtmpe,rtmps,rtmpt,rtmpte,rtmpts,"
515
    "rtsp,scp,sftp,smb,smbs,smtp,smtps,tftp";
516
#endif
517
#ifdef FUZZ_PROTOCOLS_DICT
518
  allowed_protocols = "dict";
519
#endif
520
#ifdef FUZZ_PROTOCOLS_FILE
521
  allowed_protocols = "file";
522
#endif
523
#ifdef FUZZ_PROTOCOLS_FTP
524
  allowed_protocols = "ftp,ftps";
525
#endif
526
#ifdef FUZZ_PROTOCOLS_GOPHER
527
  allowed_protocols = "gopher,gophers";
528
#endif
529
#ifdef FUZZ_PROTOCOLS_HTTP
530
  allowed_protocols = "http";
531
#endif
532
#ifdef FUZZ_PROTOCOLS_HTTPS
533
  allowed_protocols = "https";
534
#endif
535
#ifdef FUZZ_PROTOCOLS_IMAP
536
  allowed_protocols = "imap,imaps";
537
#endif
538
0
#ifdef FUZZ_PROTOCOLS_LDAP
539
0
  allowed_protocols = "ldap,ldaps";
540
0
#endif
541
#ifdef FUZZ_PROTOCOLS_MQTT
542
  allowed_protocols = "mqtt";
543
#endif
544
#ifdef FUZZ_PROTOCOLS_POP3
545
  allowed_protocols = "pop3,pop3s";
546
#endif
547
#ifdef FUZZ_PROTOCOLS_RTMP
548
  allowed_protocols = "rtmp,rtmpe,rtmps,rtmpt,rtmpte,rtmpts";
549
#endif
550
#ifdef FUZZ_PROTOCOLS_RTSP
551
  allowed_protocols = "rtsp";
552
#endif
553
#ifdef FUZZ_PROTOCOLS_SCP
554
  allowed_protocols = "scp";
555
#endif
556
#ifdef FUZZ_PROTOCOLS_SFTP
557
  allowed_protocols = "sftp";
558
#endif
559
#ifdef FUZZ_PROTOCOLS_SMB
560
  allowed_protocols = "smb,smbs";
561
#endif
562
#ifdef FUZZ_PROTOCOLS_SMTP
563
  allowed_protocols = "smtp,smtps";
564
#endif
565
#ifdef FUZZ_PROTOCOLS_TFTP
566
  allowed_protocols = "tftp";
567
#endif
568
#ifdef FUZZ_PROTOCOLS_WS
569
  allowed_protocols = "ws,wss";
570
#endif
571
572
0
  FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_PROTOCOLS_STR, allowed_protocols));
573
574
0
EXIT_LABEL:
575
576
0
  return rc;
577
0
}