c++c++17eigeneigen3

Why does indexing with an array of negative indexes in Eigen not raise an assertion error?


I'm porting an algorithm from python which uses one array to index another. In the python algorithm, -1 values are used in the index array as a flag to indicate invalid data. I expected this to raise an assertion error in Eigen, as would occur if you tried to access an array with an out of bounds index, but to do my surprise, it did not.

Here is a simple example of the situation I encountered:

Eigen::Array4i values;
values << 0, 1, 2, 3;
Eigen::Array4i values2;
values2 << 0, 1, 2, 3;
Eigen::Array4i indexes;
indexes << -1, -2, -1, -2;
std::cout << values(indexes) << "\n\n";
std::cout << values2(indexes) << "\n\n";
// std::cout << values(-1); //eigen_assert(index >= 0 && index < size()) fails.

Which outputs (both running with the debugger and with the -O3 flag):

0
1
0
1

3
2
3
2

So clearly this isn't working as it would in python (not very surprising), and the fact it isn't consistent makes me think this might be undefined behavior returning junk values. This leads me to my questions:

  1. Why does this not raise an assertion error?
  2. Is this behavior of returning (potentially junk values) instead of raising an error or a segfault reliable? In my program, since the -1 values already indicate invalid data, I don't actually care about the what the index returns, as I will mask it out later anyways.

I am using Eigen 3.4.0 and c++17


Solution

  • Using negative indices in Eigen is undefined behavior it is printing the memory before the array and there are no assertions for indexing using arrays in Eigen's codebase, you should stop using -1 for invalid indices.

    note that the single element accessor is checked, values(-1) triggers an assert in debug mode, but using an array of indices does not, both are still undefined behavior.

    The good news is that address sanitizer in both gcc and clang can catch it, see godbolt demo unfortunately MSVC address sanitizer cannot catch it, even then they won't detect all the invalid uses, for example if you index into a 2d array you will get the wrong result but still be inside buffer bounds, so it is not detected.

    It is not printing "junk", it is spilling the contents of the stack/heap, depending on where the matrix is stored (for example dynamic matrices are allocated on the heap) this could segfault the application, or leak your users credentials, it is completely undefined behavior.