This is an automated email from the ASF dual-hosted git repository.
asf-gitbox-commits pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mina-site.git
The following commit(s) were added to refs/heads/master by this push:
new 273732fc7 Added a warning regarding the serialization impact with the
newer versions. Added some documentation about the TLS inernal handling
273732fc7 is described below
commit 273732fc795b4a2050dcb51174ca26735a02114a
Author: emmanuel lecharny <[email protected]>
AuthorDate: Tue Sep 15 09:46:59 2026 +0200
Added a warning regarding the serialization impact with the newer versions.
Added some documentation about the TLS inernal handling
---
source/mina-project/__index.md | 12 +-
.../technical-documentation/ssl-tls-internal.md | 171 ++++++++++++++++++++-
2 files changed, 181 insertions(+), 2 deletions(-)
diff --git a/source/mina-project/__index.md b/source/mina-project/__index.md
index 2e3312724..0a736b3d9 100644
--- a/source/mina-project/__index.md
+++ b/source/mina-project/__index.md
@@ -18,7 +18,17 @@ Apache MINA is often called:
However, it's much more than that. Please take a look around the list of the
*[features](features.html)* that enable rapid network application development,
and *[what people says about MINA](testimonials.html)*.
-Please grab yourself a *[2.0.x download](downloads_2_0.html)* or a *[2.1.x
download](downloads_2_1.html)*, try our *[Quick Start
Guide](quick-start-guide.html)*, surf our *[FAQ](faq.html)* or start join us on
*[our community](../contact.html)*
+Please grab yourself a *[2.1.x download](downloads_2_2.html)*, a *[2.1.x
download](downloads_2_1.html)* or a *[2.0.x download](downloads_2_0.html)*, try
our *[Quick Start Guide](quick-start-guide.html)*, surf our *[FAQ](faq.html)*
or start join us on *[our community](../contact.html)*
+
+## WARNING
+
+The fix for **CVE-2026-47065** changed the form **MINA** writes for serialized
objects exchanged through _IoBuffer.putObject_ / _getObject_ and
_ObjectSerializationCodecFactory_: after the class name, the full standard
**Java** serialization class descriptor is now written.
+
+Streams produced by **2.2.7**, **2.1.12** or **2.0.28** and earlier cannot be
read by **2.2.8**, **2.1.13** or **2.0.29** and later, and streams produced by
the newer releases cannot be read by the older ones.
+
+Arrays, primitives and non-Serializable classes are unaffected.
+
+Upgrades across this boundary fail with a _BufferDataException_ wrapping an
_EOFException_ or a _StreamCorruptedException_, and payloads persisted before
the upgrade become unreadable after it.
<div class="news">
{{< grabpage "mina-project/news.md" >}}
diff --git a/source/mina-project/technical-documentation/ssl-tls-internal.md
b/source/mina-project/technical-documentation/ssl-tls-internal.md
index 3fac1c4cf..864c34435 100644
--- a/source/mina-project/technical-documentation/ssl-tls-internal.md
+++ b/source/mina-project/technical-documentation/ssl-tls-internal.md
@@ -112,7 +112,7 @@ Most of the time, records will be smaller (and the packet
size is stored in the
Now, considering the very nature of **TCP**, we may receive a TLS record in
small chunks, or more than one TLS record in a packet of data. We have to deal
with both cases.
-We will use an internal pending buffer that will contain the incoming data
until they can be fully processed. The important point is that the *SslEngine*
class is using *ByteBuffer* to get things done, though a set of methods:
+We will use an internal pending buffer that will contain the incoming data
until they can be fully processed. The important point is that the *SslEngine*
class is using *ByteBuffer* to get things done, through a set of methods:
* _wrap(ByteBuffer src, [int offset, int length,] ByteBuffer dst)_: Encrypt
the data from the source buffer into the destination buffer
* _unwrap(ByteBuffer src, [int offset, int length,] ByteBuffer dst)_: Decrypt
the data from the source buffer into the destination buffer
@@ -134,6 +134,175 @@ The following schema shows that we are switching from an
incoming _IoBuffer_ to
+----------------+
```
+Also note that we receive data by chunks (starting with a 1024 buffer), which
may not be decodable. And this is problematic froma performance piont of vue:
Imagine we are going to receive a block of 55Kb of data, it won't fit on a
single TLS packet. We will need 4 TLS packets to contain all the data. And we
start by reading a 1024 bytes buffer...
+
+Here is how MINA will proceed:
+* Read 1024 bytes in the incoming buffer
+* Allocate a buffer capable of storing the size defined in the TLS packet
+* Try to decode it (it will fail)
+* So free the destination buffer
+* Resize the incoming buffer, increasing its size to hold 2048 more bytes
+* Try to read 2048 bytes (we now have 3072 bytes in the buffer)
+* Allocate a buffer capable of storing the size defined in the TLS packet
+* Try to decode it (it will fail again...)
+* So free the destination buffer
+* Resize the incoming buffer, adding 4096 bytes into it
+* Repeat reading 4096 bytes stored in the 3072 + 4096 sized buffer (7168 bytes)
+* Allocate a buffer capable of storing the size defined in the TLS packet
+* Try to decode it (it will still fail...): The TLS packet contains 16384 -
padding bytes of data.
+* So free the destination buffer
+* Resize again the incoming buffer, adding 8192 bytes
+* Read 8192 bytes leading to a incoming buffer containing 15360 bytes
+* Allocate a buffer capable of storing the size defined in the TLS packet
+* Which won't be decoded either
+* So free the destination buffer
+* Resize the incoming buffer, adding 16384 bytes, leading to a 31744 bytes
buffer
+* Read 16384 bytes of data
+* Allocate a buffer capable of storing the size defined in the TLS packet
+* And now the decoding could work!
+* Copy the decoded buffer into a buffer that will be send to the IoHandler
+* Free the decoing buffer
+* and as we have some remaining data in the incoming buffer,
+* Allocate a buffer capable of storing the size defined in the TLS packet
+* Try to decode it (and it will fail...)
+* Free the buffer
+* etc etc until we have read and processed all the data.
+
+Here is how it translates in method calls:
+
+```
+AbstractPollingIoProcessor.read
+ IoBuffer.allocate(1024)
+ ...
+ SslFilter.messageReceived
+ SslHandler.receive
+ receive_start
+ resume_decode_buffer
+ mDecodedBuffer <- message
+ receive_loop
+ dest <- allocate(ZERO)
+ SslEngine.unwrap() -> BUFFER_OVERFLOW
+ dest free
+ suspend_decode_buffer
+ throw_pending_error
+ forward_writes
+ forward_received
+ forward_events
+ IoBuffer.allocate(2048)
+ ...
+ SslFilter.messageReceived
+ SslHandler.receive
+ receive_start
+ resume_decode_buffer
+ mDecodedBuffer.expand -> 3072
+ source free
+ receive_loop
+ dest <- allocate(ZERO)
+ SslEngine.unwrap() -> BUFFER_OVERFLOW
+ dest free
+ suspend_decode_buffer
+ throw_pending_error
+ forward_writes
+ forward_received
+ forward_events
+ IoBuffer.allocate(4096)
+ ...
+ SslFilter.messageReceived
+ SslHandler.receive
+ receive_start
+ resume_decode_buffer
+ mDecodedBuffer.expand -> 7168
+ source free
+ receive_loop
+ dest <- allocate(ZERO)
+ SslEngine.unwrap() -> BUFFER_OVERFLOW
+ dest free
+ suspend_decode_buffer
+ throw_pending_error
+ forward_writes
+ forward_received
+ forward_events
+ IoBuffer.allocate(8192)
+ ...
+ SslFilter.messageReceived
+ SslHandler.receive
+ receive_start
+ resume_decode_buffer
+ mDecodedBuffer.expand -> 15360
+ source free
+ receive_loop
+ dest <- allocate(ZERO)
+ SslEngine.unwrap() -> BUFFER_OVERFLOW
+ dest free
+ suspend_decode_buffer
+ throw_pending_error
+ forward_writes
+ forward_received
+ forward_events
+ IoBuffer.allocate(16384)
+ ...
+ SslFilter.messageReceived
+ SslHandler.receive
+ receive_start
+ resume_decode_buffer
+ mDecodedBuffer.expand -> 31744
+ source free
+ receive_loop
+ dest <- allocate(16400)
+ SslEngine.unwrap() -> OK
+ mReceiveQueue <- dest (16367 useful data, 33 padding removed)
+ receive_loop
+ dest <- allocate(ZERO)
+ SslEngine.unwrap() -> BUFFER_OVERFLOW
+ dest free
+ suspend_decode_buffer
+ throw_pending_error
+ forward_writes
+ forward_received
+ next.messageReceived -> We are propagating the first chunk of data
to the IoHandler
+ forward_events
+```
+
+As we can see, the mDecodedBuffer has been expended 4 times, so is the session
receiving buffer, and we have allocated 5 destination buffer (although four
have been allocated with a 0 size).
+
+To schematize, here are the allocated (or resized) buffers:
+
+```
+input mDecodedBuffer
++----+ +----+
+|1024| -> |1024|
++----+ +----+
+
++--------+ +----+--------+
+| 2048 | -> |1024| 2048 | 3072
++--------+ +----+--------+
+
++------------+ +----+--------+------------+
+| 4096 | -> |1024| 2048 | 4096 | 7168
++------------+ +----+--------+------------+
+
++----------------+ +----+--------+------------+----------------+
+| 8192 | -> |1024| 2048 | 4096 | 8192 | 15360
++----------------+ +----+--------+------------+----------------+
+
++--------------------+
+----+--------+------------+----------------+--------------------+
+| 16384 | -> |1024| 2048 | 4096 | 8192 |
16384 | 31744
++--------------------+
+----+--------+------------+----------------+--------------------+
+ ^ ^
^
+ | |
|
+ +----- First chunk of data, 16367 bytes ------+--
some more --+
+```
+
+
+As we can see it's a VERY costly process, where we allocate *many* buffers
that get discardd immediately. There is a lot of room for improvement!
+
+We can decide to use a bigger input buffer. By default, it's sized to 1Kb in
order to avoid using a lot of memory, and it's fine when we only receive small
chunk of data. For big data exchanges, it would be valuable to define a bigger
size.
+The key here is that there is an adaptative mechanism where we double the size
of the input buffer if the read data fulfilled it (uo to a limit), and divide
it by two if we read less than what the buffer could contain. This adaptative
mechanism is fine when we don't know the size of the receivd data, ad if it can
vary a lot. When the incoming data are stable, we will oscillate between two
sizes (doubling the buffer size, then divide it by two) which is a huge penalty
to pay.
+
+Regarding the mDecoderBuffer, it's the same thing: until we can successfully
decode it its size will increase. As a TLS record is always maxing to 16384
bytes, it would be a better idea to allocate a buffer this size and use it,
when we know we will be able to decode it (ie when the input bufffer is bigger
than the TLS data size, plus some margin, because of the padding bytes).
+
+All in all, when in TLS mode, the proper strategy would be to use a 16Kb +
padding input buffer, and same thing for the mDecodedBuffer, when we have
received enough data.
+
#### Receiving small chunks
In the case we receive a **TLS** record split in several packets, we won't be
able to uncrypt them until we have received a full **TLS** record.