Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Idempotent handling of missing
fdAT
chunk in a truncated PNG file.
Fuzzing found a broken PNG file where the sequence of chunks looks more or less like this: `IHDR`, `acTL`, `IDAT`, `fcTL`, `fctL`, EOF. This commit preserves this repro file under `fuzz/corpus/buf_independent/regressions`. Before this commit such fuzzing input would lead to problems when resuming a call to `next_frame` after an interittent EOF. There were a few loosely related problems: 1. `fn read_until_image_data` would unnecessarily set `self.subframe` in the `Decoded::FrameControl` branch. This was unnecessary because `self.subframe` is also set after breaking out of the loop. 2. `fn read_until_image_data` is called to position the input stream at the beginning of the next image frame - it breaks out of its loop and returns after encountering the start of the next `IDAT` or `fdAT` chunk. This means that there is a discrepancy: - Whether we are in the middle of an `IDAT`/`fdAT` data stream can be determined by checking `self.subframe.consumed_and_flushed`. - But before this commit `fn next_frame` would decide whether to call `read_until_image_data` based on `self.next_frame`, rather than based on `self.subframe.consumed_and_flushed`. 3. After calling `fn finish_decoding` from `fn next_frame` the `self.subframe.consumed_and_flushed` wouldn't be reset. Problem2 above meant that after an intermittent `UnexpectedEof` in `read_until_image_data` retrying a call to `next_frame` would *not* retry `read_until_image_data` (because `self.next_frame` would be updated by the previous `read_until_image_data`, and now `fn next_frame` would incorrectly treat this as not needing to call `read_until_image_data`, even though we still haven't reached an `IDAT` nor `fdAT` chunk). This was fixed by changing how `fn next_frame` decides whether to call `read_until_image_data` (as a side-effect this allowed deleting `subframe_idx`). Fixing problem2 was insufficient, because problem1 meant that `consumed_and_flushed` was prematurely set (before actually encountering an `IDAT` or `fdAT` frame). And we also needed to fix problem3 to ensure that the next call to `next_frame` knows that it is time to advance to the next `IDAT` / `fdAT` chunks.
- Loading branch information