@@ -47,6 +47,14 @@ pub enum TransferError {
4747 MissingBufferUsage ( #[ from] MissingBufferUsageError ) ,
4848 #[ error( transparent) ]
4949 MissingTextureUsage ( #[ from] MissingTextureUsageError ) ,
50+ #[ error(
51+ "Copy at offset {start_offset} bytes would end up overrunning the 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 { .. }
@@ -342,8 +351,15 @@ pub(crate) fn validate_linear_texture_data(
342351 return Err ( TransferError :: UnspecifiedRowsPerImage ) ;
343352 } ;
344353
345- // Avoid underflow in the subtraction by checking bytes_in_copy against buffer_size first.
346- if bytes_in_copy > buffer_size || offset > buffer_size - bytes_in_copy {
354+ if offset > buffer_size {
355+ return Err ( TransferError :: BufferStartOffsetOverrun {
356+ start_offset : offset,
357+ buffer_size : bytes_in_copy,
358+ side : buffer_side,
359+ } ) ;
360+ }
361+ // NOTE: Should never underflow because of our earlier check.
362+ if bytes_in_copy > buffer_size - offset {
347363 return Err ( TransferError :: BufferOverrun {
348364 start_offset : offset,
349365 size : bytes_in_copy,
@@ -983,10 +999,19 @@ pub(super) fn copy_buffer_to_buffer(
983999 . map_err ( TransferError :: MissingBufferUsage ) ?;
9841000 let dst_barrier = dst_pending. map ( |pending| pending. into_hal ( dst_buffer, state. snatch_guard ) ) ;
9851001
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- } ;
1002+ // TODO: This check isn't part of the spec., but it looks like it should be.
1003+ if source_offset > src_buffer. size {
1004+ return Err ( TransferError :: BufferStartOffsetOverrun {
1005+ start_offset : destination_offset,
1006+ buffer_size : dst_buffer. size ,
1007+ side : CopySide :: Destination ,
1008+ }
1009+ . into ( ) ) ;
1010+ }
1011+ let size = size. unwrap_or_else ( || {
1012+ // NOTE: Should never underflow because of our earlier check.
1013+ src_buffer. size - source_offset
1014+ } ) ;
9901015
9911016 if !size. is_multiple_of ( wgt:: COPY_BUFFER_ALIGNMENT ) {
9921017 return Err ( TransferError :: UnalignedCopySize ( size) . into ( ) ) ;
@@ -1019,8 +1044,7 @@ pub(super) fn copy_buffer_to_buffer(
10191044 }
10201045 }
10211046
1022- let destination_end_offset = destination_offset + size;
1023- if source_end_offset > src_buffer. size {
1047+ if size > src_buffer. size - source_offset {
10241048 return Err ( TransferError :: BufferOverrun {
10251049 start_offset : source_offset,
10261050 size,
@@ -1029,7 +1053,21 @@ pub(super) fn copy_buffer_to_buffer(
10291053 }
10301054 . into ( ) ) ;
10311055 }
1032- if destination_end_offset > dst_buffer. size {
1056+ // NOTE: Should never overflow because of our earlier check.
1057+ let source_end_offset = source_offset + size;
1058+
1059+ // TODO: Checks for source buffer overrun don't quite match spec., but it seems important to do
1060+ // things in this order.
1061+ if destination_offset > dst_buffer. size {
1062+ return Err ( TransferError :: BufferStartOffsetOverrun {
1063+ start_offset : destination_offset,
1064+ buffer_size : dst_buffer. size ,
1065+ side : CopySide :: Destination ,
1066+ }
1067+ . into ( ) ) ;
1068+ }
1069+ // NOTE: Should never underflow because of our earlier check.
1070+ if size > dst_buffer. size - destination_offset {
10331071 return Err ( TransferError :: BufferOverrun {
10341072 start_offset : destination_offset,
10351073 size,
@@ -1038,6 +1076,8 @@ pub(super) fn copy_buffer_to_buffer(
10381076 }
10391077 . into ( ) ) ;
10401078 }
1079+ // NOTE: Should never overflow because of our earlier check.
1080+ let destination_end_offset = destination_offset + size;
10411081
10421082 // This must happen after parameter validation (so that errors are reported
10431083 // as required by the spec), but before any side effects.
@@ -1051,14 +1091,14 @@ pub(super) fn copy_buffer_to_buffer(
10511091 . buffer_memory_init_actions
10521092 . extend ( dst_buffer. initialization_status . read ( ) . create_action (
10531093 dst_buffer,
1054- destination_offset..( destination_offset + size ) ,
1094+ destination_offset..destination_end_offset ,
10551095 MemoryInitKind :: ImplicitlyInitialized ,
10561096 ) ) ;
10571097 state
10581098 . buffer_memory_init_actions
10591099 . extend ( src_buffer. initialization_status . read ( ) . create_action (
10601100 src_buffer,
1061- source_offset..( source_offset + size ) ,
1101+ source_offset..source_end_offset ,
10621102 MemoryInitKind :: NeedsInitializedMemory ,
10631103 ) ) ;
10641104
0 commit comments