fuzz coverage

Coverage Report

Created: 2025-06-01 19:34

/Users/eugenesiegel/btc/bitcoin/src/util/strencodings.cpp
Line
Count
Source (jump to first uncovered line)
1
// Copyright (c) 2009-2010 Satoshi Nakamoto
2
// Copyright (c) 2009-present The Bitcoin Core developers
3
// Distributed under the MIT software license, see the accompanying
4
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6
#include <util/strencodings.h>
7
8
#include <crypto/hex_base.h>
9
#include <span.h>
10
11
#include <array>
12
#include <cassert>
13
#include <cstring>
14
#include <limits>
15
#include <optional>
16
#include <ostream>
17
#include <string>
18
#include <vector>
19
20
static const std::string CHARS_ALPHA_NUM = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
21
22
static const std::string SAFE_CHARS[] =
23
{
24
    CHARS_ALPHA_NUM + " .,;-_/:?@()", // SAFE_CHARS_DEFAULT
25
    CHARS_ALPHA_NUM + " .,;-_?@", // SAFE_CHARS_UA_COMMENT
26
    CHARS_ALPHA_NUM + ".-_", // SAFE_CHARS_FILENAME
27
    CHARS_ALPHA_NUM + "!*'();:@&=+$,/?#[]-_.~%", // SAFE_CHARS_URI
28
};
29
30
std::string SanitizeString(std::string_view str, int rule)
31
91.6k
{
32
91.6k
    std::string result;
33
91.6k
    for (char c : str) {
34
0
        if (SAFE_CHARS[rule].find(c) != std::string::npos) {
35
0
            result.push_back(c);
36
0
        }
37
0
    }
38
91.6k
    return result;
39
91.6k
}
40
41
bool IsHex(std::string_view str)
42
0
{
43
0
    for (char c : str) {
44
0
        if (HexDigit(c) < 0) return false;
45
0
    }
46
0
    return (str.size() > 0) && (str.size()%2 == 0);
47
0
}
48
49
template <typename Byte>
50
std::optional<std::vector<Byte>> TryParseHex(std::string_view str)
51
0
{
52
0
    std::vector<Byte> vch;
53
0
    vch.reserve(str.size() / 2); // two hex characters form a single byte
54
55
0
    auto it = str.begin();
56
0
    while (it != str.end()) {
57
0
        if (IsSpace(*it)) {
58
0
            ++it;
59
0
            continue;
60
0
        }
61
0
        auto c1 = HexDigit(*(it++));
62
0
        if (it == str.end()) return std::nullopt;
63
0
        auto c2 = HexDigit(*(it++));
64
0
        if (c1 < 0 || c2 < 0) return std::nullopt;
65
0
        vch.push_back(Byte(c1 << 4) | Byte(c2));
66
0
    }
67
0
    return vch;
68
0
}
Unexecuted instantiation: _Z11TryParseHexISt4byteENSt3__18optionalINS1_6vectorIT_NS1_9allocatorIS4_EEEEEENS1_17basic_string_viewIcNS1_11char_traitsIcEEEE
Unexecuted instantiation: _Z11TryParseHexIhENSt3__18optionalINS0_6vectorIT_NS0_9allocatorIS3_EEEEEENS0_17basic_string_viewIcNS0_11char_traitsIcEEEE
69
template std::optional<std::vector<std::byte>> TryParseHex(std::string_view);
70
template std::optional<std::vector<uint8_t>> TryParseHex(std::string_view);
71
72
bool SplitHostPort(std::string_view in, uint16_t& portOut, std::string& hostOut)
73
0
{
74
0
    bool valid = false;
75
0
    size_t colon = in.find_last_of(':');
76
    // if a : is found, and it either follows a [...], or no other : is in the string, treat it as port separator
77
0
    bool fHaveColon = colon != in.npos;
78
0
    bool fBracketed = fHaveColon && (in[0] == '[' && in[colon - 1] == ']'); // if there is a colon, and in[0]=='[', colon is not 0, so in[colon-1] is safe
79
0
    bool fMultiColon{fHaveColon && colon != 0 && (in.find_last_of(':', colon - 1) != in.npos)};
80
0
    if (fHaveColon && (colon == 0 || fBracketed || !fMultiColon)) {
81
0
        uint16_t n;
82
0
        if (ParseUInt16(in.substr(colon + 1), &n)) {
83
0
            in = in.substr(0, colon);
84
0
            portOut = n;
85
0
            valid = (portOut != 0);
86
0
        }
87
0
    } else {
88
0
        valid = true;
89
0
    }
90
0
    if (in.size() > 0 && in[0] == '[' && in[in.size() - 1] == ']') {
91
0
        hostOut = in.substr(1, in.size() - 2);
92
0
    } else {
93
0
        hostOut = in;
94
0
    }
95
96
0
    return valid;
97
0
}
98
99
std::string EncodeBase64(std::span<const unsigned char> input)
100
0
{
101
0
    static const char *pbase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
102
103
0
    std::string str;
104
0
    str.reserve(((input.size() + 2) / 3) * 4);
105
0
    ConvertBits<8, 6, true>([&](int v) { str += pbase64[v]; }, input.begin(), input.end());
106
0
    while (str.size() % 4) str += '=';
107
0
    return str;
108
0
}
109
110
std::optional<std::vector<unsigned char>> DecodeBase64(std::string_view str)
111
0
{
112
0
    static const int8_t decode64_table[256]{
113
0
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
114
0
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
115
0
        -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1,
116
0
        -1, -1, -1, -1, -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
117
0
        15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28,
118
0
        29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
119
0
        49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
120
0
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
121
0
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
122
0
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
123
0
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
124
0
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
125
0
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
126
0
    };
127
128
0
    if (str.size() % 4 != 0) return {};
129
    /* One or two = characters at the end are permitted. */
130
0
    if (str.size() >= 1 && str.back() == '=') str.remove_suffix(1);
131
0
    if (str.size() >= 1 && str.back() == '=') str.remove_suffix(1);
132
133
0
    std::vector<unsigned char> ret;
134
0
    ret.reserve((str.size() * 3) / 4);
135
0
    bool valid = ConvertBits<6, 8, false>(
136
0
        [&](unsigned char c) { ret.push_back(c); },
137
0
        str.begin(), str.end(),
138
0
        [](char c) { return decode64_table[uint8_t(c)]; }
139
0
    );
140
0
    if (!valid) return {};
141
142
0
    return ret;
143
0
}
144
145
std::string EncodeBase32(std::span<const unsigned char> input, bool pad)
146
3.42k
{
147
3.42k
    static const char *pbase32 = "abcdefghijklmnopqrstuvwxyz234567";
148
149
3.42k
    std::string str;
150
3.42k
    str.reserve(((input.size() + 4) / 5) * 8);
151
157k
    ConvertBits<8, 5, true>([&](int v) { str += pbase32[v]; }, input.begin(), input.end());
152
3.42k
    if (pad) {
153
1.36k
        while (str.size() % 8) {
154
0
            str += '=';
155
0
        }
156
1.36k
    }
157
3.42k
    return str;
158
3.42k
}
159
160
std::string EncodeBase32(std::string_view str, bool pad)
161
0
{
162
0
    return EncodeBase32(MakeUCharSpan(str), pad);
163
0
}
164
165
std::optional<std::vector<unsigned char>> DecodeBase32(std::string_view str)
166
0
{
167
0
    static const int8_t decode32_table[256]{
168
0
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
169
0
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
170
0
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1,
171
0
        -1, -1, -1, -1, -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
172
0
        15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1,  0,  1,  2,
173
0
         3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
174
0
        23, 24, 25, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
175
0
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
176
0
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
177
0
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
178
0
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
179
0
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
180
0
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
181
0
    };
182
183
0
    if (str.size() % 8 != 0) return {};
184
    /* 1, 3, 4, or 6 padding '=' suffix characters are permitted. */
185
0
    if (str.size() >= 1 && str.back() == '=') str.remove_suffix(1);
186
0
    if (str.size() >= 2 && str.substr(str.size() - 2) == "==") str.remove_suffix(2);
187
0
    if (str.size() >= 1 && str.back() == '=') str.remove_suffix(1);
188
0
    if (str.size() >= 2 && str.substr(str.size() - 2) == "==") str.remove_suffix(2);
189
190
0
    std::vector<unsigned char> ret;
191
0
    ret.reserve((str.size() * 5) / 8);
192
0
    bool valid = ConvertBits<5, 8, false>(
193
0
        [&](unsigned char c) { ret.push_back(c); },
194
0
        str.begin(), str.end(),
195
0
        [](char c) { return decode32_table[uint8_t(c)]; }
196
0
    );
197
198
0
    if (!valid) return {};
199
200
0
    return ret;
201
0
}
202
203
namespace {
204
template <typename T>
205
bool ParseIntegral(std::string_view str, T* out)
206
0
{
207
0
    static_assert(std::is_integral_v<T>);
208
    // Replicate the exact behavior of strtol/strtoll/strtoul/strtoull when
209
    // handling leading +/- for backwards compatibility.
210
0
    if (str.length() >= 2 && str[0] == '+' && str[1] == '-') {
211
0
        return false;
212
0
    }
213
0
    const std::optional<T> opt_int = ToIntegral<T>((!str.empty() && str[0] == '+') ? str.substr(1) : str);
214
0
    if (!opt_int) {
215
0
        return false;
216
0
    }
217
0
    if (out != nullptr) {
218
0
        *out = *opt_int;
219
0
    }
220
0
    return true;
221
0
}
Unexecuted instantiation: strencodings.cpp:_ZN12_GLOBAL__N_113ParseIntegralIiEEbNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPT_
Unexecuted instantiation: strencodings.cpp:_ZN12_GLOBAL__N_113ParseIntegralIxEEbNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPT_
Unexecuted instantiation: strencodings.cpp:_ZN12_GLOBAL__N_113ParseIntegralIhEEbNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPT_
Unexecuted instantiation: strencodings.cpp:_ZN12_GLOBAL__N_113ParseIntegralItEEbNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPT_
Unexecuted instantiation: strencodings.cpp:_ZN12_GLOBAL__N_113ParseIntegralIjEEbNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPT_
Unexecuted instantiation: strencodings.cpp:_ZN12_GLOBAL__N_113ParseIntegralIyEEbNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEEPT_
222
}; // namespace
223
224
bool ParseInt32(std::string_view str, int32_t* out)
225
0
{
226
0
    return ParseIntegral<int32_t>(str, out);
227
0
}
228
229
bool ParseInt64(std::string_view str, int64_t* out)
230
0
{
231
0
    return ParseIntegral<int64_t>(str, out);
232
0
}
233
234
bool ParseUInt8(std::string_view str, uint8_t* out)
235
0
{
236
0
    return ParseIntegral<uint8_t>(str, out);
237
0
}
238
239
bool ParseUInt16(std::string_view str, uint16_t* out)
240
0
{
241
0
    return ParseIntegral<uint16_t>(str, out);
242
0
}
243
244
bool ParseUInt32(std::string_view str, uint32_t* out)
245
0
{
246
0
    return ParseIntegral<uint32_t>(str, out);
247
0
}
248
249
bool ParseUInt64(std::string_view str, uint64_t* out)
250
0
{
251
0
    return ParseIntegral<uint64_t>(str, out);
252
0
}
253
254
std::string FormatParagraph(std::string_view in, size_t width, size_t indent)
255
0
{
256
0
    assert(width >= indent);
257
0
    std::stringstream out;
258
0
    size_t ptr = 0;
259
0
    size_t indented = 0;
260
0
    while (ptr < in.size())
261
0
    {
262
0
        size_t lineend = in.find_first_of('\n', ptr);
263
0
        if (lineend == std::string::npos) {
264
0
            lineend = in.size();
265
0
        }
266
0
        const size_t linelen = lineend - ptr;
267
0
        const size_t rem_width = width - indented;
268
0
        if (linelen <= rem_width) {
269
0
            out << in.substr(ptr, linelen + 1);
270
0
            ptr = lineend + 1;
271
0
            indented = 0;
272
0
        } else {
273
0
            size_t finalspace = in.find_last_of(" \n", ptr + rem_width);
274
0
            if (finalspace == std::string::npos || finalspace < ptr) {
275
                // No place to break; just include the entire word and move on
276
0
                finalspace = in.find_first_of("\n ", ptr);
277
0
                if (finalspace == std::string::npos) {
278
                    // End of the string, just add it and break
279
0
                    out << in.substr(ptr);
280
0
                    break;
281
0
                }
282
0
            }
283
0
            out << in.substr(ptr, finalspace - ptr) << "\n";
284
0
            if (in[finalspace] == '\n') {
285
0
                indented = 0;
286
0
            } else if (indent) {
287
0
                out << std::string(indent, ' ');
288
0
                indented = indent;
289
0
            }
290
0
            ptr = finalspace + 1;
291
0
        }
292
0
    }
293
0
    return out.str();
294
0
}
295
296
/** Upper bound for mantissa.
297
 * 10^18-1 is the largest arbitrary decimal that will fit in a signed 64-bit integer.
298
 * Larger integers cannot consist of arbitrary combinations of 0-9:
299
 *
300
 *   999999999999999999  1^18-1
301
 *  9223372036854775807  (1<<63)-1  (max int64_t)
302
 *  9999999999999999999  1^19-1     (would overflow)
303
 */
