未内联Boost System自定义error_code时触发段错误的问题
Boost System自定义error_code栈溢出问题分析
问题描述
严格按照Boost System规范实现自定义error_code,将代码拆分到头文件与实现文件后,调用make_error_code触发GCC ASAN栈缓冲区溢出错误;但将make_error_code内联到头文件后,问题消失。需确认该用法是否存在遗漏。
现有代码实现
头文件(mylib_error_codes.hpp)
#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
实现文件(mylib_error_codes.cpp)
#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
测试代码与错误信息
测试代码
auto const input{libmyimg::error::invalid_width}; auto const ec{make_error_code(input)};
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)
问题原因分析
- 模板特化可见性问题:
boost::system::is_error_code_enum<libmyimg::error>的特化必须在所有使用该枚举转换为error_code的代码前可见。当make_error_code放在实现文件中时,测试代码的翻译单元可能因头文件包含顺序或符号解析问题,导致Boost内部无法识别该枚举为error_code兼容类型,触发错误逻辑。 - ODR(单定义规则)冲突:
make_error_code在实现文件中定义时,若多个翻译单元包含头文件并调用该函数,可能因符号重复定义或链接时的符号不一致,导致未定义行为。
解决方案
将make_error_code标记为inline并移至头文件中,确保所有翻译单元能看到一致的函数实现,同时保证模板特化的可见性:
修改后的头文件
#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 override; std::string message(int ev) const override; char const* message(int ev, char* buffer, std::size_t len) const noexcept override; }; boost::system::error_category const& myimg_category(); // 标记为inline,放入头文件 inline boost::system::error_code make_error_code(error e) { return boost::system::error_code(static_cast<int>(e), myimg_category()); } } // namespace libmyimg namespace boost::system { template <> struct is_error_code_enum<::libmyimg::error> : std::true_type {}; } // namespace boost::system
实现文件无需修改
保持原实现文件内容不变,仅移除make_error_code的定义(因已移至头文件)。
额外检查点
- 确保所有调用
make_error_code或使用枚举隐式转换为error_code的翻译单元,都正确包含头文件mylib_error_codes.hpp。 - 验证头文件路径是否正确,避免因路径错误导致的重复包含或符号缺失。
内容的提问来源于stack exchange,提问作者SupAl
相关产品推荐
相关产品推荐

