Skip to content

Commit e90aace

Browse files
nicalcwfitzgerald
andauthored
Validate texture copy ranges earlier to prevent integer overflow (#3090)
Co-authored-by: Connor Fitzgerald <connorwadefitzgerald@gmail.com>
1 parent 46b1216 commit e90aace

File tree

7 files changed

+146
-63
lines changed

7 files changed

+146
-63
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ Additionally `Surface::get_default_config` now returns an Option and returns Non
109109
- Bother to free the `hal::Api::CommandBuffer` when a `wgpu_core::command::CommandEncoder` is dropped. By @jimblandy in [#3069](https://github.com/gfx-rs/wgpu/pull/3069).
110110
- Fixed the mipmap example by adding the missing WRITE_TIMESTAMP_INSIDE_PASSES feature. By @Olaroll in [#3081](https://github.com/gfx-rs/wgpu/pull/3081).
111111
- Avoid panicking in some interactions with invalid resources by @nical in (#3094)[https://github.com/gfx-rs/wgpu/pull/3094]
112+
- Fixed an integer overflow in `copy_texture_to_texture` by @nical [#3090](https://github.com/gfx-rs/wgpu/pull/3090)
112113
- Remove `wgpu_types::Features::DEPTH24PLUS_STENCIL8`, making `wgpu::TextureFormat::Depth24PlusStencil8` available on all backends. By @Healthire in (#3151)[https://github.com/gfx-rs/wgpu/pull/3151]
113114
- Fix an integer overflow in `queue_write_texture` by @nical in (#3146)[https://github.com/gfx-rs/wgpu/pull/3146]
114115
- Make `RenderPassCompatibilityError` and `CreateShaderModuleError` not so huge. By @jimblandy in (#3226)[https://github.com/gfx-rs/wgpu/pull/3226]

wgpu-core/src/command/clear.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -280,9 +280,8 @@ pub(crate) fn clear_texture<A: HalApi>(
280280
// clear_texture api in order to remove this check and call the cheaper
281281
// change_replace_tracked whenever possible.
282282
let dst_barrier = texture_tracker
283-
.set_single(storage, dst_texture_id.0, selector, clear_usage)
283+
.set_single(dst_texture, dst_texture_id.0, selector, clear_usage)
284284
.unwrap()
285-
.1
286285
.map(|pending| pending.into_hal(dst_texture));
287286
unsafe {
288287
encoder.transition_textures(dst_barrier.into_iter());

wgpu-core/src/command/transfer.rs

Lines changed: 58 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -159,12 +159,8 @@ pub enum CopyError {
159159
pub(crate) fn extract_texture_selector<A: hal::Api>(
160160
copy_texture: &ImageCopyTexture,
161161
copy_size: &Extent3d,
162-
texture_guard: &Storage<Texture<A>, TextureId>,
162+
texture: &Texture<A>,
163163
) -> Result<(TextureSelector, hal::TextureCopyBase, wgt::TextureFormat), TransferError> {
164-
let texture = texture_guard
165-
.get(copy_texture.texture)
166-
.map_err(|_| TransferError::InvalidTexture(copy_texture.texture))?;
167-
168164
let format = texture.desc.format;
169165
let copy_aspect =
170166
hal::FormatAspects::from(format) & hal::FormatAspects::from(copy_texture.aspect);
@@ -710,8 +706,19 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
710706
return Ok(());
711707
}
712708

709+
let dst_texture = texture_guard
710+
.get(destination.texture)
711+
.map_err(|_| TransferError::InvalidTexture(destination.texture))?;
712+
713+
let (hal_copy_size, array_layer_count) = validate_texture_copy_range(
714+
destination,
715+
&dst_texture.desc,
716+
CopySide::Destination,
717+
copy_size,
718+
)?;
719+
713720
let (dst_range, dst_base, _) =
714-
extract_texture_selector(destination, copy_size, &*texture_guard)?;
721+
extract_texture_selector(destination, copy_size, dst_texture)?;
715722

716723
// Handle texture init *before* dealing with barrier transitions so we
717724
// have an easier time inserting "immediate-inits" that may be required
@@ -732,11 +739,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
732739
}
733740
let src_barrier = src_pending.map(|pending| pending.into_hal(src_buffer));
734741

735-
let (dst_texture, dst_pending) = cmd_buf
742+
let dst_pending = cmd_buf
736743
.trackers
737744
.textures
738745
.set_single(
739-
&*texture_guard,
746+
dst_texture,
740747
destination.texture,
741748
dst_range,
742749
hal::TextureUses::COPY_DST,
@@ -754,12 +761,6 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
754761
let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_texture));
755762

756763
let format_desc = dst_texture.desc.format.describe();
757-
let (hal_copy_size, array_layer_count) = validate_texture_copy_range(
758-
destination,
759-
&dst_texture.desc,
760-
CopySide::Destination,
761-
copy_size,
762-
)?;
763764
let (required_buffer_bytes_in_copy, bytes_per_array_layer) = validate_linear_texture_data(
764765
&source.layout,
765766
dst_texture.desc.format,
@@ -839,19 +840,25 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
839840
return Ok(());
840841
}
841842

842-
let (src_range, src_base, _) =
843-
extract_texture_selector(source, copy_size, &*texture_guard)?;
843+
let src_texture = texture_guard
844+
.get(source.texture)
845+
.map_err(|_| TransferError::InvalidTexture(source.texture))?;
846+
847+
let (hal_copy_size, array_layer_count) =
848+
validate_texture_copy_range(source, &src_texture.desc, CopySide::Source, copy_size)?;
849+
850+
let (src_range, src_base, _) = extract_texture_selector(source, copy_size, src_texture)?;
844851

845852
// Handle texture init *before* dealing with barrier transitions so we
846853
// have an easier time inserting "immediate-inits" that may be required
847854
// by prior discards in rare cases.
848855
handle_src_texture_init(cmd_buf, device, source, copy_size, &texture_guard)?;
849856

850-
let (src_texture, src_pending) = cmd_buf
857+
let src_pending = cmd_buf
851858
.trackers
852859
.textures
853860
.set_single(
854-
&*texture_guard,
861+
src_texture,
855862
source.texture,
856863
src_range,
857864
hal::TextureUses::COPY_SRC,
@@ -900,8 +907,6 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
900907
let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_buffer));
901908

902909
let format_desc = src_texture.desc.format.describe();
903-
let (hal_copy_size, array_layer_count) =
904-
validate_texture_copy_range(source, &src_texture.desc, CopySide::Source, copy_size)?;
905910
let (required_buffer_bytes_in_copy, bytes_per_array_layer) = validate_linear_texture_data(
906911
&destination.layout,
907912
src_texture.desc.format,
@@ -998,10 +1003,37 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
9981003
return Ok(());
9991004
}
10001005

1006+
let src_texture = texture_guard
1007+
.get(source.texture)
1008+
.map_err(|_| TransferError::InvalidTexture(source.texture))?;
1009+
let dst_texture = texture_guard
1010+
.get(destination.texture)
1011+
.map_err(|_| TransferError::InvalidTexture(source.texture))?;
1012+
1013+
// src and dst texture format must be the same.
1014+
let src_format = src_texture.desc.format;
1015+
let dst_format = dst_texture.desc.format;
1016+
if src_format != dst_format {
1017+
return Err(TransferError::MismatchedTextureFormats {
1018+
src_format,
1019+
dst_format,
1020+
}
1021+
.into());
1022+
}
1023+
1024+
let (src_copy_size, array_layer_count) =
1025+
validate_texture_copy_range(source, &src_texture.desc, CopySide::Source, copy_size)?;
1026+
let (dst_copy_size, _) = validate_texture_copy_range(
1027+
destination,
1028+
&dst_texture.desc,
1029+
CopySide::Destination,
1030+
copy_size,
1031+
)?;
1032+
10011033
let (src_range, src_tex_base, _) =
1002-
extract_texture_selector(source, copy_size, &*texture_guard)?;
1034+
extract_texture_selector(source, copy_size, src_texture)?;
10031035
let (dst_range, dst_tex_base, _) =
1004-
extract_texture_selector(destination, copy_size, &*texture_guard)?;
1036+
extract_texture_selector(destination, copy_size, dst_texture)?;
10051037
if src_tex_base.aspect != dst_tex_base.aspect {
10061038
return Err(TransferError::MismatchedAspects.into());
10071039
}
@@ -1012,11 +1044,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
10121044
handle_src_texture_init(cmd_buf, device, source, copy_size, &texture_guard)?;
10131045
handle_dst_texture_init(cmd_buf, device, destination, copy_size, &texture_guard)?;
10141046

1015-
let (src_texture, src_pending) = cmd_buf
1047+
let src_pending = cmd_buf
10161048
.trackers
10171049
.textures
10181050
.set_single(
1019-
&*texture_guard,
1051+
src_texture,
10201052
source.texture,
10211053
src_range,
10221054
hal::TextureUses::COPY_SRC,
@@ -1037,11 +1069,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
10371069
.into_iter()
10381070
.collect();
10391071

1040-
let (dst_texture, dst_pending) = cmd_buf
1072+
let dst_pending = cmd_buf
10411073
.trackers
10421074
.textures
10431075
.set_single(
1044-
&*texture_guard,
1076+
dst_texture,
10451077
destination.texture,
10461078
dst_range,
10471079
hal::TextureUses::COPY_DST,
@@ -1057,29 +1089,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
10571089
);
10581090
}
10591091

1060-
// src and dst texture format must be the same.
1061-
let src_format = src_texture.desc.format;
1062-
let dst_format = dst_texture.desc.format;
1063-
1064-
if src_format != dst_format {
1065-
return Err(TransferError::MismatchedTextureFormats {
1066-
src_format,
1067-
dst_format,
1068-
}
1069-
.into());
1070-
}
1071-
10721092
barriers.extend(dst_pending.map(|pending| pending.into_hal(dst_texture)));
10731093

1074-
let (src_copy_size, array_layer_count) =
1075-
validate_texture_copy_range(source, &src_texture.desc, CopySide::Source, copy_size)?;
1076-
let (dst_copy_size, _) = validate_texture_copy_range(
1077-
destination,
1078-
&dst_texture.desc,
1079-
CopySide::Destination,
1080-
copy_size,
1081-
)?;
1082-
10831094
let hal_copy_size = hal::CopyExtent {
10841095
width: src_copy_size.width.min(dst_copy_size.width),
10851096
height: src_copy_size.height.min(dst_copy_size.height),

wgpu-core/src/device/queue.rs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -594,14 +594,13 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
594594
return Ok(());
595595
}
596596

597-
// For clear we need write access to the texture.
598-
// TODO: Can we acquire write lock later?
599-
let (mut texture_guard, _) = hub.textures.write(&mut token);
597+
let (mut texture_guard, _) = hub.textures.write(&mut token); // For clear we need write access to the texture. TODO: Can we acquire write lock later?
598+
let dst = texture_guard.get_mut(destination.texture).unwrap();
599+
600600
let (selector, dst_base, texture_format) =
601-
extract_texture_selector(destination, size, &*texture_guard)?;
601+
extract_texture_selector(destination, size, dst)?;
602602
let format_desc = texture_format.describe();
603603

604-
let dst = texture_guard.get_mut(destination.texture).unwrap();
605604
if !dst.desc.usage.contains(wgt::TextureUsages::COPY_DST) {
606605
return Err(
607606
TransferError::MissingCopyDstUsageFlag(None, Some(destination.texture)).into(),
@@ -655,6 +654,12 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
655654
(size.depth_or_array_layers - 1) * block_rows_per_image + height_blocks;
656655
let stage_size = stage_bytes_per_row as u64 * block_rows_in_copy as u64;
657656

657+
if !dst.desc.usage.contains(wgt::TextureUsages::COPY_DST) {
658+
return Err(
659+
TransferError::MissingCopyDstUsageFlag(None, Some(destination.texture)).into(),
660+
);
661+
}
662+
658663
let mut trackers = device.trackers.lock();
659664
let encoder = device.pending_writes.activate();
660665

@@ -698,10 +703,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
698703
}
699704
}
700705

701-
let (dst, transition) = trackers
706+
let dst = texture_guard.get(destination.texture).unwrap();
707+
let transition = trackers
702708
.textures
703709
.set_single(
704-
&*texture_guard,
710+
dst,
705711
destination.texture,
706712
selector,
707713
hal::TextureUses::COPY_DST,

wgpu-core/src/track/texture.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -517,15 +517,13 @@ impl<A: hub::HalApi> TextureTracker<A> {
517517
///
518518
/// If the ID is higher than the length of internal vectors,
519519
/// the vectors will be extended. A call to set_size is not needed.
520-
pub fn set_single<'a>(
520+
pub fn set_single(
521521
&mut self,
522-
storage: &'a hub::Storage<Texture<A>, TextureId>,
522+
texture: &Texture<A>,
523523
id: TextureId,
524524
selector: TextureSelector,
525525
new_state: TextureUses,
526-
) -> Option<(&'a Texture<A>, Drain<'_, PendingTransition<TextureUses>>)> {
527-
let texture = storage.get(id).ok()?;
528-
526+
) -> Option<Drain<'_, PendingTransition<TextureUses>>> {
529527
let (index32, epoch, _) = id.unzip();
530528
let index = index32 as usize;
531529

@@ -535,7 +533,7 @@ impl<A: hub::HalApi> TextureTracker<A> {
535533

536534
unsafe {
537535
insert_or_barrier_update(
538-
texture_data_from_texture(storage, index32),
536+
(&texture.life_guard, &texture.full_range),
539537
Some(&mut self.start_set),
540538
&mut self.end_set,
541539
&mut self.metadata,
@@ -551,7 +549,7 @@ impl<A: hub::HalApi> TextureTracker<A> {
551549
)
552550
}
553551

554-
Some((texture, self.temp.drain(..)))
552+
Some(self.temp.drain(..))
555553
}
556554

557555
/// Sets the given state for all texture in the given tracker.

wgpu/tests/root.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ mod resource_error;
1616
mod shader;
1717
mod shader_primitive_index;
1818
mod texture_bounds;
19+
mod transfer;
1920
mod vertex_indices;
2021
mod write_texture;
2122
mod zero_init_texture_after_discard;

wgpu/tests/transfer.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
use crate::common::{fail, initialize_test, TestParameters};
2+
3+
#[test]
4+
fn copy_overflow_z() {
5+
// A simple crash test exercising validation that used to happen a bit too
6+
// late, letting an integer overflow slip through.
7+
initialize_test(TestParameters::default(), |ctx| {
8+
let mut encoder = ctx
9+
.device
10+
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
11+
12+
let t1 = ctx.device.create_texture(&wgpu::TextureDescriptor {
13+
label: None,
14+
dimension: wgpu::TextureDimension::D2,
15+
size: wgpu::Extent3d {
16+
width: 256,
17+
height: 256,
18+
depth_or_array_layers: 1,
19+
},
20+
format: wgpu::TextureFormat::Rgba8Uint,
21+
usage: wgpu::TextureUsages::COPY_DST,
22+
mip_level_count: 1,
23+
sample_count: 1,
24+
});
25+
let t2 = ctx.device.create_texture(&wgpu::TextureDescriptor {
26+
label: None,
27+
dimension: wgpu::TextureDimension::D2,
28+
size: wgpu::Extent3d {
29+
width: 256,
30+
height: 256,
31+
depth_or_array_layers: 1,
32+
},
33+
format: wgpu::TextureFormat::Rgba8Uint,
34+
usage: wgpu::TextureUsages::COPY_DST,
35+
mip_level_count: 1,
36+
sample_count: 1,
37+
});
38+
39+
fail(&ctx.device, || {
40+
// Validation should catch the silly selected z layer range without panicking.
41+
encoder.copy_texture_to_texture(
42+
wgpu::ImageCopyTexture {
43+
texture: &t1,
44+
mip_level: 1,
45+
origin: wgpu::Origin3d::ZERO,
46+
aspect: wgpu::TextureAspect::All,
47+
},
48+
wgpu::ImageCopyTexture {
49+
texture: &t2,
50+
mip_level: 1,
51+
origin: wgpu::Origin3d {
52+
x: 0,
53+
y: 0,
54+
z: 3824276442,
55+
},
56+
aspect: wgpu::TextureAspect::All,
57+
},
58+
wgpu::Extent3d {
59+
width: 100,
60+
height: 3,
61+
depth_or_array_layers: 613286111,
62+
},
63+
);
64+
ctx.queue.submit(Some(encoder.finish()));
65+
});
66+
})
67+
}

0 commit comments

Comments
 (0)