Virtual thread pinning on RemoteJWKSet.get

Issue #542 open
Павел Белоносов created an issue

We use Java 21 and Spring Boot 3.2.4 (nimbus-jose-jwt 9.24.4) with enabled virtual threads in plain web application. First request to secured resource leads to thread pinning:

com.nimbusds.jose.jwt@9.24.4/com.nimbusds.jose.jwk.source.RemoteJWKSet.get(RemoteJWKSet.java:437) <== monitors:1

Full stack trace:

Thread[#137,ForkJoinPool-1-worker-1,5,CarrierThreads]
    java.base/java.lang.VirtualThread$VThreadContinuation.onPinned(VirtualThread.java:185)
    java.base/jdk.internal.vm.Continuation.onPinned0(Continuation.java:393)
    java.base/java.lang.VirtualThread.park(VirtualThread.java:592)
    java.base/java.lang.System$2.parkVirtualThread(System.java:2639)
    java.base/jdk.internal.misc.VirtualThreads.park(VirtualThreads.java:54)
    java.base/java.util.concurrent.locks.LockSupport.park(LockSupport.java:369)
    java.base/sun.nio.ch.Poller.pollIndirect(Poller.java:139)
    java.base/sun.nio.ch.Poller.poll(Poller.java:102)
    java.base/sun.nio.ch.Poller.poll(Poller.java:87)
    java.base/sun.nio.ch.NioSocketImpl.park(NioSocketImpl.java:175)
    java.base/sun.nio.ch.NioSocketImpl.park(NioSocketImpl.java:201)
    java.base/sun.nio.ch.NioSocketImpl.implRead(NioSocketImpl.java:309)
    java.base/sun.nio.ch.NioSocketImpl.read(NioSocketImpl.java:346)
    java.base/sun.nio.ch.NioSocketImpl$1.read(NioSocketImpl.java:796)
    java.base/java.net.Socket$SocketInputStream.read(Socket.java:1099)
    java.base/sun.security.ssl.SSLSocketInputRecord.read(SSLSocketInputRecord.java:489)
    java.base/sun.security.ssl.SSLSocketInputRecord.readHeader(SSLSocketInputRecord.java:483)
    java.base/sun.security.ssl.SSLSocketInputRecord.bytesInCompletePacket(SSLSocketInputRecord.java:70)
    java.base/sun.security.ssl.SSLSocketImpl.readApplicationRecord(SSLSocketImpl.java:1461)
    java.base/sun.security.ssl.SSLSocketImpl$AppInputStream.read(SSLSocketImpl.java:1066)
    java.base/java.io.BufferedInputStream.fill(BufferedInputStream.java:291)
    java.base/java.io.BufferedInputStream.read1(BufferedInputStream.java:347)
    java.base/java.io.BufferedInputStream.implRead(BufferedInputStream.java:420)
    java.base/java.io.BufferedInputStream.read(BufferedInputStream.java:399)
    java.base/sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:827)
    java.base/sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:759)
    java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1690)
    java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1599)
    java.base/java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:531)
    java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:307)
    spring.web@6.1.5/org.springframework.http.client.SimpleClientHttpRequest.executeInternal(SimpleClientHttpRequest.java:88)
    spring.web@6.1.5/org.springframework.http.client.AbstractStreamingClientHttpRequest.executeInternal(AbstractStreamingClientHttpRequest.java:70)
    spring.web@6.1.5/org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:66)
    spring.web@6.1.5/org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:889)
    spring.web@6.1.5/org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:730)
    spring.security.oauth2.jose@6.2.3/org.springframework.security.oauth2.jwt.NimbusJwtDecoder$JwkSetUriJwtDecoderBuilder$RestOperationsResourceRetriever.getResponse(NimbusJwtDecoder.java:488)
    spring.security.oauth2.jose@6.2.3/org.springframework.security.oauth2.jwt.NimbusJwtDecoder$JwkSetUriJwtDecoderBuilder$RestOperationsResourceRetriever.retrieveResource(NimbusJwtDecoder.java:478)
    com.nimbusds.jose.jwt@9.24.4/com.nimbusds.jose.jwk.source.RemoteJWKSet.updateJWKSetFromURL(RemoteJWKSet.java:305)
    com.nimbusds.jose.jwt@9.24.4/com.nimbusds.jose.jwk.source.RemoteJWKSet.get(RemoteJWKSet.java:437) <== monitors:1
    com.nimbusds.jose.jwt@9.24.4/com.nimbusds.jose.proc.JWSVerificationKeySelector.selectJWSKeys(JWSVerificationKeySelector.java:157)
    com.nimbusds.jose.jwt@9.24.4/com.nimbusds.jwt.proc.DefaultJWTProcessor.selectKeys(DefaultJWTProcessor.java:283)
    com.nimbusds.jose.jwt@9.24.4/com.nimbusds.jwt.proc.DefaultJWTProcessor.process(DefaultJWTProcessor.java:354)
    com.nimbusds.jose.jwt@9.24.4/com.nimbusds.jwt.proc.DefaultJWTProcessor.process(DefaultJWTProcessor.java:303)
    spring.security.oauth2.jose@6.2.3/org.springframework.security.oauth2.jwt.NimbusJwtDecoder.createJwt(NimbusJwtDecoder.java:158)
    spring.security.oauth2.jose@6.2.3/org.springframework.security.oauth2.jwt.NimbusJwtDecoder.decode(NimbusJwtDecoder.java:138)
    spring.security.oauth2.client@6.2.3/org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizationCodeAuthenticationProvider.getJwt(OidcAuthorizationCodeAuthenticationProvider.java:247)
    spring.security.oauth2.client@6.2.3/org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizationCodeAuthenticationProvider.createOidcToken(OidcAuthorizationCodeAuthenticationProvider.java:238)
    spring.security.oauth2.client@6.2.3/org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizationCodeAuthenticationProvider.authenticate(OidcAuthorizationCodeAuthenticationProvider.java:156)
    spring.security.core@6.2.3/org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:182)
    spring.security.core@6.2.3/org.springframework.security.authentication.ObservationAuthenticationManager.lambda$authenticate$1(ObservationAuthenticationManager.java:54)
    io.micrometer.observation.Observation.observe(Observation.java:565)
    spring.security.core@6.2.3/org.springframework.security.authentication.ObservationAuthenticationManager.authenticate(ObservationAuthenticationManager.java:53)
    spring.security.oauth2.client@6.2.3/org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter.attemptAuthentication(OAuth2LoginAuthenticationFilter.java:196)
    spring.security.web@6.2.3/org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:231)
    spring.security.web@6.2.3/org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:221)
    spring.security.web@6.2.3/org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
    spring.security.web@6.2.3/org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)
    spring.security.web@6.2.3/org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
    spring.security.oauth2.client@6.2.3/org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter.doFilterInternal(OAuth2AuthorizationRequestRedirectFilter.java:181)
    spring.web@6.1.5/org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
    spring.security.web@6.2.3/org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
    spring.security.web@6.2.3/org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)
    spring.security.web@6.2.3/org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
    spring.security.web@6.2.3/org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107)
    spring.security.web@6.2.3/org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93)
    spring.security.web@6.2.3/org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
    spring.security.web@6.2.3/org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)
    spring.security.web@6.2.3/org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
    spring.security.web@6.2.3/org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:117)
    spring.web@6.1.5/org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
    spring.security.web@6.2.3/org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
    spring.security.web@6.2.3/org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)
    spring.security.web@6.2.3/org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
    spring.web@6.1.5/org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91)
    spring.web@6.1.5/org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
    spring.security.web@6.2.3/org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
    spring.security.web@6.2.3/org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)
    spring.security.web@6.2.3/org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
    spring.security.web@6.2.3/org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90)
    spring.security.web@6.2.3/org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75)
    spring.web@6.1.5/org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
    spring.security.web@6.2.3/org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
    spring.security.web@6.2.3/org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)
    spring.security.web@6.2.3/org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
    spring.security.web@6.2.3/org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82)
    spring.security.web@6.2.3/org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69)
    spring.security.web@6.2.3/org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
    spring.security.web@6.2.3/org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)
    spring.security.web@6.2.3/org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
    spring.security.web@6.2.3/org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62)
    spring.web@6.1.5/org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
    spring.security.web@6.2.3/org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
    spring.security.web@6.2.3/org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)
    spring.security.web@6.2.3/org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
    spring.security.web@6.2.3/org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42)
    spring.web@6.1.5/org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
    spring.security.web@6.2.3/org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)
    spring.security.web@6.2.3/org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$0(ObservationFilterChainDecorator.java:323)
    spring.security.web@6.2.3/org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:224)
    spring.security.web@6.2.3/org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)
    spring.security.web@6.2.3/org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233)
    spring.security.web@6.2.3/org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191)
    spring.web@6.1.5/org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113)
    spring.webmvc@6.1.5/org.springframework.web.servlet.handler.HandlerMappingIntrospector.lambda$createCacheFilter$3(HandlerMappingIntrospector.java:195)
    spring.web@6.1.5/org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113)
    spring.web@6.1.5/org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74)
    spring.security.config@6.2.3/org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebMvcSecurityConfiguration.java:230)
    spring.web@6.1.5/org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:352)
    spring.web@6.1.5/org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:268)
    org.apache.tomcat.embed.core@10.1.19/org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
    org.apache.tomcat.embed.core@10.1.19/org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
    spring.web@6.1.5/org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101)
    org.apache.tomcat.embed.core@10.1.19/org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
    org.apache.tomcat.embed.core@10.1.19/org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
    spring.web@6.1.5/org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:109)
    spring.web@6.1.5/org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
    org.apache.tomcat.embed.core@10.1.19/org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
    org.apache.tomcat.embed.core@10.1.19/org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
    io.sentry.spring.jakarta@7.6.0-module/io.sentry.spring.jakarta.tracing.SentryTracingFilter.doFilterWithTransaction(SentryTracingFilter.java:107)
    io.sentry.spring.jakarta@7.6.0-module/io.sentry.spring.jakarta.tracing.SentryTracingFilter.doFilterInternal(SentryTracingFilter.java:87)
    spring.web@6.1.5/org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
    org.apache.tomcat.embed.core@10.1.19/org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
    org.apache.tomcat.embed.core@10.1.19/org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
    spring.web@6.1.5/org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
    spring.web@6.1.5/org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
    org.apache.tomcat.embed.core@10.1.19/org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
    org.apache.tomcat.embed.core@10.1.19/org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
    io.sentry.spring.jakarta@7.6.0-module/io.sentry.spring.jakarta.SentrySpringFilter.doFilterInternal(SentrySpringFilter.java:71)
    spring.web@6.1.5/org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
    org.apache.tomcat.embed.core@10.1.19/org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
    org.apache.tomcat.embed.core@10.1.19/org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
    org.apache.tomcat.embed.core@10.1.19/org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167)
    org.apache.tomcat.embed.core@10.1.19/org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90)
    org.apache.tomcat.embed.core@10.1.19/org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482)
    org.apache.tomcat.embed.core@10.1.19/org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115)
    org.apache.tomcat.embed.core@10.1.19/org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)
    org.apache.tomcat.embed.core@10.1.19/org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
    org.apache.tomcat.embed.core@10.1.19/org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344)
    org.apache.tomcat.embed.core@10.1.19/org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:391)
    org.apache.tomcat.embed.core@10.1.19/org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)
    org.apache.tomcat.embed.core@10.1.19/org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:896)
    org.apache.tomcat.embed.core@10.1.19/org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1744)
    org.apache.tomcat.embed.core@10.1.19/org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
    java.base/java.lang.VirtualThread.run(VirtualThread.java:311)

Would be nice if Nimbus became Loom-friendly and replace synchronized methods to ReentrantLock for virtual thread support.

This class in pgjdbc seems like an interesting approach.

Comments (2)

  1. Vladimir Dzhuvinov
    • changed status to open

    Ouch. Loom is a great thing, but it's still not practised here and as such unknown territory. I'll ask the original contributor for his opinion.

  2. Thomas Rørvik Skjølberg

    Good and bad news:

    • Getting JWKs via code that rely on ReentrantLock is already in place
    • Spring has not yet upgraded from the tested and tried RemoteJWKSet, so in the short term this will require some custom code.

    So while I guess RemoteJWKSetcould be upgraded to use ReentrantLock, perhaps filing and issue on the Spring project is best.

  3. Log in to comment