[ 
https://issues.apache.org/jira/browse/KAFKA-21280?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Jun Rao resolved KAFKA-21280.
-----------------------------
    Fix Version/s: 4.5.0
       Resolution: Fixed

merged the PR to trunk

> SslTransportLayer.transferFrom sends stale buffer contents after a failed 
> file read
> -----------------------------------------------------------------------------------
>
>                 Key: KAFKA-21280
>                 URL: https://issues.apache.org/jira/browse/KAFKA-21280
>             Project: Kafka
>          Issue Type: Bug
>          Components: network
>    Affects Versions: 2.5.0, 4.3.1
>            Reporter: Mykyta Bozhenko
>            Assignee: Mykyta Bozhenko
>            Priority: Major
>             Fix For: 4.5.0
>
>
> When TLS is enabled, {{SslTransportLayer.transferFrom}} copies records from 
> the segment into {{fileChannelBuffer}} and writes that buffer to the TLS 
> stream. The buffer is cleared before each read:
> {code:java}
> if (!fileChannelBuffer.hasRemaining()) {
>     fileChannelBuffer.clear();
>     ...
>     int bytesRead = fileChannel.read(fileChannelBuffer, pos);
>     if (bytesRead <= 0)
>         break;
>     fileChannelBuffer.flip();
> }
> int networkBytesWritten = write(fileChannelBuffer);
> {code}
> If the read throws, or returns no bytes, the buffer is left cleared instead 
> of empty: {{hasRemaining()}} is true and the buffer still holds the previous 
> chunk. When something was already written in the same call, the 
> {{IOException}} is swallowed ({{if (totalBytesWritten > 0) return 
> totalBytesWritten}}). The next call then skips the read, writes the stale 
> buffer to the socket as the next chunk of the file, advances the position, 
> and fails on the following read the same way. A read error on the segment 
> never surfaces; the rest of the requested region is sent as copies of an 
> earlier chunk.
> Reproduced on trunk (03e6606430): one controller and three brokers, JDK 17, 
> Linux 6.8, the leader's log directory on {{dm-dust}}. The tail of a 10 MiB 
> segment is made unreadable while its head stays in the page cache, then a 
> consumer reads the partition from the beginning over the SSL listener. On the 
> broker's network thread:
> {code}
> pread64(176, ..., 32768, 2850816) = 16384
> pread64(176, ..., 32768, 2867200) = -1 EIO (Input/output error)
> {code}
> followed by 168 more {{-1 EIO}} at offsets advancing by 32768, covering the 
> whole 5.5 MiB region. The broker logs nothing about it: no exception, no 
> disconnect, the log directory stays online and the leader is unchanged. The 
> consumer gets 5456 of 20000 records, then:
> {code}
> Record batch for partition t1-0 at offset 5456 is invalid, cause: Record is 
> corrupt (stored crc = 1243940787, computed crc = 114489509)
> {code}
> The same scenario over the PLAINTEXT listener surfaces the error: 
> {{sendfile}} returns {{EIO}} and the broker closes the connection with 
> {{java.io.IOException: Input/output error}}.
> Impact:
> * Clients and followers on a TLS listener receive bytes that are not in the 
> file. The CRC check catches it, so a readable record on healthy replicas is 
> reported as corrupt, and the partition cannot be consumed past it from this 
> leader.
> * Disk read errors on TLS listeners never reach the code that handles I/O 
> errors, so KAFKA-16304 cannot work for TLS until this is fixed.
> * The stale bytes come from an earlier transfer on the same connection, so 
> nothing from another connection is exposed.
> The {{bytesRead <= 0}} branch leaves the buffer in the same state; 
> {{FileRecords.writeTo}}'s size check usually rejects a truncated file before 
> that is reached.
> Proposed fix: leave the buffer empty when a read does not succeed (for 
> example set its limit to 0 when the read throws or returns no bytes), so the 
> next call reads again and the original {{IOException}} propagates.
> Disclosure per the ASF generative tooling guidance: the reproduction and the 
> analysis were produced with AI assistance; I have reviewed and am responsible 
> for all of it.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to