Coverage Report

Created: 2026-08-17 07:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openssl/crypto/bio/bio_print.c
Line
Count
Source
1
/*
2
 * Copyright 1995-2026 The OpenSSL Project Authors. All Rights Reserved.
3
 *
4
 * Licensed under the Apache License 2.0 (the "License").  You may not use
5
 * this file except in compliance with the License.  You can obtain a copy
6
 * in the file LICENSE in the source distribution or at
7
 * https://www.openssl.org/source/license.html
8
 */
9
10
#include <stdio.h>
11
#include <string.h>
12
#include "internal/cryptlib.h"
13
#include "crypto/ctype.h"
14
#include "internal/numbers.h"
15
#include <openssl/bio.h>
16
#include <openssl/configuration.h>
17
18
int BIO_printf(BIO *bio, const char *format, ...)
19
0
{
20
0
    va_list args;
21
0
    int ret;
22
23
0
    va_start(args, format);
24
25
0
    ret = BIO_vprintf(bio, format, args);
26
27
0
    va_end(args);
28
0
    return ret;
29
0
}
30
31
#if defined(_MSC_VER) && _MSC_VER < 1900
32
/*
33
 * _MSC_VER described here:
34
 * https://learn.microsoft.com/en-us/cpp/overview/compiler-versions?view=msvc-170
35
 *
36
 * Beginning with the UCRT in Visual Studio 2015 and Windows 10, snprintf is no
37
 * longer identical to _snprintf. The snprintf behavior is now C99 standard
38
 * conformant. The difference is that if you run out of buffer, snprintf
39
 * null-terminates the end of the buffer and returns the number of characters
40
 * that would have been required whereas _snprintf doesn't null-terminate the
41
 * buffer and returns -1. Also, snprintf() includes one more character in the
42
 * output because it doesn't null-terminate the buffer.
43
 * [ https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/snprintf-snprintf-snprintf-l-snwprintf-snwprintf-l?view=msvc-170#remarks
44
 *
45
 * for older MSVC (older than 2015) we can use _vscprintf() and _vsnprintf()
46
 * as suggested here:
47
 * https://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010
48
 *
49
 */
50
static int msvc_translate_printf_format(const char *format, const char **out,
51
    char **tmp)
52
{
53
    /* Valid printf conversion specifiers, grouped by category: signed
54
     * integers (d i), unsigned (o u x X), floating-point (f F e E g G a A),
55
     * misc (c s p n) and MSVC-specific (S Z C). */
56
    static const char conv[] = "diouxXfFeEgGaAcspnSZC";
57
    const char *p = format;
58
    char *dst = NULL, *q = NULL;
59
60
    /*
61
     * The VS 2013 CRT does not understand the C99 z, t and j length
62
     * modifiers. Translate z and t to I (both are pointer-sized on Windows)
63
     * and j to I64 (intmax_t is 64 bits). Every input character expands to
64
     * at most three output characters (j -> I64), so 3 * length is a safe
65
     * bound for the buffer.
66
     *
67
     * This is done in a single pass: nothing is allocated until the first
68
     * modifier is seen, so formats that need no translation return the
69
     * original string untouched. EMIT_CHAR() appends a character to the
70
     * output once the buffer exists; before that it is a no-op.
71
     */
72
#define EMIT_CHAR(c)     \
73
    do {                 \
74
        if (dst != NULL) \
75
            *q++ = (c);  \
76
    } while (0)
77
78
    *out = format;
79
    *tmp = NULL;
80
81
    while (*p != '\0') {
82
        if (*p != '%') { /* literal character */
83
            EMIT_CHAR(*p);
84
            p++;
85
            continue;
86
        }
87
        p++; /* consume '%' */
88
        if (*p == '%') { /* literal "%%" */
89
            EMIT_CHAR('%');
90
            EMIT_CHAR('%');
91
            p++;
92
            continue;
93
        }
94
        EMIT_CHAR('%');
95
        while (*p != '\0' && strchr(conv, *p) == NULL) {
96
            char c = *p++;
97
            if (c != 'z' && c != 't' && c != 'j') { /* verbatim */
98
                EMIT_CHAR(c);
99
                continue;
100
            }
101
            if (dst == NULL) { /* first modifier: allocate + flush prefix */
102
                size_t len = strlen(format);
103
                if (len > (SIZE_MAX - 1) / 3) /* make static analysis happy */
104
                    return 0;
105
                dst = (char *)OPENSSL_malloc(3 * len + 1);
106
                if (dst == NULL)
107
                    return 0;
108
                q = dst;
109
                memcpy(q, format, (size_t)(p - 1 - format));
110
                q += p - 1 - format;
111
            }
112
            EMIT_CHAR('I');
113
            if (c == 'j') {
114
                EMIT_CHAR('6');
115
                EMIT_CHAR('4');
116
            }
117
        }
118
        if (*p != '\0') { /* copy the conversion specifier */
119
            EMIT_CHAR(*p);
120
            p++;
121
        }
122
    }
123
#undef EMIT_CHAR
124
125
    if (dst != NULL) {
126
        *q = '\0';
127
        *out = dst;
128
        *tmp = dst;
129
    }
130
    return 1;
131
}
132
133
static int msvc_bio_vprintf(BIO *bio, const char *format, va_list args)
134
{
135
    char buf[512];
136
    char *abuf, *fmt_alloc;
137
    const char *fmt;
138
    int ret, sz;
139
140
    if (!msvc_translate_printf_format(format, &fmt, &fmt_alloc))
141
        return -1;
142
143
    sz = _vsnprintf_s(buf, sizeof(buf), _TRUNCATE, fmt, args);
144
    if (sz == -1) {
145
        sz = _vscprintf(fmt, args) + 1;
146
        abuf = (char *)OPENSSL_malloc(sz);
147
        if (abuf == NULL) {
148
            ret = -1;
149
        } else {
150
            sz = _vsnprintf(abuf, sz, fmt, args);
151
            ret = BIO_write(bio, abuf, sz);
152
            OPENSSL_free(abuf);
153
        }
154
    } else {
155
        ret = BIO_write(bio, buf, sz);
156
    }
157
158
    OPENSSL_free(fmt_alloc);
159
    return ret;
160
}
161
#endif
162
163
#ifdef _MSC_VER
164
/*
165
 * This function is for unit test on windows only when built with Visual Studio
166
 */
