Coverage Report

Created: 2026-09-06 13:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/tmp/bitcoin/src/univalue/lib/univalue_read.cpp
Line
Count
Source
1
// Copyright 2014 BitPay Inc.
2
// Copyright (c) 2015-present The Bitcoin Core developers
3
// Distributed under the MIT software license, see the accompanying
4
// file COPYING or https://opensource.org/licenses/mit-license.php.
5
6
#include <univalue.h>
7
#include <univalue_utffilter.h>
8
9
#include <cstdint>
10
#include <cstring>
11
#include <string>
12
#include <string_view>
13
#include <utility>
14
#include <vector>
15
16
/*
17
 * According to stackexchange, the original json test suite wanted
18
 * to limit depth to 22.  Widely-deployed PHP bails at depth 512,
19
 * so we will follow PHP's lead, which should be more than sufficient
20
 * (further stackexchange comments indicate depth > 32 rarely occurs).
21
 */
22
static constexpr size_t MAX_JSON_DEPTH = 512;
23
24
static bool json_isdigit(int ch)
25
8.00M
{
26
8.00M
    return ((ch >= '0') && (ch <= '9'));
27
8.00M
}
28
29
// convert hexadecimal string to unsigned integer
30
static const char *hatoui(const char *first, const char *last,
31
                          unsigned int& out)
32
670
{
33
670
    unsigned int result = 0;
34
3.35k
    for (; first != last; ++first)
35
2.68k
    {
36
2.68k
        int digit;
37
2.68k
        if (json_isdigit(*first))
38
1.36k
            digit = *first - '0';
39
40
1.31k
        else if (*first >= 'a' && *first <= 'f')
41
1.29k
            digit = *first - 'a' + 10;
42
43
22
        else if (*first >= 'A' && *first <= 'F')
44
22
            digit = *first - 'A' + 10;
45
46
0
        else
47
0
            break;
48
49
2.68k
        result = 16 * result + digit;
50
2.68k
    }
51
670
    out = result;
52
53
670
    return first;
54
670
}
55
56
enum jtokentype getJsonToken(std::string& tokenVal, unsigned int& consumed,
57
                            const char *raw, const char *end)
