fix(cpp): fix memory leaks and null dereference crashes - #879
Merged
Conversation
- ChunkReader::reset() now clears wrapped buffer pointer after free to prevent double-free in subsequent destroy() - ChunkReader::load_by_meta() now frees allocated buffer and returns early on read failure, preventing memory leak - LZ4Compressor::compress() now frees compressed_buf_ on both LZ4_compress_default failure and mem_realloc failure paths - C wrapper string getters (by_name/by_index) now null-check the return value of get_value<String*>() before dereferencing, preventing crash on non-STRING/TEXT/BLOB column types
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## develop #879 +/- ##
===========================================
- Coverage 60.48% 60.47% -0.02%
===========================================
Files 744 744
Lines 49014 49021 +7
Branches 7784 7787 +3
===========================================
- Hits 29646 29643 -3
- Misses 17966 17972 +6
- Partials 1402 1406 +4 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes several C++ robustness issues in TsFile’s reader/compression/C-wrapper layers by addressing concrete memory-management bugs (double-free/leaks) and preventing a null dereference when requesting string values via the C API.
Changes:
- Prevent double-free in
ChunkReader::reset()by clearing the wrapped buffer pointer after freeing it. - Fix a read-failure leak in
ChunkReader::load_by_meta()by freeing the allocated buffer onread()error and returning early. - Fix
LZ4Compressor::compress()error-path leaks by freeingcompressed_buf_when compression or realloc fails. - Add null guards in C-wrapper string getters to avoid dereferencing a null
common::String*.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| cpp/src/reader/chunk_reader.cc | Avoids double-free on reset and plugs a buffer leak when chunk-header reads fail. |
| cpp/src/compress/lz4_compressor.cc | Frees the internal compression buffer on LZ4 failure paths to prevent leaks. |
| cpp/src/cwrapper/tsfile_cwrapper.cc | Returns nullptr instead of crashing when a non-string column is requested as string. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fix 4 classes of memory/crash bugs discovered during a systematic scan of the C++ codebase.
Changes
1. ChunkReader double-free (
src/reader/chunk_reader.cc)reset()frees the wrapped buffer viamem_free(file_data_buf)but does not clear the wrapped pointer inByteStream. Whendestroy()is subsequently called,get_wrapped_buf()returns the already-freed pointer andmem_free(buf)is called again → double-free crash.Fix: Call
in_stream_.clear_wrapped_buf()immediately aftermem_free()to null out the stale pointer.destroy()already has its own null-guard, so this prevents the double-free.2. ChunkReader read-failure memory leak (
src/reader/chunk_reader.cc)load_by_meta()allocatesfile_data_bufviamem_alloc(), then callsread_file_->read(). The original code only freed the buffer on the partial-success path (IS_SUCC(ret) && ret_read_len < MIN_SERIALIZED_SIZE). Ifread()itself returns an error, the buffer is never freed and never passed towrap_from()→ memory leak for every failed chunk load.Fix: Add an early-exit path after
read(): if!IS_SUCC(ret), free the buffer and return immediately.3. LZ4Compressor error-path leaks (
src/compress/lz4_compressor.cc)compress()allocatescompressed_buf_viamem_alloc(), then has two error paths that do not free it:LZ4_compress_defaultreturns ≤ 0 →compressed_buf_leakedmem_reallocreturns nullptr →compressed_buf_leakedFix: Add
mem_free(compressed_buf_); compressed_buf_ = nullptr;on both error paths.4. C wrapper string getter null dereference (
src/cwrapper/tsfile_cwrapper.cc)Both
tsfile_result_set_get_value_by_{name,index}_string()callr->get_value<common::String*>()which delegates toField::get_string_value(). For non-STRING/TEXT/BLOB column types, this returnsnullptr. The code then unconditionally dereferencesret->len_→ SIGSEGV.Fix: Add a null check before the dereference, returning
nullptr(which the Python caller already handles correctly, returningNone).Verification
TsFile_Testwith ASan/Debug)spotless:applyFiles Changed
cpp/src/reader/chunk_reader.cccpp/src/compress/lz4_compressor.cccpp/src/cwrapper/tsfile_cwrapper.cc