From 756957f866631e399a484b144e9744e73b4ead40 Mon Sep 17 00:00:00 2001 From: Yunze Xu Date: Tue, 8 Nov 2022 15:27:11 +0800 Subject: [PATCH] Fix MessageId::getDataAsString() crashed with MSVC debug config Fixes https://github.com/apache/pulsar-client-cpp/issues/107 ### Motivation The `MessageId::getDataAsString()` API returns a `std::string` to the application side. In most cases it's not an issue. However, when building Windows DLLs with `LINK_STATIC=ON`, the library will be built with `/MTd` or `/MT` option to link 3rd party dependencies statically. In this case, the DLL and the application have different C runtime libraries that allocate or deallocate memory. The returned `std::string` object is allocated inside the DLL, while it will be destroyed in the application. The destruction could crash because the application C runtime cannot find the heap address from the C runtime in DLL. ### Modifications For MSVC debug build, change the API to return a const reference to `std::string`. Then the original `std::string` object will be deallocated inside the DLL. --- include/pulsar/Message.h | 7 +++++++ lib/Message.cc | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/include/pulsar/Message.h b/include/pulsar/Message.h index a778660a..b7b3fdd0 100644 --- a/include/pulsar/Message.h +++ b/include/pulsar/Message.h @@ -90,8 +90,15 @@ class PULSAR_PUBLIC Message { * Get string representation of the message * * @return the string representation of the message payload + * + * NOTE: For MSVC with debug mode, return a thread local std::string object to avoid memory allocation + * across DLLs and applications, which could lead to a crash. */ +#if defined(_MSC_VER) && !defined(NDEBUG) + const std::string& getDataAsString() const; +#else std::string getDataAsString() const; +#endif /** * Get key value message. diff --git a/lib/Message.cc b/lib/Message.cc index 84f203f7..46aeb473 100644 --- a/lib/Message.cc +++ b/lib/Message.cc @@ -54,7 +54,15 @@ const void* Message::getData() const { return impl_->payload.data(); } std::size_t Message::getLength() const { return impl_->payload.readableBytes(); } +#if defined(_MSC_VER) && !defined(NDEBUG) +const std::string& Message::getDataAsString() const { + thread_local std::string value; + value = std::string{static_cast(getData()), getLength()}; + return value; +} +#else std::string Message::getDataAsString() const { return std::string((const char*)getData(), getLength()); } +#endif Message::Message() : impl_() {}