Skip to content

Commit b0c6ad9

Browse files
committed
feat: check VortexReadAt::read_at results in the I/O driver
This ensures misbehaving VortexReadAt implementations cannot trigger later bad behavior by the I/O driver. Instead, the driver raises an error as close as possible to the badly behaving VortexReadAt implementation. Signed-off-by: Daniel King <dan@spiraldb.com>
1 parent f843857 commit b0c6ad9

1 file changed

Lines changed: 57 additions & 0 deletions

File tree

vortex-file/src/read/request.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ use vortex_buffer::Alignment;
1212
use vortex_error::VortexError;
1313
use vortex_error::VortexExpect;
1414
use vortex_error::VortexResult;
15+
use vortex_error::vortex_bail;
16+
use vortex_error::vortex_err;
1517

1618
/// An I/O request, either a single read or a coalesced set of reads.
1719
pub(crate) struct IoRequest(IoRequestInner);
@@ -107,6 +109,17 @@ impl Debug for ReadRequest {
107109

108110
impl ReadRequest {
109111
pub(crate) fn resolve(self, result: VortexResult<BufferHandle>) {
112+
let result = result.and_then(|buffer| {
113+
if self.length != buffer.len() {
114+
vortex_bail!(
115+
"ReadRequest: expected buffer of length {} but received {}.",
116+
self.length,
117+
buffer.len()
118+
)
119+
}
120+
Ok(buffer)
121+
});
122+
110123
if let Err(e) = self.callback.send(result) {
111124
tracing::debug!("ReadRequest {} dropped before resolving: {e}", self.id);
112125
}
@@ -133,6 +146,50 @@ impl Debug for CoalescedRequest {
133146

134147
impl CoalescedRequest {
135148
pub fn resolve(self, result: VortexResult<BufferHandle>) {
149+
let result = result.and_then(|buffer| {
150+
let expected_length = self.range.end.saturating_sub(self.range.start);
151+
let buffer_len = buffer.len() as u64;
152+
if expected_length != buffer_len {
153+
vortex_bail!(
154+
"CoalescedRequest: expected buffer of length {} but received {}.",
155+
expected_length,
156+
buffer_len
157+
)
158+
}
159+
160+
for req in self.requests.iter() {
161+
let request_offset = req.offset.checked_sub(self.range.start).ok_or_else(|| {
162+
vortex_err!(
163+
"CoalescedRequest: sub-request for length {} at file offset {} preceeds coalesced range: {}..{}.",
164+
req.length,
165+
req.offset,
166+
self.range.start,
167+
self.range.end,
168+
)
169+
})?;
170+
if request_offset > buffer_len {
171+
vortex_bail!(
172+
"CoalescedRequest: sub-request for length {} at buffer offset {} (file offset {}) is unsatisfiable by buffer of length {}.",
173+
req.length,
174+
request_offset,
175+
req.offset,
176+
buffer_len
177+
)
178+
}
179+
let request_end = request_offset.saturating_add(req.length as u64);
180+
if request_end > buffer_len {
181+
vortex_bail!(
182+
"CoalescedRequest: sub-request for length {} at buffer offset {} (file offset {}) is unsatisfiable by buffer of length {}.",
183+
req.length,
184+
request_offset,
185+
req.offset,
186+
buffer_len
187+
)
188+
}
189+
}
190+
Ok(buffer)
191+
});
192+
136193
match result {
137194
Ok(buffer) => {
138195
let base = match buffer.ensure_aligned(Alignment::none()) {

0 commit comments

Comments
 (0)