Skip to content

GH-55479: [C++][FlightRPC] Give gRPC real pb::FlightData messages instead of casting Arrow structs - #55480

Open
Algunenano wants to merge 2 commits into
apache:mainfrom
Algunenano:flight-cfi-casts
Open

Algunenano wants to merge 2 commits into
apache:mainfrom
Algunenano:flight-cfi-casts

Conversation

@Algunenano

@Algunenano Algunenano commented Nov 10, 2026 •

Copy link
Copy Markdown

Rationale for this change

WritePayload() and ReadPayload() pass a FlightPayload or internal::FlightData to gRPC as a pb::FlightData through reinterpret_cast, and SerializationTraits<pb::FlightData> casts it back. The object is not a pb::FlightData, which is undefined behavior and which Clang CFI (-fsanitize=cfi-unrelated-cast) rejects. We found this in ClickHouse, which embeds Arrow and is enabling Clang CFI in its release builds.

What changes are included in this PR?

gRPC only passes the message to SerializationTraits<pb::FlightData>, which we control. So instead of a cast, WritePayload() and ReadPayload() give gRPC an empty, real pb::FlightData that sits next to a pointer to the Arrow struct, and the traits read the struct back through FlightPayloadOfMessage() and FlightDataOfMessage(). No protobuf message is built or serialized, so the zero-copy path is unchanged.

Are these changes tested?

By the existing arrow-flight-test, built with -flto=thin -fwhole-program-vtables -fvisibility=hidden -fsanitize=cfi -fno-sanitize-trap=cfi -fsanitize-recover=cfi. Before: all 94 tests pass, with 9 CFI reports on arrow::flight::protocol::FlightData (the 8 casts in serialization_internal.cc, and grpc/impl/call_op_set.h on the same object). After: all 94 tests pass and those reports are gone.

Are there any user-facing changes?

No.

Was AI used for this PR?

PR code and description written by:

  • Human
  • AI

Reviewed before submission by:

  • Human
  • AI
  • Not reviewed

…es instead of casting Arrow structs

WritePayload() and ReadPayload() passed a FlightPayload or
internal::FlightData to gRPC as a pb::FlightData through
reinterpret_cast, and SerializationTraits<pb::FlightData> cast it back.
The object is not a pb::FlightData, which is undefined behavior and
which Clang CFI (-fsanitize=cfi-unrelated-cast) rejects.

gRPC only passes the message to SerializationTraits, so give it an
empty, real pb::FlightData that sits next to a pointer to the Arrow
struct, and read the struct back from it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Both helpers use non-standard-layout offsetof expressions that trigger GCC warnings promoted to errors, breaking default Debug Flight builds.

1 open issue
What changed in this PR

This PR addresses GH-55479 by passing real protobuf FlightData objects to gRPC instead of casting Arrow structs, targeting Clang control-flow integrity failures while preserving Flight’s zero-copy serialization path.

Changes:

  • Wraps protobuf messages alongside pointers to Arrow payloads and receive buffers.
  • Updates serialization traits to retrieve those pointers through new helper functions.
File Description
cpp/​src/​arrow/​flight/​transport/​grpc/​serialization_internal.cc Adds message wrappers and accessors; updates read/write helpers to pass real protobuf objects.
cpp/​src/​arrow/​flight/​transport/​grpc/​customize_grpc.h Declares the accessors and uses them in gRPC serialization traits.

Review effort: Balanced

// `message` points to the `message` member of one of the structs above.
const FlightPayload& FlightPayloadOfMessage(const void* message) {
return *reinterpret_cast<const FlightPayloadMessage*>(
static_cast<const char*>(message) - offsetof(FlightPayloadMessage, message))
@github-actions

Copy link
Copy Markdown

⚠️ GitHub issue #55479 has been automatically assigned in GitHub to PR creator.

offsetof() on the message wrappers, which are not standard-layout, is
conditionally-supported and warns, which fails Debug builds with
-Werror. GCC, Clang and MSVC support it for types without virtual base
classes. The code no longer casts between unrelated types, so
-Wstrict-aliasing does not need to be silenced anymore.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings November 10, 2026 17:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Copilot review overview

5 open issues

Review effort: Lite

// offsetof() below is used on types that are not standard-layout, which is
// conditionally-supported. GCC, Clang and MSVC support it for types without
// virtual base classes, as here.
#ifndef _WIN32
Comment on lines +222 to +227
const FlightPayload* payload;
};

struct FlightDataMessage {
pb::FlightData message;
flight::internal::FlightData* data;
Comment on lines +234 to +242
return *reinterpret_cast<const FlightPayloadMessage*>(
static_cast<const char*>(message) - offsetof(FlightPayloadMessage, message))
->payload;
}

flight::internal::FlightData* FlightDataOfMessage(void* message) {
return reinterpret_cast<FlightDataMessage*>(static_cast<char*>(message) -
offsetof(FlightDataMessage, message))
->data;
Comment on lines 245 to 252
arrow::Result<bool> WritePayload(
const FlightPayload& payload,
::grpc::ClientReaderWriter<pb::FlightData, pb::PutResult>* writer) {
RETURN_NOT_OK(payload.Validate());
// Pretend to be pb::FlightData and intercept in SerializationTraits
return writer->Write(*reinterpret_cast<const pb::FlightData*>(&payload),
::grpc::WriteOptions());
FlightPayloadMessage message;
message.payload = &payload;
return writer->Write(message.message, ::grpc::WriteOptions());
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants