C++20共享内存日志写入触发Core Dump问题求助
参考Youtube教程实现C++20共享内存日志模块,向内存块写入数据时持续触发Core Dump,尝试strncpy等字符串操作方法未解决问题。以下是相关代码片段及调试信息:
main.cpp
变量log_type为int类型,通过argv赋值
switch (log_type) { case 1: char do_test[6]; std::memset(do_test, 0, sizeof(do_test)); std::snprintf(do_test, sizeof(do_test), "%s", "Hello"); logs::BLogger::append_log(do_test, std::strlen(do_test)); logs::BLogger::test_console_log_data(); break; case 2: logs::BLogger::test_console_log_data(); break; case 3: break; default: break; } logs::BLogger::destroy(); std::cout << "Program is now ending..." << std::endl;
logger.cpp
错误发生在std::memmove调用时
bool logs::BLogger::append_log(char* _data_p, std::size_t _size) { if (!m_logfile_data_p) return false; if (_size + m_log_size >= m_log_size_limit) create_log_file(); std::memmove(tail(), _data_p, _size + 1); m_log_size = m_log_size + _size; return true; } bool logs::BLogger::start(uint32_t _log_size = LOG_SIZE_LIMIT) { // std::lock_guard<std::mutex> start_guard(m_log_mutex); m_directory.append(LOG_DIRECTORY).append("/").append(shared::s_app_name).append("/"); name_log_file(); m_log_size_limit = _log_size; if (m_log_size_limit > LOG_SIZE_LIMIT) m_log_size_limit = LOG_SIZE_LIMIT; /** * @brief * Checks if directory exists, and proceeds to create one if it does not exists. */ if (!shared::is_directory(m_directory)) { if (!shared::create_directory(m_directory)) return false; } std::string filepath; filepath.append(m_directory).append(m_file_name); /** * @brief * Checks if file exists, and proceeds to create one if it does not exists. */ if (!shared::is_file(filepath)) { if (!shared::create_file(filepath)) return false; } if (!logs::BLogSharedMemory::attach_memory_block(filepath, m_logfile_data_p)) return false; m_log_size = std::strlen(m_logfile_data_p); std::cout << "[logger] Is NULL: " << (m_logfile_data_p ? "No" : "Yes") << std::endl; std::cout << "[logger] Log size: " << m_log_size << std::endl; return true; }
logger.hpp(关键片段)
class BLogger final { // ================================================== // * CONSTRUCTOR(S) // ================================================== public: /** * @brief * Default constructor */ BLogger() = delete; /** * @brief * Default destructor. */ ~BLogger(); // ================================================== // * INLINE METHOD(S) // ================================================== private: /** * @brief * Get the tail of logfile. */ static char* tail(void) { return m_logfile_data_p + m_log_size; } // ================================================== // * METHOD(S) // ================================================== public: /** * @brief * Attempts to check if directory exists and if folder can be created. * @param _log_size * The file created will have a size limit. */ static bool start(uint32_t _log_size); /** * @brief * Attempts to append data into log. * @param _data_p * The data to append into * @param _size * Size of the data. */ static bool append_log(char* _data_p, std::size_t _size); // ================================================== // * MEMBER(S) // ================================================== private: /** * @brief * The data used when opening the file in shared memory. */ static char* m_logfile_data_p; /** * @brief * Size of the log */ static std::size_t m_log_size; };
shared_memory.hpp
/** * @brief * Shared memory for I/O purposes. */ class BLogSharedMemory final { // ================================================== // * CONSTRUCTOR(S) // ================================================== public: /** * @brief * Prevent creation of shared memory class. */ BLogSharedMemory() = delete; private: /** * @brief * Default destructor. */ ~BLogSharedMemory(); // ================================================== // * METHOD(S) // ================================================== public: /** * @brief * Attempts to get memory information. * @param _path * The path of the log file to exist. * @param _file_name * Name of the file to create. The path must start from the path of bin if relative. * @param _block_p * The block used for appending. * @param _block_size * Size of the block. */ static bool attach_memory_block(const std::string& _file_name, char*& _block_p, int _block_size = 4096); /** * @brief * Detaches memory block information. * @param _block_p * The block used for appending. * @return * FALSE if memory block detachment fails. */ static bool detach_memory_block(char*& _block_p); // ================================================== // * MEMBER(S) // ================================================== private: /** * @brief * Key of the file for access. */ static int m_shm_id; };
shared_memory.cpp
bool logs::BLogSharedMemory::attach_memory_block(const std::string& _file_name, char*& _block_p, int _block_size) { // key_t key = SHARED_MEMORY_KEY; key_t key; if ((key = ftok(_file_name.c_str(), 0)) == -1) return false; m_shm_id = shmget(key, _block_size, IPC_EXCL | IPC_CREAT | 0666); if (m_shm_id == -1 && errno == EEXIST) { m_shm_id = shmget(key, _block_size, IPC_CREAT | 0666); if (m_shm_id == -1) return false; } /** * @note * shmat returns shmid_ds structure associated with the memory, or error. */ _block_p = (char*)shmat(m_shm_id, NULL, 0); std::cout << "Is NULL (shm): " << (_block_p ? "No" : "Yes") << std::endl; if (_block_p == (char*) -1 || !_block_p) { std::cout << "Shared memory: shmat error!" << std::endl; return false; } return true; }
错误前输出
Is NULL (shm): No [logger] Is NULL: No [logger] Log size: 0 Log type: 1 Segmentation fault
GDB错误栈
#0 std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_data (this=0x5) at /usr/include/c++/11/bits/basic_string.h:194 194 _M_data() const (gdb) bt full #0 std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_data (this=0x5) at /usr/include/c++/11/bits/basic_string.h:194 No locals. #1 std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_is_local (this=0x5) at /usr/include/c++/11/bits/basic_string.h:230 No locals. #2 std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_dispose (this=0x5) at /usr/include/c++/11/bits/basic_string.h:239 No locals. #3 std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string (this=0x5, __in_chrg=<optimized out>) at /usr/include/c++/11/bits/basic_string.h:672 No locals. #4 0x0000aaaad7a83ffc in memmove (__len=6, __src=<optimized out>, __dest=<optimized out>) at /usr/include/aarch64-linux-gnu/bits/string_fortified.h:36 No locals. #5 logs::BLogger::append_log (_data_p=_data_p@entry=0xffffdbc872c8 "Hello", _size=5) at src/logs/logs_logger.cpp:225 No locals. #6 0x0000aaaad7a828bc in main (_argc=3, _argv=0xffffdbc87468) at experiment/logging/main.cpp:121 do_test = "Hello" opt = <optimized out> log_type = 1 char_log_type = "1", '\000' <repeats 18 times>
问题原因分析
静态成员未显式初始化
BLogger类的m_logfile_data_p和m_log_size是静态成员变量,若未在类外显式初始化(如char* logs::BLogger::m_logfile_data_p = nullptr;),其初始值为随机内存值。虽然当前输出显示m_log_size=0,但这是巧合,实际运行中可能因随机值导致内存计算错误,触发越界访问。共享内存未初始化导致的未定义行为
shmget创建的共享内存块内容为随机值,start函数中用std::strlen(m_logfile_data_p)计算初始日志长度时,若内存中不存在'\0'终止符,strlen会持续读取直到越界,触发未定义行为。即使当前返回0,后续写入操作仍可能受随机内存影响。create_log_file函数潜在错误
虽然当前_size + m_log_size >= m_log_size_limit条件不满足(不会调用create_log_file),但GDB栈中显示std::string析构错误(this=0x5为无效地址),说明该函数可能存在std::string的非法操作(如访问空指针或无效内存),间接触发Core Dump。共享内存大小与日志限制不匹配
共享内存默认大小为4096字节,若LOG_SIZE_LIMIT大于该值,m_log_size_limit会被设为更大的数值,后续写入时可能忽略实际内存边界,导致越界访问共享内存。
内容的提问来源于stack exchange,提问作者Benny Ng

