Coverage Report

Created: 2026-10-22 06:56

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