From 8c3ba3c8b1b089ff0f551735d38a4b03f5e33c85 Mon Sep 17 00:00:00 2001 From: omerTT <98276573+omertt27@users.noreply.github.com> Date: Thu, 11 Jun 2026 00:15:39 +0300 Subject: [PATCH] GH-50149: [C++][Parquet] Handle OOM as soft failure in parquet encoding fuzzer When the CappedMemoryPool (2.2 GB limit) triggers OOM during encoding roundtrip, the resulting Status::OutOfMemory was passed to ARROW_CHECK_OK or .ValueOrDie(), both of which call ARROW_LOG(FATAL) -> std::abort(). This is not an exception, so BEGIN_PARQUET_CATCH_EXCEPTIONS cannot intercept it, and OSS-Fuzz sees a process crash instead of a resource-limit event. Add FuzzCheckOk(Status) in the anonymous namespace: returns the status if OutOfMemory (soft failure, propagates up to LogFuzzStatus which already handles it), calls ARROW_CHECK_OK for any other non-OK status (hard abort preserved so OSS-Fuzz still catches real decoder/encoder bugs). Six call sites fixed in TypedFuzzEncoding::Fuzz(); three invariant checks (decoder value count, BinaryBuilder state, byte count) intentionally left as hard aborts because they indicate actual implementation bugs. Co-Authored-By: Claude Sonnet 4.6 --- .../parquet/arrow/fuzz_encoding_internal.cc | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/cpp/src/parquet/arrow/fuzz_encoding_internal.cc b/cpp/src/parquet/arrow/fuzz_encoding_internal.cc index 4270eb543788..80c800707a5a 100644 --- a/cpp/src/parquet/arrow/fuzz_encoding_internal.cc +++ b/cpp/src/parquet/arrow/fuzz_encoding_internal.cc @@ -155,6 +155,14 @@ namespace { // Just to use std::vector while avoiding std::vector using BooleanSlot = std::array; +// OOM during fuzzing is an expected soft failure; any other non-OK status +// indicates a real bug and should abort so OSS-Fuzz can report it. +Status FuzzCheckOk(const Status& st) { + if (st.IsOutOfMemory()) return st; + ARROW_CHECK_OK(st); + return Status::OK(); +} + template using PoolAllocator = ::arrow::stl::allocator; @@ -283,7 +291,7 @@ struct TypedFuzzEncoding { // Read as Arrow directly and use that as reference ARROW_ASSIGN_OR_RAISE(reference_array_, DecodeArrow(source_encoding_, encoded_data_)); - ARROW_CHECK_OK(reference_array_->ValidateFull()); + RETURN_NOT_OK(FuzzCheckOk(reference_array_->ValidateFull())); } else { ARROW_ASSIGN_OR_RAISE(reference_values_, Decode(source_encoding_, encoded_data_, num_values_)); @@ -300,13 +308,15 @@ struct TypedFuzzEncoding { encoder->Put(*reference_array_); auto reencoded_buffer = encoder->FlushValues(); auto reencoded_data = reencoded_buffer->template span_as(); - auto array = DecodeArrow(roundtrip_encoding_, reencoded_data).ValueOrDie(); - ARROW_CHECK_OK(array->ValidateFull()); - ARROW_CHECK_OK(CompareAgainstReference(array)); + auto array_result = DecodeArrow(roundtrip_encoding_, reencoded_data); + RETURN_NOT_OK(FuzzCheckOk(array_result.status())); + auto array = std::move(*array_result); + RETURN_NOT_OK(FuzzCheckOk(array->ValidateFull())); + RETURN_NOT_OK(FuzzCheckOk(CompareAgainstReference(array))); // Compare with reading raw values for (const int chunk_size : chunk_sizes()) { - ARROW_CHECK_OK(RunOnDecodedChunks(roundtrip_encoding_, reencoded_data, - chunk_size, compare_chunk)); + RETURN_NOT_OK(FuzzCheckOk(RunOnDecodedChunks(roundtrip_encoding_, reencoded_data, + chunk_size, compare_chunk))); } } else { encoder->Put(reference_values_.data(), @@ -315,8 +325,8 @@ struct TypedFuzzEncoding { auto reencoded_data = reencoded_buffer->template span_as(); // Vary chunk sizes for (const int chunk_size : chunk_sizes()) { - ARROW_CHECK_OK(RunOnDecodedChunks(roundtrip_encoding_, reencoded_data, - chunk_size, compare_chunk)); + RETURN_NOT_OK(FuzzCheckOk(RunOnDecodedChunks(roundtrip_encoding_, reencoded_data, + chunk_size, compare_chunk))); } } END_PARQUET_CATCH_EXCEPTIONS