Skip to content

avro: bound VLQDecoder::long against overlong varints - #10407

Merged
Jefffrey merged 3 commits into
apache:mainfrom
STiFLeR7:fix/10290-vlq-decoder-overlong-varint
Jul 25, 2026
Merged

avro: bound VLQDecoder::long against overlong varints#10407
Jefffrey merged 3 commits into
apache:mainfrom
STiFLeR7:fix/10290-vlq-decoder-overlong-varint

Conversation

@STiFLeR7

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Rationale for this change

A malformed or malicious Avro file with an unterminated run of varint continuation bytes drives VLQDecoder::long's self.shift past 63, panicking inside << self.shift ("attempt to shift left with overflow") in debug builds. In release builds without overflow-checks the shift is silently masked instead — arguably worse, since the decoder keeps running and can produce a wrong value rather than fail loudly. Any service that reads attacker-controlled Avro bytes through ReaderBuilder::build can hit this from the public API.

There's a prior attempt at this in #9887 (closed by the stale bot, not rejected — no activity for 60 days). That PR used checked_shl(self.shift).unwrap_or(0) to silently drop overflowing contribution bits, which stops the panic but places no bound on how many continuation bytes it will consume, and @alamb asked whether malformed input should return an error instead of being silently absorbed — that question was never answered before the PR went stale.

What changes are included in this PR?

  • VLQDecoder::long now bounds the varint to 10 bytes, mirroring the bound already enforced by read_varint_array's handling of its 10th byte a few lines below in the same file, and returns AvroError::ParseError on an overlong/malformed varint instead of panicking or silently truncating. The decoder's internal state is reset on error so a subsequent call decodes a fresh varint cleanly.
  • This changes long's signature from Option<i64> to Result<Option<i64>, AvroError>, updating its 6 call sites in block.rs/header.rs. All of them already sit inside functions returning Result<_, AvroError> and already propagate sibling parse errors via ?, so each call site only needed a ? added. This is a purely internal, non-breaking change — vlq, block, and header are all private (non-pub) modules within arrow-avro, not part of the crate's public API.

Are these changes tested?

  • Added test_long_overlong_varint_returns_error reproducing the exact 19-byte input from the issue's cargo-fuzz repro (Avro magic + 13 0xFF continuation bytes), asserting the decoder now errors instead of panicking, and that it resets cleanly for the next call.
  • Added test_long_roundtrip, a direct unit test for VLQDecoder::long's happy path (zig-zag decode), which had no dedicated test before this change — only the unsigned read_varint/read_varint_array siblings were covered by test_varint.
  • Verified the exact fuzzer input from the issue no longer panics end-to-end through the public ReaderBuilder::build API (ad-hoc integration test, not committed).
  • cargo test -p arrow-avro --lib (402 passed), cargo clippy -p arrow-avro --all-targets -- -D warnings, and cargo fmt -p arrow-avro -- --check all clean.

Are there any user-facing changes?

No public API changes (vlq/block/header are private modules). Behaviorally, malformed/malicious Avro input that previously panicked (or silently produced garbage in a release build) now returns a AvroError::ParseError from the public decode/ReaderBuilder::build path, which is the intended, documented behavior for malformed input elsewhere in these same decoders.


This PR was prepared with AI assistance (Claude Code). I reviewed the root cause in the source, traced every call site of VLQDecoder::long to confirm the signature change is safe and non-breaking, verified the module-privacy claim before choosing this approach, and ran the fuzzer repro from the issue against the built fix myself before opening this PR.

A malformed or malicious Avro file with an unterminated run of varint
continuation bytes drove `self.shift` past 63, panicking inside
`<< self.shift` ("attempt to shift left with overflow") in debug builds.
In release builds without overflow-checks the shift is silently masked
instead, which is arguably worse: the decoder would keep running and
produce a wrong value rather than fail. Any service that reads
attacker-controlled Avro bytes through `ReaderBuilder::build` could hit
this.

`long` now bounds the varint to 10 bytes (mirroring the bound already
enforced by `read_varint_array`'s handling of its 10th byte a few lines
below) and returns `AvroError::ParseError` instead of panicking or
silently truncating. All 6 call sites in block.rs/header.rs already sit
inside functions returning `Result<_, AvroError>` and already propagate
sibling parse errors via `?`, so this is a purely internal, non-breaking
change - `vlq`, `block`, and `header` are private modules.

