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

Andrey Yarovoy reassigned HDDS-17094:
-------------------------------------

    Assignee: Andrey Yarovoy

> ECBlockInputStream serial read can return wrong data on a retry after a 
> failed read
> -----------------------------------------------------------------------------------
>
>                 Key: HDDS-17094
>                 URL: https://issues.apache.org/jira/browse/HDDS-17094
>             Project: Apache Ozone
>          Issue Type: Bug
>          Components: EC Client
>            Reporter: Andrey Yarovoy
>            Assignee: Andrey Yarovoy
>            Priority: Minor
>
> h3. Problem
> `ECBlockInputStream` keeps its own `position` for the block group and one 
> inner block stream per data index. In the common case without seeks it trusts 
> that each inner stream is already at the right offset: 
> `seekStreamIfNecessary` only checks and re-seeks inner streams after `seeked` 
> has been set by `seek()` or `closeStreams()`.
> `readWithStrategy` advances `position` only after a cell has been read fully. 
> If the inner stream fails partway through a cell, it has already moved 
> forward by the bytes it returned before failing, but `position` has not. If 
> `seeked` is still false, the next read on the same `ECBlockInputStream` reads 
> that index from the stream's current offset, not the offset `position` 
> implies, and silently returns the wrong data.
> Reproduced in a unit test with rs-3-2, 100-byte cells, serial read:
> # read 101 bytes: the index 2 stream is at offset 1, `position` is 101
> # index 2 fails after 49 more bytes: `BadDataLocationException`, `position` 
> stays 101, the index 2 stream is at 50
> # clear the error and read 199 bytes without a seek: the first byte read does 
> not match the block data at offset 101
> h3. Impact
> The main read path is not affected:
> * every `ECBlockInputStream` is created by `ECBlockInputStreamFactoryImpl` 
> behind `ECBlockInputStreamProxy`
> * `readWithStrategy` wraps each `IOException` in a 
> `BadDataLocationException`, and on that exception the proxy closes the reader 
> and fails over to reconstruction, so the stream with the stale offset is 
> never read again
> The exposure is limited to an unchecked exception thrown by an inner block 
> stream. `readWithStrategy` does not catch it, and the proxy rethrows it but 
> keeps the same reader. A caller that catches it and reads again from the same 
> stream would get wrong data, with no error. It is not known whether any inner 
> stream throws unchecked exceptions on a read in practice. This is a latent 
> correctness hazard rather than an observed failure.
> h3. Proposed fix
> When `readWithStrategy` exits with any exception, set `seeked = true`, so 
> that the next read checks and re-seeks the inner streams, as after a `seek()`:
> {code:java}
> boolean completed = false;
> try {
> ... // existing loop
> completed = true;
> } finally {
> if (!completed) {
> seeked = true;
> }
> }
> {code}
> h3. Testing
> Add a `TestECBlockInputStream` case: a cell fails partway 
> (`TestBlockInputStream#setShouldError(true, position, error)`), the error is 
> cleared, and a read on the same stream without a seek must return the correct 
> bytes. Cover both an `IOException` and an unchecked exception from the inner 
> stream.



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

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to