Skip to content

Commit 7baf351

Browse files
authored
Require BufRead + Seek bound for decoding (#558)
1 parent 243205a commit 7baf351

14 files changed

+207
-125
lines changed

benches/decoder.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::fs;
1+
use std::{fs, io::Cursor};
22

33
use criterion::{
44
criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion, Throughput,
@@ -95,8 +95,8 @@ fn bench_read_row(g: &mut BenchmarkGroup<WallTime>, data: Vec<u8>, name: &str) {
9595
});
9696
}
9797

98-
fn create_reader(data: &[u8]) -> Reader<&[u8]> {
99-
let mut decoder = Decoder::new(data);
98+
fn create_reader(data: &[u8]) -> Reader<Cursor<&[u8]>> {
99+
let mut decoder = Decoder::new(Cursor::new(data));
100100

101101
// Cover default transformations used by the `image` crate when constructing
102102
// `image::codecs::png::PngDecoder`.

examples/change-png-info.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/// Tests "editing"/re-encoding of an image:
22
/// decoding, editing, re-encoding
33
use std::fs::File;
4-
use std::io::BufWriter;
4+
use std::io::{BufReader, BufWriter};
55
use std::path::Path;
66
pub type BoxResult<T> = Result<T, Box<dyn std::error::Error + Send + Sync>>;
77

@@ -11,7 +11,7 @@ fn main() -> BoxResult<()> {
1111
let path_in = Path::new(r"./tests/pngsuite/basi0g01.png");
1212
// The decoder is a build for reader and can be used to set various decoding options
1313
// via `Transformations`. The default output transformation is `Transformations::IDENTITY`.
14-
let decoder = png::Decoder::new(File::open(path_in)?);
14+
let decoder = png::Decoder::new(BufReader::new(File::open(path_in)?));
1515
let mut reader = decoder.read_info()?;
1616
// Allocate the output buffer.
1717
let png_info = reader.info();

examples/corpus-bench.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::{fs, path::PathBuf};
1+
use std::{fs, io::Cursor, path::PathBuf};
22

33
use clap::Parser;
44
use png::Decoder;
@@ -62,7 +62,7 @@ fn run_encode(
6262

6363
#[inline(never)]
6464
fn run_decode(image: &[u8], output: &mut [u8]) {
65-
let mut reader = Decoder::new(image).read_info().unwrap();
65+
let mut reader = Decoder::new(Cursor::new(image)).read_info().unwrap();
6666
reader.next_frame(output).unwrap();
6767
}
6868

@@ -107,7 +107,7 @@ fn main() {
107107

108108
// Parse
109109
let data = fs::read(entry.path()).unwrap();
110-
let mut decoder = Decoder::new(&*data);
110+
let mut decoder = Decoder::new(Cursor::new(&*data));
111111
if decoder.read_header_info().ok().map(|h| h.color_type)
112112
== Some(png::ColorType::Indexed)
113113
{

fuzz/fuzz_targets/buf_independent.rs

+70-27
Original file line numberDiff line numberDiff line change
@@ -26,41 +26,58 @@
2626
use libfuzzer_sys::fuzz_target;
2727

2828
use std::fmt::Debug;
29-
use std::io::Read;
29+
use std::io::{BufRead, BufReader, Cursor, Seek};
3030

3131
mod smal_buf {
32+
use std::io::{BufRead, Cursor, Read, Seek};
3233

33-
use std::io::Read;
34-
35-
/// A reader that reads at most `n` bytes.
36-
pub struct SmalBuf<R: Read> {
37-
inner: R,
38-
cap: usize,
34+
/// A reader that returns at most 1 byte in a single call to `read`.
35+
pub struct SmalBuf {
36+
inner: Cursor<Vec<u8>>,
3937
}
4038

41-
impl<R: Read> SmalBuf<R> {
42-
pub fn new(inner: R, cap: usize) -> Self {
43-
SmalBuf { inner, cap }
39+
impl SmalBuf {
40+
pub fn new(inner: Vec<u8>) -> Self {
41+
SmalBuf {
42+
inner: Cursor::new(inner),
43+
}
4444
}
4545
}
4646

47-
impl<R: Read> Read for SmalBuf<R> {
47+
impl Read for SmalBuf {
4848
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
49-
let len = buf.len().min(self.cap);
50-
self.inner.read(&mut buf[..len])
49+
if buf.is_empty() {
50+
return Ok(0);
51+
}
52+
self.inner.read(&mut buf[..1])
53+
}
54+
}
55+
impl BufRead for SmalBuf {
56+
fn fill_buf(&mut self) -> std::io::Result<&[u8]> {
57+
let buf = self.inner.fill_buf()?;
58+
Ok(&buf[..buf.len().min(1)])
59+
}
60+
61+
fn consume(&mut self, amt: usize) {
62+
self.inner.consume(amt);
63+
}
64+
}
65+
impl Seek for SmalBuf {
66+
fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
67+
self.inner.seek(pos)
5168
}
5269
}
5370
}
5471

5572
mod intermittent_eofs {
5673

5774
use std::cell::Cell;
58-
use std::io::Read;
75+
use std::io::{BufRead, Read, Seek};
5976
use std::rc::Rc;
6077

6178
/// A reader that returns `std::io::ErrorKind::UnexpectedEof` errors in every other read.
6279
/// EOFs can be temporarily disabled and re-enabled later using the associated `EofController`.
63-
pub struct IntermittentEofs<R: Read> {
80+
pub struct IntermittentEofs<R: BufRead + Seek> {
6481
inner: R,
6582

6683
/// Controls whether intermittent EOFs happen at all.
@@ -71,7 +88,7 @@ mod intermittent_eofs {
7188
eof_soon: bool,
7289
}
7390

74-
impl<R: Read> IntermittentEofs<R> {
91+
impl<R: BufRead + Seek> IntermittentEofs<R> {
7592
pub fn new(inner: R) -> Self {
7693
Self {
7794
inner,
@@ -85,7 +102,7 @@ mod intermittent_eofs {
85102
}
86103
}
87104

88-
impl<R: Read> Read for IntermittentEofs<R> {
105+
impl<R: BufRead + Seek> Read for IntermittentEofs<R> {
89106
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
90107
if self.controller.are_intermittent_eofs_enabled() && self.eof_soon {
91108
self.eof_soon = false;
@@ -101,6 +118,20 @@ mod intermittent_eofs {
101118
inner_result
102119
}
103120
}
121+
impl<R: BufRead + Seek> BufRead for IntermittentEofs<R> {
122+
fn fill_buf(&mut self) -> std::io::Result<&[u8]> {
123+
self.inner.fill_buf()
124+
}
125+
126+
fn consume(&mut self, amt: usize) {
127+
self.inner.consume(amt);
128+
}
129+
}
130+
impl<R: BufRead + Seek> Seek for IntermittentEofs<R> {
131+
fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
132+
self.inner.seek(pos)
133+
}
134+
}
104135

105136
pub struct EofController {
106137
are_intermittent_eofs_enabled: Cell<bool>,
@@ -141,18 +172,24 @@ fuzz_target!(|data: &[u8]| {
141172
let _ = test_data(data);
142173
});
143174

175+
trait BufReadSeek: BufRead + Seek {}
176+
impl<T> BufReadSeek for T where T: BufRead + Seek {}
177+
144178
#[inline(always)]
145179
fn test_data<'a>(data: &'a [u8]) -> Result<(), ()> {
146-
let baseline_reader = Box::new(data);
147-
let byte_by_byte_reader = Box::new(smal_buf::SmalBuf::new(data, 1));
180+
let baseline_reader = Box::new(Cursor::new(data));
181+
let byte_by_byte_reader = Box::new(smal_buf::SmalBuf::new(data.to_owned()));
148182
let intermittent_eofs_reader = Box::new(intermittent_eofs::IntermittentEofs::new(
149-
smal_buf::SmalBuf::new(data, 1),
183+
smal_buf::SmalBuf::new(data.to_owned()),
150184
));
151185
let intermittent_eofs_controller = intermittent_eofs_reader.controller();
152-
let data_readers: Vec<Box<dyn Read + 'a>> = vec![
153-
baseline_reader,
154-
byte_by_byte_reader,
155-
intermittent_eofs_reader,
186+
187+
// `Decoder` used to internally wrap the provided reader with a `BufReader`. Now that it has
188+
// been removed, fuzzing would be far too slow if we didn't use a BufReader here.
189+
let data_readers: Vec<BufReader<Box<dyn BufReadSeek>>> = vec![
190+
BufReader::new(baseline_reader),
191+
BufReader::new(byte_by_byte_reader),
192+
BufReader::new(intermittent_eofs_reader),
156193
];
157194

158195
let decoders = data_readers
@@ -196,8 +233,14 @@ fn test_data<'a>(data: &'a [u8]) -> Result<(), ()> {
196233
.zip(buffers.iter_mut())
197234
.enumerate()
198235
.map(|(i, (png_reader, buffer))| {
199-
let eof_controller = if i == 2 { Some(&intermittent_eofs_controller) } else { None };
200-
retry_after_eofs(eof_controller, || png_reader.next_frame(buffer.as_mut_slice()))
236+
let eof_controller = if i == 2 {
237+
Some(&intermittent_eofs_controller)
238+
} else {
239+
None
240+
};
241+
retry_after_eofs(eof_controller, || {
242+
png_reader.next_frame(buffer.as_mut_slice())
243+
})
201244
})
202245
.assert_all_results_are_consistent()
203246
.collect::<Result<Vec<_>, _>>()
@@ -222,7 +265,7 @@ fn retry_after_eofs<T>(
222265
}
223266
}
224267
}
225-
},
268+
}
226269
_ => (),
227270
}
228271
break result;

fuzz/fuzz_targets/decode.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
#![no_main]
22

33
use libfuzzer_sys::fuzz_target;
4+
use std::io::Cursor;
45

56
#[inline(always)]
67
fn png_decode(data: &[u8]) -> Result<(Option<png::OutputInfo>, Vec<u8>), ()> {
78
let limits = png::Limits { bytes: 1 << 16 };
8-
let decoder = png::Decoder::new_with_limits(data, limits);
9+
let decoder = png::Decoder::new_with_limits(Cursor::new(data), limits);
910
let mut reader = decoder.read_info().map_err(|_| ())?;
1011

1112
if reader.info().raw_bytes() > 5_000_000 {

fuzz/fuzz_targets/roundtrip.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#![no_main]
22

33
use libfuzzer_sys::fuzz_target;
4+
use std::io::Cursor;
45
use png::{Filter, ColorType, BitDepth};
56

67
fuzz_target!(|data: (u8, u8, u8, u8, u8, Vec<u8>, Vec<u8>)| {
@@ -63,7 +64,7 @@ fn encode_png<'a>(width: u8, filter: u8, compression: u8, color_type: u8, raw_bi
6364
}
6465

6566
fn decode_png(data: &[u8]) -> (png::OutputInfo, Vec<u8>) {
66-
let decoder = png::Decoder::new(data);
67+
let decoder = png::Decoder::new(Cursor::new(data));
6768
let mut reader = decoder.read_info().unwrap();
6869

6970
let mut img_data = vec![0u8; reader.info().raw_bytes()];

src/decoder/mod.rs

+12-9
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use self::stream::{DecodeOptions, DecodingError, FormatErrorInner, CHUNK_BUFFER_
1010
use self::transform::{create_transform_fn, TransformFn};
1111
use self::unfiltering_buffer::UnfilteringBuffer;
1212

13-
use std::io::Read;
13+
use std::io::{BufRead, Seek};
1414
use std::mem;
1515

1616
use crate::adam7::{self, Adam7Info};
@@ -86,7 +86,7 @@ impl Default for Limits {
8686
}
8787

8888
/// PNG Decoder
89-
pub struct Decoder<R: Read> {
89+
pub struct Decoder<R: BufRead + Seek> {
9090
read_decoder: ReadDecoder<R>,
9191
/// Output transformations
9292
transform: Transformations,
@@ -121,7 +121,7 @@ impl<'data> Row<'data> {
121121
}
122122
}
123123

124-
impl<R: Read> Decoder<R> {
124+
impl<R: BufRead + Seek> Decoder<R> {
125125
/// Create a new decoder configuration with default limits.
126126
pub fn new(r: R) -> Decoder<R> {
127127
Decoder::new_with_limits(r, Limits::default())
@@ -159,17 +159,18 @@ impl<R: Read> Decoder<R> {
159159
///
160160
/// ```
161161
/// use std::fs::File;
162+
/// use std::io::BufReader;
162163
/// use png::{Decoder, Limits};
163164
/// // This image is 32×32, 1bit per pixel. The reader buffers one row which requires 4 bytes.
164165
/// let mut limits = Limits::default();
165166
/// limits.bytes = 3;
166-
/// let mut decoder = Decoder::new_with_limits(File::open("tests/pngsuite/basi0g01.png").unwrap(), limits);
167+
/// let mut decoder = Decoder::new_with_limits(BufReader::new(File::open("tests/pngsuite/basi0g01.png").unwrap()), limits);
167168
/// assert!(decoder.read_info().is_err());
168169
///
169170
/// // This image is 32x32 pixels, so the decoder will allocate less than 10Kib
170171
/// let mut limits = Limits::default();
171172
/// limits.bytes = 10*1024;
172-
/// let mut decoder = Decoder::new_with_limits(File::open("tests/pngsuite/basi0g01.png").unwrap(), limits);
173+
/// let mut decoder = Decoder::new_with_limits(BufReader::new(File::open("tests/pngsuite/basi0g01.png").unwrap()), limits);
173174
/// assert!(decoder.read_info().is_ok());
174175
/// ```
175176
pub fn set_limits(&mut self, limits: Limits) {
@@ -248,8 +249,9 @@ impl<R: Read> Decoder<R> {
248249
/// eg.
249250
/// ```
250251
/// use std::fs::File;
252+
/// use std::io::BufReader;
251253
/// use png::Decoder;
252-
/// let mut decoder = Decoder::new(File::open("tests/pngsuite/basi0g01.png").unwrap());
254+
/// let mut decoder = Decoder::new(BufReader::new(File::open("tests/pngsuite/basi0g01.png").unwrap()));
253255
/// decoder.set_ignore_text_chunk(true);
254256
/// assert!(decoder.read_info().is_ok());
255257
/// ```
@@ -262,8 +264,9 @@ impl<R: Read> Decoder<R> {
262264
/// eg.
263265
/// ```
264266
/// use std::fs::File;
267+
/// use std::io::BufReader;
265268
/// use png::Decoder;
266-
/// let mut decoder = Decoder::new(File::open("tests/iccp/broken_iccp.png").unwrap());
269+
/// let mut decoder = Decoder::new(BufReader::new(File::open("tests/iccp/broken_iccp.png").unwrap()));
267270
/// decoder.set_ignore_iccp_chunk(true);
268271
/// assert!(decoder.read_info().is_ok());
269272
/// ```
@@ -281,7 +284,7 @@ impl<R: Read> Decoder<R> {
281284
/// PNG reader (mostly high-level interface)
282285
///
283286
/// Provides a high level that iterates over lines or whole images.
284-
pub struct Reader<R: Read> {
287+
pub struct Reader<R: BufRead + Seek> {
285288
decoder: ReadDecoder<R>,
286289
bpp: BytesPerPixel,
287290
subframe: SubframeInfo,
@@ -317,7 +320,7 @@ struct SubframeInfo {
317320
consumed_and_flushed: bool,
318321
}
319322

320-
impl<R: Read> Reader<R> {
323+
impl<R: BufRead + Seek> Reader<R> {
321324
/// Advances to the start of the next animation frame and
322325
/// returns a reference to the `FrameControl` info that describes it.
323326
/// Skips and discards the image data of the previous frame if necessary.

src/decoder/read_decoder.rs

+6-11
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
use super::stream::{
2-
DecodeOptions, Decoded, DecodingError, FormatErrorInner, StreamingDecoder, CHUNK_BUFFER_SIZE,
3-
};
1+
use super::stream::{DecodeOptions, Decoded, DecodingError, FormatErrorInner, StreamingDecoder};
42
use super::Limits;
53

6-
use std::io::{BufRead, BufReader, ErrorKind, Read};
4+
use std::io::{BufRead, ErrorKind, Read, Seek};
75

86
use crate::chunk;
97
use crate::common::Info;
@@ -18,14 +16,14 @@ use crate::common::Info;
1816
/// * `finish_decoding_image_data()` - discarding remaining data from `IDAT` / `fdAT` sequence
1917
/// * `read_until_end_of_input()` - reading until `IEND` chunk
2018
pub(crate) struct ReadDecoder<R: Read> {
21-
reader: BufReader<R>,
19+
reader: R,
2220
decoder: StreamingDecoder,
2321
}
2422

25-
impl<R: Read> ReadDecoder<R> {
23+
impl<R: BufRead + Seek> ReadDecoder<R> {
2624
pub fn new(r: R) -> Self {
2725
Self {
28-
reader: BufReader::with_capacity(CHUNK_BUFFER_SIZE, r),
26+
reader: r,
2927
decoder: StreamingDecoder::new(),
3028
}
3129
}
@@ -34,10 +32,7 @@ impl<R: Read> ReadDecoder<R> {
3432
let mut decoder = StreamingDecoder::new_with_options(options);
3533
decoder.limits = Limits::default();
3634

37-
Self {
38-
reader: BufReader::with_capacity(CHUNK_BUFFER_SIZE, r),
39-
decoder,
40-
}
35+
Self { reader: r, decoder }
4136
}
4237

4338
pub fn set_limits(&mut self, limits: Limits) {

0 commit comments

Comments
 (0)