304
static const int64_t UPPER_BOUND = 1000000000000000000LL - 1LL;
305
306
/** Helper function for ParseFixedPoint */
307
static inline bool ProcessMantissaDigit(char ch, int64_t &mantissa, int &mantissa_tzeros)
308
0
{
309
0
    if(ch == '0')
310
0
        ++mantissa_tzeros;
311
0
    else {
312
0
        for (int i=0; i<=mantissa_tzeros; ++i) {
313
0
            if (mantissa > (UPPER_BOUND / 10LL))
314
0
                return false; /* overflow */
315
0
            mantissa *= 10;
316
0
        }
317
0
        mantissa += ch - '0';
318
0
        mantissa_tzeros = 0;
319
0
    }
320
0
    return true;
321
0
}
322
323
bool ParseFixedPoint(std::string_view val, int decimals, int64_t *amount_out)
324
0
{
325
0
    int64_t mantissa = 0;
326
0
    int64_t exponent = 0;
327
0
    int mantissa_tzeros = 0;
328
0
    bool mantissa_sign = false;
329
0
    bool exponent_sign = false;
330
0
    int ptr = 0;
331
0
    int end = val.size();
332
0
    int point_ofs = 0;
333
334
0
    if (ptr < end && val[ptr] == '-') {
335
0
        mantissa_sign = true;
336
0
        ++ptr;
337
0
    }
338
0
    if (ptr < end)
339
0
    {
340
0
        if (val[ptr] == '0') {
341
            /* pass single 0 */
342
0
            ++ptr;
343
0
        } else if (val[ptr] >= '1' && val[ptr] <= '9') {
344
0
            while (ptr < end && IsDigit(val[ptr])) {
345
0
                if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros))
346
0
                    return false; /* overflow */
347
0
                ++ptr;
348
0
            }
349
0
        } else return false; /* missing expected digit */
