From 04d09a6427b6a10e5ca62397fc76cf83c8e7a252 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Wed, 14 Oct 2026 11:21:32 +0200 Subject: [PATCH] Fix undefined behaviour in const_fixed_iterator with negative offsets sizeof(value_type) * val converted a negative difference_type to unsigned, so it += -1, it[-1] and std::prev() overflowed the pointer. Cast the size to difference_type before multiplying. --- include/protozero/iterators.hpp | 10 +++++----- test/unit/test_iterators.cpp | 28 ++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/include/protozero/iterators.hpp b/include/protozero/iterators.hpp index 0f44c465..7a1bc433 100644 --- a/include/protozero/iterators.hpp +++ b/include/protozero/iterators.hpp @@ -243,30 +243,30 @@ class const_fixed_iterator { } const_fixed_iterator& operator+=(difference_type val) noexcept { - m_data += (sizeof(value_type) * val); + m_data += (static_cast(sizeof(value_type)) * val); return *this; } friend const_fixed_iterator operator+(const_fixed_iterator lhs, difference_type rhs) noexcept { const_fixed_iterator tmp{lhs}; - tmp.m_data += (sizeof(value_type) * rhs); + tmp.m_data += (static_cast(sizeof(value_type)) * rhs); return tmp; } friend const_fixed_iterator operator+(difference_type lhs, const_fixed_iterator rhs) noexcept { const_fixed_iterator tmp{rhs}; - tmp.m_data += (sizeof(value_type) * lhs); + tmp.m_data += (static_cast(sizeof(value_type)) * lhs); return tmp; } const_fixed_iterator& operator-=(difference_type val) noexcept { - m_data -= (sizeof(value_type) * val); + m_data -= (static_cast(sizeof(value_type)) * val); return *this; } friend const_fixed_iterator operator-(const_fixed_iterator lhs, difference_type rhs) noexcept { const_fixed_iterator tmp{lhs}; - tmp.m_data -= (sizeof(value_type) * rhs); + tmp.m_data -= (static_cast(sizeof(value_type)) * rhs); return tmp; } diff --git a/test/unit/test_iterators.cpp b/test/unit/test_iterators.cpp index db00a9fb..9a33531a 100644 --- a/test/unit/test_iterators.cpp +++ b/test/unit/test_iterators.cpp @@ -40,3 +40,31 @@ TEST_CASE("get_packed varint rejects a truncated trailing varint") { REQUIRE_THROWS_AS(item.get_packed_uint64(), protozero::end_of_buffer_exception); } + +TEST_CASE("fixed_iterator supports negative offsets") { + // field 1, wire type 2; length 12; fixed32 values 1, 2 and 3. + const std::string buffer{"\x0a\x0c\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00", 14}; + protozero::pbf_reader item{buffer}; + + REQUIRE(item.next()); + const auto range = item.get_packed_fixed32(); + const auto end = range.end(); + + REQUIRE(*std::prev(end) == 3); + REQUIRE(end[-1] == 3); + REQUIRE(end[-3] == 1); + REQUIRE(*(end + -2) == 2); + REQUIRE(*(-2 + end) == 2); + REQUIRE(*(end - 2) == 2); + REQUIRE(*(range.begin() - -1) == 2); + + auto it = end; + it += -1; + REQUIRE(*it == 3); + it -= 1; + REQUIRE(*it == 2); + it -= -1; + REQUIRE(*it == 3); + REQUIRE(it - end == -1); + REQUIRE(range.begin() - end == -3); +}