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
9394constexpr double kMinCompressionRatio = 1.2 ;
95+ constexpr uint32_t kMaxRawMetadata3Len = 128U * 1024U * 1024U ;
9496
9597constexpr 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