Potential issue on ARM32 processors with taking a reference of a newly-inserted map value

Hi, I’m new here. Fixed an interesting segfault today. Here’s how it goes.

I have a std::map of the following struct:

struct State {
size_t a;
boost::posix_time::ptime b;
bool c;
bool d;
};

std::map<std::string, State> myMap;

Then I do a find-or-create operation and take a reference to the result:

std::string key = arbitraryString();
State& s = myMap[key];

Now, as long as I only touch the first two members of the struct, all is rosy.
s.a++;
s.b = boost::posix_time::microsec_clock::universal_time();

But I found that as soon as I touched s.c or s.d, a segfault occurs at State& s = myMap[key];:

if (!s.c) { … // Eventually causes a segfault at the assignment of s. Before the segfault, the value of s.c is often random data and not the expected value.

While this segfault is spurious, it is frequent enough to reproduce reliably.
Unfortunately, this may be impractical to reproduce for many of you as it only crashed on two of our armv7 hosts: a Motorola G6, and a Samsung J6. arm64 hosts appear to be unaffected.

My suspicion is that a temporary struct is being created with myMap[key], and the reference of that is being returned before it is added to the map, but the memory is invalidated shortly after.

It could be related to caching/register optimization, as the issue doesn’t happen if State::c and State::d are declared before State::a.

The issue doesn’t occur in std::unordered_map.

Clang details:
Android (5220042 based on r346389c) clang version 8.0.7 (https://android.googlesource.com/toolchain/clang b55f2d4ebfd35bf643d27dbca1bb228957008617) (https://android.googlesource.com/toolchain/llvm 3c393fe7a7e13b0fba4ac75a01aa683d7a5b11cd) (based on LLVM 8.0.7svn)
Target: armv7a-unknown-linux-android29
Thread model: posix

libc++ details:
Actually I really don’t know how to get this. Help:
ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/bin/arm-linux-androideabi-readelf -a ./ndk-bundle/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libc++.so.28
readelf: Error: ./ndk-bundle/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libc++.so.28: Failed to read file header

Is anyone able to confirm? What more information do I need to collect?

You’re reading from uninitialized memory. That’s undefined behavior.

There shouldn’t be any uninitialized reads here, as State should be value-initialized:

If an insertion is performed, the mapped value is value-initialized (default-constructed for class types, zero-initialized otherwise) and a reference to it is returned.

Indeed, this problem also exists if the members of the struct are all assigned default values in the definition:

struct State {
size_t a = 0;
boost::posix_time::ptime b = boost::posix_time::microsec_clock::universal_time();
bool c = false;
bool d = false;
};

There shouldn’t be any uninitialized reads here, as State should be value-initialized:

If an insertion is performed, the mapped value is value-initialized (default-constructed for class types, zero-initialized otherwise) and a reference to it is returned.

The default constructor which is called does not zero initialize the members.

Indeed, this problem also exists if the members of the struct are all assigned default values in the definition:

To help any further I would need to see a full compilable example which reproduces the error on your end, and not just code snippets.

have you tried running this code under the ASAN, MSAN, and UBSAN?