Skip to content

GH-3497: Fix thread-unsafe static singleton in DefaultValuesWriterFactory causing use-after-free#3498

Open
arouel wants to merge 1 commit intoapache:masterfrom
arouel:fix-gh-3497
Open

GH-3497: Fix thread-unsafe static singleton in DefaultValuesWriterFactory causing use-after-free#3498
arouel wants to merge 1 commit intoapache:masterfrom
arouel:fix-gh-3497

Conversation

@arouel
Copy link
Copy Markdown

@arouel arouel commented Apr 19, 2026

Rationale for this change

DefaultValuesWriterFactory delegates to static singleton instances of DefaultV1ValuesWriterFactory and DefaultV2ValuesWriterFactory. These singletons store a mutable parquetProperties reference via initialize(), which gets overwritten by whichever ParquetProperties instance calls it last. When multiple Parquet writers run concurrently in the same JVM, the merger's column writers end up using the appender's ByteBufferAllocator. When the appender is closed and its allocator released, the merger crashes with IllegalStateException: Already closed on direct ByteBuffer access because its encoder slabs were allocated from a now-closed arena.

What changes are included in this PR?

Replace the static final DEFAULT_V1_WRITER_FACTORY and DEFAULT_V2_WRITER_FACTORY singletons in DefaultValuesWriterFactory with fresh instances created per initialize() call. This ensures each ParquetProperties instance (and its allocator) is isolated to its own factory, eliminating cross-contamination between concurrent writers sharing the same JVM.

Are these changes tested?

Yes. A new test testFactoryIsolation_eachPropertiesUsesOwnAllocator in DefaultValuesWriterFactoryTest verifies that two independently built ParquetProperties instances with different allocators produce ValuesWriter instances that use their own respective allocators. The test fails against the original code.

Are there any user-facing changes?

No API changes.

Closes #3497

…iterFactory` causing use-after-free

`DefaultValuesWriterFactory` delegates to static singleton instances of `DefaultV1ValuesWriterFactory` and `DefaultV2ValuesWriterFactory`. These singletons store a mutable `parquetProperties` reference via `initialize()`, which gets overwritten by whichever `ParquetProperties` instance calls it last. When multiple Parquet writers run concurrently in the same JVM, the merger's column writers end up using the appender's `ByteBufferAllocator`. When the appender is closed and its allocator released, the merger crashes with `IllegalStateException: Already closed` on direct ByteBuffer access because its encoder slabs were allocated from a now-closed arena.

Replace the static final `DEFAULT_V1_WRITER_FACTORY` and `DEFAULT_V2_WRITER_FACTORY` singletons in `DefaultValuesWriterFactory` with fresh instances created per `initialize()` call. This ensures each `ParquetProperties` instance (and its allocator) is isolated to its own factory, eliminating cross-contamination between concurrent writers sharing the same JVM.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DefaultValuesWriterFactory uses thread-unsafe static singletons, causing cross-writer allocator contamination

1 participant