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(())
+    }
 }

Reply via email to