Skip to content

Fix out-of-bounds access in EXI bitstream bounds check#129

Open
lippit wants to merge 2 commits into
EVerest:mainfrom
lippit:main
Open

Fix out-of-bounds access in EXI bitstream bounds check#129
lippit wants to merge 2 commits into
EVerest:mainfrom
lippit:main

Conversation

@lippit

@lippit lippit commented Jun 24, 2026

Copy link
Copy Markdown

Fix out-of-bounds access in EXI bitstream bounds check and undefined behavior in V2GTP header parsing

Fix 1:
exi_bitstream_has_overflow only advanced and validated byte_pos when bit_count reached a full byte (EXI_BITSTREAM_MAX_BIT_COUNT). It never checked that the byte about to be accessed was within the stream before exi_bitstream_read_bit and exi_bitstream_write_bit dereference data[byte_pos]. This allowed two out-of-bounds accesses:

  • Empty input (data_size == 0): the first bit access dereferences data[0] on a zero-length buffer. Every message is decoded by first reading the 8-bit EXI header, so all decoders are reachable with a zero-byte input.
  • End-of-buffer off-by-one: when bit_count rolls over at the last valid byte, byte_pos is advanced to data_size (one past the end) and the next access touches data[data_size].

Fix 2:
When left-shifting uint8_t bytes from stream_data, each operand is implicitly promoted to signed int. Shifting a value into the sign bit (e.g. stream_data[4] << 24) is undefined behavior in C, which can lead to incorrect payload length and payload id values depending on the compiler.

Describe your changes

Add a bounds check after the byte-advance block that rejects when byte_pos >= data_size. It is placed after the advance so it validates the position actually about to be dereferenced, and it runs even when bit_count is 0, so empty input and a bad init offset are caught as well. The now-redundant else-return is removed, since the new check covers it; the increment stays guarded so byte_pos is not pushed past the end on the overflow path.

Cast the operands to uint16_t / uint32_t before shifting so the operations are performed on unsigned types of the intended width.

Checklist before requesting a review

  • I have performed a self-review of my code
  • I have made corresponding changes to the documentation
  • I read the contribution documentation and made sure that my changes meet its requirements

exi_bitstream_has_overflow only advanced and validated byte_pos when
bit_count reached a full byte (EXI_BITSTREAM_MAX_BIT_COUNT). It never
checked that the byte about to be accessed was within the stream before
exi_bitstream_read_bit and exi_bitstream_write_bit dereference
data[byte_pos]. This allowed two out-of-bounds accesses:

- Empty input (data_size == 0): the first bit access dereferences
  data[0] on a zero-length buffer. Every message is decoded by first
  reading the 8-bit EXI header, so all decoders are reachable with a
  zero-byte input.
- End-of-buffer off-by-one: when bit_count rolls over at the last valid
  byte, byte_pos is advanced to data_size (one past the end) and the
  next access touches data[data_size].

Signed-off-by: Georg Lipič <georg.lippitsch@gmx.at>
@lippit lippit requested review from barsnick and chausGit as code owners June 24, 2026 12:24
@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@barsnick barsnick self-assigned this Jun 25, 2026
When left-shifting uint8_t bytes from stream_data, each operand is
implicitly promoted to signed int. Shifting a value into the sign bit
(e.g. stream_data[4] << 24) is undefined behavior in C, which can lead
to incorrect payload length and payload id values depending on the
compiler.

Cast the operands to uint16_t / uint32_t before shifting so the
operations are performed on unsigned types of the intended width.

Signed-off-by: Georg Lipič <georg.lippitsch@gmx.at>
@barsnick

Copy link
Copy Markdown
Contributor

Good catch.

I will take a look and do some testing.

@lippit

lippit commented Jun 29, 2026

Copy link
Copy Markdown
Author

Happy to also submit a PR to the main project with regenerate files.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants