SIGFPE when accessing unordered_map
Asked Answered
D

5

17

I have an unordered_map<Block, int> with Block being a simple struct defined as follows:

struct Block {
    size_t start;
    size_t end;

    bool operator==(const Block& b) const {
        return start == b.start && end == b.end;
    }
};

namespace std {
template<>
struct hash<Block> {
    size_t operator()(const Block& b) const {
        return b.start;
    }
};
} 

When trying to access the map, I do get the following error message in gdb (same for both g++ 4.7.1 as well as clang++ 3.1):

Program received signal SIGFPE, Arithmetic exception.
0x0000000000401e0b in std::__detail::_Mod_range_hashing::operator() (this=0x7fffffffd8e0, __num=0, __den=0)
    at /usr/include/c++/4.7/bits/hashtable_policy.h:245
245     { return __num % __den; }

My libstdc++ version is 3.4.17 (i.e. the version from GCC 4.7)

Relevant backtrace:

#0  0x0000000000401e0b in std::__detail::_Mod_range_hashing::operator() (this=0x7fffffffd8e0, __num=0, __den=0)
    at /usr/include/c++/4.7/bits/hashtable_policy.h:245
#1  0x0000000000407199 in std::__detail::_Hash_code_base<Block, std::pair<Block const, int>, std::_Select1st<std::pair<Block const, int> >, std::hash<Block>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, true>::_M_bucket_index (this=0x7fffffffd8e0, __c=0, __n=0) at /usr/include/c++/4.7/bits/hashtable_policy.h:787
#2  0x0000000000405230 in std::_Hashtable<Block, std::pair<Block const, int>, std::allocator<std::pair<Block const, int> >, std::_Select1st<std::pair<Block const, int> >, std::equal_to<Block>, std::hash<Block>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, true, false, true>::_M_bucket_index
    (this=0x7fffffffd8e0, __k=..., __c=0) at /usr/include/c++/4.7/bits/hashtable.h:466
#3  0x00000000004038de in std::__detail::_Map_base<Block, std::pair<Block const, int>, std::_Select1st<std::pair<Block const, int> >, true, std::_Hashtable<Block, std::pair<Block const, int>, std::allocator<std::pair<Block const, int> >, std::_Select1st<std::pair<Block const, int> >, std::equal_to<Block>, std::hash<Block>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, true, false, true> >::at (
    this=0x7fffffffd8e0, __k=...) at /usr/include/c++/4.7/bits/hashtable_policy.h:474
#4  0x0000000000403001 in SplicedAlignment::FindOptimalEndBlock() const::{lambda(Block const&)#1}::operator()(Block const&) const (__closure=0x7fffffffd990, block=...) at splicing.cpp:151
#5  0x00000000004040b3 in std::for_each<__gnu_cxx::__normal_iterator<Block const*, std::vector<Block, std::allocator<Block> > >, SplicedAlignment::FindOptimalEndBlock() const::{lambda(Block const&)#1}>(__gnu_cxx::__normal_iterator<Block const*, std::vector<Block, std::allocator<Block> > >, SplicedAlignment::FindOptimalEndBlock() const::{lambda(Block const&)#1}, SplicedAlignment::FindOptimalEndBlock() const::{lambda(Block const&)#1}) (__first=..., __last=..., __f=...)
    at /usr/include/c++/4.7/bits/stl_algo.h:4442

Edit: I didn't think it would actually make a difference where I call the function as long as I give it the same arguments, but apparently it does:

