Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Segfault when compiling with mixed -std options in gcc #2017

Closed
niyue opened this issue Nov 14, 2020 · 5 comments
Closed

Segfault when compiling with mixed -std options in gcc #2017

niyue opened this issue Nov 14, 2020 · 5 comments

Comments

@niyue
Copy link

niyue commented Nov 14, 2020

My env:

  1. Debian bullseye 20201012
  2. gcc (Debian 10.2.0-16) 10.2.0
  3. fmt 7.1.2 (installed via vcpkg)

I have a very simple usage like below:

std::string double_to_string(double value) {
  return fmt::format("{}", value);
}

// the test case looks like:
auto string_value = double_to_string(3.14); // 3.14 is a literal, and all my 3 cases with double literal as input causes segfault

The test case for this usage causes a segfault.

  1. it worked fine for fmt 7.0.3 but failed for 7.1.2
  2. it only causes problem under "Release" mode, for "Debug" mode, it works well

Here is the stack trace in the core dump I collected:

#0  0x00005634baa9045b in std::back_insert_iterator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > fmt::v7::detail::write<char, std::back_insert_iterator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, double, 0>(std::back_insert_iterator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, double) ()
(gdb) bt full
#0  0x00005634baa9045b in std::back_insert_iterator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > fmt::v7::detail::write<char, std::back_insert_iterator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, double, 0>(std::back_insert_iterator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, double) ()
No symbol table info available.
#1  0x00005634baa8deb8 in fmt::v7::detail::vformat[abi:cxx11](fmt::v7::basic_string_view<char>, fmt::v7::format_args) ()
No symbol table info available.
#2  0x00005634ba2e12af in ____C_A_T_C_H____T_E_S_T____4() ()
No symbol table info available.
#3  0x00005634ba1c10eb in Catch::RunContext::invokeActiveTestCase() ()
No symbol table info available.
#4  0x00005634ba1d5c08 in Catch::RunContext::runCurrentTest(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&) ()
No symbol table info available.
#5  0x00005634ba1e584f in Catch::RunContext::runTest(Catch::TestCase const&) ()
No symbol table info available.
#6  0x00005634ba1eea42 in Catch::Session::runInternal() ()
No symbol table info available.
#7  0x00005634ba1ef01b in Catch::Session::run() ()
No symbol table info available.
#8  0x00005634ba18bb30 in main ()
No symbol table info available.
@peterazmanov
Copy link

I think we have the same issue (fmt is installed via conan).

The test case to reproduce:

main.cpp

#include <fmt/format.h>

#include <iostream>

int main()
{
    std::cout << fmt::to_string(10.) << std::endl;
    std::cout << fmt::format("{}", 10.) << std::endl;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.7)
project(fmt-segfault)

set(CMAKE_CXX_STANDARD 11)
add_subdirectory(fmt)

set(CMAKE_CXX_STANDARD 14)

add_executable(fmt-segfault
  main.cpp
)

target_include_directories(fmt-segfault PRIVATE
  fmt/include)

target_link_libraries(fmt-segfault
  fmt::fmt
)

The cause of the issues is that the library code and our code are compiled with different versions of the standard. If I change the standard in the CMakeLists.txt above to be the same everything is OK. When I install the library in header-only mode the problem disappears too.

The problem is in the line:

static const auto specs = basic_format_specs<Char>();

The enclosing function is compiled twice with different standard.
c++11: constexpr basic_format_specs() is not constexpr (can be checked with static_assert(fmt::detail::fill_t<char>::make()[0] == ' ', " ") that fails) so the dynamic initialization for the static variable is inlined.
c++14 or later: In the client code the constructor of basic_format_specs is proper constexpr and compiler initializes the variable in compile time and places it in the read-only part of the data segment.
In runtime the library code is called that tries to initialize static variable but the variable is read-only hence segfault.

Honestly I don't know whose issue it is. :)

Reproducable with gcc 9.3.1 and clang 9.0.1.
fmt version 7.1.2

@vitaut
Copy link
Contributor

vitaut commented Nov 14, 2020

I wasn't able to repro this on {fmt} 7.1.2 built from source and mixing standards is generally not recommended.

That said a PR with a workaround would be welcome.

@vitaut
Copy link
Contributor

vitaut commented Nov 14, 2020

Nevermind, it wasn't reproducing with clang. I did manage to repro with gcc-10.

@vitaut
Copy link
Contributor

vitaut commented Nov 14, 2020

Bisected to 3c13a88.

@vitaut vitaut changed the title formatting double to string causes segfault Segfault when compiling with mixed -std options in gcc Nov 14, 2020
@vitaut
Copy link
Contributor

vitaut commented Nov 14, 2020

Worked around in f81c14a but please note that mixing different standard versions is probably a UB.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants