Coverage Report

Created: 2026-10-15 06:25

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
0
{
21
0
  char buffer[4096];
22
0
  va_list args;
23
0
  va_start(args, format);
24
0
  va_list copy;
25
0
  va_copy(copy, args);
26
0
  curl_mvsnprintf(buffer, sizeof(buffer), format, copy);
27
0
  va_end(copy);
28
0
  va_copy(copy, args);
29
0
  curl_mvsnprintf(buffer, 1, format, copy);
30
0
  va_end(copy);
31
0
  va_copy(copy, args);
32
0
  curl_mvsprintf(buffer, format, copy);
33
0
  va_end(copy);
34
0
  va_copy(copy, args);
35
0
  curl_free(curl_mvaprintf(format, copy));
36
0
  va_end(copy);
37
0
  if(stream) {
38
0
    va_copy(copy, args);
39
0
    curl_mvfprintf(stream, format, copy);
40
0
    va_end(copy);
41
0
  }
42
0
  va_end(args);
43
0
}
44
45
/** Exercise the stdout va_list wrapper with an empty formatted result. */
46
void ExerciseVariadicStdout(const char *format, ...)
47
0
{
48
0
  va_list args;
49
0
  va_start(args, format);
50
0
  curl_mvprintf(format, args);
51
0
  va_end(args);
52
0
}
53
54
/** Consume the in-memory serialization produced by the legacy form API. */
55
size_t DiscardFormData(void *, const char *, size_t length)
56
0
{
57
0
  return length;
58
0
}
59
60
/** Keep deprecated, still-supported form helpers reachable in profiling. */
61
void ExerciseLegacyForms()
62
0
{
63
0
  curl_httppost *first = nullptr;
64
0
  curl_httppost *last = nullptr;
65
0
  curl_formadd(&first, &last, CURLFORM_COPYNAME, "field",
66
0
               CURLFORM_COPYCONTENTS, "value", CURLFORM_END);
67
0
  const char contents[] = "file contents";
68
0
  curl_formadd(&first, &last, CURLFORM_COPYNAME, "attachment",
69
0
               CURLFORM_BUFFER, "example.txt", CURLFORM_BUFFERPTR, contents,
70
0
               CURLFORM_BUFFERLENGTH, static_cast<long>(sizeof(contents) - 1),
71
0
               CURLFORM_CONTENTTYPE, "text/plain", CURLFORM_END);
72
0
  curl_forms fields[] = {
73
0
      {CURLFORM_COPYNAME, "array-field"},
74
0
      {CURLFORM_COPYCONTENTS, "array-value"},
75
0
      {CURLFORM_END, nullptr}};
76
0
  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
0
  curl_formadd(&first, &last, CURLFORM_COPYNAME, "empty-files",
80
0
               CURLFORM_FILE, "/dev/null", CURLFORM_CONTENTTYPE, "text/plain",
81
0
               CURLFORM_FILE, "/dev/null", CURLFORM_FILENAME, "empty.txt",
82
0
               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
0
  char invalid_name[] = {'a', '\0', 'b'};
88
0
  curl_formadd(&first, &last, CURLFORM_COPYNAME, "invalid-files",
89
0
               CURLFORM_FILE, "/dev/null", CURLFORM_FILE, "/dev/null",
90
0
               CURLFORM_FILE, "/dev/null", CURLFORM_COPYNAME, invalid_name,
91
0
               CURLFORM_NAMELENGTH, static_cast<long>(sizeof(invalid_name)),
92
0
               CURLFORM_END);
93
0
  if(first)
94
0
    curl_formget(first, nullptr, DiscardFormData);
95
0
  curl_formfree(first);
96
0
  curl_formfree(nullptr);
97
0
}
98
99
}  // namespace
100
101
void ExercisePublicApiCoverage()
102
0
{
103
0
  if(curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK)
104
0
    return;
105
106
0
  char buffer[4096];
107
0
  curl_msnprintf(buffer, sizeof(buffer),
108
0
                "%+08d %i %u %#o %#x %#X %ld %lu %lld %llu %%",
109
0
                -42, 0, 42U, 42U, 42U, 42U, -42L, 42UL, -42LL, 42ULL);
110
0
  curl_msnprintf(buffer, sizeof(buffer), "%*.*s %c %p",
111
0
                -12, 4, "example", 'x', static_cast<void *>(buffer));
112
0
  curl_msnprintf(buffer, sizeof(buffer), "%+.3f %.2e %g %G",
113
0
                1.25, -1.25, 0.00125, 12500.0);
114
0
  curl_msnprintf(buffer, 1, "%s", "truncated");
115
0
  curl_msnprintf(buffer, 0, "%s", "zero-capacity");
116
0
  curl_msprintf(buffer, "%s:%d", "value", 42);
117
0
  curl_free(curl_maprintf("%2$s:%1$d", 42, "position"));
118
  // The stdout wrappers remain quiet while covering their public entrypoints.
119
0
  curl_mprintf("%s", "");
120
0
  ExerciseVariadicStdout("%s", "");
121
122
0
  FILE *stream = std::tmpfile();
123
0
  if(stream)
124
0
    curl_mfprintf(stream, "%s:%d", "stream", 42);
125
0
  ExerciseVariadicFormatting(stream, "%s:%+08d:%.2f", "variadic", -42, 1.25);
126
0
  if(stream)
127
0
    std::fclose(stream);
128
129
0
  ExerciseLegacyForms();
130
0
  curl_global_cleanup();
131
0
}
132
133
}  // namespace full_coverage