Search code examples
c++boostboost-system

Boost system segmentation fault when not inlining


So I tried to add my own error_code in my application, based on Boost System.

Basically, I tried first to follow the documentation: https://www.boost.org/doc/libs/1_83_0/libs/system/doc/html/system.html#usage_defining_library_specific_error_codes and did exactly as in the example.

I split up the functions in a header and an impl file so I have:

// Header
#pragma once

#include <boost/system/error_code.hpp>

namespace libmyimg {

enum class error {
    success = 0,

    invalid_signature,
    invalid_width,
    invalid_height,
    unsupported_bit_depth,
    unsupported_channel_count
};

class myimg_category_impl : public boost::system::error_category {
  public:
    const char* name() const noexcept;

    std::string message(int ev) const;
    char const* message(int ev, char* buffer, std::size_t len) const noexcept;
};

boost::system::error_category const& myimg_category();

boost::system::error_code make_error_code(error e);
} // namespace libmyimg

namespace boost::system {

template <> struct is_error_code_enum<::libmyimg::error> : std::true_type {};

} // namespace boost::system

// Implementation:
#include "mylib_error_codes.hpp"

namespace libmyimg {

const char* myimg_category_impl::name() const noexcept { return "libmyimg"; }

std::string myimg_category_impl::message(int ev) const
{
    char buffer[64];
    return this->message(ev, buffer, sizeof(buffer));
}

char const*
myimg_category_impl::message(int ev, char* buffer, std::size_t len) const noexcept
{
    switch (static_cast<error>(ev)) {
    case error::success: return "No error";
    case error::invalid_signature: return "Invalid image signature";
    case error::invalid_width: return "Invalid image width";
    case error::invalid_height: return "Invalid image height";
    case error::unsupported_bit_depth: return "Unsupported bit depth";
    case error::unsupported_channel_count:
        return "Unsupported number of channels";
    }

    std::snprintf(buffer, len, "Unknown libmyimg error %d", ev);
    return buffer;
}

boost::system::error_category const& myimg_category()
{
    static const myimg_category_impl instance;
    return instance;
}

boost::system::error_code make_error_code(error e)
{
    return boost::system::error_code(static_cast<int>(e), myimg_category());
}

} // namespace libmyimg

Calling this code from a minimal test:

auto const input{libmyimg::error::invalid_width};
auto const ec{make_error_code(input)};

I get a segmentation fault (output from GCC asan):

==35495==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffd65bf30c0 at pc 0x55ca6996b608 bp 0x7ffd65bf3040 sp 0x7ffd65bf3030
WRITE of size 8 at 0x7ffd65bf30c0 thread T0
    #0 0x55ca6996b607 in boost::system::error_code::error_code(int, boost::system::error_category const&) /home/my_project/build/default/vcpkg_installed/x64-linux/include/boost/system/detail/error_code.hpp:129
    #1 0x55ca69f548d3 in libmyimg::make_error_code(libmyimg::error) /home/my_project/src/error_codes/driver_error_codes.cpp:76
    #2 0x55ca699a68fd in DOCTEST_ANON_FUNC_2 /home/my_project/src/tests/error_codes.test.cpp:20
    #3 0x55ca69a12bac in doctest::Context::run() /home/my_project/build/default/vcpkg_installed/x64-linux/include/doctest/doctest.h:7007
    #4 0x55ca69a149c0 in main /home/my_project/src/tests/main.test.cpp:16
    #5 0x7f3516e33d8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #6 0x7f3516e33e3f in __libc_start_main_impl ../csu/libc-start.c:392
    #7 0x55ca699605c4 in _start (/home/my_project/build/default/src/tests/driver_unit_tests+0x13d5c4)

However, I noticed that this does not happen if I inline my make_error_code in the header file, but I suspect that this is just a coincidence caused by messing up somewhere else.

Have I missed something in the usage?


Solution

  • I found the answer.

    I have a list of of packages in CMake via vcpkg. In these packages Boost is installed (v1.82). I also have a local, system-wide installation (v1.74). The test had not linked to the project boost (v1.82), and instead linked to the system version. Adding the vcpkg boost install path in CMake fixed the problem.