#35281 univalue: respect token end pointer
| Reviewability | Reviews | Agreement | Size | |||||||
|---|---|---|---|---|---|---|---|---|---|---|
| #35281 univalue: respect token end pointer | Ready | 0 (+1) | Blocked | 31 + 3 tests | ||||||
|
|
|
|
| ||||||
Reviewability: ReadyStalePaused Agreement: StrongPositivePositive w/ caveatsNeutralMildDisputedBlockedCrickets Reviews: current code-review ACKs, then (+stale ACKs) and -NACKs; greener = more ACKs. Size: lines added or modified outside tests, then in tests; greener = smaller. | ||||||||||
Goal
Enforces buffer end boundaries in UniValue's JSON tokenizer when matching keyword literals and inspecting number prefixes. Replaces `strncmp` keyword checks with `std::string_view::starts_with` and adds bounds checks before indexing into adjacent digit characters. Also updates the UniValue unit tests, roundtrip test data, and fuzz harness to exercise non-null-terminated string views.
Problem: UniValue's `getJsonToken` accepts explicit `raw` and `end` pointers to parse bounded string views without requiring null termination. In two places—matching `null`, `true`, and `false` via `strncmp`, and checking `firstDigit[1]` to reject leading zeros—the parser inspected bytes past `end` if the input terminated prematurely, causing out-of-bounds reads.
Evidence of importance:
- Fixes #28260 ('getJsonToken assumes underlying string is null-terminated but requires end pointer')
- PR description: 'Avoid reading past the supplied end pointer in two places: matching JSON keywords with strncmp; validating number prefixes with firstDigit[1].'
Scope: Modifies internal UniValue parsing code, strips trailing newlines from UniValue test JSON files to eliminate rtrim logic, and changes the parse_univalue fuzz target from std::string to std::string_view.
Risk of a mistake: medium (rpc_api, other). A flaw in JSON tokenization could incorrectly reject valid JSON or crash the node during RPC and REST request handling.
Changed since the previous assessment: Removed trailing whitespace trimming from the UniValue unit tester by stripping newlines from round-trip JSON test files.
Reviewability
Ready
Nothing known that would invalidate a review now.
Waiting on (from the thread read):
- author: address maflcko's objection regarding test buffer isolation 2026-08-07
- reviewer: sedited to re-review following requested rebase 2026-10-02
Agreement
Blocked
objection that needs a change, nobody has replied (maflcko)
Thread notes: sedited requested a rebase which ferminquant completed; maflcko's objection regarding bounded input test isolation remains open.
Objections, suggestions, and questions:
| Reviewer | Type | Harm, request, or question | Status | Must be | Author replied | Quote |
|---|---|---|---|---|---|---|
| maflcko (member) | question, style | why the ? syntax is needed in parse_univalue fuzz target | fixed · fix pushed · author confirmed it was not needed and removed it in a commit | answered | yes | 2026-05-13: 'why the `?`?' Status from: 2026-05-14: ferminquant: 'You're right, it's not needed. I fixed it in a new commit.' |
| carloantinarella (contributor) | suggestion, scope | add tests for fractions and exponent numbers | accepted · fix pushed · author added requested tests and reviewer verified with tACK | answered | yes | 2026-06-08: 'Would it be the case to add also few tests for fractions and exp numbers?' Status from: 2026-06-09: carloantinarella: 'Thanks for the added tests.' |
| sedited (member) | suggestion, maintenance | reduce redundant unit tests and avoid introducing a new helper utility | fixed · fix pushed · author reduced unit tests to minimal regressions and removed the helper | answered | yes | 2026-07-06: 'I think most of these unit tests are redundant however. Also not sure we should be introducing a new utility for this.' Status from: 2026-08-06: ferminquant: 'Reduced the unit coverage to the minimal bounded-end regressions and removed the extra helper.' |
| sedited (member) | suggestion, approach | use std::string_view raw_view{raw, end} instead of pointer arithmetic | fixed · fix pushed · author updated std::string_view construction in push | answered | yes | 2026-07-06: 'I would prefer if this used the more straight forward `std::string_view raw_view{raw, end}` than doing pointer arithmetic.' Status from: 2026-08-06: ferminquant: 'Updated the std::string_view construction.' |
| maflcko (member) | suggestion, maintenance | remove test as it is covered by the fuzz test and other tests | fixed · fix pushed · author removed redundant test coverage | answered | no | 2026-08-04: 'can be removed as well, given the fuzz test, and the other tests.' Status from: 2026-08-06: ferminquant: 'Reduced the unit coverage to the minimal bounded-end regressions and removed the extra helper.' |
| maflcko (member) | suggestion, approach | use a string view on a buffer instead of copying | fixed · fix pushed · author kept fixture inputs as std::string_view without copying | answered | yes | 2026-08-07: 'This should be a string view on a buffer, like in the fuzz test' Status from: 2026-08-07: ferminquant: 'Kept fixture inputs as std::string_view without copying.' |
| maflcko (member) | suggestion, approach | use a correctly-sized buffer without null-termination or move to a fixture file | fixed · fix pushed · author updated bounded_input_test to use non-null-terminated buffers | answered | yes | 2026-08-07: 'Seems fine, but this should either be a correctly-sized buffer (without null-term), or it should be moved to a fixture file.' Status from: 2026-08-07: ferminquant: 'Changed bounded_input_test() to use correctly sized, non-null-terminated buffers.' |
| maflcko (member) | objection, correctness | test is adding back a null-byte, defeating non-null-terminated buffer testing | fixed · fix pushed · author removed no_nul_test() | changed | yes | 2026-08-07: 'Well, this is adding back the null-byte' Status from: 2026-08-07: ferminquant: 'Removed no_nul_test().' |
| maflcko (member) | objection, correctness | tests do not properly isolate bounded input if trailing data or whitespace is present | open | changed | no | 2026-08-07: 'Either this needs to be copied into a fresh buffer (so that truly no data follows), or the tests need to be adjusted to not have trailing whitespace.' |
Support:
- carloantinarella (contributor) (Tested ACK, 2026-06-09) [tested]: tested after additional coverage for fractions and exponents was added
- sedited (member) (Concept ACK, 2026-07-06) [read]: fixes two separate out of bounds reads
Participants:
- maflcko (member, objection, 6 statements 2026-05-13 to 2026-08-07): concerned tests do not properly isolate bounded input if trailing data or whitespace is present
- carloantinarella (contributor, support, 2 statements 2026-06-08 to 2026-06-09): Tested ACK after fractions and exponent tests were added
- sedited (member, support, 7 statements 2026-07-06 to 2026-10-02): Concept ACK for fixing out-of-bounds reads; suggested dropping redundant tests and requested a rebase
Files
5 lines under test/bench/ci.
- src/univalue/test/unitester.cpp +13/-15
- src/univalue/lib/univalue_read.cpp +11/-6
- src/test/fuzz/parse_univalue.cpp +3/-2
- src/univalue/test/round1.json +1/-1
- src/univalue/test/round2.json +1/-1
- src/univalue/test/round3.json +1/-1
- src/univalue/test/round4.json +1/-1
- src/univalue/test/round5.json +1/-1
- src/univalue/test/round6.json +1/-1
- src/univalue/test/round7.json +1/-1
Card
The short text the ranking pass compares across PRs.
Fixes out-of-bounds reads in UniValue's JSON tokenizer by respecting the buffer end pointer during keyword matching and number validation. Previously, parsing non-null-terminated string views could read past the buffer end when encountering truncated keywords or bare minus/zero prefixes. This resolves issue #28260 and updates UniValue unit and fuzz tests to exercise bounded string views.
Data
thread read through 2026-10-02 (20 statements), head 045b0ef7b4
labels: RPC/REST/ZMQ
Categories
RPC / REST / ZMQ
P3 · bug fix (ranking pass: Moved up 8: out-of-bounds reads in the JSON tokenizer used by every RPC request is the most security-adjacent item left in P3.)
Fixes out-of-bounds reads in JSON parsing when matching keywords or digit prefixes without null termination (#28260). Because RPC inputs are typically null-terminated strings in practice, this provides a worthwhile hardening fix rather than resolving an active failure.
Membership: Carries the RPC/REST/ZMQ label and fixes token parsing in the UniValue JSON library used by RPC interfaces.
Factors: security/stability 1, bug 1, performance 0, user value 0, leverage 0
Utilities (logging, arguments, libraries)
P1 · bug fix (assessed alone as P2; Raised to P1: out-of-bounds reads in the shared JSON tokenizer are a memory-safety defect in a core parsing primitive, which this category ranks first.)
Enforces buffer boundaries in UniValue's getJsonToken to eliminate two out-of-bounds reads when processing non-null-terminated string views (#28260). Correctness and memory-safety fixes in vendored parsing primitives carry durable value across the codebase.
Membership: Directly modifies src/univalue/lib/univalue_read.cpp, part of the vendored UniValue library covered by utilities.
Factors: security/stability 1, bug 1, performance 0, user value 0, leverage 0