Repository navigation
Conversation
There was a problem hiding this comment.
Code Review
This pull request improves thread safety in ObjectDescriptorImpl by ensuring that hash validator creation and stream size queries are properly synchronized under the mu_ mutex. It also adds robust concurrent tests to verify these changes. The review feedback highlights several violations of the repository's style guide regarding the use of auto where explicit types should be used instead, specifically for obscured return types (hash_function, hash_validator, and reader) and primitive types (idx).
bf10a09 to
a8191a4
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #16625 +/- ##
==========================================
- Coverage 92.45% 92.45% -0.01%
==========================================
Files 2269 2269
Lines 222034 222192 +158
==========================================
+ Hits 205292 205426 +134
- Misses 16742 16766 +24 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
a8191a4 to
af1bd29
Compare
af1bd29 to
0b6ec83
Compare
…mpl::Read Acquire mu_ in ObjectDescriptorImpl::Read() before inspecting metadata_ and read_object_spec_ (and before checking prewarmed_ranges_), require a std::unique_lock<std::mutex> witness in CreateHashValidator(), and lock mu_ in ObjectDescriptorImpl::StreamSize(). Without holding mu_, concurrent metadata_ move-assignments in OnRead() (e.g. when MakeSubsequentStream() or OnResume() processes a new stream's first BidiReadObjectResponse) race with Read() / CreateHashValidator() reading heap-allocated fields such as the 16-byte raw md5_hash digest, triggering TSAN data races and SIGSEGV crashes.
de0701a to
258a90a
Compare
258a90a to
f99a74a
Compare
Summary
Acquires
mu_inObjectDescriptorImpl::Read()before readingmetadata_andread_object_spec_(and before checkingprewarmed_ranges_), enforces the lock precondition onCreateHashValidator()viastd::unique_lock<std::mutex> const&, and locksmu_inObjectDescriptorImpl::StreamSize().Motivation
SIGSEGVinObjectDescriptorImpl::Read()/CreateHashValidator():ObjectDescriptorImpl::OnRead()andObjectDescriptorImpl::Resume()mutatemetadata_(metadata_ = std::move(*response->mutable_metadata())) andread_object_spec_(mutable_read_handle()/ApplyRedirectErrors()) on background gRPC completion threads while holdingmu_.ObjectDescriptorImpl::Read()inspectedmetadata_(has_value(),has_finalize_time(),size()), invokedCreateHashValidator()(readingmetadata_->checksums().crc32c()andmetadata_->checksums().md5_hash()), and readread_object_spec_.bucket()/read_object_spec_.object()before acquiringmu_.MakeSubsequentStream()/OnResume()) orBidiReadObjectResponsemove-assignsmetadata_while another thread callsRead(), heap-allocated protobuf strings such as the 16-byte rawmd5_hash(which exceeds the 15-byte SSO buffer onlibstdc++) can be swapped/freed mid-read insideCreateHashValidator()/MD5FromProto()(Base64Encode()), causing aSIGSEGV.prewarmed_ranges_immediately after acquiringmu_also avoids allocating and discardingHashFunction,HashValidator, andReadRangeobjects on pre-warmed cache hits.MultiStreamManageraccess inObjectDescriptorImpl::StreamSize():MultiStreamManageris not internally synchronized and requires callers to holdmu_, soStreamSize()now acquiresstd::scoped_lock<std::mutex> lk(mu_).Changes
google/cloud/storage/internal/async/object_descriptor_impl.{h,cc}:std::unique_lock<std::mutex> lk(mu_)inObjectDescriptorImpl::Read()before accessingprewarmed_ranges_,metadata_, andread_object_spec_.std::unique_lock<std::mutex> const&toObjectDescriptorImpl::CreateHashValidator()(matchingAssurePendingStreamQueued()) to enforce at compile time thatmu_is held, and guardmetadata_.has_value()defensively.mu_inObjectDescriptorImpl::StreamSize().google/cloud/storage/internal/async/object_descriptor_impl_test.cc:ConcurrentReadAndMetadataUpdateandConcurrentMultiStreamReadAndStreamSizemulti-threaded unit tests exercising concurrentRead(),MakeSubsequentStream(),metadata(), andStreamSize()calls against backgroundOnRead()metadata andread_handleupdates.Verification
ObjectDescriptorImpl.ConcurrentReadAndMetadataUpdatereproduces the ThreadSanitizer data race prior to this change (Object::InternalSwapinObjectDescriptorImpl::OnReadatobject_descriptor_impl.cc:516vs unlocked read inObjectDescriptorImpl::Readatobject_descriptor_impl.cc:261).object_descriptorandconnection_impl_openasync unit test suites pass cleanly under--config=tsan(--runs_per_test=5).