Skip to content

Commit 469643d

Browse files
committed
EXP: [C++] Make IPC message decoding stricter
Error out when the advertised message metadata length is larger than expected.
1 parent f486053 commit 469643d

5 files changed

Lines changed: 354 additions & 9 deletions

File tree

cpp/src/arrow/ipc/message.cc

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,10 @@ Result<std::unique_ptr<Message>> ReadMessage(std::shared_ptr<Buffer> metadata,
330330
}
331331

332332
ARROW_RETURN_NOT_OK(decoder.Consume(metadata));
333+
if (decoder.buffered_size() > 0) {
334+
return Status::Invalid("Message metadata too long by ", decoder.buffered_size(),
335+
" bytes");
336+
}
333337

334338
switch (decoder.state()) {
335339
case MessageDecoder::State::INITIAL:
@@ -384,6 +388,10 @@ static Result<std::unique_ptr<Message>> ReadMessageInternal(
384388
/*allow_short_read=*/false));
385389

386390
ARROW_RETURN_NOT_OK(decoder.Consume(SliceBuffer(metadata, 0, metadata_length)));
391+
if (decoder.buffered_size() > 0) {
392+
return Status::Invalid("Message metadata too long by ", decoder.buffered_size(),
393+
" bytes");
394+
}
387395

388396
switch (decoder.state()) {
389397
case MessageDecoder::State::INITIAL:
@@ -469,9 +477,14 @@ Future<std::shared_ptr<Message>> ReadMessageAsync(int64_t offset, int32_t metada
469477
->ReadAsync(context, offset, metadata_length + body_length,
470478
/*allow_short_read=*/false)
471479
.Then([=](std::shared_ptr<Buffer> metadata) -> Result<std::shared_ptr<Message>> {
480+
// TODO reconcile + factor out with blocking ReadMessage logic
472481
DCHECK_EQ(metadata->size(), metadata_length + body_length);
473482
ARROW_RETURN_NOT_OK(
474483
state->decoder->Consume(SliceBuffer(metadata, 0, metadata_length)));
484+
if (state->decoder->buffered_size() > 0) {
485+
return Status::Invalid("Message metadata too long by ",
486+
state->decoder->buffered_size(), " bytes");
487+
}
475488
switch (state->decoder->state()) {
476489
case MessageDecoder::State::INITIAL:
477490
return std::move(state->result);
@@ -484,12 +497,15 @@ Future<std::shared_ptr<Message>> ReadMessageAsync(int64_t offset, int32_t metada
484497
" invalid. File offset: ", offset,
485498
", metadata length: ", metadata_length);
486499
case MessageDecoder::State::BODY: {
487-
auto body = SliceBuffer(metadata, metadata_length, body_length);
488-
if (body->size() < state->decoder->next_required_size()) {
489-
return Status::IOError("Expected to be able to read ",
490-
state->decoder->next_required_size(),
491-
" bytes for message body, got ", body->size());
500+
if (body_length != state->decoder->next_required_size()) {
501+
// The streaming decoder got out of sync with the actual advertised
502+
// metadata and body size, which signals an invalid IPC file.
503+
return Status::IOError(
504+
"Invalid IPC file: advertised body size is ", body_length,
505+
", but message decoder expects to read ",
506+
state->decoder->next_required_size(), " bytes instead");
492507
}
508+
auto body = SliceBuffer(metadata, metadata_length, body_length);
493509
RETURN_NOT_OK(state->decoder->Consume(body));
494510
return std::move(state->result);
495511
}
@@ -755,6 +771,8 @@ class MessageDecoder::MessageDecoderImpl {
755771

756772
int64_t next_required_size() const { return next_required_size_ - buffered_size_; }
757773

774+
int64_t buffered_size() const { return buffered_size_; }
775+
758776
MessageDecoder::State state() const { return state_; }
759777

760778
private:
@@ -1020,6 +1038,8 @@ Status MessageDecoder::Consume(std::shared_ptr<Buffer> buffer) {
10201038

10211039
int64_t MessageDecoder::next_required_size() const { return impl_->next_required_size(); }
10221040

1041+
int64_t MessageDecoder::buffered_size() const { return impl_->buffered_size(); }
1042+
10231043
MessageDecoder::State MessageDecoder::state() const { return impl_->state(); }
10241044

10251045
// ----------------------------------------------------------------------

cpp/src/arrow/ipc/message.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,9 @@ class ARROW_EXPORT MessageDecoder {
386386
/// decoder
387387
int64_t next_required_size() const;
388388

389+
// TODO document this
390+
int64_t buffered_size() const;
391+
389392
/// \brief Return the current state of the decoder.
390393
///
391394
/// This method is provided for users who want to optimize performance.

0 commit comments

Comments
 (0)