From d77c131df33d88c555593692c291f4198e074a82 Mon Sep 17 00:00:00 2001 From: Sergei Zimmerman Date: Thu, 5 Feb 2026 02:01:50 +0300 Subject: [PATCH] libutil: Add overflow check to alignUp Old code with size + (size % 8 ? 8 - (size % 8) : 0) also suffered from this. --- src/libutil-tests/alignment.cc | 28 +++++++++++++++++++++++ src/libutil/include/nix/util/alignment.hh | 9 ++++++-- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/libutil-tests/alignment.cc b/src/libutil-tests/alignment.cc index bef0c435dc0..d68bcde4f47 100644 --- a/src/libutil-tests/alignment.cc +++ b/src/libutil-tests/alignment.cc @@ -15,4 +15,32 @@ TEST(alignUp, notAPowerOf2) ASSERT_DEATH({ alignUp(1u, 42); }, "alignment must be a power of 2"); } +template +class alignUpOverflowTest : public ::testing::Test +{}; + +using UnsignedTypes = ::testing::Types; +TYPED_TEST_SUITE(alignUpOverflowTest, UnsignedTypes); + +TYPED_TEST(alignUpOverflowTest, lastSafeValue) +{ + constexpr auto max = std::numeric_limits::max(); + ASSERT_EQ(alignUp(max - 15, 16), (max - 15) & ~TypeParam{15}); + ASSERT_NO_THROW(alignUp(max - 15, 16)); +} + +TYPED_TEST(alignUpOverflowTest, overflowThrows) +{ + constexpr auto max = std::numeric_limits::max(); + ASSERT_THROW(alignUp(max - 14, 16), Error); + ASSERT_THROW(alignUp(max, 16), Error); + ASSERT_THROW(alignUp(max, 2), Error); +} + +TYPED_TEST(alignUpOverflowTest, alignmentOneNeverOverflows) +{ + constexpr auto max = std::numeric_limits::max(); + ASSERT_EQ(alignUp(max, 1), max); +} + } // namespace nix diff --git a/src/libutil/include/nix/util/alignment.hh b/src/libutil/include/nix/util/alignment.hh index a4e5af4d6c0..4bf48f11576 100644 --- a/src/libutil/include/nix/util/alignment.hh +++ b/src/libutil/include/nix/util/alignment.hh @@ -1,9 +1,11 @@ #pragma once ///@file +#include "nix/util/error.hh" + #include +#include #include -#include #include namespace nix { @@ -16,7 +18,10 @@ template constexpr T alignUp(T val, unsigned alignment) { assert(std::has_single_bit(alignment) && "alignment must be a power of 2"); - T mask = ~(T{alignment} - 1u); + assert(alignment <= std::numeric_limits::max()); + T mask = ~(static_cast(alignment) - 1u); + if (val > std::numeric_limits::max() - (alignment - 1)) /* Overflow check. */ + throw Error("can't align %d to %d: value is too large", val, alignment); return (val + alignment - 1) & mask; }