350
0
    } else return false; /* empty string or loose '-' */
351
0
    if (ptr < end && val[ptr] == '.')
352
0
    {
353
0
        ++ptr;
354
0
        if (ptr < end && IsDigit(val[ptr]))
355
0
        {
356
0
            while (ptr < end && IsDigit(val[ptr])) {
357
0
                if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros))
358
0
                    return false; /* overflow */
359
0
                ++ptr;
360
0
                ++point_ofs;
361
0
            }
362
0
        } else return false; /* missing expected digit */
363
0
    }
364
0
    if (ptr < end && (val[ptr] == 'e' || val[ptr] == 'E'))
365
0
    {
366
0
        ++ptr;
367
0
        if (ptr < end && val[ptr] == '+')
368
0
            ++ptr;
369
0
        else if (ptr < end && val[ptr] == '-') {
370
0
            exponent_sign = true;
371
0
            ++ptr;
372
0
        }
373
0
        if (ptr < end && IsDigit(val[ptr])) {
374
0
            while (ptr < end && IsDigit(val[ptr])) {
375
0
                if (exponent > (UPPER_BOUND / 10LL))
376
0
                    return false; /* overflow */
377
0
                exponent = exponent * 10 + val[ptr] - '0';
378
0
                ++ptr;
379
0
            }
380
0
        } else return false; /* missing expected digit */
381
0
    }
