Apache Avro C++创建GenericDatum时触发段错误问题
Apache Avro C++ 创建GenericDatum在C17/20下触发段错误,C14正常
在使用Apache Avro C库创建GenericDatum实例时,遇到版本相关的段错误问题:C14编译环境下运行正常,但切换到C17或C20时,创建GenericDatum的代码会直接触发段错误。
复现代码
avro_read.cc
#include <fstream> #include <avro/GenericDatum.hh> #include <avro/ValidSchema.hh> #include <avro/Compiler.hh> int main(int argc, char* argv[]) { // 读取并编译schema std::ifstream in("schema.avsc"); avro::ValidSchema valid_schema; avro::compileJsonSchema(in, valid_schema); // 创建通用数据(触发段错误) avro::GenericDatum datum(valid_schema); return 0; }
CMakeLists.txt
cmake_minimum_required(VERSION 3.20) project(repro CXX) find_package(unofficial-avro-cpp CONFIG REQUIRED) add_executable(avro_read avro_read.cc) target_link_libraries(avro_read PRIVATE unofficial::avro-cpp::avrocpp)
vcpkg.json
{ "name": "segfault-repro", "version-string": "unversioned", "description": "A simple repro for the avro GenericDatum segfault", "dependencies": [ "avro-cpp" ] }
schema.avsc
{ "type": "record", "name": "__root__", "fields": [ { "name": "name", "type": [ "null", "string" ] }, { "name": "total", "type": [ "null", "long" ] } ] }
编译与运行命令
cmake -S . -B .build -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_TOOLCHAIN_FILE=$HOME/vcpkg/scripts/buildsystems/vcpkg.cmake cmake --build .build .build/avro_read
调试信息
GDB输出
Program received signal SIGSEGV, Segmentation fault. 0x00005555555ecd70 in ?? () (gdb) bt #0 0x00005555555ecd70 in ?? () #1 0x000055555556b46d in main ()
Valgrind输出
==2398834== Memcheck, a memory error detector ==2398834== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al. ==2398834== Using Valgrind-3.20.0 and LibVEX; rerun with -h for copyright info ==2398834== Command: .build/avro_read ==2398834== vex amd64->IR: unhandled instruction bytes: 0xF1 0x60 0x3E 0x0 0x0 0x0 0x0 0x0 0x10 0x7F vex amd64->IR: REX=0 REX.W=0 REX.R=0 REX.X=0 REX.B=0 vex amd64->IR: VEX=0 VEX.L=0 VEX.nVVVV=0x0 ESC=NONE vex amd64->IR: PFX.66=0 PFX.F2=0 PFX.F3=0 ==2398834== Invalid read of size 1 ==2398834== at 0x4DC7EC0: ??? ==2398834== by 0x4DC60DF: ??? ==2398834== by 0x7: ??? ==2398834== Address 0x1b is not stack'd, malloc'd or (recently) free'd ==2398834== ==2398834== ==2398834== Process terminating with default action of signal 11 (SIGSEGV) ==2398834== Access not within mapped region at address 0x1B ==2398834== at 0x4DC7EC0: ??? ==2398834== by 0x4DC60DF: ??? ==2398834== by 0x7: ??? ==2398834== If you believe this happened as a result of a stack ==2398834== overflow in your program's main thread (unlikely but ==2398834== possible), you can try to increase the size of the ==2398834== main thread stack using the --main-stacksize= flag. ==2398834== The main thread stack size used in this run was 8388608. ==2398834== ==2398834== HEAP SUMMARY: ==2398834== in use at exit: 84,688 bytes in 40 blocks ==2398834== total heap usage: 123 allocs, 83 frees, 97,888 bytes allocated ==2398834== ==2398834== LEAK SUMMARY: ==2398834== definitely lost: 0 bytes in 0 blocks ==2398834== indirectly lost: 0 bytes in 0 blocks ==2398834== possibly lost: 0 bytes in 0 blocks ==2398834== still reachable: 84,688 bytes in 40 blocks ==2398834== suppressed: 0 bytes in 0 blocks ==2398834== Reachable blocks (those to which a pointer was found) are not shown. ==2398834== To see them, rerun with: --leak-check=full --show-leak-kinds=all ==2398834== ==2398834== For lists of detected and suppressed errors, rerun with: -s ==2398834== ERROR SUMMARY: 2 errors from 1 contexts (suppressed: 0 from 0) [1] 2398834 segmentation fault valgrind --leak-check=full .build/avro_read
问题根源分析
Avro C的GenericDatum.hh头文件会根据C版本自动选择使用std::any(C17及以上)或boost::any(C14及以下):
#if __cplusplus >= 201703L #include <any> #else #include "boost/any.hpp" #endif
测试发现,当强制指定C14编译时(添加set(CMAKE_CXX_STANDARD 14)到CMakeLists.txt),程序运行正常;切换到C17或C++20则触发段错误。推测问题出在Avro对std::any的处理逻辑上:
- 当处理包含可空类型(联合类型,如
["null", "string"])的schema时,GenericDatum初始化过程中可能错误地对空的std::any执行std::any_cast,而std::any在未持有值的状态下执行any_cast会直接触发未定义行为(如段错误)。 boost::any对空值的处理更宽松,不会立即触发段错误,因此C++14环境下表现正常。
解决方案
- 临时规避方案:强制使用C++14编译,在CMakeLists.txt中添加:
set(CMAKE_CXX_STANDARD 14)
修复库代码:修改Avro C++的
GenericDatum初始化逻辑,确保处理联合类型时:- 正确初始化
std::any为联合类型的默认值(如null) - 在执行
any_cast前检查std::any是否持有有效值(通过std::any::has_value()判断)
- 正确初始化
升级库版本:检查是否使用了最新版的Avro C++,如果不是,尝试升级到最新版本,确认该问题是否已被官方修复。
内容的提问来源于stack exchange,提问作者alevenberg
相关产品推荐
相关产品推荐

