Coverage Report

Created: 2026-10-28 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/curl_fuzzer/full_coverage/public_api.cc
Line
Count
Source
1
/*
2
 * Copyright (C) Max Dymond, <cmeister2@gmail.com>, et al.
3
 *
4
 * SPDX-License-Identifier: curl
5
 */
6
7
#include "full_coverage/public_api.h"
8
9
#include <cstdarg>
10
#include <cstdio>
11
12
#include <curl/curl.h>
13
#include <curl/mprintf.h>
14
15
namespace full_coverage {
16
namespace {
17
18
/** Reuse a valid argument list across the public va_list formatting APIs. */
19
void ExerciseVariadicFormatting(FILE *stream, const char *format, ...)
20
3
{
21
3
  char buffer[4096];
22
3
  va_list args;
23
3
  va_start(args, format);
24
3
  va_list copy;
25
3
  va_copy(copy, args);
26
3
  curl_mvsnprintf(buffer, sizeof(buffer), format, copy);
27
3
  va_end(copy);
28
3
  va_copy(copy, args);
29
3
  curl_mvsnprintf(buffer, 1, format, copy);
30
3
  va_end(copy);
31
3
  va_copy(copy, args);
32
3
  curl_mvsprintf(buffer, format, copy);
33
3
  va_end(copy);
34
3
  va_copy(copy, args);
35
3
  curl_free(curl_mvaprintf(format, copy));
36
3
  va_end(copy);
37
3
  if(stream) {
38
3
    va_copy(copy, args);
39
3
    curl_mvfprintf(stream, format, copy);
40
3
    va_end(copy);
41
3
  }
42
3
  va_end(args);
43
3
}
44
45
/** Exercise the stdout va_list wrapper with an empty formatted result. */
46
void ExerciseVariadicStdout(const char *format, ...)
47
3
{
48
3
  va_list args;
49
3
  va_start(args, format);
50
3
  curl_mvprintf(format, args);
51
3
  va_end(args);
52
3
}
53
54
/** Consume the in-memory serialization produced by the legacy form API. */
55
size_t DiscardFormData(void *, const char *, size_t length)
56
6
{
57
6
  return length;
58
6
}
59
60
/** Keep deprecated, still-supported form helpers reachable in profiling. */
61
void ExerciseLegacyForms()
62
3
{
63
3
  curl_httppost *first = nullptr;
64
3
  curl_httppost *last = nullptr;
65
3
  curl_formadd(&first, &last, CURLFORM_COPYNAME, "field",
66
3
               CURLFORM_COPYCONTENTS, "value", CURLFORM_END);
67
3
  const char contents[] = "file contents";
68
3
  curl_formadd(&first, &last, CURLFORM_COPYNAME, "attachment",
69
3
               CURLFORM_BUFFER, "example.txt", CURLFORM_BUFFERPTR, contents,
70
3
               CURLFORM_BUFFERLENGTH, static_cast<long>(sizeof(contents) - 1),
71
3
               CURLFORM_CONTENTTYPE, "text/plain", CURLFORM_END);
72
3
  curl_forms fields[] = {
73
3
      {CURLFORM_COPYNAME, "array-field"},
74
3
      {CURLFORM_COPYCONTENTS, "array-value"},
75
3
      {CURLFORM_END, nullptr}};
76
3
  curl_formadd(&first, &last, CURLFORM_ARRAY, fields, CURLFORM_END);
77
  // Multiple files in one field create the legacy API's subordinate chain.
78
  // /dev/null supplies a deterministic, empty local file for serialization.
79
3
  curl_formadd(&first, &last, CURLFORM_COPYNAME, "empty-files",
80
3
               CURLFORM_FILE, "/dev/null", CURLFORM_CONTENTTYPE, "text/plain",
81
3
               CURLFORM_FILE, "/dev/null", CURLFORM_FILENAME, "empty.txt",
82
3
               CURLFORM_END);
83
84
  // A name containing NUL bytes is documented as invalid. Put it on the last
85
  // of three files so rejection cleans up both copied fields and a partially
86
  // constructed subordinate chain, while preserving the previously added form.
87
3
  char invalid_name[] = {'a', '\0', 'b'};
88
3
  curl_formadd(&first, &last, CURLFORM_COPYNAME, "invalid-files",
89
3
               CURLFORM_FILE, "/dev/null", CURLFORM_FILE, "/dev/null",
90
3
               CURLFORM_FILE, "/dev/null", CURLFORM_COPYNAME, invalid_name,
91
3
               CURLFORM_NAMELENGTH, static_cast<long>(sizeof(invalid_name)),
92
3
               CURLFORM_END);
93
3
  if(first)
94
3
    curl_formget(first, nullptr, DiscardFormData);
95
3
  curl_formfree(first);
96
3
  curl_formfree(nullptr);
97
3
}
98
99
}  // namespace
100
101
void ExercisePublicApiCoverageWithInitializedCurl()
102
3
{
103
3
  char buffer[4096];
104
3
  curl_msnprintf(buffer, sizeof(buffer),
105
3
                "%+08d %i %u %#o %#x %#X %ld %lu %lld %llu %%",
106
3
                -42, 0, 42U, 42U, 42U, 42U, -42L, 42UL, -42LL, 42ULL);
107
3
  curl_msnprintf(buffer, sizeof(buffer), "%*.*s %c %p",
108
3
                -12, 4, "example", 'x', static_cast<void *>(buffer));
109
3
  curl_msnprintf(buffer, sizeof(buffer), "%+.3f %.2e %g %G",
110
3
                1.25, -1.25, 0.00125, 12500.0);
111
3
  curl_msnprintf(buffer, 1, "%s", "truncated");
112
3
  curl_msnprintf(buffer, 0, "%s", "zero-capacity");
113
3
  curl_msprintf(buffer, "%s:%d", "value", 42);
114
3
  curl_free(curl_maprintf("%2$s:%1$d", 42, "position"));
115
  // The stdout wrappers remain quiet while covering their public entrypoints.
116
3
  curl_mprintf("%s", "");
117
3
  ExerciseVariadicStdout("%s", "");
118
119
3
  FILE *stream = std::tmpfile();
120
3
  if(stream)
121
3
    curl_mfprintf(stream, "%s:%d", "stream", 42);
122
3
  ExerciseVariadicFormatting(stream, "%s:%+08d:%.2f", "variadic", -42, 1.25);
123
3
  if(stream)
124
3
    std::fclose(stream);
125
126
3
  ExerciseLegacyForms();
127
3
}
128
129
void ExercisePublicApiCoverage()
130
1
{
131
1
  if(curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK)
132
0
    return;
133
134
1
  ExercisePublicApiCoverageWithInitializedCurl();
135
1
  curl_global_cleanup();
136
1
}
137
138
}  // namespace full_coverage