Coverage Report

Created: 2026-08-05 14:35

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