diff --git a/CHANGELOG.md b/CHANGELOG.md index bf05a41e37..03ddbc0d53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -111,6 +111,7 @@ By @teoxoy in [#4185](https://github.com/gfx-rs/wgpu/pull/4185) - Add stub support for device destroy and device validity. By @bradwerth in [4163](https://github.com/gfx-rs/wgpu/pull/4163) - Add trace-level logging for most entry points in wgpu-core By @nical in [4183](https://github.com/gfx-rs/wgpu/pull/4183) - Add `Rgb10a2Uint` format. By @teoxoy in [4199](https://github.com/gfx-rs/wgpu/pull/4199) +- Validate that resources are used on the right device. By @nical in [4207](https://github.com/gfx-rs/wgpu/pull/4207) #### Vulkan diff --git a/deno_webgpu/error.rs b/deno_webgpu/error.rs index a68592adfc..b966e5d7cf 100644 --- a/deno_webgpu/error.rs +++ b/deno_webgpu/error.rs @@ -104,9 +104,9 @@ impl From for WebGpuError { match err { DeviceError::Lost => WebGpuError::Lost, DeviceError::OutOfMemory => WebGpuError::OutOfMemory, - DeviceError::ResourceCreationFailed | DeviceError::Invalid => { - WebGpuError::Validation(fmt_err(&err)) - } + DeviceError::ResourceCreationFailed + | DeviceError::Invalid + | DeviceError::WrongDevice => WebGpuError::Validation(fmt_err(&err)), } } } diff --git a/tests/tests/device.rs b/tests/tests/device.rs index 991170e739..ebd331b4b7 100644 --- a/tests/tests/device.rs +++ b/tests/tests/device.rs @@ -11,7 +11,6 @@ fn device_initialization() { } #[test] -#[ignore] fn device_mismatch() { initialize_test( // https://github.com/gfx-rs/wgpu/issues/3927 diff --git a/wgpu-core/src/command/render.rs b/wgpu-core/src/command/render.rs index 2c310ed043..b875764bb6 100644 --- a/wgpu-core/src/command/render.rs +++ b/wgpu-core/src/command/render.rs @@ -10,7 +10,7 @@ use crate::{ RenderCommand, RenderCommandError, StateChange, }, device::{ - AttachmentData, Device, MissingDownlevelFlags, MissingFeatures, + AttachmentData, Device, DeviceError, MissingDownlevelFlags, MissingFeatures, RenderPassCompatibilityCheckType, RenderPassCompatibilityError, RenderPassContext, }, error::{ErrorFormatter, PrettyError}, @@ -18,7 +18,6 @@ use crate::{ hal_api::HalApi, hub::Token, id, - id::DeviceId, identity::GlobalIdentityHandlerFactory, init_tracker::{MemoryInitKind, TextureInitRange, TextureInitTrackerAction}, pipeline::{self, PipelineFlags}, @@ -520,12 +519,12 @@ pub enum ColorAttachmentError { /// Error encountered when performing a render pass. #[derive(Clone, Debug, Error)] pub enum RenderPassErrorInner { + #[error(transparent)] + Device(DeviceError), #[error(transparent)] ColorAttachment(#[from] ColorAttachmentError), #[error(transparent)] Encoder(#[from] CommandEncoderError), - #[error("Device {0:?} is invalid")] - InvalidDevice(DeviceId), #[error("Attachment texture view {0:?} is invalid")] InvalidAttachment(id::TextureViewId), #[error("The format of the depth-stencil attachment ({0:?}) is not a depth-stencil format")] @@ -658,6 +657,12 @@ impl From for RenderPassErrorInner { } } +impl From for RenderPassErrorInner { + fn from(error: DeviceError) -> Self { + Self::Device(error) + } +} + /// Error encountered when performing a render pass. #[derive(Clone, Debug, Error)] #[error("{scope}")] @@ -1351,12 +1356,11 @@ impl Global { }); } - let device = &device_guard[cmd_buf.device_id.value]; + let device_id = cmd_buf.device_id.value; + + let device = &device_guard[device_id]; if !device.is_valid() { - return Err(RenderPassErrorInner::InvalidDevice( - cmd_buf.device_id.value.0, - )) - .map_pass_err(init_scope); + return Err(DeviceError::Invalid).map_pass_err(init_scope); } cmd_buf.encoder.open_pass(base.label); @@ -1451,6 +1455,11 @@ impl Global { .add_single(&*bind_group_guard, bind_group_id) .ok_or(RenderCommandError::InvalidBindGroup(bind_group_id)) .map_pass_err(scope)?; + + if bind_group.device_id.value != device_id { + return Err(DeviceError::WrongDevice).map_pass_err(scope); + } + bind_group .validate_dynamic_bindings(index, &temp_offsets, &cmd_buf.limits) .map_pass_err(scope)?; @@ -1518,6 +1527,10 @@ impl Global { .ok_or(RenderCommandError::InvalidPipeline(pipeline_id)) .map_pass_err(scope)?; + if pipeline.device_id.value != device_id { + return Err(DeviceError::WrongDevice).map_pass_err(scope); + } + info.context .check_compatible( &pipeline.pass_context, @@ -1635,6 +1648,11 @@ impl Global { .buffers .merge_single(&*buffer_guard, buffer_id, hal::BufferUses::INDEX) .map_pass_err(scope)?; + + if buffer.device_id.value != device_id { + return Err(DeviceError::WrongDevice).map_pass_err(scope); + } + check_buffer_usage(buffer.usage, BufferUsages::INDEX) .map_pass_err(scope)?; let buf_raw = buffer @@ -1683,6 +1701,11 @@ impl Global { .buffers .merge_single(&*buffer_guard, buffer_id, hal::BufferUses::VERTEX) .map_pass_err(scope)?; + + if buffer.device_id.value != device_id { + return Err(DeviceError::WrongDevice).map_pass_err(scope); + } + check_buffer_usage(buffer.usage, BufferUsages::VERTEX) .map_pass_err(scope)?; let buf_raw = buffer @@ -2265,6 +2288,10 @@ impl Global { .ok_or(RenderCommandError::InvalidRenderBundle(bundle_id)) .map_pass_err(scope)?; + if bundle.device_id.value != device_id { + return Err(DeviceError::WrongDevice).map_pass_err(scope); + } + info.context .check_compatible( &bundle.context, diff --git a/wgpu-core/src/device/global.rs b/wgpu-core/src/device/global.rs index e592d113ba..67f00a4013 100644 --- a/wgpu-core/src/device/global.rs +++ b/wgpu-core/src/device/global.rs @@ -1326,6 +1326,10 @@ impl Global { Err(..) => break binding_model::CreateBindGroupError::InvalidLayout, }; + if bind_group_layout.device_id.value.0 != device_id { + break DeviceError::WrongDevice.into(); + } + let mut layout_id = id::Valid(desc.layout); if let Some(id) = bind_group_layout.as_duplicate() { layout_id = id; diff --git a/wgpu-core/src/device/mod.rs b/wgpu-core/src/device/mod.rs index 3e2a245127..cacd836ec6 100644 --- a/wgpu-core/src/device/mod.rs +++ b/wgpu-core/src/device/mod.rs @@ -300,6 +300,8 @@ pub enum DeviceError { OutOfMemory, #[error("Creation of a resource failed for a reason other than running out of memory.")] ResourceCreationFailed, + #[error("Attempt to use a resource with a different device from the one that created it")] + WrongDevice, } impl From for DeviceError { diff --git a/wgpu-core/src/device/queue.rs b/wgpu-core/src/device/queue.rs index 089f4ff717..815f60588f 100644 --- a/wgpu-core/src/device/queue.rs +++ b/wgpu-core/src/device/queue.rs @@ -397,6 +397,7 @@ impl Global { } let result = self.queue_write_staging_buffer_impl( + queue_id, device, device_token, &staging_buffer, @@ -464,6 +465,7 @@ impl Global { } let result = self.queue_write_staging_buffer_impl( + queue_id, device, device_token, &staging_buffer, @@ -531,6 +533,7 @@ impl Global { fn queue_write_staging_buffer_impl( &self, + device_id: id::DeviceId, device: &mut super::Device, device_token: &mut Token>, staging_buffer: &StagingBuffer, @@ -551,6 +554,10 @@ impl Global { .as_ref() .ok_or(TransferError::InvalidBuffer(buffer_id))?; + if dst.device_id.value.0 != device_id { + return Err(DeviceError::WrongDevice.into()); + } + let src_buffer_size = staging_buffer.size; self.queue_validate_write_buffer_impl(dst, buffer_id, buffer_offset, src_buffer_size)?; @@ -627,6 +634,10 @@ impl Global { .get_mut(destination.texture) .map_err(|_| TransferError::InvalidTexture(destination.texture))?; + if dst.device_id.value.0 != queue_id { + return Err(DeviceError::WrongDevice.into()); + } + if !dst.desc.usage.contains(wgt::TextureUsages::COPY_DST) { return Err( TransferError::MissingCopyDstUsageFlag(None, Some(destination.texture)).into(), @@ -1105,6 +1116,11 @@ impl Global { Some(cmdbuf) => cmdbuf, None => continue, }; + + if cmdbuf.device_id.value.0 != queue_id { + return Err(DeviceError::WrongDevice.into()); + } + #[cfg(feature = "trace")] if let Some(ref trace) = device.trace { trace.lock().add(Action::Submit( diff --git a/wgpu-core/src/device/resource.rs b/wgpu-core/src/device/resource.rs index c839d6a828..f6cbe60134 100644 --- a/wgpu-core/src/device/resource.rs +++ b/wgpu-core/src/device/resource.rs @@ -1670,6 +1670,7 @@ impl Device { } fn create_buffer_binding<'a>( + device_id: id::DeviceId, bb: &binding_model::BufferBinding, binding: u32, decl: &wgt::BindGroupLayoutEntry, @@ -1727,6 +1728,11 @@ impl Device { .buffers .add_single(storage, bb.buffer_id, internal_use) .ok_or(Error::InvalidBuffer(bb.buffer_id))?; + + if buffer.device_id.value.0 != device_id { + return Err(DeviceError::WrongDevice.into()); + } + check_buffer_usage(buffer.usage, pub_usage)?; let raw_buffer = buffer .raw @@ -1797,6 +1803,7 @@ impl Device { } fn create_texture_binding( + device_id: id::DeviceId, view: &resource::TextureView, texture_guard: &Storage, id::TextureId>, internal_use: hal::TextureUses, @@ -1818,6 +1825,11 @@ impl Device { .ok_or(binding_model::CreateBindGroupError::InvalidTexture( view.parent_id.value.0, ))?; + + if texture.device_id.value.0 != device_id { + return Err(DeviceError::WrongDevice.into()); + } + check_texture_usage(texture.desc.usage, pub_usage)?; used_texture_ranges.push(TextureInitTrackerAction { @@ -1889,6 +1901,7 @@ impl Device { let (res_index, count) = match entry.resource { Br::Buffer(ref bb) => { let bb = Self::create_buffer_binding( + self_id, bb, binding, decl, @@ -1911,6 +1924,7 @@ impl Device { let res_index = hal_buffers.len(); for bb in bindings_array.iter() { let bb = Self::create_buffer_binding( + self_id, bb, binding, decl, @@ -1933,6 +1947,10 @@ impl Device { .add_single(&*sampler_guard, id) .ok_or(Error::InvalidSampler(id))?; + if sampler.device_id.value.0 != self_id { + return Err(DeviceError::WrongDevice.into()); + } + // Allowed sampler values for filtering and comparison let (allowed_filtering, allowed_comparison) = match ty { wgt::SamplerBindingType::Filtering => (None, false), @@ -1981,6 +1999,11 @@ impl Device { .samplers .add_single(&*sampler_guard, id) .ok_or(Error::InvalidSampler(id))?; + + if sampler.device_id.value.0 != self_id { + return Err(DeviceError::WrongDevice.into()); + } + hal_samplers.push(&sampler.raw); } @@ -1998,6 +2021,7 @@ impl Device { "SampledTexture, ReadonlyStorageTexture or WriteonlyStorageTexture", )?; Self::create_texture_binding( + self_id, view, &texture_guard, internal_use, @@ -2026,6 +2050,7 @@ impl Device { Self::texture_use_parameters(binding, decl, view, "SampledTextureArray, ReadonlyStorageTextureArray or WriteonlyStorageTextureArray")?; Self::create_texture_binding( + self_id, view, &texture_guard, internal_use, @@ -2324,6 +2349,11 @@ impl Device { let Some(bind_group_layout) = try_get_bind_group_layout(bgl_guard, id) else { return Err(Error::InvalidBindGroupLayout(id)); }; + + if bind_group_layout.device_id.value.0 != self_id { + return Err(DeviceError::WrongDevice.into()); + } + count_validator.merge(&bind_group_layout.assume_deduplicated().count_validator); } count_validator @@ -2457,6 +2487,10 @@ impl Device { .get(desc.stage.module) .map_err(|_| validation::StageError::InvalidModule)?; + if shader_module.device_id.value.0 != self_id { + return Err(DeviceError::WrongDevice.into()); + } + { let flag = wgt::ShaderStages::COMPUTE; let provided_layouts = match desc.layout { @@ -2500,6 +2534,10 @@ impl Device { .get(pipeline_layout_id) .map_err(|_| pipeline::CreateComputePipelineError::InvalidLayout)?; + if layout.device_id.value.0 != self_id { + return Err(DeviceError::WrongDevice.into()); + } + let late_sized_buffer_groups = Device::make_late_sized_buffer_groups(&shader_binding_sizes, layout, &*bgl_guard); @@ -2843,11 +2881,20 @@ impl Device { } })?; + if shader_module.device_id.value.0 != self_id { + return Err(DeviceError::WrongDevice.into()); + } + let provided_layouts = match desc.layout { Some(pipeline_layout_id) => { let pipeline_layout = pipeline_layout_guard .get(pipeline_layout_id) .map_err(|_| pipeline::CreateRenderPipelineError::InvalidLayout)?; + + if pipeline_layout.device_id.value.0 != self_id { + return Err(DeviceError::WrongDevice.into()); + } + Some(Device::get_introspection_bind_group_layouts( pipeline_layout, &*bgl_guard,