This is an automated email from the ASF dual-hosted git repository.
xuanwo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/opendal.git
The following commit(s) were added to refs/heads/main by this push:
new fbf692456 fix(reader): fix potential out-of-bound seek (#7282)
fbf692456 is described below
commit fbf6924563bb34380674a31a3c448898e0a7b630
Author: dentiny <[email protected]>
AuthorDate: Mon Mar 23 23:38:34 2026 -0700
fix(reader): fix potential out-of-bound seek (#7282)
---
core/core/src/types/read/futures_async_reader.rs | 43 +++++++++++++++++++++++-
1 file changed, 42 insertions(+), 1 deletion(-)
diff --git a/core/core/src/types/read/futures_async_reader.rs
b/core/core/src/types/read/futures_async_reader.rs
index de5aa3b20..a237d1bf5 100644
--- a/core/core/src/types/read/futures_async_reader.rs
+++ b/core/core/src/types/read/futures_async_reader.rs
@@ -151,6 +151,15 @@ impl AsyncSeek for FuturesAsyncReader {
}
let new_pos = new_pos as u64;
+ let length = self.end - self.start;
+
+ // Check if new_pos is past the end of the range.
+ if new_pos > length {
+ return Poll::Ready(Err(io::Error::new(
+ io::ErrorKind::InvalidInput,
+ "invalid seek to a position beyond the end of the range",
+ )));
+ }
if (self.pos..self.pos + self.buf.remaining() as
u64).contains(&new_pos) {
let cnt = new_pos - self.pos;
@@ -160,7 +169,7 @@ impl AsyncSeek for FuturesAsyncReader {
self.stream = BufferStream::new(
self.ctx.clone(),
new_pos + self.start,
- Some(self.end - self.start - new_pos),
+ Some(length - new_pos),
);
}
@@ -286,4 +295,36 @@ mod tests {
Ok(())
}
+
+ #[tokio::test]
+ async fn test_seek_past_end_returns_error() -> Result<()> {
+ let op = Operator::via_iter(services::MEMORY_SCHEME, [])?;
+ op.write(
+ "test",
+ Buffer::from(vec![Bytes::from("Hello"), Bytes::from("World")]),
+ )
+ .await?;
+
+ let acc = op.into_inner();
+ let ctx = Arc::new(ReadContext::new(
+ acc,
+ "test".to_string(),
+ OpRead::new(),
+ OpReader::new(),
+ ));
+
+ // Range 4..8, in total 4 bytes of logical data.
+ let mut fr = FuturesAsyncReader::new(ctx, 4..8);
+
+ // Seek to position 5, which is past the 4-byte logical range.
+ let res = fr.seek(SeekFrom::Start(5)).await;
+ assert!(res.is_err());
+ assert_eq!(res.unwrap_err().kind(), io::ErrorKind::InvalidInput);
+
+ // Seeking exactly to the end should still work.
+ let pos = fr.seek(SeekFrom::Start(4)).await.unwrap();
+ assert_eq!(pos, 4);
+
+ Ok(())
+ }
}