Description
When building CPR with /MT (static CRT) on MSVC, CPR defines an inline const std::unordered_map as a global variable(include/cpr/error.h:93). With MSVC, each translation unit generates its own copy of the destructor registration code. Although the data are COMDAT-folded into a single instance, the destructor registrations are not folded, causing the same object to be registered multiple times for destruction.
As a result, CPR triggers multiple destructor calls on the same global object during program shutdown, which leads to double free / memory corruption / crash.
This issue does not appear with /MD, because the dynamic CRT deduplicates destructor registration internally.
Example/How to Reproduce
- Build CPR using MSVC with
/MT or /MTd
- Include CPR headers in multiple translation units (may require calling 'to_string(code)')
- Link and run the program
- Program crashes on exit due to double-destruction of the inline global map
Possible Fix
Avoid inline global objects with non-trivial destructors.
// include/cpr/error.h:93
inline const std::unordered_map<ErrorCode, std::string> error_code_to_string_mapping = { /*...*/ };
Use a function-local static instead:
const std::unordered_map<...>& get_error_map() {
static const std::unordered_map<...> map = {
/* ... */
};
return map;
}
inline std::string to_string(const cpr::ErrorCode& code) {
return cpr::error_code_map().at(code);
}
Or
inline std::string to_string(const cpr::ErrorCode& code) {
static const std::unordered_map<...> error_code_to_string_mapping = { ... };
return error_code_to_string_mapping.at(code);
}
Where did you get it from?
vcpkg
Additional Context/Your Environment
- OS: Windows 10/Windows 7
- Version: 1.12.0
Description
When building CPR with
/MT(static CRT) on MSVC, CPR defines aninline const std::unordered_mapas a global variable(include/cpr/error.h:93). With MSVC, each translation unit generates its own copy of the destructor registration code. Although the data are COMDAT-folded into a single instance, the destructor registrations are not folded, causing the same object to be registered multiple times for destruction.As a result, CPR triggers multiple destructor calls on the same global object during program shutdown, which leads to double free / memory corruption / crash.
This issue does not appear with
/MD, because the dynamic CRT deduplicates destructor registration internally.Example/How to Reproduce
/MTor/MTdPossible Fix
Avoid inline global objects with non-trivial destructors.
Use a function-local static instead:
Or
Where did you get it from?
vcpkg
Additional Context/Your Environment