Skip to content

Commit

Permalink
Allow unspecified bits when deserializing the API's bitflags (#3229)
Browse files Browse the repository at this point in the history
  • Loading branch information
nical authored Nov 26, 2022
1 parent d4b1d57 commit 82e9dcf
Show file tree
Hide file tree
Showing 10 changed files with 79 additions and 42 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ Bottom level categories:
- 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]
- Fix an integer overflow in `queue_write_texture` by @nical in (#3146)[https://github.com/gfx-rs/wgpu/pull/3146]
- Make `RenderPassCompatibilityError` and `CreateShaderModuleError` not so huge. By @jimblandy in (#3226)[https://github.com/gfx-rs/wgpu/pull/3226]
- Check for invalid bitflag bits in wgpu-core and allow them to be captured/replayed by @nical in (#3229)[https://github.com/gfx-rs/wgpu/pull/3229]

#### WebGPU

Expand Down
11 changes: 0 additions & 11 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ version = "0.10"
arrayvec = "0.7"
async-executor = "1.0"
bitflags = "1"
bitflags_serde_shim = "0.2"
bit-vec = "0.6"
bytemuck = "1.4"
cargo-run-wasm = "0.2.0"
Expand Down
2 changes: 2 additions & 0 deletions wgpu-core/src/binding_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ pub enum CreateBindGroupLayoutError {
TooManyBindings(BindingTypeMaxCountError),
#[error("Binding index {binding} is greater than the maximum index {maximum}")]
InvalidBindingIndex { binding: u32, maximum: u32 },
#[error("Invalid visibility {0:?}")]
InvalidVisibility(wgt::ShaderStages),
}

//TODO: refactor this to move out `enum BindingError`.
Expand Down
19 changes: 15 additions & 4 deletions wgpu-core/src/device/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -600,8 +600,8 @@ impl<A: HalApi> Device<A> {

let mut usage = conv::map_buffer_usage(desc.usage);

if desc.usage.is_empty() {
return Err(resource::CreateBufferError::EmptyUsage);
if desc.usage.is_empty() || desc.usage.contains_invalid_bits() {
return Err(resource::CreateBufferError::InvalidUsage(desc.usage));
}

if !self
Expand Down Expand Up @@ -717,8 +717,8 @@ impl<A: HalApi> Device<A> {
) -> Result<resource::Texture<A>, resource::CreateTextureError> {
use resource::{CreateTextureError, TextureDimensionError};

if desc.usage.is_empty() {
return Err(CreateTextureError::EmptyUsage);
if desc.usage.is_empty() || desc.usage.contains_invalid_bits() {
return Err(CreateTextureError::InvalidUsage(desc.usage));
}

conv::check_texture_dimension_size(
Expand Down Expand Up @@ -1560,6 +1560,13 @@ impl<A: HalApi> Device<A> {
error,
})?;
}

if entry.visibility.contains_invalid_bits() {
return Err(
binding_model::CreateBindGroupLayoutError::InvalidVisibility(entry.visibility),
);
}

if entry.visibility.contains(wgt::ShaderStages::VERTEX) {
if writable_storage == WritableStorage::Yes {
required_features |= wgt::Features::VERTEX_WRITABLE_STORAGE;
Expand Down Expand Up @@ -2650,6 +2657,10 @@ impl<A: HalApi> Device<A> {
for (i, cs) in color_targets.iter().enumerate() {
if let Some(cs) = cs.as_ref() {
let error = loop {
if cs.write_mask.contains_invalid_bits() {
break Some(pipeline::ColorStateError::InvalidWriteMask(cs.write_mask));
}

let format_features = self.describe_format_features(adapter, cs.format)?;
if !format_features
.allowed_usages
Expand Down
2 changes: 2 additions & 0 deletions wgpu-core/src/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,8 @@ pub enum ColorStateError {
},
#[error("blend factors for {0:?} must be `One`")]
InvalidMinMaxBlendFactors(wgt::BlendComponent),
#[error("invalid write mask {0:?}")]
InvalidWriteMask(wgt::ColorWrites),
}

#[derive(Clone, Debug, Error)]
Expand Down
8 changes: 4 additions & 4 deletions wgpu-core/src/resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,8 +242,8 @@ pub enum CreateBufferError {
AccessError(#[from] BufferAccessError),
#[error("buffers that are mapped at creation have to be aligned to `COPY_BUFFER_ALIGNMENT`")]
UnalignedSize,
#[error("Buffers cannot have empty usage flags")]
EmptyUsage,
#[error("Invalid usage flags {0:?}")]
InvalidUsage(wgt::BufferUsages),
#[error("`MAP` usage can only be combined with the opposite `COPY`, requested {0:?}")]
UsageMismatch(wgt::BufferUsages),
#[error("Buffer size {requested} is greater than the maximum buffer size ({maximum})")]
Expand Down Expand Up @@ -490,8 +490,8 @@ pub enum TextureDimensionError {
pub enum CreateTextureError {
#[error(transparent)]
Device(#[from] DeviceError),
#[error("Textures cannot have empty usage flags")]
EmptyUsage,
#[error("Invalid usage flags {0:?}")]
InvalidUsage(wgt::TextureUsages),
#[error(transparent)]
InvalidDimension(#[from] TextureDimensionError),
#[error("Depth texture ({1:?}) can't be created as {0:?}")]
Expand Down
5 changes: 2 additions & 3 deletions wgpu-types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,12 @@ rustdoc-args = ["--cfg", "docsrs"]
[lib]

[features]
trace = ["serde", "bitflags_serde_shim"]
replay = ["serde", "bitflags_serde_shim"]
trace = ["serde"]
replay = ["serde"]

[dependencies]
bitflags.workspace = true
serde = { workspace = true, features = ["serde_derive"], optional = true }
bitflags_serde_shim = { workspace = true, optional = true }

[dev-dependencies]
serde_json.workspace = true
69 changes: 51 additions & 18 deletions wgpu-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,48 @@ use serde::{Deserialize, Serialize};
use std::hash::{Hash, Hasher};
use std::{num::NonZeroU32, ops::Range};

// Use this macro instead of the one provided by the bitflags_serde_shim crate
// because the latter produces an error when deserializing bits that are not
// specified in the bitflags, while we want deserialization to succeed and
// and unspecified bits to lead to errors handled in wgpu-core.
// Note that plainly deriving Serialize and Deserialized would have a similar
// behavior to this macro (unspecified bit do not produce an error).
macro_rules! impl_bitflags {
($name:ident) => {
#[cfg(feature = "trace")]
impl serde::Serialize for $name {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.bits().serialize(serializer)
}
}

#[cfg(feature = "replay")]
impl<'de> serde::Deserialize<'de> for $name {
fn deserialize<D>(deserializer: D) -> Result<$name, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = <_ as serde::Deserialize<'de>>::deserialize(deserializer)?;
// Note: newer version of bitflags replaced from_bits_unchecked with
// from_bits_retain which is not marked as unsafe (same implementation).
Ok(unsafe { $name::from_bits_unchecked(value) })
}
}

impl $name {
/// Returns true if the bitflags contains bits that are not part of
/// the bitflags definition.
pub fn contains_invalid_bits(&self) -> bool {
let all = Self::all().bits();
(self.bits() | all) != all
}
}
};
}

/// Integral type used for buffer offsets.
pub type BufferAddress = u64;
/// Integral type used for buffer slice sizes.
Expand Down Expand Up @@ -115,8 +157,7 @@ bitflags::bitflags! {
}
}

#[cfg(feature = "bitflags_serde_shim")]
bitflags_serde_shim::impl_serde_for_bitflags!(Backends);
impl_bitflags!(Backends);

impl From<Backend> for Backends {
fn from(backend: Backend) -> Self {
Expand Down Expand Up @@ -633,8 +674,7 @@ bitflags::bitflags! {
}
}

#[cfg(feature = "bitflags_serde_shim")]
bitflags_serde_shim::impl_serde_for_bitflags!(Features);
impl_bitflags!(Features);

impl Features {
/// Mask of all features which are part of the upstream WebGPU standard.
Expand Down Expand Up @@ -1090,8 +1130,7 @@ bitflags::bitflags! {
}
}

#[cfg(feature = "bitflags_serde_shim")]
bitflags_serde_shim::impl_serde_for_bitflags!(DownlevelFlags);
impl_bitflags!(DownlevelFlags);

impl DownlevelFlags {
/// All flags that indicate if the backend is WebGPU compliant
Expand Down Expand Up @@ -1213,8 +1252,7 @@ bitflags::bitflags! {
}
}

#[cfg(feature = "bitflags_serde_shim")]
bitflags_serde_shim::impl_serde_for_bitflags!(ShaderStages);
impl_bitflags!(ShaderStages);

/// Dimensions of a particular texture view.
///
Expand Down Expand Up @@ -1666,8 +1704,7 @@ impl TextureFormatFeatureFlags {
}
}

#[cfg(feature = "bitflags_serde_shim")]
bitflags_serde_shim::impl_serde_for_bitflags!(TextureFormatFeatureFlags);
impl_bitflags!(TextureFormatFeatureFlags);

/// Features supported by a given texture format
///
Expand Down Expand Up @@ -3085,8 +3122,7 @@ bitflags::bitflags! {
}
}

#[cfg(feature = "bitflags_serde_shim")]
bitflags_serde_shim::impl_serde_for_bitflags!(ColorWrites);
impl_bitflags!(ColorWrites);

impl Default for ColorWrites {
fn default() -> Self {
Expand Down Expand Up @@ -3644,8 +3680,7 @@ bitflags::bitflags! {
}
}

#[cfg(feature = "bitflags_serde_shim")]
bitflags_serde_shim::impl_serde_for_bitflags!(BufferUsages);
impl_bitflags!(BufferUsages);

/// Describes a [`Buffer`](../wgpu/struct.Buffer.html).
///
Expand Down Expand Up @@ -3846,8 +3881,7 @@ bitflags::bitflags! {
}
}

#[cfg(feature = "bitflags_serde_shim")]
bitflags_serde_shim::impl_serde_for_bitflags!(TextureUsages);
impl_bitflags!(TextureUsages);

/// Configures a [`Surface`] for presentation.
///
Expand Down Expand Up @@ -5046,8 +5080,7 @@ bitflags::bitflags! {
}
}

#[cfg(feature = "bitflags_serde_shim")]
bitflags_serde_shim::impl_serde_for_bitflags!(PipelineStatisticsTypes);
impl_bitflags!(PipelineStatisticsTypes);

/// Argument buffer layout for draw_indirect commands.
#[repr(C)]
Expand Down
3 changes: 2 additions & 1 deletion wgpu/tests/buffer_usages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ fn buffer_usage() {
Bu::MAP_WRITE | Bu::COPY_SRC | Bu::STORAGE,
Bu::all(),
];
let always_fail = &[Bu::empty()];
let invalid_bits = unsafe { Bu::from_bits_unchecked(0b1111111111111) };
let always_fail = &[Bu::empty(), invalid_bits];

try_create(
false,
Expand Down

0 comments on commit 82e9dcf

Please sign in to comment.