From 4075c922d200717dd406a2a64495e6a093a95e6e Mon Sep 17 00:00:00 2001 From: linrrarity Date: Thu, 14 May 2026 11:24:06 +0800 Subject: [PATCH] [Fix](arrow flight) Fix arrow::Status inline static empty msg core (#63191) Related PR: #62489 Problem Summary: ```cpp static const std::string no_message = ""; return ok() ? no_message : state_->msg; ``` In the clang-built Doris BE binary, this inline static `std::string` is emitted as a weak/COMDAT object and placed in `.data.rel.ro`. ```bash $ readelf -sW "$BIN" | c++filt | grep 'Status::message.*no_message' 105229: 000000007d6958e0 32 OBJECT WEAK DEFAULT 28 arrow::Status::message[abi:cxx11]() const::no_message[abi:cxx11] $ readelf -SW doris/output/be/lib/doris_be | grep '\[ *28\]' [28] .data.rel.ro PROGBITS 000000007d0f7720 7d0f6720 7e4208 00 WA 0 0 32 ``` After relocation, RELRO makes this section read-only. However, C++ function-local statics are lazily initialized on first execution, so the first call to `Status::message()` tries to construct `no_message` at runtime. The `std::string` constructor writes to the object storage in `.data.rel.ro`, which triggers `SIGSEGV invalid permissions for mapped object` like: *FromStatus(arrow::Status)* ```text *** Query id: 0-0 *** *** is nereids: 0 *** *** tablet id: 0 *** *** Aborted at 1778559900 (unix time) try "date -d @1778559900" if you are using GNU date *** *** Current BE git commitID: f02e9e680c8 *** *** SIGSEGV invalid permissions for mapped object (@0xaaaaf1ecbf28) received by PID 3634450 (TID 3636517 OR 0xfffa35fe97c0) from PID 18446744073473408808; stack trace: *** 0# doris::signal::(anonymous namespace)::FailureSignalHandler(int, siginfo_t*, void*) at /root/selectdb-core/be/src/common/signal_handler.h:421 1# PosixSignals::chained_handler(int, siginfo_t*, void*) [clone .part.0] in /opt/jdk/lib/server/libjvm.so 2# JVM_handle_linux_signal in /opt/jdk/lib/server/libjvm.so 3# 0x0000FFFFAE091830 in linux-vdso.so.1 4# std::__cxx11::basic_string, std::allocator >::basic_string >(char const*, std::allocator const&) at /root/toolchain/ldb-toolchain-v0.21/bin/../lib/gcc/aarch64-linux-gnu/13/../../../../include/c++/13/bits/basic_string.h:632 5# arrow::flight::internal::TransportStatus::FromStatus(arrow::Status const&) in /opt/selectdb/4.1.3.2026042721/be/lib/doris_be 6# arrow::flight::transport::grpc::ToGrpcStatus(arrow::Status const&, grpc::ServerContext*) in /opt/selectdb/4.1.3.2026042721/be/lib/doris_be 7# arrow::flight::transport::grpc::(anonymous namespace)::GrpcServiceHandler::DoGet(grpc::ServerContext*, arrow::flight::protocol::Ticket const*, grpc::ServerWriter*) in /opt/selectdb/4.1.3.2026042721/be/lib/doris_be 8# grpc::Status grpc::internal::CatchingFunctionHandler::RunHandler(grpc::internal::MethodHandler::HandlerParameter const&)::{lambda()#1}>(grpc::internal::ServerStreamingHandler::RunHandler(grpc::internal::MethodHandler::HandlerParameter const&)::{lambda()#1}&&) in /opt/selectdb/4.1.3.2026042721/be/lib/doris_be 9# grpc::internal::ServerStreamingHandler::RunHandler(grpc::internal::MethodHandler::HandlerParameter const&) in /opt/selectdb/4.1.3.2026042721/be/lib/doris_be 10# grpc::Server::SyncRequest::ContinueRunAfterInterception() in /opt/selectdb/4.1.3.2026042721/be/lib/doris_be 11# grpc::Server::SyncRequest::Run(std::shared_ptr const&, bool) in /opt/selectdb/4.1.3.2026042721/be/lib/doris_be 12# grpc::ThreadManager::MainWorkLoop() in /opt/selectdb/4.1.3.2026042721/be/lib/doris_be 13# grpc::ThreadManager::WorkerThread::WorkerThread(grpc::ThreadManager*)::$_0::__invoke(void*) in /opt/selectdb/4.1.3.2026042721/be/lib/doris_be 14# grpc_core::(anonymous namespace)::ThreadInternalsPosix::ThreadInternalsPosix(char const*, void (*)(void*), void*, bool*, grpc_core::Thread::Options const&)::{lambda(void*)#1}::__invoke(void*) in /opt/selectdb/4.1.3.2026042721/be/lib/doris_be 15# start_thread in /lib64/libc.so.6 16# thread_start in /lib64/libc.so.6 ``` *ToStatus()* ```text *** SIGSEGV invalid permissions for mapped object (@0x56448ff1ef38) received by PID 66637 (TID 67372 OR 0x7f2687017640) from PID 18446744071829581624; stack trace: *** 0# doris::signal::(anonymous namespace)::FailureSignalHandler(int, siginfo_t*, void*) at ../src/common/signal_handler.h:417 1# PosixSignals::chained_handler(int, siginfo*, void*) [clone .part.0] in /usr/lib/jvm/java-17-openjdk-amd64/lib/server/libjvm.so 2# JVM_handle_linux_signal in /usr/lib/jvm/java-17-openjdk-amd64/lib/server/libjvm.so 3# 0x00007F2836CE7520 in /lib/x86_64-linux-gnu/libc.so.6 4# std::__cxx11::basic_string, std::allocator >::basic_string >(char const*, std::allocator const&) at /usr/local/ldb-toolchain-v0.26/bin/../lib/gcc/x86_64-pc-linux-gnu/15/include/g++-v15/bits/basic_string.h:707 5# arrow::Status::WithDetail(std::shared_ptr) const in /mnt/disk1/PERFORMANCE_ENV/be/lib/doris_be 6# arrow::flight::internal::TransportStatus::ToStatus() const in /mnt/disk1/PERFORMANCE_ENV/be/lib/doris_be 7# arrow::flight::transport::grpc::FromGrpcStatus(grpc::Status const&, grpc::ClientContext*) in /mnt/disk1/PERFORMANCE_ENV/be/lib/doris_be 8# arrow::flight::transport::grpc::(anonymous namespace)::GrpcClientInterceptorAdapter::Intercept(grpc::experimental::InterceptorBatchMethods*) in /mnt/disk1/PERFORMANCE_ENV/be/lib/doris_be 9# grpc::internal::InterceptorBatchMethodsImpl::RunInterceptors() in /mnt/disk1/PERFORMANCE_ENV/be/lib/doris_be 10# grpc::internal::CallOpSet, grpc::internal::CallNoOp<4>, grpc::internal::CallNoOp<5>, grpc::internal::CallNoOp<6> >::FinalizeResult(void**, bool*) in /mnt/disk1/PERFORMANCE_ENV/be/lib/doris_be 11# grpc::ClientReaderWriter::Finish() in /mnt/disk1/PERFORMANCE_ENV/be/lib/doris_be 12# arrow::flight::transport::grpc::(anonymous namespace)::FinishableDataStream, arrow::flight::internal::FlightData>::DoFinish() in /mnt/disk1/PERFORMANCE_ENV/be/lib/doris_be 13# arrow::flight::transport::grpc::(anonymous namespace)::WritableDataStream, arrow::flight::internal::FlightData>::DoFinish() in /mnt/disk1/PERFORMANCE_ENV/be/lib/doris_be 14# arrow::flight::internal::ClientDataStream::Finish(arrow::Status) in /mnt/disk1/PERFORMANCE_ENV/be/lib/doris_be 15# arrow::flight::ClientStreamReader::Next() in /mnt/disk1/PERFORMANCE_ENV/be/lib/doris_be 16# doris::PythonClient::read_batch(std::shared_ptr*) at ./be/build_RELEASE/../src/udf/python/python_client.cpp:136 17# doris::PythonUDFClient::evaluate(arrow::RecordBatch const&, std::shared_ptr*) at ./be/build_RELEASE/../src/udf/python/python_udf_client.cpp:36 ``` Move the empty message/detail sentinels out of the header inline path and make non-OK statuses return `state_->msg` directly. This avoids touching the empty OK-message sentinel on error paths and prevents the inline weak/COMDAT `std::string` object from being lazily constructed from a read-only mapping. --- thirdparty/download-thirdparty.sh | 7 +++ ...rrow-17.0.0-status-inline-static-fix.patch | 58 +++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 thirdparty/patches/apache-arrow-17.0.0-status-inline-static-fix.patch diff --git a/thirdparty/download-thirdparty.sh b/thirdparty/download-thirdparty.sh index 52481b24fca863..8a0517ec483d50 100755 --- a/thirdparty/download-thirdparty.sh +++ b/thirdparty/download-thirdparty.sh @@ -435,6 +435,13 @@ if [[ " ${TP_ARCHIVES[*]} " =~ " ARROW " ]]; then # apache-arrow-17.0.0-force-write-int96-timestamps.patch : # Introducing the parameter that forces writing int96 timestampes for compatibility with Paimon cpp. patch -p1 <"${TP_PATCH_DIR}/apache-arrow-17.0.0-force-write-int96-timestamps.patch" + + # apache-arrow-17.0.0-status-inline-static-fix.patch : + # Move Status::message()/detail() empty sentinels out of header + # inline function-local statics. Clang can place those weak inline + # std::string objects in RELRO, then crash while initializing them. + patch -p1 <"${TP_PATCH_DIR}/apache-arrow-17.0.0-status-inline-static-fix.patch" + touch "${PATCHED_MARK}" fi cd - diff --git a/thirdparty/patches/apache-arrow-17.0.0-status-inline-static-fix.patch b/thirdparty/patches/apache-arrow-17.0.0-status-inline-static-fix.patch new file mode 100644 index 00000000000000..2a1ed534077303 --- /dev/null +++ b/thirdparty/patches/apache-arrow-17.0.0-status-inline-static-fix.patch @@ -0,0 +1,58 @@ +diff --git a/cpp/src/arrow/status.cc b/cpp/src/arrow/status.cc +index a9581cadc9..1b7ee7df62 100644 +--- a/cpp/src/arrow/status.cc ++++ b/cpp/src/arrow/status.cc +@@ -17,6 +17,17 @@ + + namespace arrow { + ++const std::string& Status::NoMessage() { ++ static const std::string* no_message = new std::string(); ++ return *no_message; ++} ++ ++const std::shared_ptr& Status::NoDetail() { ++ static const std::shared_ptr* no_detail = ++ new std::shared_ptr(); ++ return *no_detail; ++} ++ + Status::Status(StatusCode code, const std::string& msg) + : Status::Status(code, msg, nullptr) {} + +diff --git a/cpp/src/arrow/status.h b/cpp/src/arrow/status.h +index 983b61629d..a49a982922 100644 +--- a/cpp/src/arrow/status.h ++++ b/cpp/src/arrow/status.h +@@ -330,14 +330,18 @@ class ARROW_EXPORT [[nodiscard]] Status : public util::EqualityComparablemsg; ++ if (ARROW_PREDICT_FALSE(state_ != NULLPTR)) { ++ return state_->msg; ++ } ++ return NoMessage(); + } + + /// \brief Return the status detail attached to this message. + const std::shared_ptr& detail() const { +- static std::shared_ptr no_detail = NULLPTR; +- return state_ ? state_->detail : no_detail; ++ if (ARROW_PREDICT_FALSE(state_ != NULLPTR)) { ++ return state_->detail; ++ } ++ return NoDetail(); + } + + const void* debug_state_addr() const { return state_; } +@@ -396,6 +400,8 @@ class ARROW_EXPORT [[nodiscard]] Status : public util::EqualityComparable& NoDetail(); + void CopyFrom(const Status& s); + inline void MoveFrom(Status& s); + };