[spv-out]: Ensure array subscripts are in bounds.

This commit is contained in:
Jim Blandy
2021-05-27 15:02:38 -07:00
committed by Dzmitry Malyshau
parent a7cacab276
commit c16f2097ad
19 changed files with 1415 additions and 104 deletions

View File

@@ -1,7 +1,7 @@
use super::{compose::validate_compose, ComposeError, FunctionInfo, ShaderStages, TypeFlags};
use crate::{
arena::{Arena, Handle},
proc::ResolveError,
proc::{ProcError, ResolveError},
};
#[derive(Clone, Debug, thiserror::Error)]
@@ -17,8 +17,8 @@ pub enum ExpressionError {
InvalidBaseType(Handle<crate::Expression>),
#[error("Accessing with index {0:?} can't be done")]
InvalidIndexType(Handle<crate::Expression>),
#[error("Accessing index {1} is out of {0:?} bounds")]
IndexOutOfBounds(Handle<crate::Expression>, u32),
#[error("Accessing index {1:?} is out of {0:?} bounds")]
IndexOutOfBounds(Handle<crate::Expression>, crate::ScalarValue),
#[error("The expression {0:?} may only be indexed by a constant")]
IndexMustBeConstant(Handle<crate::Expression>),
#[error("Function argument {0:?} doesn't exist")]
@@ -41,6 +41,8 @@ pub enum ExpressionError {
InvalidSwizzleComponent(crate::SwizzleComponent, crate::VectorSize),
#[error(transparent)]
Compose(#[from] ComposeError),
#[error(transparent)]
Proc(#[from] ProcError),
#[error("Operation {0:?} can't work with {1:?}")]
InvalidUnaryOperandType(crate::UnaryOperator, Handle<crate::Expression>),
#[error("Operation {0:?} can't work with {1:?} and {2:?}")]
@@ -144,8 +146,9 @@ impl super::Validator {
let stages = match *expression {
E::Access { base, index } => {
let base_type = resolver.resolve(base)?;
// See the documentation for `Expression::Access`.
let dynamic_indexing_restricted = match *resolver.resolve(base)? {
let dynamic_indexing_restricted = match *base_type {
Ti::Vector { .. } => false,
Ti::Matrix { .. } | Ti::Array { .. } => true,
Ti::Pointer { .. } | Ti::ValuePointer { size: Some(_), .. } => false,
@@ -174,6 +177,36 @@ impl super::Validator {
{
return Err(ExpressionError::IndexMustBeConstant(base));
}
// If we know both the length and the index, we can do the
// bounds check now.
if let crate::proc::IndexableLength::Known(known_length) =
base_type.indexable_length(module)?
{
if let E::Constant(k) = function.expressions[index] {
if let crate::Constant {
// We must treat specializable constants as unknown.
specialization: None,
// Non-scalar indices should have been caught above.
inner: crate::ConstantInner::Scalar { value, .. },
..
} = module.constants[k]
{
match value {
crate::ScalarValue::Uint(u) if u >= known_length as u64 => {
return Err(ExpressionError::IndexOutOfBounds(base, value));
}
crate::ScalarValue::Sint(s)
if s < 0 || s >= known_length as i64 =>
{
return Err(ExpressionError::IndexOutOfBounds(base, value));
}
_ => (),
}
}
}
}
ShaderStages::all()
}
E::AccessIndex { base, index } => {
@@ -208,7 +241,10 @@ impl super::Validator {
let limit = resolve_index_limit(module, base, resolver.resolve(base)?, true)?;
if index >= limit {
return Err(ExpressionError::IndexOutOfBounds(base, index));
return Err(ExpressionError::IndexOutOfBounds(
base,
crate::ScalarValue::Uint(limit as _),
));
}
ShaderStages::all()
}

View File

@@ -52,6 +52,8 @@ pub enum TypeError {
InvalidArrayBaseType(Handle<crate::Type>),
#[error("The constant {0:?} can not be used for an array size")]
InvalidArraySizeConstant(Handle<crate::Constant>),
#[error("Array type {0:?} must have a length of one or more")]
NonPositiveArrayLength(Handle<crate::Constant>),
#[error("Array stride {stride} is smaller than the base element size {base_size}")]
InsufficientArrayStride { stride: u32, base_size: u32 },
#[error("Field '{0}' can't be dynamically-sized, has type {1:?}")]
@@ -288,15 +290,15 @@ impl super::Validator {
let sized_flag = match size {
crate::ArraySize::Constant(const_handle) => {
match constants.try_get(const_handle) {
let length_is_positive = match constants.try_get(const_handle) {
Some(&crate::Constant {
inner:
crate::ConstantInner::Scalar {
width: _,
value: crate::ScalarValue::Uint(_),
value: crate::ScalarValue::Uint(length),
},
..
}) => {}
}) => length > 0,
// Accept a signed integer size to avoid
// requiring an explicit uint
// literal. Type inference should make
@@ -305,14 +307,18 @@ impl super::Validator {
inner:
crate::ConstantInner::Scalar {
width: _,
value: crate::ScalarValue::Sint(_),
value: crate::ScalarValue::Sint(length),
},
..
}) => {}
}) => length > 0,
other => {
log::warn!("Array size {:?}", other);
return Err(TypeError::InvalidArraySizeConstant(const_handle));
}
};
if !length_is_positive {
return Err(TypeError::NonPositiveArrayLength(const_handle));
}
TypeFlags::SIZED