binder: RuntimeException during outbound serialization leaks the call and never notifies the peer
Los mantenedores suelen responder en 1 día
Evaluación
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Aptitud para principiantes
- 48/100
- Tipo de issue
- Error
- Claridad
- Bien especificado
- Estado de actividad
- Activo
- Área
- backend-api-design
Línea de trabajo
Start with Outbound.sendInternal() and trace its callers in MultiMessageClientStream, SingleMessageClientStream, MultiMessageServerStream, and SingleMessageServerStream; inspect the listed start, half-close, headers, and close entry points. Compare their exception handling with writeMessage() and the core marshaller guards. Add coverage for the throwing-metadata reproductions on both client and server; done means failures close the call with non-OK status, release transport resources, and do not escape.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
What version of gRPC-Java are you using?
1.85.0-SNAPSHOT
What is your environment?
Android
What did you expect to see?
When serializing outbound metadata or a message throws an unchecked exception from a binder stream method, the stream should end deterministically, the same way it does when writeMessage() throws:
- the peer receives a close for that
callId(so it fails immediately instead of waiting out its deadline); - the local
ClientCall.Listener.onClose()/ServerCall.Listener.onCancel()runs with a non-OK status, and the application is not told the RPC completed successfully; - the call is removed from the transport's
ongoingCalls(and the in-use count decremented) so gracefulshutdown()can finish; - The unchecked exception does not escape to the caller.
ClientCallImpl.sendMessageInternal() and ServerCallImpl.sendMessageInternal() already catch RuntimeException from marshallers and cancel the stream; the four entry points below should behave the same.
What did you see instead?
grpc-binder serializes headers, messages and trailers lazily inside Outbound.sendInternal(). That method may call into user-supplied code (Parcelable.writeToParcel(), Metadata.BinaryStreamMarshaller.toStream(), InputStream.read()/close() on marshaller streams) but only catches IOException; every caller above it (Outbound.send(), the four *ClientStream/*ServerStream classes, Inbound.onTransportReady()) only catches StatusException. An unchecked exception escapes the transport entirely from four entry points that core does not guard:
| Entry point | Core caller | Guarded by core? |
|---|---|---|
MultiMessageClientStream.start() (writes request headers) |
ClientCallImpl.startInternal() → stream.start() |
No |
SingleMessageClientStream.halfClose() (writes headers + the single request + half-close) |
ClientCallImpl.halfCloseInternal() |
No |
MultiMessageServerStream.writeHeaders() |
ServerCallImpl.sendHeadersInternal() |
No |
SingleMessageServerStream.close() / MultiMessageServerStream.close() (writes headers, pending message, status + trailers) |
ServerCallImpl.closeInternal() |
No |
(For unary calls writeMessage() only stashes pendingSingleMessage; all serialization, including of the message, happens in halfClose()/close(), outside core's sendMessage() guard.)
Steps to reproduce the bug
Common fixture:
// A Parcelable whose writeToParcel() always throws.
static final class ThrowingParcelable implements Parcelable {
@Override public void writeToParcel(Parcel parcel, int flags) {
throw new IllegalStateException("writeToParcel() failed");
}
// describeContents()/CREATOR omitted
}
static final Metadata.Key<ThrowingParcelable> POISON_KEY =
ParcelableUtils.metadataKey("poison-bin", ThrowingParcelable.CREATOR);
Server side:
- Register a unary echo method (
ServerCalls.asyncUnaryCall) wrapped in aServerInterceptorwhoseSimpleForwardingServerCall.close()doestrailers.put(POISON_KEY, ne w ThrowingParcelable())before delegating, and a second interceptor that records whether the appListenergetsonComplete()oronCancel(). - From the client,
ClientCalls.futureUnaryCall(channel.newCall(METHOD, CallOptions.DEFAULT.withDeadlineAfter(2, SECONDS)), Empty.getDefaultInstance()).get(10, SECONDS). - Assert the status is not
DEADLINE_EXCEEDED→ fails (it isDEADLINE_EXCEEDED). - Assert the recorded server listener outcome is
onCancel→ fails (it isonComplete). server.shutdown(); assertTrue(server.awaitTermination(10, SECONDS))→ fails.
Stack of the escaping exception: ServerCalls$ServerCallStreamObserverImpl.onCompleted → ServerCallImpl.close → SingleMessageServerStream.close → ServerOutbound.sendSingleMessageAndClose → Outbound.send → Outbound.sendInternal → ServerOutbound.writeSuffix → MetadataHelper.writeMetadata → ParcelableInputStream.writeToParcel, surfacing in JumpToApplicationThreadServerStreamListener$HalfClosed.runInContext.
Client side:
- Complete one clean unary RPC on the channel first, so the binder transport is
READYand the nextstart()reaches the binder stream synchronously (otherwiseDelayedClientTransportdefers it to the app executor and the exception surfaces there instead). - Wrap the channel with a
ClientInterceptorwhoseSimpleForwardingClientCall.start()doesheaders.put(POISON_KEY, new ThrowingParcelable()), andnewCall()aBIDI_STREAMINGmethod withwithDeadlineAfter(2, SECONDS). call.start(listener, new Metadata()). Assert it does not throw → fails (IllegalStateException: writeToParcel() failed).- Assert
listener.onClose()is invoked within 10 s → fails (TimeoutException; the deadline never fires). channel.shutdown(); assertTrue(channel.awaitTermination(10, SECONDS))→ fails.
- Lenguaje dominante
- Java
- Estrellas
- 12.1k
- Forks
- 4k
- Merge medio
- 2 d 6 h
- PR fusionados (30 d)
- 26
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Sin plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de grpc/grpc-java
-
channelz: ServerData `calls_failed` counter not incremented upon client cancellationQuizá libre de nuevo @stdcout42 la tomó hace 13 días y no hay ningún pull request abierto. Abiertoenhancement
Dificultad 2/5 1-3 horas Aptitud para principiantes 88/100
grpc/grpc-java#13063 · 4 comentarios ·
Los mantenedores suelen responder en 1 día
-
Android: ProxyDetectorImpl crashes when DefaultProxySelector contains an invalid proxy portPosiblemente ocupada @kkmurthyt21 la tomó hace 28 días. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
grpc/grpc-java#13052 · 4 comentarios ·
Los mantenedores suelen responder en 1 día
-
Support of `dns:name` URIsAbiertodocs enhancement
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
grpc/grpc-java#10824 · 8 comentarios ·
Los mantenedores suelen responder en 1 día
-
Dificultad 4/5 3-5 días Aptitud para principiantes 45/100
Los mantenedores suelen responder en 1 día
-
enhancement
Dificultad 5/5 Más de una semana Aptitud para principiantes 35/100
Los mantenedores suelen responder en 1 día
Todos los issues de grpc/grpc-java
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 66/100
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 64/100
utopia-rise/godot-jvm#1004 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
spring-projects/spring-grpc#442 ·
-
Expose numberOfPermits in RateLimiterEvent.toString() and the ratelimiterevents actuator DTOPosiblemente ocupada Un pull request vinculado a esta issue está abierto o ya se fusionó. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
resilience4j/resilience4j#2547 ·
Los mantenedores suelen responder en 9 días
-
Clock.MakeDate continues execution and returns a rolled-over instant after dispatching error on invalid datePosiblemente ocupada Un pull request vinculado a esta issue está abierto o ya se fusionó. Abierto
Dificultad 1/5 Menos de una hora Aptitud para principiantes 82/100
mit-cml/appinventor-sources#4155 ·
Los mantenedores suelen responder en 1 día