167
int ossl_BIO_snprintf_msvc(char *buf, size_t n, const char *format, ...)
168
{
169
    va_list args;
170
    int ret;
171
172
    va_start(args, format);
173
#if defined(_MSC_VER) && _MSC_VER < 1900
174
    {
175
        char *fmt_alloc;
176
        const char *fmt;
177
178
        if (!msvc_translate_printf_format(format, &fmt, &fmt_alloc)) {
179
            ret = -1;
180
        } else {
181
            ret = _vsnprintf_s(buf, n, _TRUNCATE, fmt, args);
182
            OPENSSL_free(fmt_alloc);
183
        }
184
    }
185
#else
186
    ret = _vsnprintf_s(buf, n, _TRUNCATE, format, args);
187
#endif
188
    va_end(args);
189
190
    return ret;
191
}
192
#endif
193
194
int BIO_vprintf(BIO *bio, const char *format, va_list args)
195
0
{
196
0
    va_list cp_args;
197
0
#if !defined(_MSC_VER) || _MSC_VER >= 1900
198
0
    int sz;
199
0
#endif
200
0
    int ret = -1;
201
202
0
    va_copy(cp_args, args);
203
#if defined(_MSC_VER) && _MSC_VER < 1900
204
    ret = msvc_bio_vprintf(bio, format, cp_args);
205
#else
206
0
    char buf[512];
207
0
    char *abuf;
208
    /*
209
     * some compilers modify va_list, hence each call to v*printf()
210
     * should operate with its own instance of va_list. The first
211
     * call to vsnprintf() here uses args we got in function argument.
212
     * The second call is going to use cp_args we made earlier.
213
     */
214
0
    sz = vsnprintf(buf, sizeof(buf), format, args);
215
0
    if (sz >= 0) {
216
0
        if ((size_t)sz >= sizeof(buf)) {
217
0
            sz += 1;
218
0
            abuf = (char *)OPENSSL_malloc(sz);
219
0
            if (abuf == NULL) {
220
0
                ret = -1;
221
0
            } else {
222
0
                sz = vsnprintf(abuf, sz, format, cp_args);
223
0
                ret = BIO_write(bio, abuf, sz);
224
0
                OPENSSL_free(abuf);
225
0
            }
226
0
        } else {
227
            /* vsnprintf returns length not including nul-terminator */
228
0
            ret = BIO_write(bio, buf, sz);
229
0
        }
230
0
    }
231
0
#endif
232
0
    va_end(cp_args);
233
0
    return ret;
234
0
}
235
236
/*
237
 * For historical reasons BIO_snprintf and friends return a failure for string
238
 * truncation (-1) instead of the POSIX requirement of a success with the
239
 * number of characters that would have been written. Upon seeing -1 on
240
 * return, the caller must treat output buf as unsafe (as a buf with missing
241
 * nul terminator).
242
 */
243
int BIO_snprintf(char *buf, size_t n, const char *format, ...)
244
21.8k
{
245
21.8k
    va_list args;
246
21.8k
    int ret;
247
248
21.8k
    va_start(args, format);
249
21.8k
    ret = BIO_vsnprintf(buf, n, format, args);
250
21.8k
    va_end(args);
251
252
21.8k
    return ret;
253
21.8k
}
254
255
int BIO_vsnprintf(char *buf, size_t n, const char *format, va_list args)
256
21.8k
{
257
#if defined(_MSC_VER) && _MSC_VER < 1900
258
    char *fmt_alloc;
259
    const char *fmt;
260
#endif
261
21.8k
    int ret;
262
263
#if defined(_MSC_VER) && _MSC_VER < 1900
264
    if (!msvc_translate_printf_format(format, &fmt, &fmt_alloc))
265
        return -1;
266
    ret = _vsnprintf_s(buf, n, _TRUNCATE, fmt, args);
267
    OPENSSL_free(fmt_alloc);
268
#else
269
21.8k
    ret = vsnprintf(buf, n, format, args);
270
21.8k
    if ((size_t)ret >= n)
271
0
        ret = -1;
272
21.8k
#endif
273
21.8k
    return ret;
274
21.8k
}