Skip to content

Commit 7d138e2

Browse files
authored
Avoid overflow in check texture copy bounds. (#2963)
1 parent a0dfb28 commit 7d138e2

File tree

4 files changed

+107
-36
lines changed

4 files changed

+107
-36
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,8 @@ jobs:
225225
for backend in ${{ matrix.backends }}; do
226226
echo "======= NATIVE TESTS $backend ======";
227227
WGPU_BACKEND=$backend cargo nextest run -p wgpu --no-fail-fast
228+
# Test that we catch overflows in `--release` builds too.
229+
WGPU_BACKEND=$backend cargo nextest run --release -p wgpu --no-fail-fast
228230
done
229231
230232
fmt:

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ the same every time it is rendered, we now warn if it is missing.
6464

6565
#### General
6666
- Free `StagingBuffers` even when an error occurs in the operation that consumes them. By @jimblandy in [#2961](https://github.com/gfx-rs/wgpu/pull/2961)
67+
- Avoid overflow when checking that texture copies fall within bounds. By @jimblandy in [#2963](https://github.com/gfx-rs/wgpu/pull/2963)
6768
- Improve the validation and error reporting of buffer mappings by @nical in [#2848](https://github.com/gfx-rs/wgpu/pull/2848)
6869
- Fix compilation errors when using wgpu-core in isolation while targetting `wasm32-unknown-unknown` by @Seamooo in [#2922](https://github.com/gfx-rs/wgpu/pull/2922)
6970
- Fixed opening of RenderDoc library by @abuffseagull in [#2930](https://github.com/gfx-rs/wgpu/pull/2930)

wgpu-core/src/command/transfer.rs

Lines changed: 45 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use std::iter;
2525
pub type ImageCopyBuffer = wgt::ImageCopyBuffer<BufferId>;
2626
pub type ImageCopyTexture = wgt::ImageCopyTexture<TextureId>;
2727

28-
#[derive(Clone, Debug)]
28+
#[derive(Clone, Copy, Debug)]
2929
pub enum CopySide {
3030
Source,
3131
Destination,
@@ -326,37 +326,52 @@ pub(crate) fn validate_texture_copy_range(
326326
_ => {}
327327
}
328328

329-
let x_copy_max = texture_copy_view.origin.x + copy_size.width;
330-
if x_copy_max > extent.width {
331-
return Err(TransferError::TextureOverrun {
332-
start_offset: texture_copy_view.origin.x,
333-
end_offset: x_copy_max,
334-
texture_size: extent.width,
335-
dimension: TextureErrorDimension::X,
336-
side: texture_side,
337-
});
338-
}
339-
let y_copy_max = texture_copy_view.origin.y + copy_size.height;
340-
if y_copy_max > extent.height {
341-
return Err(TransferError::TextureOverrun {
342-
start_offset: texture_copy_view.origin.y,
343-
end_offset: y_copy_max,
344-
texture_size: extent.height,
345-
dimension: TextureErrorDimension::Y,
346-
side: texture_side,
347-
});
348-
}
349-
let z_copy_max = texture_copy_view.origin.z + copy_size.depth_or_array_layers;
350-
if z_copy_max > extent.depth_or_array_layers {
351-
return Err(TransferError::TextureOverrun {
352-
start_offset: texture_copy_view.origin.z,
353-
end_offset: z_copy_max,
354-
texture_size: extent.depth_or_array_layers,
355-
dimension: TextureErrorDimension::Z,
356-
side: texture_side,
357-
});
329+
/// Return `Ok` if a run `size` texels long starting at `start_offset` falls
330+
/// entirely within `texture_size`. Otherwise, return an appropriate a`Err`.
331+
fn check_dimension(
332+
dimension: TextureErrorDimension,
333+
side: CopySide,
334+
start_offset: u32,
335+
size: u32,
336+
texture_size: u32,
337+
) -> Result<(), TransferError> {
338+
// Avoid underflow in the subtraction by checking start_offset against
339+
// texture_size first.
340+
if start_offset <= texture_size && size <= texture_size - start_offset {
341+
Ok(())
342+
} else {
343+
Err(TransferError::TextureOverrun {
344+
start_offset,
345+
end_offset: start_offset.wrapping_add(size),
346+
texture_size,
347+
dimension,
348+
side,
349+
})
350+
}
358351
}
359352

353+
check_dimension(
354+
TextureErrorDimension::X,
355+
texture_side,
356+
texture_copy_view.origin.x,
357+
copy_size.width,
358+
extent.width,
359+
)?;
360+
check_dimension(
361+
TextureErrorDimension::Y,
362+
texture_side,
363+
texture_copy_view.origin.y,
364+
copy_size.height,
365+
extent.height,
366+
)?;
367+
check_dimension(
368+
TextureErrorDimension::Z,
369+
texture_side,
370+
texture_copy_view.origin.z,
371+
copy_size.depth_or_array_layers,
372+
extent.depth_or_array_layers,
373+
)?;
374+
360375
if texture_copy_view.origin.x % block_width != 0 {
361376
return Err(TransferError::UnalignedCopyOriginX);
362377
}

wgpu/tests/texture_bounds.rs

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::num::NonZeroU32;
55

66
#[test]
77
fn bad_copy_origin() {
8-
fn try_origin(origin: wgpu::Origin3d, should_panic: bool) {
8+
fn try_origin(origin: wgpu::Origin3d, size: wgpu::Extent3d, should_panic: bool) {
99
let mut parameters = TestParameters::default();
1010
if should_panic {
1111
parameters = parameters.failure();
@@ -23,15 +23,68 @@ fn bad_copy_origin() {
2323
},
2424
&data,
2525
BUFFER_COPY_LAYOUT,
26-
TEXTURE_SIZE,
26+
size,
2727
);
2828
});
2929
}
3030

31-
try_origin(wgpu::Origin3d { x: 0, y: 0, z: 0 }, false);
32-
try_origin(wgpu::Origin3d { x: 1, y: 0, z: 0 }, true);
33-
try_origin(wgpu::Origin3d { x: 0, y: 1, z: 0 }, true);
34-
try_origin(wgpu::Origin3d { x: 0, y: 0, z: 1 }, true);
31+
try_origin(wgpu::Origin3d { x: 0, y: 0, z: 0 }, TEXTURE_SIZE, false);
32+
try_origin(wgpu::Origin3d { x: 1, y: 0, z: 0 }, TEXTURE_SIZE, true);
33+
try_origin(wgpu::Origin3d { x: 0, y: 1, z: 0 }, TEXTURE_SIZE, true);
34+
try_origin(wgpu::Origin3d { x: 0, y: 0, z: 1 }, TEXTURE_SIZE, true);
35+
36+
try_origin(
37+
wgpu::Origin3d {
38+
x: TEXTURE_SIZE.width - 1,
39+
y: TEXTURE_SIZE.height - 1,
40+
z: TEXTURE_SIZE.depth_or_array_layers - 1,
41+
},
42+
wgpu::Extent3d {
43+
width: 1,
44+
height: 1,
45+
depth_or_array_layers: 1,
46+
},
47+
false,
48+
);
49+
try_origin(
50+
wgpu::Origin3d {
51+
x: u32::MAX,
52+
y: 0,
53+
z: 0,
54+
},
55+
wgpu::Extent3d {
56+
width: 1,
57+
height: 1,
58+
depth_or_array_layers: 1,
59+
},
60+
true,
61+
);
62+
try_origin(
63+
wgpu::Origin3d {
64+
x: u32::MAX,
65+
y: 0,
66+
z: 0,
67+
},
68+
wgpu::Extent3d {
69+
width: 1,
70+
height: 1,
71+
depth_or_array_layers: 1,
72+
},
73+
true,
74+
);
75+
try_origin(
76+
wgpu::Origin3d {
77+
x: u32::MAX,
78+
y: 0,
79+
z: 0,
80+
},
81+
wgpu::Extent3d {
82+
width: 1,
83+
height: 1,
84+
depth_or_array_layers: 1,
85+
},
86+
true,
87+
);
3588
}
3689

3790
const TEXTURE_SIZE: wgpu::Extent3d = wgpu::Extent3d {

0 commit comments

Comments
 (0)