Skip to content

Commit e720322

Browse files
fix(core): validate start + size for buffer ops in more places
1 parent 3dfd2fe commit e720322

3 files changed

Lines changed: 110 additions & 13 deletions

File tree

wgpu-core/src/command/transfer.rs

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,14 @@ pub enum TransferError {
4747
MissingBufferUsage(#[from] MissingBufferUsageError),
4848
#[error(transparent)]
4949
MissingTextureUsage(#[from] MissingTextureUsageError),
50+
#[error(
51+
"Offset {start_offset} is outside bounds of the {side:?} buffer of size {buffer_size}"
52+
)]
53+
BufferStartOffsetOverrun {
54+
start_offset: BufferAddress,
55+
buffer_size: BufferAddress,
56+
side: CopySide,
57+
},
5058
#[error(
5159
"Copy at offset {start_offset} for {size} bytes would end up overrunning the bounds of the {side:?} buffer of size {buffer_size}"
5260
)]
@@ -185,6 +193,7 @@ impl WebGpuError for TransferError {
185193

186194
Self::BufferOverrun { .. }
187195
| Self::TextureOverrun { .. }
196+
| Self::BufferStartOffsetOverrun { .. }
188197
| Self::UnsupportedPartialTransfer { .. }
189198
| Self::InvalidCopyWithinSameTexture { .. }
190199
| Self::InvalidTextureAspect { .. }
@@ -983,10 +992,19 @@ pub(super) fn copy_buffer_to_buffer(
983992
.map_err(TransferError::MissingBufferUsage)?;
984993
let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_buffer, state.snatch_guard));
985994

986-
let (size, source_end_offset) = match size {
987-
Some(size) => (size, source_offset + size),
988-
None => (src_buffer.size - source_offset, src_buffer.size),
989-
};
995+
// TODO: This check isn't part of the spec., but it looks like it should be.
996+
if source_offset >= src_buffer.size {
997+
return Err(TransferError::BufferStartOffsetOverrun {
998+
start_offset: destination_offset,
999+
buffer_size: dst_buffer.size,
1000+
side: CopySide::Destination,
1001+
}
1002+
.into());
1003+
}
1004+
let size = size.unwrap_or_else(|| {
1005+
// NOTE: Should never underflow because of our earlier check.
1006+
src_buffer.size - source_offset
1007+
});
9901008

9911009
if size % wgt::COPY_BUFFER_ALIGNMENT != 0 {
9921010
return Err(TransferError::UnalignedCopySize(size).into());
@@ -1019,8 +1037,7 @@ pub(super) fn copy_buffer_to_buffer(
10191037
}
10201038
}
10211039