This directly resolves the open question from @alamb's review on the
stale, closed apache#9887 (apache#9887 (comment)):
whether malformed input should error rather than be silently absorbed.
The bound also fixes a gap in that PR's `checked_shl(..).unwrap_or(0)`
approach, which capped the accumulated value but placed no limit on how
many continuation bytes it would consume.

Fixes apache#10290

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions github-actions Bot added arrow Changes to the arrow crate arrow-avro arrow-avro crate labels Jul 22, 2026
/// bound already enforced by [`read_varint_array`] for the stateless decoders below).
pub fn long(&mut self, buf: &mut &[u8]) -> Result<Option<i64>, AvroError> {
while let Some(byte) = buf.first().copied() {
if self.shift == 63 && byte >= 0x02 {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding more branching to the loop makes me nervous...I'd like to see some benchmarks for this.

Perhaps something like

            self.in_progress |=
                ((byte & 0x7F) as u64)
                    .checked_shl(self.shift)
                    .ok_or(AvroError::ParseError(
                        "Malformed Avro varint: too many continuation bytes".to_string(),
                    ))?;

would work?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good news on the benchmark front first: Jefffrey's bot run above shows every avro_reader case at ~1.00x ratio vs. base (both wall-clock and CPU), so the extra branch isn't measurable here.

On the checked_shl suggestion specifically — I don't think it's equivalent, and I'd like to explain why before swapping it in. checked_shl(shift) only returns None when the shift amount itself is >= 64 (i.e. an 11th+ continuation byte). It does not detect the case that actually matters at the boundary: on the 10th byte (shift == 63), a byte like 0x7F (0b111_1111) shifted left by 63 doesn't overflow the shift-amount check at all — it just silently drops the top 6 bits of that byte's payload via ordinary << semantics, and checked_shl returns Some(...) with a truncated, wrong value rather than None. So that version would still decode a malformed 10-byte varint successfully, just to the wrong i64 — which is the same "silently produce something instead of erroring" outcome @alamb's review on the stale #9887 was trying to move away from (discussion).

The byte >= 0x02 check at shift == 63 is what actually catches that: it mirrors the exact bound read_varint_array already enforces for its own 10th byte a few lines below ((b < 0x02).then_some(...)), so this isn't a new convention, just applying the file's existing one to the stateful decoder too.

Given the benchmarks come back flat, I'd lean toward keeping the explicit check for correctness — but happy to revisit if you'd rather accept the narrower checked_shl guarantee (catches unbounded/DoS-style inputs, just not this one specific truncation case) for less branching.

@Jefffrey

Copy link
Copy Markdown
Contributor

run benchmarks decoder avro_reader

@adriangbot

This comment was marked as duplicate.

@adriangbot

This comment was marked as duplicate.

@adriangbot

Copy link
Copy Markdown

🤖 Arrow criterion benchmark completed (GKE) | trigger

Instance: c4a-highmem-16 (12 vCPU / 65 GiB)

CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected
Details

group                                              fix_10290-vlq-decoder-overlong-varint    main
-----                                              -------------------------------------    ----
array_creation/string_array_1000_chars             1.03     27.8±0.15µs        ? ?/sec      1.00     27.0±0.07µs        ? ?/sec
array_creation/string_array_100_chars              1.00      7.3±0.11µs        ? ?/sec      1.01      7.4±0.12µs        ? ?/sec
array_creation/string_array_10_chars               1.00      5.2±0.08µs        ? ?/sec      1.02      5.3±0.01µs        ? ?/sec
array_creation/string_view_1000_chars              1.02     29.0±0.67µs        ? ?/sec      1.00     28.4±0.24µs        ? ?/sec
array_creation/string_view_100_chars               1.00      8.6±0.08µs        ? ?/sec      1.00      8.6±0.06µs        ? ?/sec
array_creation/string_view_10_chars                1.02      5.7±0.01µs        ? ?/sec      1.00      5.6±0.08µs        ? ?/sec
avro_reader/string_array_1000_chars                1.00    247.9±0.47µs        ? ?/sec      1.01    251.4±0.41µs        ? ?/sec
avro_reader/string_array_100_chars                 1.00     61.2±0.30µs        ? ?/sec      1.01     61.7±0.30µs        ? ?/sec
avro_reader/string_array_10_chars                  1.01     41.9±0.19µs        ? ?/sec      1.00     41.6±0.36µs        ? ?/sec
avro_reader/string_view_1000_chars                 1.00    240.5±0.39µs        ? ?/sec      1.02    244.5±1.06µs        ? ?/sec
avro_reader/string_view_100_chars                  1.00     62.5±0.22µs        ? ?/sec      1.00     62.6±0.38µs        ? ?/sec
avro_reader/string_view_10_chars                   1.00     41.8±0.22µs        ? ?/sec      1.01     42.2±0.28µs        ? ?/sec
string_operations/string_array_value_1000_chars    1.00     92.2±0.02ns        ? ?/sec      1.00     92.0±0.19ns        ? ?/sec
string_operations/string_array_value_100_chars     1.00     91.8±0.02ns        ? ?/sec      1.00     92.2±0.04ns        ? ?/sec
string_operations/string_array_value_10_chars      1.00     91.9±0.03ns        ? ?/sec      1.00     92.0±0.06ns        ? ?/sec
string_operations/string_view_value_1000_chars     1.00    758.7±1.99ns        ? ?/sec      1.01    763.9±1.24ns        ? ?/sec
string_operations/string_view_value_100_chars      1.00    758.6±1.70ns        ? ?/sec      1.00    761.8±1.33ns        ? ?/sec
string_operations/string_view_value_10_chars       1.00    758.8±2.17ns        ? ?/sec      1.00    762.2±2.21ns        ? ?/sec

Resource Usage

base (merge-base)

Metric Value
Wall time 170.0s
Peak memory 15.7 MiB
Avg memory 7.1 MiB
CPU user 152.2s
CPU sys 11.8s
Peak spill 0 B

branch

Metric Value
Wall time 165.0s
Peak memory 15.7 MiB
Avg memory 10.1 MiB
CPU user 148.3s
CPU sys 11.9s
Peak spill 0 B

File an issue against this benchmark runner

@adriangbot

Copy link
Copy Markdown

🤖 Arrow criterion benchmark completed (GKE) | trigger

Instance: c4a-highmem-16 (12 vCPU / 65 GiB)

CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected
Details

group                       fix_10290-vlq-decoder-overlong-varint    main
-----                       -------------------------------------    ----
Array/100                   1.00      3.9±0.02µs   464.4 MB/sec      1.00      3.9±0.02µs   465.5 MB/sec
Array/10000                 1.00    364.0±1.38µs   599.4 MB/sec      1.00    364.0±0.90µs   599.2 MB/sec
Array/1000000               1.00    145.6±0.49µs   172.4 GB/sec      1.00    146.4±0.44µs   171.5 GB/sec
Binary(Bytes)/100           1.00  1443.2±18.74ns  1784.2 MB/sec      1.00  1445.9±18.11ns  1780.8 MB/sec
Binary(Bytes)/10000         1.01    118.7±0.92µs     2.1 GB/sec      1.00    117.9±0.39µs     2.1 GB/sec
Binary(Bytes)/1000000       1.00     47.3±0.11µs   531.7 GB/sec      1.00     47.2±0.05µs   532.3 GB/sec
Boolean/100                 1.00    880.8±5.96ns  1191.0 MB/sec      1.00    879.3±4.31ns  1193.0 MB/sec
Boolean/10000               1.00     73.9±0.41µs  1420.0 MB/sec      1.00     73.6±0.08µs  1425.8 MB/sec
Boolean/1000000             1.00     30.2±0.04µs   339.6 GB/sec      1.00     30.2±0.03µs   339.1 GB/sec
Date32/100                  1.00    958.5±8.26ns  1130.3 MB/sec      1.00    955.5±6.65ns  1133.8 MB/sec
Date32/10000                1.00     80.7±0.43µs  1439.4 MB/sec      1.00     80.4±0.36µs  1443.8 MB/sec
Date32/1000000              1.00     32.6±0.04µs   370.8 GB/sec      1.00     32.6±0.05µs   370.9 GB/sec
Decimal128/100              1.00      2.2±0.08µs   524.2 MB/sec      1.00      2.2±0.07µs   524.4 MB/sec
Decimal128/10000            1.00    174.9±1.07µs   708.3 MB/sec      1.01    177.0±3.52µs   699.7 MB/sec
Decimal128/1000000          1.00     70.8±0.55µs   183.8 GB/sec      1.03     73.0±1.89µs   178.3 GB/sec
Enum(Dictionary)/100        1.00  1448.6±18.39ns   724.2 MB/sec      1.00  1441.4±16.93ns   727.8 MB/sec
Enum(Dictionary)/10000      1.01     92.6±0.89µs  1133.2 MB/sec      1.00     92.0±0.51µs  1140.0 MB/sec
Enum(Dictionary)/1000000    1.00     37.7±0.09µs   272.1 GB/sec      1.00     37.8±0.14µs   271.3 GB/sec
FixedSizeBinary/100         1.00    982.3±8.05ns     2.5 GB/sec      1.00    979.9±6.64ns     2.5 GB/sec
FixedSizeBinary/10000       1.00     75.0±0.71µs     3.2 GB/sec      1.00     74.6±0.21µs     3.2 GB/sec
FixedSizeBinary/1000000     1.00     29.5±0.20µs   819.9 GB/sec      1.00     29.5±0.14µs   819.6 GB/sec
Float32/100                 1.00    816.6±8.79ns  1634.9 MB/sec      1.00    813.5±7.64ns  1641.3 MB/sec
Float32/10000               1.01     64.6±0.74µs     2.0 GB/sec      1.00     63.8±0.32µs     2.0 GB/sec
Float32/1000000             1.01     26.2±0.09µs   497.2 GB/sec      1.00     26.0±0.06µs   501.2 GB/sec
Float64/100                 1.00   822.4±11.31ns     2.0 GB/sec      1.00   822.5±13.07ns     2.0 GB/sec
Float64/10000               1.01     65.5±0.75µs     2.6 GB/sec      1.00     64.6±0.48µs     2.6 GB/sec
Float64/1000000             1.01     26.5±0.09µs   632.3 GB/sec      1.00     26.3±0.04µs   636.8 GB/sec
Int32/100                   1.00    955.4±7.16ns  1133.9 MB/sec      1.00   953.3±10.60ns  1136.5 MB/sec
Int32/10000                 1.01     80.7±0.37µs  1439.1 MB/sec      1.00     80.3±0.32µs  1446.4 MB/sec
Int32/1000000               1.00     32.7±0.11µs   369.7 GB/sec      1.00     32.7±0.08µs   370.5 GB/sec
Int32_Id/100                1.00    955.3±6.41ns   634.9 MB/sec      1.00    959.0±3.85ns   632.4 MB/sec
Int32_Id/10000              1.00     79.3±0.52µs   862.7 MB/sec      1.00     79.6±0.44µs   859.2 MB/sec
Int32_Id/1000000            1.00     32.0±0.18µs   232.6 GB/sec      1.01     32.2±0.02µs   231.4 GB/sec
Int64/100                   1.00    951.0±7.96ns  1139.2 MB/sec      1.00    950.1±8.55ns  1140.3 MB/sec
Int64/10000                 1.00     80.4±0.53µs  1444.1 MB/sec      1.00     80.2±0.61µs  1447.0 MB/sec
Int64/1000000               1.00     32.4±0.05µs   373.3 GB/sec      1.00     32.5±0.11µs   372.3 GB/sec
Interval/100                1.00   1219.2±7.13ns  1720.9 MB/sec      1.00  1213.1±11.53ns  1729.5 MB/sec
Interval/10000              1.00     92.3±0.55µs     2.2 GB/sec      1.00     92.0±0.27µs     2.2 GB/sec
Interval/1000000            1.00     37.5±0.08µs   546.7 GB/sec      1.00     37.4±0.07µs   547.5 GB/sec
Map/100                     1.00      8.4±0.04µs   379.8 MB/sec      1.00      8.4±0.06µs   380.2 MB/sec
Map/10000                   1.00    821.0±1.51µs   405.5 MB/sec      1.00    818.5±2.06µs   406.8 MB/sec
Map/1000000                 1.00    334.7±0.61µs   101.2 GB/sec      1.00    333.3±1.06µs   101.7 GB/sec
Mixed/100                   1.00      3.2±0.05µs   840.8 MB/sec      1.00      3.2±0.02µs   842.6 MB/sec
Mixed/10000                 1.00    265.2±0.80µs  1159.4 MB/sec      1.00    264.9±1.08µs  1160.8 MB/sec
Mixed/1000000               1.00    107.3±0.10µs   311.3 GB/sec      1.00    107.1±0.24µs   311.9 GB/sec
Nested(Struct)/100          1.00      2.5±0.02µs   776.1 MB/sec      1.04      2.6±0.25µs   746.9 MB/sec
Nested(Struct)/10000        1.00    203.2±0.41µs   993.6 MB/sec      1.00    203.3±0.94µs   993.4 MB/sec
Nested(Struct)/1000000      1.00     82.9±0.26µs   247.0 GB/sec      1.00     83.2±0.52µs   246.1 GB/sec
String/100                  1.01  1484.3±15.54ns  1303.0 MB/sec      1.00  1476.0±14.53ns  1310.3 MB/sec
String/10000                1.00    128.1±1.25µs  1561.2 MB/sec      1.00    127.8±1.33µs  1564.0 MB/sec
String/1000000              1.00     52.3±0.22µs   385.4 GB/sec      1.00     52.1±0.11µs   386.6 GB/sec
StringView/100              1.01      2.5±0.02µs   777.4 MB/sec      1.00      2.5±0.02µs   782.8 MB/sec
StringView/10000            1.01    211.8±2.19µs   944.1 MB/sec      1.00    209.7±1.69µs   953.3 MB/sec
StringView/1000000          1.00     85.4±0.12µs   235.9 GB/sec      1.00     85.6±0.30µs   235.3 GB/sec
TimeMicros/100              1.00    993.6±8.47ns  1238.2 MB/sec      1.00    990.5±7.10ns  1242.0 MB/sec
TimeMicros/10000            1.00     85.6±0.51µs  1548.3 MB/sec      1.00     85.4±0.49µs  1550.9 MB/sec
TimeMicros/1000000          1.00     34.6±0.06µs   400.2 GB/sec      1.00     34.6±0.01µs   399.9 GB/sec
TimeMillis/100              1.00    968.0±8.53ns  1180.3 MB/sec      1.00    965.9±8.48ns  1182.9 MB/sec
TimeMillis/10000            1.01     82.4±0.45µs  1501.9 MB/sec      1.00     81.9±0.25µs  1510.5 MB/sec
TimeMillis/1000000          1.00     33.4±0.06µs   389.0 GB/sec      1.00     33.5±0.07µs   388.0 GB/sec
TimestampMicros/100         1.00  1181.4±14.19ns  1453.0 MB/sec      1.00  1177.3±13.94ns  1458.1 MB/sec
TimestampMicros/10000       1.00     95.2±0.68µs  1802.5 MB/sec      1.00     95.0±1.09µs  1807.5 MB/sec
TimestampMicros/1000000     1.00     38.6±0.05µs   434.1 GB/sec      1.01     38.9±0.15µs   431.4 GB/sec
TimestampMillis/100         1.00  1112.9±12.63ns  1371.1 MB/sec      1.00  1109.5±14.42ns  1375.2 MB/sec
TimestampMillis/10000       1.01     88.3±0.71µs  1728.6 MB/sec      1.00     87.7±0.48µs  1739.5 MB/sec
TimestampMillis/1000000     1.00     35.7±0.11µs   417.6 GB/sec      1.00     35.7±0.06µs   417.6 GB/sec
UUID/100                    1.00      3.2±0.03µs  1392.5 MB/sec      1.00      3.2±0.03µs  1393.1 MB/sec
UUID/10000                  1.00    292.2±0.57µs  1534.1 MB/sec      1.00    290.8±1.43µs  1541.5 MB/sec
UUID/1000000                1.01    118.7±0.28µs   368.8 GB/sec      1.00    118.0±0.52µs   371.0 GB/sec

Resource Usage

base (merge-base)

Metric Value
Wall time 1805.4s
Peak memory 879.3 MiB
Avg memory 378.9 MiB
CPU user 1796.7s
CPU sys 3.1s
Peak spill 0 B

branch

Metric Value
Wall time 1805.4s
Peak memory 882.9 MiB
Avg memory 393.9 MiB
CPU user 1797.4s
CPU sys 2.7s
Peak spill 0 B

File an issue against this benchmark runner

Comment thread arrow-avro/src/reader/vlq.rs Outdated
Comment thread arrow-avro/src/reader/vlq.rs Outdated
Per @Jefffrey's review: the decoder is stateful across calls, so
"buf starts with more than 10 continuation bytes" was misleading -
those bytes can accumulate across multiple `long` calls, not just
within a single `buf` slice. Simplified the wording and referenced
`i64`, the type actually being decoded, rather than the internal
`u64` accumulator.

@Jefffrey Jefffrey left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we're probably fine in terms of performance; this decode path isnt exactly a hot path, its mainly for decoding lengths before blocks where blocks hold the actual data

@Jefffrey
Jefffrey merged commit b8b59fb into apache:main Jul 25, 2026
@Jefffrey

Copy link
Copy Markdown
Contributor

Thanks @STiFLeR7 & @etseidl

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

arrow Changes to the arrow crate arrow-avro arrow-avro crate bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

avro: VLQDecoder::long doesn't protect against invalid input

4 participants