BuiltinSnappyCompressorV2: Skip memcpy when not needed (fix UB)#14886
BuiltinSnappyCompressorV2: Skip memcpy when not needed (fix UB)#14886evanj wants to merge 1 commit into
Conversation
✅ clang-tidy: No findings on changed linesCompleted in 60.5s. |
Running the rust-rocksdb tests under valgrind failed with the following memcheck error. The error is that snappy first calls GetAppendBuffer() to get an output buffer. It then writes into the prefix, then passes it to Append(). The current implementation was doing an unnecessary memcpy in this case, with the same source and destination pointer. This is technically undefined behavior since the arguments to memcpy are restrict pointers. Add a unit test that reproduces the valgrind error: Source and destination overlap in memcpy(0x8aa5122, 0x8aa5122, 194) at 0x49206AC: __GI_memcpy (vg_replace_strmem.c:1155) by 0x550788F: rocksdb::(anonymous namespace)::BuiltinSnappyCompressorV2::CompressBlock(rocksdb::Slice, char*, unsigned long*, rocksdb::CompressionType*, rocksdb::ManagedPtr<rocksdb::Compressor::WorkingArea, rocksdb::Compressor, &rocksdb::Compressor::ReleaseWorkingArea>*)::MySink::Append(char const*, unsigned long) (util/compression.cc:510) by 0x574445B: snappy::Compress(snappy::Source*, snappy::Sink*) (in /usr/lib/aarch64-linux-gnu/libsnappy.so.1.1.8) by 0x550765F: rocksdb::(anonymous namespace)::BuiltinSnappyCompressorV2::CompressBlock(rocksdb::Slice, char*, unsigned long*, rocksdb::CompressionType*, rocksdb::ManagedPtr<rocksdb::Compressor::WorkingArea, rocksdb::Compressor, &rocksdb::Compressor::ReleaseWorkingArea>*) (util/compression.cc:529) by 0x405D4BF: rocksdb::DBCompressionTest_SnappyCompressBlockRoundTrip_Test::TestBody() (util/compression_test.cc:2755)
c3c7058 to
27a8840
Compare
🟡 Codex Code ReviewAuto-triggered after CI passed — reviewing commit 27a8840 ❌ Codex review failed before producing findings. ℹ️ About this responseGenerated by Codex CLI. Limitations:
Commands:
|
✅ Claude Code ReviewAuto-triggered after CI passed — reviewing commit 27a8840 SummaryClean, minimal fix for real undefined behavior (overlapping No high-severity findings. Full review (click to expand)Findings🔴 HIGHNone. 🟡 MEDIUMNone. 🟢 LOW / NITL1. Comment style nit -- lowercase start —
|
| Context | Assumptions hold? | Action needed? |
|---|---|---|
| WritePreparedTxnDB | YES | None |
| ReadOnly DB | N/A (no writes) | None |
| CompactionService | YES | None |
| Concurrent writers | YES (MySink is stack-local) | None |
| All other contexts | YES | None |
Assumption stress-test: Verified that data == output_ + pos_ can only occur via GetAppendBuffer, partial overlaps cannot occur per the snappy Sink contract, and pos_ += n correctly executes unconditionally outside the guard.
Positive Observations
- Minimal, surgical fix -- exactly one condition added.
- Pointer equality check is the optimal approach (avoids both UB and unnecessary work).
- Well-constructed test with full compress-decompress round trip.
- Excellent PR description with exact valgrind error and root cause.
ℹ️ About this response
Generated by Claude Code.
Review methodology: claude_md/code_review.md
Limitations:
- Claude may miss context from files not in the diff
- Large PRs may be truncated
- Always apply human judgment to AI suggestions
Commands:
/claude-review [context]— Request a code review/claude-query <question>— Ask about the PR or codebase
Running the rust-rocksdb tests under valgrind failed with the following memcheck error. The error is that snappy first calls GetAppendBuffer() to get an output buffer. It then writes into the prefix, then passes it to Append(). The current implementation was doing an unnecessary memcpy in this case, with the same source and destination pointer. This is technically undefined behavior since the arguments to memcpy are restrict pointers.
Add a unit test that reproduces the valgrind error: