@@ -285,17 +285,21 @@ std::string FormatMessageType(MessageType type) {
285285
286286namespace {
287287
288- Status ReadFieldsSubset (int64_t offset, int32_t metadata_length,
289- io::RandomAccessFile* file,
288+ Status ReadFieldsSubset (int64_t offset, io::RandomAccessFile* file,
290289 const FieldsLoaderFunction& fields_loader,
291290 const std::shared_ptr<Buffer>& metadata, int64_t required_size,
292291 std::shared_ptr<Buffer>& body) {
292+ DCHECK_GE (static_cast <size_t >(metadata->size ()), sizeof (int32_t ));
293+ const auto continuation = util::SafeLoadAs<int32_t >(metadata->data ());
294+ // Either 8 bytes (32-bit continuation indicator + 32-bit little-endian length prefix)
295+ // or 4 bytes for legacy IPC without continuation indicator
296+ const auto continuation_size = (continuation == internal::kIpcContinuationToken )
297+ ? 2 * sizeof (int32_t )
298+ : sizeof (int32_t );
299+
293300 const flatbuf::Message* message = nullptr ;
294- uint8_t continuation_metadata_size = sizeof (int32_t ) + sizeof (int32_t );
295- // skip 8 bytes (32-bit continuation indicator + 32-bit little-endian length prefix)
296- RETURN_NOT_OK (internal::VerifyMessage (metadata->data () + continuation_metadata_size,
297- metadata->size () - continuation_metadata_size,
298- &message));
301+ RETURN_NOT_OK (internal::VerifyMessage (metadata->data () + continuation_size,
302+ metadata->size () - continuation_size, &message));
299303 auto batch = message->header_as_RecordBatch ();
300304 if (batch == nullptr ) {
301305 return Status::IOError (
@@ -305,8 +309,8 @@ Status ReadFieldsSubset(int64_t offset, int32_t metadata_length,
305309 RETURN_NOT_OK (fields_loader (batch, &io_recorded_random_access_file));
306310 const auto & read_ranges = io_recorded_random_access_file.GetReadRanges ();
307311 for (const auto & range : read_ranges) {
308- auto read_result = file->ReadAt (offset + metadata_length + range.offset , range. length ,
309- body->mutable_data () + range.offset );
312+ auto read_result = file->ReadAt (offset + metadata-> size () + range.offset ,
313+ range. length , body->mutable_data () + range.offset );
310314 if (!read_result.ok ()) {
311315 return Status::IOError (" Failed to read message body, error " ,
312316 read_result.status ().ToString ());
@@ -330,6 +334,10 @@ Result<std::unique_ptr<Message>> ReadMessage(std::shared_ptr<Buffer> metadata,
330334 }
331335
332336 ARROW_RETURN_NOT_OK (decoder.Consume (metadata));
337+ if (decoder.buffered_size () > 0 ) {
338+ return Status::Invalid (" Message metadata too long by " , decoder.buffered_size (),
339+ " bytes" );
340+ }
333341
334342 switch (decoder.state ()) {
335343 case MessageDecoder::State::INITIAL :
@@ -384,6 +392,10 @@ static Result<std::unique_ptr<Message>> ReadMessageInternal(
384392 /* allow_short_read=*/ false ));
385393
386394 ARROW_RETURN_NOT_OK (decoder.Consume (SliceBuffer (metadata, 0 , metadata_length)));
395+ if (decoder.buffered_size () > 0 ) {
396+ return Status::Invalid (" Message metadata too long by " , decoder.buffered_size (),
397+ " bytes" );
398+ }
387399
388400 switch (decoder.state ()) {
389401 case MessageDecoder::State::INITIAL :
@@ -402,7 +414,7 @@ static Result<std::unique_ptr<Message>> ReadMessageInternal(
402414 // requested field ranges into it.
403415 ARROW_ASSIGN_OR_RAISE (
404416 body, AllocateBuffer (decoder.next_required_size (), default_memory_pool ()));
405- RETURN_NOT_OK (ReadFieldsSubset (offset, metadata_length, file, fields_loader,
417+ RETURN_NOT_OK (ReadFieldsSubset (offset, file, fields_loader,
406418 SliceBuffer (metadata, 0 , metadata_length),
407419 decoder.next_required_size (), body));
408420 } else if (body_length.has_value ()) {
@@ -469,9 +481,14 @@ Future<std::shared_ptr<Message>> ReadMessageAsync(int64_t offset, int32_t metada
469481 ->ReadAsync (context, offset, metadata_length + body_length,
470482 /* allow_short_read=*/ false )
471483 .Then ([=](std::shared_ptr<Buffer> metadata) -> Result<std::shared_ptr<Message>> {
484+ // TODO reconcile + factor out with blocking ReadMessage logic
472485 DCHECK_EQ (metadata->size (), metadata_length + body_length);
473486 ARROW_RETURN_NOT_OK (
474487 state->decoder ->Consume (SliceBuffer (metadata, 0 , metadata_length)));
488+ if (state->decoder ->buffered_size () > 0 ) {
489+ return Status::Invalid (" Message metadata too long by " ,
490+ state->decoder ->buffered_size (), " bytes" );
491+ }
475492 switch (state->decoder ->state ()) {
476493 case MessageDecoder::State::INITIAL :
477494 return std::move (state->result );
@@ -484,12 +501,15 @@ Future<std::shared_ptr<Message>> ReadMessageAsync(int64_t offset, int32_t metada
484501 " invalid. File offset: " , offset,
485502 " , metadata length: " , metadata_length);
486503 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 ());
504+ if (body_length != state->decoder ->next_required_size ()) {
505+ // The streaming decoder got out of sync with the actual advertised
506+ // metadata and body size, which signals an invalid IPC file.
507+ return Status::IOError (
508+ " Invalid IPC file: advertised body size is " , body_length,
509+ " , but message decoder expects to read " ,
510+ state->decoder ->next_required_size (), " bytes instead" );
492511 }
512+ auto body = SliceBuffer (metadata, metadata_length, body_length);
493513 RETURN_NOT_OK (state->decoder ->Consume (body));
494514 return std::move (state->result );
495515 }
@@ -755,6 +775,8 @@ class MessageDecoder::MessageDecoderImpl {
755775
756776 int64_t next_required_size () const { return next_required_size_ - buffered_size_; }
757777
778+ int64_t buffered_size () const { return buffered_size_; }
779+
758780 MessageDecoder::State state () const { return state_; }
759781
760782 private:
@@ -1020,6 +1042,8 @@ Status MessageDecoder::Consume(std::shared_ptr<Buffer> buffer) {
10201042
10211043int64_t MessageDecoder::next_required_size () const { return impl_->next_required_size (); }
10221044
1045+ int64_t MessageDecoder::buffered_size () const { return impl_->buffered_size (); }
1046+
10231047MessageDecoder::State MessageDecoder::state () const { return impl_->state (); }
10241048
10251049// ----------------------------------------------------------------------
0 commit comments