diff --git a/cpp/src/arrow/array/validate.cc b/cpp/src/arrow/array/validate.cc index c1d96375bf09..9e2a3a9132ef 100644 --- a/cpp/src/arrow/array/validate.cc +++ b/cpp/src/arrow/array/validate.cc @@ -478,6 +478,11 @@ struct ValidateArrayImpl { return Status::Invalid("Array length is negative"); } + // Shrinks length_plus_offset, so every buffer-size check below passes vacuously. + if (data.offset < 0) { + return Status::Invalid("Array offset is negative"); + } + if (layout.variadic_spec) { if (data.buffers.size() < layout.buffers.size()) { return Status::Invalid("Expected at least ", layout.buffers.size(), diff --git a/cpp/src/parquet/schema.cc b/cpp/src/parquet/schema.cc index 0cfa49c21c16..93b6e64ece12 100644 --- a/cpp/src/parquet/schema.cc +++ b/cpp/src/parquet/schema.cc @@ -544,6 +544,10 @@ void PrimitiveNode::ToParquet(void* opaque_element) const { // ---------------------------------------------------------------------- // Schema converters +// Deepest group nesting Unflatten will build before rejecting the schema. 1000 matches the JSON +// reader's kMaxNestingDepth, so every untrusted-nesting path in the library fails at one number. +static constexpr int kMaxSchemaNestingDepth = 1000; + std::unique_ptr Unflatten(const format::SchemaElement* elements, int length) { if (elements[0].num_children == 0) { if (length == 1) { @@ -560,7 +564,16 @@ std::unique_ptr Unflatten(const format::SchemaElement* elements, int lengt int pos = 0; - std::function()> NextNode = [&]() { + std::function(int)> NextNode = [&](int depth) { + // The schema is a flat list of elements whose num_children fields describe the tree, so a + // file can nest as deep as it has elements and this recursion runs one native frame per + // level. Without a cap, a schema a few tens of thousands deep overflows the thread stack + // before any consumer sees the file -- an unauthenticated remote crash on any read of an + // attacker-supplied Parquet file. Mirrors the JSON reader's kMaxNestingDepth guard. + if (depth > kMaxSchemaNestingDepth) { + throw ParquetException("Parquet schema nesting depth exceeds the maximum of " + + std::to_string(kMaxSchemaNestingDepth)); + } if (pos == length) { throw ParquetException("Malformed schema: not enough elements"); } @@ -574,13 +587,13 @@ std::unique_ptr Unflatten(const format::SchemaElement* elements, int lengt // Group node (may have 0 children, but cannot have a type) NodeVector fields; for (int i = 0; i < element.num_children; ++i) { - std::unique_ptr field = NextNode(); + std::unique_ptr field = NextNode(depth + 1); fields.push_back(NodePtr(field.release())); } return GroupNode::FromParquet(opaque_element, std::move(fields)); } }; - return NextNode(); + return NextNode(0); } std::shared_ptr FromParquet(const std::vector& schema) {