SEPURI-SAI-KRISHNA opened a new pull request, #23206:
URL: https://github.com/apache/kafka/pull/23206

   `RemoteLogInputStream.nextBatch()` reads a 4-byte batch-size field off the 
`InputStream` returned by the pluggable `RemoteStorageManager` and uses it to 
size an allocation, checking only a lower bound:
   
   ```java
   int size = logHeaderBuffer.getInt(SIZE_OFFSET);
   
   // V0 has the smallest overhead, stricter checking is done later
   if (size < LegacyRecord.RECORD_OVERHEAD_V0)
       throw new CorruptRecordException(...);
   
   int bufferSize = LOG_OVERHEAD + size;
   ByteBuffer buffer = ByteBuffer.allocate(bufferSize);   // no upper bound on 
size
   ```
   
   Nothing verifies that `size` is not absurdly large before allocating. Since 
`size` is a signed int read directly from the remote segment's bytes, a 
corrupted or bit-rotted segment — or a buggy `RemoteStorageManager` 
implementation — can drive a single allocation of up to ~2GB, per batch read.
   
   The sibling class `ByteBufferLogInputStream` parses the identical 
length-prefixed header format and already guards against exactly this:
   
   ```java
   if (recordSize > maxMessageSize)
       throw new CorruptRecordException(String.format(
           "Record size %d exceeds the largest allowable message size (%d).",
           recordSize, maxMessageSize));
   ```
   
   `RemoteLogInputStream` has no equivalent check, and is the more exposed of 
the two: `ByteBufferLogInputStream` only slices an already-in-memory, 
already-bounded `ByteBuffer`, whereas `RemoteLogInputStream` calls 
`ByteBuffer.allocate()` on the untrusted value before confirming the stream 
even holds that many bytes.
   
   Both reachable call sites are hot server-side paths, not test-only code:
   - `RemoteLogManager.read()` — consumer fetches that fall through to tiered 
storage
   - `RemoteLogManager.lookupTimestamp()` — offset-by-timestamp lookups against 
tiered segments
   
   ### Changes
   
   - `RemoteLogInputStream` now takes a `maxMessageSize` in its constructor and 
throws `CorruptRecordException` when the declared batch size exceeds it, 
mirroring the existing `ByteBufferLogInputStream` check so the two paths 
validate consistently.
   - `RemoteLogManager` threads `UnifiedLog.config().maxMessageSize()` into 
both call sites. That value is already available at each one, so no new 
configuration or plumbing is introduced.
   
   The bound is the same one the local-log read path already enforces, so a 
segment that could legitimately be written locally still reads back from remote 
storage unchanged; only sizes that could never correspond to a valid batch are 
rejected.
   
   ### Testing
   
   - Added 
`RemoteLogInputStreamTest#testNextBatchThrowsCorruptRecordExceptionWhenSizeExceedsMaxMessageSize`,
 which writes a valid record, patches the on-disk size field to exceed a 
deliberately small `maxMessageSize`, and asserts `CorruptRecordException` is 
raised instead of the oversized allocation being attempted. This mirrors 
`ByteBufferLogInputStreamTest#iteratorRaisesOnTooLargeRecords`, keeping the two 
classes' coverage symmetric.
   - Existing `RemoteLogInputStreamTest` cases were updated for the new 
constructor and pass unchanged (61 tests, 0 failures), confirming that valid 
segments across all magic values and compression types still read back 
correctly.
   - `RemoteLogManagerTest` passes (95 tests, 0 failures), covering both 
updated call sites including the log-compaction retry path in `read()`.
   
   Co-Authored-By: Claude Sonnet 5
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to