Bug 487993 - Alignment error when using Eigen with Valgrind and -m32
Summary: Alignment error when using Eigen with Valgrind and -m32
Status: RESOLVED FIXED
Alias: None
Product: valgrind
Classification: Developer tools
Component: general (show other bugs)
Version: 3.21.0
Platform: RedHat Enterprise Linux Linux
: NOR normal
Target Milestone: ---
Assignee: Paul Floyd
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2024-06-03 20:09 UTC by joebobrocks123456789
Modified: 2024-06-05 19:03 UTC (History)
1 user (show)

See Also:
Latest Commit:
Version Fixed In:
Sentry Crash Report:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description joebobrocks123456789 2024-06-03 20:09:00 UTC
The following c++ code fails with misaligned assertion only when using valgrind:
```
#include "eigen-3.4.0/Eigen/Geometry"
#include <iostream>

int main(int argc, char** argv) {
    Eigen::Affine3d* test;

    test = new Eigen::Affine3d();

    std::cout << test << std::endl;

    delete test;

    return 0;
}
```

When compiled and run normally with `g++ main.cpp -m32 -std=c++17`, the code runs properly and prints the address of `test`. When run with valgrind, however, we get `a.out: eigen-3.4.0/Eigen/src/Core/DenseStorage.h:109: Eigen::internal::plain_array<T, Size, MatrixOrArrayOptions, 16>::plain_array() [with T = double; int Size = 16; int MatrixOrArrayOptions = 0]: Assertion `(internal::UIntPtr(eigen_unaligned_array_assert_workaround_gcc47(array)) & (15)) == 0 && "this assertion is explained here: " "http://eigen.tuxfamily.org/dox-devel/group__TopicUnalignedArrayAssert.html" " **** READ THIS WEB PAGE !!! ****"' failed.`.

Note that this only occurs with the `m32` flag set.

How to reproduce:

* Download Eigen 3.4.0 (https://gitlab.com/libeigen/eigen/-/releases/3.4.0) and place in project root
* Copy the c++ code to `main.cpp`
* run `g++ main.cpp -m32 -std=c++17`
* run `./a.out` and confirm it succeeds
* run `valgrind ./a.out` and confirm it fails
Comment 1 Paul Floyd 2024-06-04 05:15:12 UTC
Can you try with the latest Valgrind? There have been a lot of changes to aligned allocators and deallocators, mainly in Valgrind 3.22.
Comment 2 Paul Floyd 2024-06-04 05:47:02 UTC
And I just tried with Valgrind 3.23 and git HEAD on FreeBSD 14.0 amd64 16.0.6 and it works OK.
Comment 3 Paul Floyd 2024-06-04 06:15:22 UTC
^^^
clang++ 16.0.6
Comment 4 joebobrocks123456789 2024-06-04 17:37:39 UTC
I have tried it with clang++ and it seems to work with valgrind 3.22, but g++ (13.2.0) still fails for me on valgrind 3.22
Comment 5 Paul Floyd 2024-06-04 20:22:33 UTC
I also get the assert on Fedora 40.

As a workaround you can use "--alignment=16".

However, I still don't quite understand what is happening. The operator new that gets called is just plain bog standard operator new(size_t) - not the aligned overload. That means Eigen::Affine3d doesn't have any alignment specifier. Operator new just calls malloc. GNU libc has a complicated way of setting the base alignment if malloc.

https://elixir.bootlin.com/glibc/glibc-2.39/source/sysdeps/generic/malloc-alignment.h

#define MALLOC_ALIGNMENT (2 * SIZE_SZ < __alignof__ (long double) \
			  ? __alignof__ (long double) : 2 * SIZE_SZ)

long double is peculiar on x86. It's 10 bytes, its sizeof is 12 but its alignof is 4.  SIZE_SZ is sizof size_t, 4. 2*SIZE_SZ is not less than alignof long double (4) so MALLOC_ALIGNMENT is 2*SIZE_SZ, or 8.

That is the same as what Valgrind uses.

The assert fires here

│      101 template <typename T, int Size, int MatrixOrArrayOptions> 
│      102 struct plain_array<T, Size, MatrixOrArrayOptions, 16>
│      103 {
│      104   EIGEN_ALIGN_TO_BOUNDARY(16) T array[Size];
│      105
│      106   EIGEN_DEVICE_FUNC
│      107   plain_array()
│      108   {
│  >   109     EIGEN_MAKE_UNALIGNED_ARRAY_ASSERT(15); 
│      110     check_static_allocation_size<T,Size>();
│      111   } 

Array is using some kind of alignas specifier, asking for 16 byte alignment. Then the assert checks that it is 16 byte aligned.

But array (in Valgrind) is 8 byte aligned:
(gdb) p &array
$8 = (double (*)[16]) 0x45df258

plain_array is wrapped in several classes. I thought that alignment propagated out from aggregate types.

If I add this

    std::cout << "alignof Eigen::Affine3d " << alignof(Eigen::Affine3d) << '\n';

and it says

alignof Eigen::Affine3d 16

which is what I expected.

When I build with clang++ I see

│      121 operator new (std::size_t sz, std::align_val_t al)

So that explains why clang++ is working.

I need to look more at what libc is doing.
Comment 6 Paul Floyd 2024-06-05 07:36:34 UTC
I think that I see the cause.

https://elixir.bootlin.com/glibc/glibc-2.37/source/sysdeps/i386/malloc-alignment.h#L22

GNU libc has a special case for x86 bumping up the alignment to 16 bytes.
Comment 7 Paul Floyd 2024-06-05 19:03:17 UTC
commit d66d3ab3017582ee41c459b812f695af405da44c (HEAD -> master, origin/master, origin/HEAD)
Author: Paul Floyd <pjfloyd@wanadoo.fr>
Date:   Wed Jun 5 21:01:22 2024 +0200

    Bug 487993 - Alignment error when using Eigen with Valgrind and -m32
    
    Not tested on Solaris, but I think that x86 is more or less unused
    on that platform these days.