Skip to content

Commit 1d32195

Browse files
committed
Harden metadata3 parsing and restore WriteFileMetaData
1 parent 0144798 commit 1d32195

4 files changed

Lines changed: 313 additions & 23 deletions

File tree

cpp/src/parquet/file_writer.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,10 @@ std::unique_ptr<ParquetFileWriter> ParquetFileWriter::Open(
587587
return result;
588588
}
589589

590+
void WriteFileMetaData(const FileMetaData& file_metadata, ArrowOutputStream* sink) {
591+
WriteFileMetaData(file_metadata, sink, /*use_metadata3=*/false);
592+
}
593+
590594
void WriteFileMetaData(const FileMetaData& file_metadata, ArrowOutputStream* sink,
591595
bool use_metadata3) {
592596
// Write MetaData

cpp/src/parquet/file_writer.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,13 @@ class PARQUET_EXPORT RowGroupWriter {
109109
std::unique_ptr<Contents> contents_;
110110
};
111111

112+
PARQUET_EXPORT
113+
void WriteFileMetaData(const FileMetaData& file_metadata,
114+
::arrow::io::OutputStream* sink);
115+
112116
PARQUET_EXPORT
113117
void WriteFileMetaData(const FileMetaData& file_metadata, ::arrow::io::OutputStream* sink,
114-
bool use_metadata3 = false);
118+
bool use_metadata3);
115119