std::for_each(blocks.begin(), blocks.end(), [&](const Block& block) {
   map.at(block);
}

leads to the error, while just having:

const Block& block = blocks[0];
map.at(block);

works perfectly fine (blocks being a simple vector<Block>&)

Dielu answered 27/11, 2012 at 9:16 Comment(13)
Unrelated to your problem, but you should never put functions/classes/etc. in the std namespace.Bluish
Here the possible duplicate #8286603Adaptive
@Sergey I saw that one, but the solution doesn't seem to have anything to do with my problem - the whole code is in a single header file actually (and yes stupid from me not to note that in the original question, mea culpa).Dielu
Also, what is glibc++? GCC comes with a libstdc++ which has the same version as the compiler, and clang have a libc++.Bluish
Thirdly, please add the GDB backtrace as well.Bluish
@Joachim No idea when the g smuggled itself in there ;) Also added the gdb backtrace. About the std namespace: As I understand it that's the way to go if I don't want to specify the hash function everytime I declare a map with my struct - if there's a better way I'm all ears obviously, but since I'm only specializing an existing struct that shouldn't be that bad should it?Dielu
Do you get the same problem with a trivial hash function, such as return 42?Giese
@Giese Yes I do, the problem doesn't seem to be the hashfunction itself, but the fact that the internal variable den of the map is 0 when it obviously shouldn't be.Dielu
According to your backtrace you use GNU libstdc++, not clang's libc++. Also post SplicedAlignment::FindOptimalEndBlock codeActivity
@aleguna Yes the backtrace is from the g++ compilation, I can include the clang one as well if that would help (the actual error line is both times in hashtable_policy.h:245 though). Added the code that actually calls the function too.Dielu
Can you post some sample data? What data do you have in blocks? I can't recreate it on my computer.Apollyon
For now I've just replaced the unordered_map with boost's hashmap which works perfectly fine. But yeah I'll probably look into this some time later when I've enough time. Still quite strange, because I don't play around with pointers at all, so how would I corrupt the hashmap? Could be an actual bug in the library.Dielu
@JoachimPileborg, what do you mean by your first comment? Specializing std::hash must be done in std:: and it is perfectly legitimate. Your advice is good, but specialization is an exception. (another common example is specializing std::less)Galenical
V
20

Aside: if your hash function cannot throw then it's quite important to give it a noexcept exception-specification, otherwise the hash table needs to store every element's hash code alongside the element itself (which increases memory usage and affects performance) so that container operations that must not throw do not have to recalculate the hash code.

The SIGFPE implies a divide by zero and from the backtrace it happens here:

    { return __num % __den; }

which probably means __den is zero. That value comes from the hash map's bucket count, which should not be zero.

Can you confirm that when it crashes m._M_bucket_count is zero?

If so, that either indicates you've corrupted the map somehow (have you tried compiling with -D_GLIBCXX_DEBUG to turn on the libstdc++ Debug Mode checks? Have you tried running under valgrind?) or there's a bug in the libstdc++ code (which is possible, but unlikely).

Some of the other answers below give examples of how the map can be corrupted, e.g. allocating storage for it with malloc but not actually constructing an object in that storage, or overwriting the object with memset.

Another possibility is that your hash map is a global variable, and you are accessing it from the constructor of another global variable, which runs into the Static Initialization Order Fiasco. If the map is used before its constructor runs, then the bucket count will be zero.

Vainglory answered 10/1, 2013 at 17:42 Comment(2)
And how to resolve the problem if the bucket_count is zero? In my case, I have checked and found out, that it is zero. And what to do now?Radu
Read the last paragraph. Debug your program to find how the corruption happened.Vainglory
M
11

In my case the same problem occurred because of static init fiasco. From one object file I called emplace() for static std::unordered_map which was defined in the second object file. Because of at start data was at BSS, value of bucket count was zero => SIGFPE.

Meissen answered 4/4, 2016 at 15:27 Comment(0)
M
4

I had exactly the same problem. It was caused by memset accidentally applied to container data.

Mcalpine answered 19/10, 2014 at 6:31 Comment(1)
In such cases, it may be useful to have the unordered_map as part of another struct, and have a pointer to that struct in the original class,i.e. using pImpl idiom.Piwowar
F
1

Will also post my circumstances for the FPE in STL code.

The code used malloc() to allocate the std::unordered_map. This of course does not call the constructor, leading to an aberrant state.

Fancy answered 31/3, 2020 at 2:30 Comment(0)
T
0

Following the tradition of other users reporting their own circumstances that caused FPE in STL code, here goes mine:

We had perfectly fine code that started dying with SIGFPE in std::map since we upgraded the compiler. Turns out, I compiled gcc 9.2 with gcc 4.8.5 in c++11 mode which is not a normal thing to do -- the normal gcc compilation is always performed in gnu++98 mode.

This caused the 4.8.5 gcc, which has an incomplete/unofficial support for c++11, to produce a faulty 9.2 gcc, which sporadically produced binaries that would exhibit various unrelated failures. For example, trying to debug the above SIGFPE led to SIGSEGV in std::basic_string destructor when compiled with -D_GLIBCXX_DEBUG, and unrelated memory corruption detected with -fsanitize=address.

Lesson here is two-fold:

  1. Do not modify the gcc build system unless you know very darn well what you're doing.
  2. Sometimes the fault can be in the compiler (but then again, the fault ends up being with whomever compiled the compiler...).
Thermionic answered 20/12, 2019 at 10:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.