This is an automated email from the ASF dual-hosted git repository.

yiguolei pushed a commit to branch branch-4.1
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-4.1 by this push:
     new ea5efa367ca [fix](be) Backport Parquet metadata size limit to 
branch-4.1 (#67631) (#68047)
ea5efa367ca is described below

commit ea5efa367ca4c5c520579a660b01d77c00aefdef
Author: Gabriel <[email protected]>
AuthorDate: Wed Sep 16 18:03:57 2026 +0800

    [fix](be) Backport Parquet metadata size limit to branch-4.1 (#67631) 
(#68047)
    
    ### What problem does this PR solve?
    
    Related PR: #67631
    
    Backport #67631 to branch-4.1. FileScannerV2 incorrectly uses the Thrift
    RPC message ceiling to limit Parquet metadata. Add the independent,
    mutable `parquet_metadata_size_limit` with a 256 MiB default, while
    preserving the file-size check and enforcing the limit before metadata
    allocation and the second read.
    
    Resolved a test insertion conflict by retaining the existing
    physical-split refinement test and adding the upstream footer tests. The
    added and removed code matches the upstream patch.
    
    ### Release note
    
    FileScannerV2 uses the independent `parquet_metadata_size_limit`
    configuration, defaulting to 256 MiB.
    
    ### Check List (For Author)
    
    - Validation: clang-format 16 check and `git diff --check` passed; full
    diff self-reviewed.
    - Unit tests: preserved the upstream footer tests. A local
    `NewParquetReaderTest.NativeFooter*` run was attempted but stopped
    during a full rebuild; no local test result is claimed for this
    backport. CI validation is required.
    - Header hygiene: the check script is unavailable on the target branch.
    - Behavior changed: Yes; valid Parquet metadata above the RPC ceiling
    can be accepted within the dedicated metadata limit.
    - Does this need documentation: No.
---
 be/src/common/config.cpp                          |  4 ++++
 be/src/common/config.h                            |  2 ++
 be/src/format_v2/parquet/parquet_file_context.cpp | 14 +++++-------
 be/src/format_v2/parquet/parquet_file_context.h   |  3 +--
 be/test/format_v2/parquet/parquet_reader_test.cpp | 26 ++++++++++++++++++++---
 5 files changed, 35 insertions(+), 14 deletions(-)

diff --git a/be/src/common/config.cpp b/be/src/common/config.cpp
index 16d41bcc3e9..29ff784a2f2 100644
--- a/be/src/common/config.cpp
+++ b/be/src/common/config.cpp
@@ -1066,6 +1066,10 @@ DEFINE_mInt32(in_memory_file_size, "1048576"); // 1MB
 
 // Max size of parquet page header in bytes
 DEFINE_mInt32(parquet_header_max_size_mb, "1");
+// Max size of parquet file metadata in bytes
+DEFINE_mInt64(parquet_metadata_size_limit, "268435456");
+DEFINE_Validator(parquet_metadata_size_limit,
+                 [](const int64_t config) -> bool { return config > 0; });
 // Max buffer size for parquet row group
 DEFINE_mInt32(parquet_rowgroup_max_buffer_mb, "128");
 // Max buffer size for parquet chunk column
diff --git a/be/src/common/config.h b/be/src/common/config.h
index 65ea01ae452..7963d3331a5 100644
--- a/be/src/common/config.h
+++ b/be/src/common/config.h
@@ -1125,6 +1125,8 @@ DECLARE_mInt32(in_memory_file_size);
 
 // Max size of parquet page header in bytes
 DECLARE_mInt32(parquet_header_max_size_mb);
+// Max size of parquet file metadata in bytes
+DECLARE_mInt64(parquet_metadata_size_limit);
 // Max buffer size for parquet row group
 DECLARE_mInt32(parquet_rowgroup_max_buffer_mb);
 // Max buffer size for parquet chunk column
diff --git a/be/src/format_v2/parquet/parquet_file_context.cpp 
b/be/src/format_v2/parquet/parquet_file_context.cpp
index 02bf3400f66..8497e5ba529 100644
--- a/be/src/format_v2/parquet/parquet_file_context.cpp
+++ b/be/src/format_v2/parquet/parquet_file_context.cpp
@@ -120,13 +120,13 @@ Status NativeParquetMetadata::init_schema(bool 
enable_mapping_varbinary,
 
 namespace detail {
 
-Status validate_native_footer_size(uint32_t serialized_size, size_t file_size,
-                                   size_t metadata_size_limit) {
+Status validate_native_footer_size(uint32_t serialized_size, size_t file_size) 
{
     if (file_size < V2_PARQUET_FOOTER_SIZE ||
         serialized_size > file_size - V2_PARQUET_FOOTER_SIZE) {
         return Status::Corruption("Parquet v2 footer size {} exceeds file size 
{}", serialized_size,
                                   file_size);
     }
+    const size_t metadata_size_limit = 
static_cast<size_t>(config::parquet_metadata_size_limit);
     if (serialized_size > metadata_size_limit) {
         return Status::Corruption("Parquet v2 footer size {} exceeds metadata 
limit {}",
                                   serialized_size, metadata_size_limit);
@@ -235,13 +235,9 @@ Status parse_native_parquet_footer(io::FileReaderSPtr file,
 
     const uint32_t serialized_size =
             decode_fixed32_le(tail.data() + tail.size() - 
V2_PARQUET_FOOTER_SIZE);
-    // The configured Thrift message ceiling also bounds this file-controlled 
allocation. Keep the
-    // check before both allocation and the optional second read so a sparse 
file cannot force a
-    // process-sized metadata buffer merely by advertising a large footer.
-    const size_t metadata_size_limit =
-            static_cast<size_t>(std::max(config::thrift_max_message_size, 0));
-    RETURN_IF_ERROR(
-            detail::validate_native_footer_size(serialized_size, file_size, 
metadata_size_limit));
+    // Keep the dedicated metadata limit independent of RPC serialization 
limits, and enforce it
+    // before allocation so file-controlled footer sizes cannot create 
unbounded memory pressure.
+    RETURN_IF_ERROR(detail::validate_native_footer_size(serialized_size, 
file_size));
     std::vector<uint8_t> serialized_metadata(serialized_size);
     if (serialized_size <= tail.size() - V2_PARQUET_FOOTER_SIZE) {
         const auto* metadata_start =
diff --git a/be/src/format_v2/parquet/parquet_file_context.h 
b/be/src/format_v2/parquet/parquet_file_context.h
index 9203cf0dccd..6f8de4f9f48 100644
--- a/be/src/format_v2/parquet/parquet_file_context.h
+++ b/be/src/format_v2/parquet/parquet_file_context.h
@@ -100,8 +100,7 @@ namespace detail {
 
 inline constexpr int64_t MAX_SERIALIZED_PARQUET_INDEX_BYTES = 64LL << 20;
 
-Status validate_native_footer_size(uint32_t serialized_size, size_t file_size,
-                                   size_t metadata_size_limit);
+Status validate_native_footer_size(uint32_t serialized_size, size_t file_size);
 
 std::string build_native_file_cache_key(std::string_view fs_name, 
std::string_view path,
                                         int64_t description_mtime, int64_t 
reader_mtime,
diff --git a/be/test/format_v2/parquet/parquet_reader_test.cpp 
b/be/test/format_v2/parquet/parquet_reader_test.cpp
index 3ec7493565d..756ea6236c9 100644
--- a/be/test/format_v2/parquet/parquet_reader_test.cpp
+++ b/be/test/format_v2/parquet/parquet_reader_test.cpp
@@ -3990,15 +3990,35 @@ TEST_F(NewParquetReaderTest, 
MutableUnknownVersionDeclinesPhysicalSplitRefinemen
     EXPECT_TRUE(children.empty());
 }
 
+TEST_F(NewParquetReaderTest, 
NativeFooterAcceptsMetadataAboveThriftMessageLimit) {
+    constexpr uint32_t metadata_size = 128UL << 20;
+    constexpr size_t file_size = 512UL << 20;
+    static_assert(metadata_size > 100UL << 20);
+
+    EXPECT_EQ(config::parquet_metadata_size_limit, 256UL << 20);
+    EXPECT_TRUE(
+            
format::parquet::detail::validate_native_footer_size(metadata_size, 
file_size).ok());
+}
+
 TEST_F(NewParquetReaderTest, 
NativeFooterSizeIsBoundedBeforeMetadataAllocation) {
-    constexpr size_t file_size = 256UL << 20;
-    constexpr size_t metadata_limit = 100UL << 20;
+    constexpr size_t file_size = 512UL << 20;
     const auto status = format::parquet::detail::validate_native_footer_size(
-            static_cast<uint32_t>(metadata_limit + 1), file_size, 
metadata_limit);
+            static_cast<uint32_t>(config::parquet_metadata_size_limit + 1), 
file_size);
     EXPECT_TRUE(status.is<ErrorCode::CORRUPTION>()) << status;
     EXPECT_NE(status.to_string().find("metadata limit"), std::string::npos);
 }
 
+TEST_F(NewParquetReaderTest, NativeFooterSizeCannotExceedFileSize) {
+    constexpr size_t file_size = 256UL << 20;
+    EXPECT_TRUE(format::parquet::detail::validate_native_footer_size(
+                        static_cast<uint32_t>(file_size - 8), file_size)
+                        .ok());
+    const auto status = format::parquet::detail::validate_native_footer_size(
+            static_cast<uint32_t>(file_size - 7), file_size);
+    EXPECT_TRUE(status.is<ErrorCode::CORRUPTION>()) << status;
+    EXPECT_NE(status.to_string().find("file size"), std::string::npos);
+}
+
 TEST_F(NewParquetReaderTest, UnknownMtimeUsesPageCacheForImmutableFile) {
     _file_path = (_test_dir / "unknown_mtime_page_cache.parquet").string();
     write_parquet_file(_file_path);


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to