1022-
let destination_end_offset = destination_offset + size;
1023-
if source_end_offset > src_buffer.size {
1040+
if size > src_buffer.size - source_offset {
10241041
return Err(TransferError::BufferOverrun {
10251042
start_offset: source_offset,
10261043
size,
@@ -1029,7 +1046,21 @@ pub(super) fn copy_buffer_to_buffer(
10291046
}
10301047
.into());
10311048
}
1032-
if destination_end_offset > dst_buffer.size {
1049+
// NOTE: Should never overflow because of our earlier check.
1050+
let source_end_offset = source_offset + size;
1051+
1052+
// TODO: Checks for source buffer overrun don't quite match spec., but it seems important to do
1053+
// things in this order.
1054+
if destination_offset >= dst_buffer.size {
1055+
return Err(TransferError::BufferStartOffsetOverrun {
1056+
start_offset: destination_offset,
1057+
buffer_size: dst_buffer.size,
1058+
side: CopySide::Destination,
1059+
}
1060+
.into());
1061+
}
1062+
// NOTE: Should never underflow because of our earlier check.
1063+
if size > dst_buffer.size - destination_offset {
10331064
return Err(TransferError::BufferOverrun {
10341065
start_offset: destination_offset,
10351066
size,
@@ -1038,6 +1069,8 @@ pub(super) fn copy_buffer_to_buffer(
10381069
}
10391070
.into());
10401071
}
1072+
// NOTE: Should never overflow because of our earlier check.
1073+
let destination_end_offset = destination_offset + size;
10411074

10421075
// This must happen after parameter validation (so that errors are reported
10431076
// as required by the spec), but before any side effects.
@@ -1051,14 +1084,14 @@ pub(super) fn copy_buffer_to_buffer(
10511084
.buffer_memory_init_actions
10521085
.extend(dst_buffer.initialization_status.read().create_action(
10531086
dst_buffer,
1054-
destination_offset..(destination_offset + size),
1087+
destination_offset..destination_end_offset,
10551088
MemoryInitKind::ImplicitlyInitialized,
10561089
));
10571090
state
10581091
.buffer_memory_init_actions
10591092
.extend(src_buffer.initialization_status.read().create_action(
10601093
src_buffer,
1061-
source_offset..(source_offset + size),
1094+
source_offset..source_end_offset,
10621095
MemoryInitKind::NeedsInitializedMemory,
10631096
));
10641097

wgpu-core/src/device/queue.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -660,7 +660,15 @@ impl Queue {
660660
if !buffer_offset.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {
661661
return Err(TransferError::UnalignedBufferOffset(buffer_offset));
662662
}
663-
if buffer_offset + buffer_size.get() > buffer.size {
663+
664+
if buffer_offset >= buffer.size {
665+
return Err(TransferError::BufferStartOffsetOverrun {
666+
start_offset: buffer_offset,
667+
buffer_size: buffer_size.get(),
668+
side: CopySide::Destination,
669+
});
670+
}
671+
if buffer_size.get() > buffer.size - buffer_offset {
664672
return Err(TransferError::BufferOverrun {
665673
start_offset: buffer_offset,
666674
size: buffer_size.get(),

wgpu-core/src/resource.rs

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,22 @@ pub enum BufferAccessError {
301301
MapAborted,
302302
#[error(transparent)]
303303
InvalidResource(#[from] InvalidResourceError),
304+
#[error("Map start offset ({offset}) goes beyond bounds of buffer of size {buffer_size}")]
305+
MapStartOffsetOverrun {
306+
offset: wgt::BufferAddress,
307+
buffer_size: wgt::BufferAddress,
308+
},
309+
#[error(
310+
"Map end offset (start at {} + size of {}) goes beyond bounds of buffer of size {}",
311+
offset,
312+
size,
313+
buffer_size
314+
)]
315+
MapEndOffsetOverrun {
316+
offset: wgt::BufferAddress,
317+
size: wgt::BufferAddress,
318+
buffer_size: wgt::BufferAddress,
319+
},
304320
}
305321

306322
impl WebGpuError for BufferAccessError {
@@ -321,7 +337,9 @@ impl WebGpuError for BufferAccessError {
321337
| Self::OutOfBoundsUnderrun { .. }
322338
| Self::OutOfBoundsOverrun { .. }
323339
| Self::NegativeRange { .. }
324-
| Self::MapAborted => return ErrorType::Validation,
340+
| Self::MapAborted
341+
| Self::MapStartOffsetOverrun { .. }
342+
| Self::MapEndOffsetOverrun { .. } => return ErrorType::Validation,
325343
};
326344
e.webgpu_error_type()
327345
}
@@ -592,6 +610,27 @@ impl Buffer {
592610
return Err((op, BufferAccessError::UnalignedRangeSize { range_size }));
593611
}
594612

613+
if offset >= self.size {
614+
return Err((
615+
op,
616+
BufferAccessError::MapStartOffsetOverrun {
617+
offset,
618+
buffer_size: self.size,
619+
},
620+
));
621+
}
622+
// NOTE: Should never underflow because of our earlier check.
623+
if range_size > self.size - offset {
624+
return Err((
625+
op,
626+
BufferAccessError::MapEndOffsetOverrun {
627+
offset,
628+
size: range_size,
629+
buffer_size: self.size,
630+
},
631+
));
632+
}
633+
// NOTE: Should never overflow because of our earlier check.
595634
let range = offset..(offset + range_size);
596635

597636
if range.start % wgt::MAP_ALIGNMENT != 0 || range.end % wgt::COPY_BUFFER_ALIGNMENT != 0 {
@@ -702,10 +741,27 @@ impl Buffer {
702741
let map_state = &*self.map_state.lock();
703742
match *map_state {
704743
BufferMapState::Init { ref staging_buffer } => {
744+
if offset >= self.size {
745+
return Err(BufferAccessError::MapStartOffsetOverrun {
746+
offset,
747+
buffer_size: self.size,
748+
});
749+
}
750+
// NOTE: Should never underflow because of our earlier check.
751+
if range_size > self.size - offset {
752+
return Err(BufferAccessError::MapEndOffsetOverrun {
753+
offset,
754+
size: range_size,
755+
buffer_size: self.size,
756+
});
757+
}
758+
// NOTE: Should never overflow because of our earlier check.
759+
let mapping_end_offset = offset + range_size;
760+
705761
// offset (u64) can not be < 0, so no need to validate the lower bound
706-
if offset + range_size > self.size {
762+
if mapping_end_offset > self.size {
707763
return Err(BufferAccessError::OutOfBoundsOverrun {
708-
index: offset + range_size - 1,
764+
index: mapping_end_offset.checked_sub(1).unwrap(),
709765
max: self.size,
710766
});
711767
}

0 commit comments

Comments
 (0)