Skip to content

Commit

Permalink
Avoid overflow in check texture copy bounds.
Browse files Browse the repository at this point in the history
Fixes #2962.
  • Loading branch information
jimblandy committed Aug 14, 2022
1 parent c6ca2a7 commit e4df8cf
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 36 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,8 @@ jobs:
for backend in ${{ matrix.backends }}; do
echo "======= NATIVE TESTS $backend ======";
WGPU_BACKEND=$backend cargo nextest run -p wgpu --no-fail-fast
# Test that we catch overflows in `--release` builds too.
WGPU_BACKEND=$backend cargo nextest run --release -p wgpu --no-fail-fast
done
fmt:
Expand Down
75 changes: 45 additions & 30 deletions wgpu-core/src/command/transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use std::iter;
pub type ImageCopyBuffer = wgt::ImageCopyBuffer<BufferId>;
pub type ImageCopyTexture = wgt::ImageCopyTexture<TextureId>;

#[derive(Clone, Debug)]
#[derive(Clone, Copy, Debug)]
pub enum CopySide {
Source,
Destination,
Expand Down Expand Up @@ -326,37 +326,52 @@ pub(crate) fn validate_texture_copy_range(
_ => {}
}

let x_copy_max = texture_copy_view.origin.x + copy_size.width;
if x_copy_max > extent.width {
return Err(TransferError::TextureOverrun {
start_offset: texture_copy_view.origin.x,
end_offset: x_copy_max,
texture_size: extent.width,
dimension: TextureErrorDimension::X,
side: texture_side,
});
}
let y_copy_max = texture_copy_view.origin.y + copy_size.height;
if y_copy_max > extent.height {
return Err(TransferError::TextureOverrun {
start_offset: texture_copy_view.origin.y,
end_offset: y_copy_max,
texture_size: extent.height,
dimension: TextureErrorDimension::Y,
side: texture_side,
});
}
let z_copy_max = texture_copy_view.origin.z + copy_size.depth_or_array_layers;
if z_copy_max > extent.depth_or_array_layers {
return Err(TransferError::TextureOverrun {
start_offset: texture_copy_view.origin.z,
end_offset: z_copy_max,
texture_size: extent.depth_or_array_layers,
dimension: TextureErrorDimension::Z,
side: texture_side,
});
/// Return `Ok` if a run `size` texels long starting at `start_offset` falls
/// entirely within `texture_size`. Otherwise, return an appropriate a`Err`.
fn check_dimension(
dimension: TextureErrorDimension,
side: CopySide,
start_offset: u32,
size: u32,
texture_size: u32,
) -> Result<(), TransferError> {
// Avoid underflow in the subtraction by checking start_offset against
// texture_size first.
if start_offset <= texture_size && size <= texture_size - start_offset {
Ok(())
} else {
Err(TransferError::TextureOverrun {
start_offset,
end_offset: start_offset.wrapping_add(size),
texture_size,
dimension,
side,
})
}
}

check_dimension(
TextureErrorDimension::X,
texture_side,
texture_copy_view.origin.x,
copy_size.width,
extent.width,
)?;
check_dimension(
TextureErrorDimension::Y,
texture_side,
texture_copy_view.origin.y,
copy_size.height,
extent.height,
)?;
check_dimension(
TextureErrorDimension::Z,
texture_side,
texture_copy_view.origin.z,
copy_size.depth_or_array_layers,
extent.depth_or_array_layers,
)?;

if texture_copy_view.origin.x % block_width != 0 {
return Err(TransferError::UnalignedCopyOriginX);
}
Expand Down
65 changes: 59 additions & 6 deletions wgpu/tests/texture_bounds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::num::NonZeroU32;

#[test]
fn bad_copy_origin() {
fn try_origin(origin: wgpu::Origin3d, should_panic: bool) {
fn try_origin(origin: wgpu::Origin3d, size: wgpu::Extent3d, should_panic: bool) {
let mut parameters = TestParameters::default();
if should_panic {
parameters = parameters.failure();
Expand All @@ -23,15 +23,68 @@ fn bad_copy_origin() {
},
&data,
BUFFER_COPY_LAYOUT,
TEXTURE_SIZE,
size,
);
});
}

try_origin(wgpu::Origin3d { x: 0, y: 0, z: 0 }, false);
try_origin(wgpu::Origin3d { x: 1, y: 0, z: 0 }, true);
try_origin(wgpu::Origin3d { x: 0, y: 1, z: 0 }, true);
try_origin(wgpu::Origin3d { x: 0, y: 0, z: 1 }, true);
try_origin(wgpu::Origin3d { x: 0, y: 0, z: 0 }, TEXTURE_SIZE, false);
try_origin(wgpu::Origin3d { x: 1, y: 0, z: 0 }, TEXTURE_SIZE, true);
try_origin(wgpu::Origin3d { x: 0, y: 1, z: 0 }, TEXTURE_SIZE, true);
try_origin(wgpu::Origin3d { x: 0, y: 0, z: 1 }, TEXTURE_SIZE, true);

try_origin(
wgpu::Origin3d {
x: TEXTURE_SIZE.width - 1,
y: TEXTURE_SIZE.height - 1,
z: TEXTURE_SIZE.depth_or_array_layers - 1,
},
wgpu::Extent3d {
width: 1,
height: 1,
depth_or_array_layers: 1,
},
false,
);
try_origin(
wgpu::Origin3d {
x: u32::MAX,
y: 0,
z: 0,
},
wgpu::Extent3d {
width: 1,
height: 1,
depth_or_array_layers: 1,
},
true,
);
try_origin(
wgpu::Origin3d {
x: u32::MAX,
y: 0,
z: 0,
},
wgpu::Extent3d {
width: 1,
height: 1,
depth_or_array_layers: 1,
},
true,
);
try_origin(
wgpu::Origin3d {
x: u32::MAX,
y: 0,
z: 0,
},
wgpu::Extent3d {
width: 1,
height: 1,
depth_or_array_layers: 1,
},
true,
);
}

const TEXTURE_SIZE: wgpu::Extent3d = wgpu::Extent3d {
Expand Down

0 comments on commit e4df8cf

Please sign in to comment.