CVE-2025-58369
Description
fs2 is a compositional, streaming I/O library for Scala. Versions up to and including 2.5.12, 3.0.0-M1 through 3.12.2, and 3.13.0-M1 through 3.13.0-M6 are vulnerable to denial of service attacks though TLS sessions using fs2-io on the JVM using the fs2.io.net.tls package. When establishing a TLS session, if one side of the connection shuts down write while the peer side is awaiting more data to progress the TLS handshake, the peer side will spin loop on the socket read, fully utilizing a CPU. The CPU is consumed until the overall connection is closed, potentially shutting down a fs2-io powered server. This issue is fixed in versions 2.5.13, 3.12.1, and 3.13.0-M7.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
co.fs2:fs2-io_2.12Maven | >= 3.0.0-M1, < 3.12.2 | 3.12.2 |
co.fs2:fs2-io_2.12Maven | >= 3.13.0-M1, < 3.13.0-M7 | 3.13.0-M7 |
co.fs2:fs2-io_2.13Maven | >= 3.0.0-M1, < 3.12.2 | 3.12.2 |
co.fs2:fs2-io_2.13Maven | >= 3.13.0-M1, < 3.13.0-M7 | 3.13.0-M7 |
co.fs2:fs2-io_3Maven | >= 3.0.0-M1, < 3.12.2 | 3.12.2 |
co.fs2:fs2-io_3Maven | >= 3.13.0-M1, < 3.13.0-M7 | 3.13.0-M7 |
co.fs2:fs2-io_0.26Maven | >= 0 | — |
co.fs2:fs2-io_0.27Maven | >= 0 | — |
co.fs2:fs2-io_2.11Maven | >= 0 | — |
co.fs2:fs2-io_2.12.0-M4Maven | >= 0 | — |
co.fs2:fs2-io_2.12.0-RC1Maven | >= 0 | — |
co.fs2:fs2-io_2.12.0-M5Maven | >= 0 | — |
co.fs2:fs2-io_2.12.0-RC2Maven | >= 0 | — |
co.fs2:fs2-io_2.13.0-M5Maven | >= 0 | — |
co.fs2:fs2-io_2.12Maven | < 2.5.13 | 2.5.13 |
co.fs2:fs2-io_2.13Maven | < 2.5.13 | 2.5.13 |
co.fs2:fs2-io_3Maven | < 2.5.13 | 2.5.13 |
Affected products
1Patches
4edf0c4f2e660Merge pull request #3624 from augi/patch-1
1 file changed · +1 −4
io/src/main/scala/fs2/io/tls/TLSEngine.scala+1 −4 modified@@ -218,10 +218,7 @@ private[tls] object TLSEngine { else binding.read(engine.getSession.getPacketBufferSize, timeout).flatMap { case Some(c) => unwrapBuffer.input(c) >> unwrapHandshake(timeout) - case None => - unwrapBuffer.inputRemains.flatMap(x => - if (x > 0) Applicative[F].unit else stopUnwrap - ) + case None => stopUnwrap } } case SSLEngineResult.HandshakeStatus.NEED_UNWRAP_AGAIN =>
46e2dc3abf99Merge pull request #3603 from mtomko/backport
2 files changed · +19 −1
io/jvm/src/main/scala/fs2/io/net/SelectingSocketGroup.scala+7 −1 modified@@ -22,6 +22,8 @@ package fs2 package io.net +import scala.concurrent.duration._ + import cats.effect.LiftIO import cats.effect.Selector import cats.effect.kernel.Async @@ -100,7 +102,11 @@ private final class SelectingSocketGroup[F[_]: LiftIO: Dns](selector: Selector)( ): Resource[F, (SocketAddress[IpAddress], Stream[F, Socket[F]])] = Resource .make(F.delay(selector.provider.openServerSocketChannel())) { ch => - F.delay(ch.close()) + def waitForDeregistration: F[Unit] = + // sleep time set to be short enough to not noticeably delay shutdown but long enough to + // give the runtime/cpu time to do something else; some guesswork involved here + F.delay(ch.isRegistered()).ifM(F.sleep(2.millis) >> waitForDeregistration, F.unit) + F.delay(ch.close()) >> waitForDeregistration } .evalMap { serverCh => val configure = address.traverse(_.resolve).flatMap { ip =>
io/shared/src/test/scala/fs2/io/net/tcp/SocketSuite.scala+12 −0 modified@@ -269,6 +269,18 @@ class SocketSuite extends Fs2Suite with SocketSuitePlatform { } } + test("sockets are released at the end of the resource scope") { + val f = + Network[IO].serverResource(port = Some(port"9071")).use { case (bindAddress, clients) => + clients.foreach(_ => IO.sleep(1.second)).compile.drain.background.surround { + Network[IO].client(bindAddress).use { client => + client.read(1).assertEquals(None) + } + } + } + f >> f >> f + } + test("endOfOutput / endOfInput ignores ENOTCONN") { Network[IO].serverResource().use { case (bindAddress, clients) => Network[IO].client(bindAddress).surround(IO.sleep(100.millis)).background.surround {
55b55dbe3199Fix compilation due to merge issue
1 file changed · +1 −0
io/jvm/src/test/scala/fs2/io/net/tls/TLSSocketSuite.scala+1 −0 modified@@ -237,6 +237,7 @@ class TLSSocketSuite extends TLSSuite { @deprecated("", "") def remoteAddress = raw.remoteAddress def writes = raw.writes + def metrics = raw.metrics def address = raw.address def getOption[A](key: SocketOption.Key[A]) = raw.getOption(key)
5c6c4c6c1ef3Merge pull request #3591 from mtomko/main
2 files changed · +19 −1
io/jvm/src/main/scala/fs2/io/net/SelectingSocketGroup.scala+7 −1 modified@@ -22,6 +22,8 @@ package fs2 package io.net +import scala.concurrent.duration._ + import cats.effect.LiftIO import cats.effect.Selector import cats.effect.kernel.Async @@ -100,7 +102,11 @@ private final class SelectingSocketGroup[F[_]: LiftIO: Dns](selector: Selector)( ): Resource[F, (SocketAddress[IpAddress], Stream[F, Socket[F]])] = Resource .make(F.delay(selector.provider.openServerSocketChannel())) { ch => - F.delay(ch.close()) + def waitForDeregistration: F[Unit] = + // sleep time set to be short enough to not noticeably delay shutdown but long enough to + // give the runtime/cpu time to do something else; some guesswork involved here + F.delay(ch.isRegistered()).ifM(F.sleep(2.millis) >> waitForDeregistration, F.unit) + F.delay(ch.close()) >> waitForDeregistration } .evalMap { serverCh => val configure = address.traverse(_.resolve).flatMap { ip =>
io/shared/src/test/scala/fs2/io/net/tcp/SocketSuite.scala+12 −0 modified@@ -269,6 +269,18 @@ class SocketSuite extends Fs2Suite with SocketSuitePlatform { } } + test("sockets are released at the end of the resource scope") { + val f = + Network[IO].serverResource(port = Some(port"9071")).use { case (bindAddress, clients) => + clients.foreach(_ => IO.sleep(1.second)).compile.drain.background.surround { + Network[IO].client(bindAddress).use { client => + client.read(1).assertEquals(None) + } + } + } + f >> f >> f + } + test("endOfOutput / endOfInput ignores ENOTCONN") { Network[IO].serverResource().use { case (bindAddress, clients) => Network[IO].client(bindAddress).surround(IO.sleep(100.millis)).background.surround {
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
10- github.com/advisories/GHSA-rrw2-px9j-qffjghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-58369ghsaADVISORY
- github.com/typelevel/fs2/commit/46e2dc3abf994dcf3d0b804b2ddb3c10c04d4976nvdWEB
- github.com/typelevel/fs2/commit/5c6c4c6c1ef330f7e6b53661ecc63d5f5ba8885cnvdWEB
- github.com/typelevel/fs2/commit/edf0c4f2e660360d1c1a8c5377ce32294de89238nvdWEB
- github.com/typelevel/fs2/issues/3590nvdWEB
- github.com/typelevel/fs2/pull/3624ghsaWEB
- github.com/typelevel/fs2/releases/tag/v3.12.2nvdWEB
- github.com/typelevel/fs2/releases/tag/v3.13.0-M7nvdWEB
- github.com/typelevel/fs2/security/advisories/GHSA-rrw2-px9j-qffjnvdWEB
News mentions
0No linked articles in our index yet.