/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 | } |