@@ -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
0 commit comments