Skip to content

Commit 53da7e2

Browse files
markdrothcopybara-github
authored andcommitted
[SubchannelStreamClient] fix use-after-free bug (grpc#38952)
Also remove some lingering references to health checking, which I missed when I originally refactored this code out of the health checking client code back in grpc#29024. Closes grpc#38952 COPYBARA_INTEGRATE_REVIEW=grpc#38952 from markdroth:subchannel_stream_client_fix 65a9657 PiperOrigin-RevId: 734676625
1 parent 6e2d261 commit 53da7e2

File tree

2 files changed

+15
-11
lines changed

2 files changed

+15
-11
lines changed

src/core/client_channel/subchannel_stream_client.cc

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ void SubchannelStreamClient::StartRetryTimerLocked() {
130130
const Duration timeout = retry_backoff_.NextAttemptDelay();
131131
if (GPR_UNLIKELY(tracer_ != nullptr)) {
132132
LOG(INFO) << tracer_ << " " << this
133-
<< ": SubchannelStreamClient health check call lost...";
133+
<< ": SubchannelStreamClient call lost...";
134134
if (timeout > Duration::Zero()) {
135135
LOG(INFO) << tracer_ << " " << this << ": ... will retry in "
136136
<< timeout.millis() << "ms.";
@@ -139,10 +139,10 @@ void SubchannelStreamClient::StartRetryTimerLocked() {
139139
}
140140
}
141141
retry_timer_handle_ = event_engine_->RunAfter(
142-
timeout, [self = Ref(DEBUG_LOCATION, "health_retry_timer")]() mutable {
142+
timeout, [self = Ref(DEBUG_LOCATION, "retry_timer")]() mutable {
143143
ExecCtx exec_ctx;
144144
self->OnRetryTimer();
145-
self.reset(DEBUG_LOCATION, "health_retry_timer");
145+
self.reset(DEBUG_LOCATION, "retry_timer");
146146
});
147147
}
148148

@@ -152,7 +152,7 @@ void SubchannelStreamClient::OnRetryTimer() {
152152
call_state_ == nullptr) {
153153
if (GPR_UNLIKELY(tracer_ != nullptr)) {
154154
LOG(INFO) << tracer_ << " " << this
155-
<< ": SubchannelStreamClient restarting health check call";
155+
<< ": SubchannelStreamClient restarting call";
156156
}
157157
StartCallLocked();
158158
}
@@ -164,9 +164,9 @@ void SubchannelStreamClient::OnRetryTimer() {
164164
//
165165

166166
SubchannelStreamClient::CallState::CallState(
167-
RefCountedPtr<SubchannelStreamClient> health_check_client,
167+
RefCountedPtr<SubchannelStreamClient> subchannel_stream_client,
168168
grpc_pollset_set* interested_parties)
169-
: subchannel_stream_client_(std::move(health_check_client)),
169+
: subchannel_stream_client_(std::move(subchannel_stream_client)),
170170
pollent_(grpc_polling_entity_create_from_pollset_set(interested_parties)),
171171
arena_(subchannel_stream_client_->call_allocator_->MakeArena()) {}
172172

@@ -299,7 +299,7 @@ void SubchannelStreamClient::CallState::AfterCallStackDestruction(
299299
void SubchannelStreamClient::CallState::OnCancelComplete(
300300
void* arg, grpc_error_handle /*error*/) {
301301
auto* self = static_cast<SubchannelStreamClient::CallState*>(arg);
302-
GRPC_CALL_COMBINER_STOP(&self->call_combiner_, "health_cancel");
302+
GRPC_CALL_COMBINER_STOP(&self->call_combiner_, "cancel_batch");
303303
self->call_->Unref(DEBUG_LOCATION, "cancel");
304304
}
305305

@@ -322,7 +322,7 @@ void SubchannelStreamClient::CallState::Cancel() {
322322
GRPC_CALL_COMBINER_START(
323323
&call_combiner_,
324324
GRPC_CLOSURE_CREATE(StartCancel, this, grpc_schedule_on_exec_ctx),
325-
absl::OkStatus(), "health_cancel");
325+
absl::OkStatus(), "cancel_batch");
326326
}
327327
}
328328

@@ -406,18 +406,22 @@ void SubchannelStreamClient::CallState::RecvTrailingMetadataReady(
406406
LOG(INFO) << self->subchannel_stream_client_->tracer_ << " "
407407
<< self->subchannel_stream_client_.get()
408408
<< ": SubchannelStreamClient CallState " << self
409-
<< ": health watch failed with status " << status;
409+
<< ": call failed with status " << status;
410410
}
411411
// Clean up.
412412
self->recv_trailing_metadata_.Clear();
413413
// Report call end.
414+
// Note: We hold a ref to the SubchannelStreamClient here to ensure
415+
// that it lives long enough for us to release the mutex, since the
416+
// call to CallEndedLocked() may release the last ref.
417+
auto subchannel_stream_client = self->subchannel_stream_client_->Ref();
414418
MutexLock lock(&self->subchannel_stream_client_->mu_);
415419
if (self->subchannel_stream_client_->event_handler_ != nullptr) {
416420
self->subchannel_stream_client_->event_handler_
417421
->RecvTrailingMetadataReadyLocked(self->subchannel_stream_client_.get(),
418422
status);
419423
}
420-
// For status UNIMPLEMENTED, give up and assume always healthy.
424+
// For status UNIMPLEMENTED, give up.
421425
self->CallEndedLocked(/*retry=*/status != GRPC_STATUS_UNIMPLEMENTED);
422426
}
423427

src/core/client_channel/subchannel_stream_client.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ class SubchannelStreamClient final
202202
Mutex mu_;
203203
std::unique_ptr<CallEventHandler> event_handler_ ABSL_GUARDED_BY(mu_);
204204

205-
// The data associated with the current health check call. It holds a ref
205+
// The data associated with the current call. It holds a ref
206206
// to this SubchannelStreamClient object.
207207
OrphanablePtr<CallState> call_state_ ABSL_GUARDED_BY(mu_);
208208

0 commit comments

Comments
 (0)