Skip to content

Possible .setZero() integer overflow

Summary

While compiling Ceres Solver 2.2.0 against Eigen at head with LTO enabled, the following warning is issued multiple times:

In function ‘run’,
    inlined from ‘setZero’ at /home/sergiu/Projects/eigen/Eigen/src/Core/CwiseNullaryOp.h:553:42,
    inlined from ‘operator=.isra’ at /home/sergiu/Projects/eigen/Eigen/src/SparseCore/SparseMatrix.h:1548:73:
/home/sergiu/Projects/eigen/Eigen/src/Core/Fill.h:128:11: warning: ‘__builtin_memset’ specified bound 18446744073709551612 exceeds maximum object size 9223372036854775807 [-Wstringop-overflow=]
  128 |     memset(dst_ptr, 0, num_bytes);
      |  

From the above error message it is not clear what code in Ceres triggers the warnings. However, after inspecting the corresponding Eigen code it looks like it does not properly handle possible overflows. Due to the use of signed integers this causes undefined behavior.

Environment

  • Operating System : Linux
  • Architecture : x86_64
  • Eigen Version : bea7f7c5 (5.0.0)
  • Compiler Version : GCC 15.2.1
  • Compile Flags : -O3 -DNDEBUG -flto=auto -fuse-ld=mold
  • Vector Extension : SSE2

Minimal Example

#include <Eigen/SparseCore>

#include <vector>

struct TripletSparseMatrix {
    const int* cols() const { return cols_; }
    const int* rows() const { return rows_; }
    int num_cols() const { return num_cols_; }
    int num_rows() const { return num_rows_; }
    int num_nonzeros() const { return num_nonzeros_; }

    const int* rows_ = nullptr;
    const int* cols_ = nullptr;
    int num_cols_ = 0;
    int num_rows_ = 0;
    int num_nonzeros_ = 0;
};

void foo(const TripletSparseMatrix& block_jacobian_transpose) {
    using SparseMatrix = Eigen::SparseMatrix<int>;
    using Triplet = Eigen::Triplet<int>;

    const int* rows = block_jacobian_transpose.rows();
    const int* cols = block_jacobian_transpose.cols();
    int num_nonzeros = block_jacobian_transpose.num_nonzeros();
    std::vector<Triplet> triplets;
    triplets.reserve(num_nonzeros);
    for (int i = 0; i < num_nonzeros; ++i) {
        triplets.emplace_back(cols[i], rows[i], 1);
    }

    SparseMatrix block_jacobian(block_jacobian_transpose.num_cols(),
                                block_jacobian_transpose.num_rows());
    block_jacobian.setFromTriplets(triplets.begin(), triplets.end());
}

int main(int argc, char** argv) {}

Compile as

> g++ -O3 -DNDEBUG  -I ~/Projects/eigen eigen5-setzero.cpp 
In file included from /home/sergiu/Projects/eigen/Eigen/Core:345,
                 from /home/sergiu/Projects/eigen/Eigen/SparseCore:11,
                 from eigen5-setzero.cpp:1:
In static member function ‘static void Eigen::internal::eigen_zero_impl<Xpr, true>::run(Xpr&) [with Xpr = Eigen::Map<Eigen::Matrix<int, -1, 1>, 0, Eigen::Stride<0, 0> >]’,
    inlined from ‘Derived& Eigen::DenseBase<Derived>::setZero() [with Derived = Eigen::Map<Eigen::Matrix<int, -1, 1>, 0, Eigen::Stride<0, 0> >]’ at /home/sergiu/Projects/eigen/Eigen/src/Core/CwiseNullaryOp.h:553:42,
    inlined from ‘Eigen::SparseMatrix<Scalar_, Options_, StorageIndex_>& Eigen::SparseMatrix<Scalar_, Flags_, StorageIndex_>::operator=(const Eigen::SparseMatrixBase<OtherDerived>&) [with OtherDerived = Eigen::SparseMatrix<int, 1, int>; Scalar_ = int; int Options_ = 0; StorageIndex_ = int]’ at /home/sergiu/Projects/eigen/Eigen/src/SparseCore/SparseMatrix.h:1548:73:
/home/sergiu/Projects/eigen/Eigen/src/Core/Fill.h:128:11: warning: ‘void* memset(void*, int, size_t)’ specified bound 18446744073709551612 exceeds maximum object size 9223372036854775807 [-Wstringop-overflow=]
  128 |     memset(dst_ptr, 0, num_bytes);
      |     ~~~~~~^~~~~~~~~~~~~~~~~~~~~~~

Another reproducer that triggers the warning:

#include <Eigen/SparseCore>

#include <vector>

struct CRSMatrix {
  int num_rows{0};
  int num_cols{0};

  std::vector<int> cols;
  std::vector<int> rows;
  std::vector<double> values;
};

void bar(CRSMatrix& jacobian)
{
  using EigenSparseMatrix = Eigen::SparseMatrix<double, Eigen::ColMajor>;

  EigenSparseMatrix sparse_jacobian =
      Eigen::Map<Eigen::SparseMatrix<double, Eigen::RowMajor>>(
          jacobian.num_rows,
          jacobian.num_cols,
          static_cast<Eigen::Index>(jacobian.values.size()),
          jacobian.rows.data(),
          jacobian.cols.data(),
          jacobian.values.data());
}

int main(int argc, char** argv) {}
Edited by Sergiu Deitsch