116120
PARQUET_EXPORT
117121
void WriteMetaDataFile(const FileMetaData& file_metadata,

cpp/src/parquet/metadata3.cc

Lines changed: 47 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#include <cassert>
2020
#include <cstring>
21+
#include <limits>
2122
#include <string>
2223
#include <vector>
2324

@@ -91,6 +92,7 @@ static_assert(IsEnumEq(format::PageType::DICTIONARY_PAGE,
9192
format3::PageType::DICTIONARY_PAGE));
9293

9394
constexpr double kMinCompressionRatio = 1.2;
95+
constexpr uint32_t kMaxRawMetadata3Len = 128U * 1024U * 1024U;
9496

9597
constexpr uint8_t kExtUUID[16] = {0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
9698
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef};
@@ -238,32 +240,32 @@ void StoreBE64(uint64_t v, void* p) {
238240
std::memcpy(p, &v, sizeof(v));
239241
}
240242

241-
MinMax Pack(format::Type::type type, const std::string& min, bool is_min_exact,
242-
const std::string& max, bool is_max_exact) {
243+
std::optional<MinMax> Pack(format::Type::type type, const std::string& min,
244+
bool is_min_exact, const std::string& max, bool is_max_exact) {
243245
switch (type) {
244246
case format::Type::BOOLEAN:
245-
return {};
247+
return MinMax{};
246248
case format::Type::INT32:
247249
case format::Type::FLOAT: {
248250
auto load = [](std::string_view v, bool is_exact) -> MinMax::Packed {
249251
return {.lo4 = LoadLE32(v.data()), .len = static_cast<int8_t>(is_exact ? 4 : -4)};
250252
};
251-
return {load(min, is_min_exact), load(max, is_max_exact), ""};
253+
return MinMax{load(min, is_min_exact), load(max, is_max_exact), ""};
252254
}
253255
case format::Type::INT64:
254256
case format::Type::DOUBLE: {
255257
auto load = [](std::string_view v, bool is_exact) -> MinMax::Packed {
256258
return {.lo8 = LoadLE64(v.data()), .len = static_cast<int8_t>(is_exact ? 8 : -8)};
257259
};
258-
return {load(min, is_min_exact), load(max, is_max_exact), ""};
260+
return MinMax{load(min, is_min_exact), load(max, is_max_exact), ""};
259261
}
260262
case format::Type::INT96: {
261263
auto load = [](std::string_view v, bool is_exact) -> MinMax::Packed {
262264
return {.lo4 = LoadLE32(v.data() + 0),
263265
.lo8 = LoadLE64(v.data() + 4),
264266
.len = static_cast<int8_t>(is_exact ? 12 : -12)};
265267
};
266-
return {load(min, is_min_exact), load(max, is_max_exact), ""};
268+
return MinMax{load(min, is_min_exact), load(max, is_max_exact), ""};
267269
}
268270
case format::Type::FIXED_LEN_BYTE_ARRAY: {
269271
// Special case for decimal16.
@@ -272,26 +274,38 @@ MinMax Pack(format::Type::type type, const std::string& min, bool is_min_exact,
272274
return {
273275
.lo8 = LoadBE64(v.data() + 8), .hi8 = LoadBE64(v.data() + 0), .len = 16};
274276
};
275-
return {load(min), load(max), ""};
277+
return MinMax{load(min), load(max), ""};
276278
}
277279
[[fallthrough]];
278280
}
279281
case format::Type::BYTE_ARRAY: {
280-
auto load = [](std::string_view v, bool is_exact, bool is_max) -> MinMax::Packed {
282+
auto load = [](std::string_view v, bool is_exact,
283+
bool is_max) -> std::optional<MinMax::Packed> {
281284
if (v.size() <= 4) {
282285
char buf[4] = {};
283286
memcpy(buf, v.data(), v.size());
284-
return {.lo4 = LoadBE32(buf),
285-
.len = static_cast<int8_t>(is_exact ? v.size()
286-
: -static_cast<int>(v.size()))};
287+
return MinMax::Packed{.lo4 = LoadBE32(buf),
288+
.len = static_cast<int8_t>(
289+
is_exact ? v.size() : -static_cast<int>(v.size()))};
287290
}
288-
return {.lo4 = LoadBE32(v.data()) + (is_max ? 1 : -1), .len = -4};
291+
const uint32_t lo4 = LoadBE32(v.data());
292+
if (is_max) {
293+
if (lo4 == std::numeric_limits<uint32_t>::max()) return std::nullopt;
294+
return MinMax::Packed{.lo4 = lo4 + 1, .len = -4};
295+
}
296+
if (lo4 == 0) return std::nullopt;
297+
return MinMax::Packed{.lo4 = lo4 - 1, .len = -4};
289298
};
290299
auto [e1, e2] = std::mismatch(max.begin(), max.end(), min.begin(), min.end());
291300
size_t prefix_len = e1 - max.begin();
292-
return {
293-
load(std::string_view(min).substr(prefix_len), is_min_exact, false),
294-
load(std::string_view(max).substr(prefix_len), is_max_exact, true),
301+
auto min_packed =
302+
load(std::string_view(min).substr(prefix_len), is_min_exact, false);
303+
auto max_packed =
304+
load(std::string_view(max).substr(prefix_len), is_max_exact, true);
305+
if (!min_packed || !max_packed) return std::nullopt;
306+
return MinMax{
307+
*min_packed,
308+
*max_packed,
295309
std::string_view(max).substr(0, prefix_len),
296310
};
297311
}
@@ -720,9 +734,9 @@ struct FlatbufferConverter {
720734
} else if (t.__isset.ENUM) {
721735
return {format3::LogicalType::EnumType, format3::CreateEmpty(root).Union()};
722736
} else if (t.__isset.DECIMAL) {
723-
return {
724-
format3::LogicalType::DecimalType,
725-
format3::CreateDecimalOptions(root, t.DECIMAL.precision, t.DECIMAL.scale).Union()};
737+
return {format3::LogicalType::DecimalType,
738+
format3::CreateDecimalOptions(root, t.DECIMAL.precision, t.DECIMAL.scale)
739+
.Union()};
726740
} else if (t.__isset.DATE) {
727741
return {format3::LogicalType::DateType, format3::CreateEmpty(root).Union()};
728742
} else if (t.__isset.TIME) {
@@ -734,9 +748,9 @@ struct FlatbufferConverter {
734748
return {format3::LogicalType::TimestampType,
735749
format3::CreateTimeOptions(root, t.TIMESTAMP.isAdjustedToUTC, tu).Union()};
736750
} else if (t.__isset.INTEGER) {
737-
return {
738-
format3::LogicalType::IntType,
739-
format3::CreateIntOptions(root, t.INTEGER.bitWidth, t.INTEGER.isSigned).Union()};
751+
return {format3::LogicalType::IntType,
752+
format3::CreateIntOptions(root, t.INTEGER.bitWidth, t.INTEGER.isSigned)
753+
.Union()};
740754
} else if (t.__isset.UNKNOWN) {
741755
return {format3::LogicalType::NullType, format3::CreateEmpty(root).Union()};
742756
} else if (t.__isset.JSON) {
@@ -1230,11 +1244,22 @@ ::arrow::Result<uint32_t> ExtractFlatbuffer(std::shared_ptr<Buffer> buf,
12301244
uint32_t raw_len = LoadLE32(p + 9);
12311245
uint32_t len_crc32 = LoadLE32(p + 13);
12321246

1247+
if (compressed_len > std::numeric_limits<uint32_t>::max() - 42) {
1248+
return ::arrow::Status::Invalid("Extended metadata compressed_len overflow");
1249+
}
1250+
uint32_t required_or_consumed = compressed_len + 42;
1251+
if (static_cast<uint64_t>(required_or_consumed) > static_cast<uint64_t>(buf->size())) {
1252+
return required_or_consumed;
1253+
}
1254+
12331255
// Verify length CRC
12341256
uint32_t expected_len_crc = ::arrow::internal::crc32(0, p + 5, 8);
12351257
if (len_crc32 != expected_len_crc) {
12361258
return ::arrow::Status::Invalid("Extended metadata length CRC mismatch");
12371259
}
1260+
if (raw_len > kMaxRawMetadata3Len) {
1261+
return ::arrow::Status::Invalid("Extended metadata raw_len exceeds maximum");
1262+
}
12381263

12391264
// Verify data CRC
12401265
uint32_t expected_crc =
@@ -1282,7 +1307,7 @@ ::arrow::Result<uint32_t> ExtractFlatbuffer(std::shared_ptr<Buffer> buf,
12821307
out_flatbuffer->assign(reinterpret_cast<const char*>(decompressed_data.data()),
12831308
raw_len);
12841309

1285-
return compressed_len + 42;
1310+
return required_or_consumed;
12861311
}
12871312

12881313
} // namespace parquet

0 commit comments

Comments
 (0)