58
6.75M
{
59
6.75M
    tokenVal.clear();
60
6.75M
    consumed = 0;
61
62
6.75M
    const char *rawStart = raw;
63
64
8.67M
    while (raw < end && (json_isspace(*raw)))          // skip whitespace
65
1.92M
        raw++;
66
67
6.75M
    if (raw >= end)
68
185k
        return JTOK_NONE;
69
70
6.57M
    switch (*raw) {
71
72
321k
    case '{':
73
321k
        raw++;
74
321k
        consumed = (raw - rawStart);
75
321k
        return JTOK_OBJ_OPEN;
76
321k
    case '}':
77
321k
        raw++;
78
321k
        consumed = (raw - rawStart);
79
321k
        return JTOK_OBJ_CLOSE;
80
124k
    case '[':
81
124k
        raw++;
82
124k
        consumed = (raw - rawStart);
83
124k
        return JTOK_ARR_OPEN;
84
123k
    case ']':
85
123k
        raw++;
86
123k
        consumed = (raw - rawStart);
87
123k
        return JTOK_ARR_CLOSE;
88
89
955k
    case ':':
90
955k
        raw++;
91
955k
        consumed = (raw - rawStart);
92
955k
        return JTOK_COLON;
93
925k
    case ',':
94
925k
        raw++;
95
925k
        consumed = (raw - rawStart);
96
925k
        return JTOK_COMMA;
97
98
796
    case 'n':
99
16.6k
    case 't':
100
19.0k
    case 'f':
101
19.0k
        if (!strncmp(raw, "null", 4)) {
102
794
            raw += 4;
103
794
            consumed = (raw - rawStart);
104
794
            return JTOK_KW_NULL;
105
18.2k
        } else if (!strncmp(raw, "true", 4)) {
106
15.8k
            raw += 4;
107
15.8k
            consumed = (raw - rawStart);
108
15.8k
            return JTOK_KW_TRUE;
109
15.8k
        } else if (!strncmp(raw, "false", 5)) {
110
2.39k
            raw += 5;
111
2.39k
            consumed = (raw - rawStart);
112
2.39k
            return JTOK_KW_FALSE;
113
2.39k
        } else
114
7
            return JTOK_ERR;
115
116
40.1k
    case '-':
117
420k
    case '0':
118
1.06M
    case '1':
119
1.38M
    case '2':
120
1.56M
    case '3':
121
1.70M
    case '4':
122
1.76M
    case '5':
123
1.85M
    case '6':
124
1.91M
    case '7':
125
2.02M
    case '8':
126
2.09M
    case '9': {
127
        // part 1: int
128
2.09M
        std::string numStr;
129
130
2.09M
        const char *first = raw;
131
132
2.09M
        const char *firstDigit = first;
133
2.09M
        if (!json_isdigit(*firstDigit))
134
40.1k
            firstDigit++;
135
2.09M
        if ((*firstDigit == '0') && json_isdigit(firstDigit[1]))
136
1
            return JTOK_ERR;
137
138
2.09M
        numStr += *raw;                       // copy first char
139
2.09M
        raw++;
140
141
2.09M
        if ((*first == '-') && (raw < end) && (!json_isdigit(*raw)))
142
0
            return JTOK_ERR;
143
144
6.57M
        while (raw < end && json_isdigit(*raw)) {  // copy digits
145
4.47M
            numStr += *raw;
146
4.47M
            raw++;
147
4.47M
        }
148
149
        // part 2: frac
150
2.09M
        if (raw < end && *raw == '.') {
151
53.9k
            numStr += *raw;                   // copy .
152
53.9k
            raw++;
153
154
53.9k
            if (raw >= end || !json_isdigit(*raw))
155
0
                return JTOK_ERR;
156
567k
            while (raw < end && json_isdigit(*raw)) { // copy digits
157
513k
                numStr += *raw;
158
513k
                raw++;
159
513k
            }
160
53.9k
        }
161
162
        // part 3: exp
163
2.09M
        if (raw < end && (*raw == 'e' || *raw == 'E')) {
164
23.3k
            numStr += *raw;                   // copy E
165
23.3k
            raw++;
166
167
23.3k
            if (raw < end && (*raw == '-' || *raw == '+')) { // copy +/-
168
23.3k
                numStr += *raw;
169
23.3k
                raw++;
170
23.3k
            }
171
172
23.3k
            if (raw >= end || !json_isdigit(*raw))
173
3
                return JTOK_ERR;
174
70.1k
            while (raw < end && json_isdigit(*raw)) { // copy digits
175
46.7k
                numStr += *raw;
176
46.7k
                raw++;
177
46.7k
            }
178
23.3k
        }
179
180
2.09M
        tokenVal = numStr;
181
2.09M
        consumed = (raw - rawStart);
182
2.09M
        return JTOK_NUMBER;
183
2.09M
        }
184
185
1.68M
    case '"': {
186
1.68M
        raw++;                                // skip "
187
188
1.68M
        std::string valStr;
189
1.68M
        JSONUTF8StringFilter writer(valStr);
190
191
1.17G
        while (true) {
192
1.17G
            if (raw >= end || (unsigned char)*raw < 0x20)
193
4
                return JTOK_ERR;
194
195
1.17G
            else if (*raw == '\\') {
196
724
                raw++;                        // skip backslash
197
198
724
                if (raw >= end)
199
0
                    return JTOK_ERR;
200
201
724
                switch (*raw) {
202
15
                case '"':  writer.push_back('\"'); break;
203
5
                case '\\': writer.push_back('\\'); break;
204
2
                case '/':  writer.push_back('/'); break;
205
5
                case 'b':  writer.push_back('\b'); break;
206
5
                case 'f':  writer.push_back('\f'); break;
207
8
                case 'n':  writer.push_back('\n'); break;
208
5
                case 'r':  writer.push_back('\r'); break;
209
5
                case 't':  writer.push_back('\t'); break;
210
211
670
                case 'u': {
212
670
                    unsigned int codepoint;
213
670
                    if (raw + 1 + 4 >= end ||
214
670
                        hatoui(raw + 1, raw + 1 + 4, codepoint) !=
215
670
                               raw + 1 + 4)
216
0
                        return JTOK_ERR;
217
670
                    writer.push_back_u(codepoint);
218
670
                    raw += 4;
219
670
                    break;
220
670
                    }
221
4
                default:
222
4
                    return JTOK_ERR;
223
224
724
                }
225
226
720
                raw++;                        // skip esc'd char
227
720
            }
228
229
1.17G
            else if (*raw == '"') {
230
1.68M
                raw++;                        // skip "
231
1.68M
                break;                        // stop scanning
232
1.68M
            }
233
234
1.17G
            else {
235
1.17G
                writer.push_back(static_cast<unsigned char>(*raw));
236
1.17G
                raw++;
237
1.17G
            }
238
1.17G
        }
239
240
1.68M
        if (!writer.finalize())
241
4
            return JTOK_ERR;
242
1.68M
        tokenVal = valStr;
243
1.68M
        consumed = (raw - rawStart);
244
1.68M
        return JTOK_STRING;
245
1.68M
        }
246
247
26
    default:
248
26
        return JTOK_ERR;
249
6.57M
    }
250
6.57M
}
251
252
enum expect_bits : unsigned {
253
    EXP_OBJ_NAME = (1U << 0),
254
    EXP_COLON = (1U << 1),
255
    EXP_ARR_VALUE = (1U << 2),
256
    EXP_VALUE = (1U << 3),
257
    EXP_NOT_VALUE = (1U << 4),
258
};
259
260
23.9M
#define expect(bit) (expectMask & (EXP_##bit))
261
5.72M
#define setExpect(bit) (expectMask |= EXP_##bit)
262
5.92M
#define clearExpect(bit) (expectMask &= ~EXP_##bit)
263
264
bool UniValue::read(std::string_view json)
265
185k
{
266
185k
    UniValue parsed;
267
185k
    if (!parsed.read_impl(json)) {
268
80
        setNull();
269
80
        return false;
270
80
    }
271
185k
    *this = std::move(parsed);
272
185k
    return true;
273
185k
}
274
275
bool UniValue::read_impl(std::string_view str_in)
276
185k
{
277
185k
    clear();
278
279
185k
    uint32_t expectMask = 0;
280
185k
    std::vector<UniValue*> stack;
281
282
185k
    std::string tokenVal;
283
185k
    unsigned int consumed;
284
185k
    enum jtokentype tok = JTOK_NONE;
285
185k
    enum jtokentype last_tok = JTOK_NONE;
286
185k
    const char* raw{str_in.data()};
287
185k
    const char* end{raw + str_in.size()};
288
4.77M
    do {
289
4.77M
        last_tok = tok;
290
291
4.77M
        tok = getJsonToken(tokenVal, consumed, raw, end);
292
4.77M
        if (tok == JTOK_NONE || tok == JTOK_ERR)
293
48
            return false;
294
4.77M
        raw += consumed;
295
296
4.77M
        bool isValueOpen = jsonTokenIsValue(tok) ||
297
4.77M
            tok == JTOK_OBJ_OPEN || tok == JTOK_ARR_OPEN;
298
299
4.77M
        if (expect(VALUE)) {
300
955k
            if (!isValueOpen)
301
2
                return false;
302
955k
            clearExpect(VALUE);
303
304
3.81M
        } else if (expect(ARR_VALUE)) {
305
353k
            bool isArrValue = isValueOpen || (tok == JTOK_ARR_CLOSE);
306
353k
            if (!isArrValue)
307
2
                return false;
308
309
353k
            clearExpect(ARR_VALUE);
310
311
3.46M
        } else if (expect(OBJ_NAME)) {
312
1.01M
            bool isObjName = (tok == JTOK_OBJ_CLOSE || tok == JTOK_STRING);
313
1.01M
            if (!isObjName)
314
4
                return false;
315
316
2.44M
        } else if (expect(COLON)) {
317
955k
            if (tok != JTOK_COLON)
318
3
                return false;
319
955k
            clearExpect(COLON);
320
321
1.49M
        } else if (!expect(COLON) && (tok == JTOK_COLON)) {
322
1
            return false;
323
1
        }
324
325
4.77M
        if (expect(NOT_VALUE)) {
326
2.26M
            if (isValueOpen)
327
2
                return false;
328
2.26M
            clearExpect(NOT_VALUE);
329
2.26M
        }
330
331
4.77M
        switch (tok) {
332
333
321k
        case JTOK_OBJ_OPEN:
334
445k
        case JTOK_ARR_OPEN: {
335
445k
            VType utyp = (tok == JTOK_OBJ_OPEN ? VOBJ : VARR);
336
445k
            if (!stack.size()) {
337
185k
                if (utyp == VOBJ)
338
184k
                    setObject();
339
737
                else
340
737
                    setArray();
341
185k
                stack.push_back(this);
342
260k
            } else {
343
260k
                UniValue tmpVal(utyp);
344
260k
                UniValue *top = stack.back();
345
260k
                top->values.push_back(tmpVal);
346
347
260k
                UniValue *newTop = &(top->values.back());
348
260k
                stack.push_back(newTop);
349
260k
            }
350
351
445k
            if (stack.size() > MAX_JSON_DEPTH)
352
2
                return false;
353
354
445k
            if (utyp == VOBJ)
355
321k
                setExpect(OBJ_NAME);
356
124k
            else
357
124k
                setExpect(ARR_VALUE);
358
445k
            break;
359
445k
            }
360
361
321k
        case JTOK_OBJ_CLOSE:
362
444k
        case JTOK_ARR_CLOSE: {
363
444k
            if (!stack.size() || (last_tok == JTOK_COMMA))
364
2
                return false;
365
366
444k
            VType utyp = (tok == JTOK_OBJ_CLOSE ? VOBJ : VARR);
367
444k
            UniValue *top = stack.back();
368
444k
            if (utyp != top->getType())
369
1
                return false;
370
371
444k
            stack.pop_back();
372
444k
            clearExpect(OBJ_NAME);
373
444k
            setExpect(NOT_VALUE);
374
444k
            break;
375
444k
            }
376
377
955k
        case JTOK_COLON: {
378
955k
            if (!stack.size())
379
0
                return false;
380
381
955k
            UniValue *top = stack.back();
382
955k
            if (top->getType() != VOBJ)
383
0
                return false;
384
385
955k
            setExpect(VALUE);
386
955k
            break;
387
955k
            }
388
389
925k
        case JTOK_COMMA: {
390
925k
            if (!stack.size() ||
391
925k
                (last_tok == JTOK_COMMA) || (last_tok == JTOK_ARR_OPEN))
392
0
                return false;
393
394
925k
            UniValue *top = stack.back();
395
925k
            if (top->getType() == VOBJ)
396
695k
                setExpect(OBJ_NAME);
397
229k
            else
398
229k
                setExpect(ARR_VALUE);
399
925k
            break;
400
925k
            }
401
402
793
        case JTOK_KW_NULL:
403
16.6k
        case JTOK_KW_TRUE:
404
19.0k
        case JTOK_KW_FALSE: {
405
19.0k
            UniValue tmpVal;
406
19.0k
            switch (tok) {
407
793
            case JTOK_KW_NULL:
408
                // do nothing more
409
793
                break;
410
15.8k
            case JTOK_KW_TRUE:
411
15.8k
                tmpVal.setBool(true);
412
15.8k
                break;
413
2.39k
            case JTOK_KW_FALSE:
414
2.39k
                tmpVal.setBool(false);
415
2.39k
                break;
416
0
            default: /* impossible */ break;
417
19.0k
            }
418
419
19.0k
            if (!stack.size()) {
420
40
                *this = tmpVal;
421
40
                break;
422
40
            }
423
424
18.9k
            UniValue *top = stack.back();
425
18.9k
            top->values.push_back(tmpVal);
426
427
18.9k
            setExpect(NOT_VALUE);
428
18.9k
            break;
429
19.0k
            }
430
431
295k
        case JTOK_NUMBER: {
432
295k
            UniValue tmpVal(VNUM, tokenVal);
433
295k
            if (!stack.size()) {
434
69
                *this = tmpVal;
435
69
                break;
436
69
            }
437
438
295k
            UniValue *top = stack.back();
439
295k
            top->values.push_back(tmpVal);
440
441
295k
            setExpect(NOT_VALUE);
442
295k
            break;
443
295k
            }
444
445
1.68M
        case JTOK_STRING: {
446
1.68M
            if (expect(OBJ_NAME)) {
447
955k
                UniValue *top = stack.back();
448
955k
                top->keys.push_back(tokenVal);
449
955k
                clearExpect(OBJ_NAME);
450
955k
                setExpect(COLON);
451
955k
            } else {
452
732k
                UniValue tmpVal(VSTR, tokenVal);
453
732k
                if (!stack.size()) {
454
3
                    *this = tmpVal;
455
3
                    break;
456
3
                }
457
732k
                UniValue *top = stack.back();
458
732k
                top->values.push_back(tmpVal);
459
732k
            }
460
461
1.68M
            setExpect(NOT_VALUE);
462
1.68M
            break;
463
1.68M
            }
464
465
0
        default:
466
0
            return false;
467
4.77M
        }
468
4.77M
    } while (!stack.empty ());
469
470
    /* Check that nothing follows the initial construct (parsed above).  */
471
185k
    tok = getJsonToken(tokenVal, consumed, raw, end);
472
185k
    if (tok != JTOK_NONE)
473
13
        return false;
474
475
185k
    return true;
476
185k
}