sepuri sai krishna created KAFKA-20769:
------------------------------------------

             Summary: ListDeserializer can silently deserialize a corrupted 
entry when the input is truncated mid-entry
                 Key: KAFKA-20769
                 URL: https://issues.apache.org/jira/browse/KAFKA-20769
             Project: Kafka
          Issue Type: Bug
          Components: clients
    Affects Versions: 4.3.1
            Reporter: sepuri sai krishna


ListDeserializer.deserialize relies on DataInputStream.read(byte[]) to read 
each list entry's payload and only treats a -1 return value as a truncated 
stream. However, InputStream.read(byte[]) is only required to read at least one 
byte and may return a partial count without reaching EOF. When the serialized 
data is truncated in the middle of an entry, the entry buffer is left 
zero-padded and is passed to the inner deserializer with no error, silently 
producing a corrupted value instead of failing.

Affected code in 
clients/src/main/java/org/apache/kafka/common/serialization/ListDeserializer.java
 (present since 2.7.0 / KAFKA-8326, still on trunk):

      byte[] payload = new byte[entrySize];
      if (dis.read(payload) == -1) {
          log.error("Ran out of bytes in serialized list");
          log.trace("Deserialized list so far: {}", deserializedList);
          throw new SerializationException("End of the stream was reached 
prematurely");
      }
      deserializedList.add(inner.deserialize(topic, headers, payload));

Why the existing bounds checks don't prevent it: readEntrySize rejects 
entrySize > data.length, but data.length is the size of the whole buffer, not 
the bytes remaining after the strategy flag, null-index list, list size, and 
prior entries have been consumed. A crafted or corrupted payload can therefore 
declare an entrySize that passes the bounds check yet
exceeds the actual remaining bytes. 
In that case:
  - 0 bytes remaining and entrySize > 0: read returns -1, so 
SerializationException is thrown (correct).
  - 1 to entrySize-1 bytes remaining (truncated mid-entry): read copies a 
partial payload and returns a positive count (not -1), the EOF check passes, 
the trailing bytes stay zero-filled, and the corrupted/zero-padded buffer is 
handed to inner.deserialize(...) with no error.

Impact: on corrupted or truncated ListSerde data, the deserializer can return a 
silently mangled element instead of raising SerializationException. This 
defeats the method's own truncation detection (the "End of the stream was 
reached prematurely" path).

Fix: use DataInputStream.readFully(byte[]), which throws EOFException when it 
cannot fill the buffer; map that to the existing SerializationException. 
Behavior is unchanged for
well-formed data and for fully-empty streams.

How it was found: identified by code inspection of ListDeserializer.deserialize 
on trunk (4.4.0-SNAPSHOT) and confirmed with a unit test. The defective line is 
unchanged since
2.7.0
(KAFKA-8326), so all releases on the current line are affected; it was not 
reproduced against a packaged release binary.

 
Reproduction (unit test): encode VARIABLE_SIZE, list length 1, entrySize == 10, 
but supply only 5 payload bytes (total buffer 14 bytes, so the entrySize > 
data.length check passes).
Before the fix no exception is thrown and a zero-padded entry is produced; 
after the fix a SerializationException ("End of the stream was reached 
prematurely") is raised.



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

Reply via email to