382
0
    if (ptr != end)
383
0
        return false; /* trailing garbage */
384
385
    /* finalize exponent */
386
0
    if (exponent_sign)
387
0
        exponent = -exponent;
388
0
    exponent = exponent - point_ofs + mantissa_tzeros;
389
390
    /* finalize mantissa */
391
0
    if (mantissa_sign)
392
0
        mantissa = -mantissa;
393
394
    /* convert to one 64-bit fixed-point value */
395
0
    exponent += decimals;
396
0
    if (exponent < 0)
397
0
        return false; /* cannot represent values smaller than 10^-decimals */
398
0
    if (exponent >= 18)
399
0
        return false; /* cannot represent values larger than or equal to 10^(18-decimals) */
400
401
0
    for (int i=0; i < exponent; ++i) {
402
0
        if (mantissa > (UPPER_BOUND / 10LL) || mantissa < -(UPPER_BOUND / 10LL))
403
0
            return false; /* overflow */
404
0
        mantissa *= 10;
405
0
    }
406
0
    if (mantissa > UPPER_BOUND || mantissa < -UPPER_BOUND)
407
0
        return false; /* overflow */
408
409
0
    if (amount_out)
410
0
        *amount_out = mantissa;
411
412
0
    return true;
413
0
}
414
415
std::string ToLower(std::string_view str)
416
0
{
417
0
    std::string r;
418
0
    r.reserve(str.size());
419
0
    for (auto ch : str) r += ToLower(ch);
420
0
    return r;
421
0
}
422
423
std::string ToUpper(std::string_view str)
424
0
{
425
0
    std::string r;
426
0
    r.reserve(str.size());
427
0
    for (auto ch : str) r += ToUpper(ch);
428
0
    return r;
429
0
}
430
431
std::string Capitalize(std::string str)
432
0
{
433
0
    if (str.empty()) return str;
434
0
    str[0] = ToUpper(str.front());
435
0
    return str;
436
0
}
437
438
std::optional<uint64_t> ParseByteUnits(std::string_view str, ByteUnit default_multiplier)
439
0
{
440
0
    if (str.empty()) {
441
0
        return std::nullopt;
442
0
    }
443
0
    auto multiplier = default_multiplier;
444
0
    char unit = str.back();
445
0
    switch (unit) {
446
0
    case 'k':
447
0
        multiplier = ByteUnit::k;
448
0
        break;
449
0
    case 'K':
450
0
        multiplier = ByteUnit::K;
451
0
        break;
452
0
    case 'm':
453
0
        multiplier = ByteUnit::m;
454
0
        break;
455
0
    case 'M':
456
0
        multiplier = ByteUnit::M;
457
0
        break;
458
0
    case 'g':
459
0
        multiplier = ByteUnit::g;
460
0
        break;
461
0
    case 'G':
462
0
        multiplier = ByteUnit::G;
463
0
        break;
464
0
    case 't':
465
0
        multiplier = ByteUnit::t;
466
0
        break;
467
0
    case 'T':
468
0
        multiplier = ByteUnit::T;
469
0
        break;
470
0
    default:
471
0
        unit = 0;
472
0
        break;
473
0
    }
474
475
0
    uint64_t unit_amount = static_cast<uint64_t>(multiplier);
476
0
    auto parsed_num = ToIntegral<uint64_t>(unit ? str.substr(0, str.size() - 1) : str);
477
0
    if (!parsed_num || parsed_num > std::numeric_limits<uint64_t>::max() / unit_amount) { // check overflow
478
0
        return std::nullopt;
479
0
    }
480
0
    return *parsed_num * unit_amount;
481
0
}