Repository: abseil/abseil-cpp

Issue #388: flat_hash_map::try_emplace() is not exception safe
Created: 2019-10-07T22:14:37Z
The current try_emplace() implementation in raw_hash_map.h reads:
  template <class K = key_type, class... Args>
  std::pair<iterator, bool> try_emplace_impl(K&& k, Args&&... args) {
    auto res = this->find_or_prepare_insert(k);
    if (res.second)
      this->emplace_at(res.first, std::piecewise_construct,
                       std::forward_as_tuple(std::forward<K>(k)),
                       std::forward_as_tuple(std::forward<Args>(args)...));
    return {this->iterator_at(res.first), res.second};
}
In case the key of the value being emplaced is not already in the table, find_or_prepare_insert() will prepare the table metadata for insertion before constructing in-place the value from the supplied arugments via emplace_at().
The problem is that if the construction of the value in emplace_at() throws, then the table will be left in a state which is inconsistent:: although according to the metadata a value is present, in reality no value was actually constructed because of the exception that was raised. This will lead to UB and erratic runtime behaviour (e.g., double free on table destruction).
I took a look at some abseil internals and it seems to me that there might be an easy fix in terms of the erase_meta_only() member function of raw_hash_set.h:
  template <class K = key_type, class... Args>
  std::pair<iterator, bool> try_emplace_impl(K &&k, Args &&... args) {
    auto res = this->find_or_prepare_insert(k);
    if (res.second)
      try {
        this->emplace_at(res.first, std::piecewise_construct,
                         std::forward_as_tuple(std::forward<K>(k)),
                         std::forward_as_tuple(std::forward<Args>(args)...));
      } catch (...) {
        this->erase_meta_only(this->iterator_at(res.first));
        throw;
      }
    return {this->iterator_at(res.first), res.second};
  }
The only problem is that erase_meta_only() needs to become a protected function, rather than private, in order for it to be usable in that context.
With this modification, my test case does not crash any more in face of exceptions thrown in try_emplace(). I suspect a similar problem (and perhaps a similar solution) exists in the insert() function.
Do you think this fix is correct? If so, would you like me to open a PR?
I would really like to get this behaviour fixed in my code, so, if you say the fix is ok, I could in the meantime patch my own copy of abseil while waiting for an official resolution.
Thanks!

Duplicate issue #1524: [Bug]: SIGSEGV from absl::flat_hash_map::insert()
Created: 2023-08-25T15:15:17Z
Describe the issue
We are using absl::flat_hash_map in our code base. When our operator new throws an exception at the wrong time in absl::flat_hash_map::insert(), the program exits with SIGSEGV (or SIGABRT, depending on your malloc implementation). Since abseil gives the basic exception guarantee, this should not happen.
Steps to reproduce the problem
Here is a reproduction on godbolt.org: https://godbolt.org/z/WaYG7hqjj
This is the minimal reproduction of the problem:
#include <absl/container/flat_hash_map.h>

void * operator new(std::size_t size) {
    throw std::bad_alloc{};
}

// Exits with SIGSEGV
int main(int, char **) {
    try {
        absl::flat_hash_map<int, int> m;
        m.insert({42, 42});
    } catch (const std::bad_alloc &) {}
    return 0;
}

What version of Abseil are you using?
lts_2023_01_25
The godbolt reproduction uses trunk.
What operating system and version are you using?
Ubuntu 22.04.3 LTS
What compiler and version are you using?
Ubuntu clang version 14.0.6
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/10
Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/11
Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/9
Selected GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/11
Candidate multilib: .;@m64
Selected multilib: .;@m64
What build system are you using?
CMake or whatever godbolt is using
Additional context
No response

Closing comment (2023-12-14T16:53:37Z): This appears to be a duplicate of #388, so I'll close it as that. Please let me know if I missed any difference.
(And per #388, as of this writing, it doesn't appear likely that these containers would be made exception-safe.)