tint: validate max nesting depth of composite types
Also increased stack size for windows + debug GN builds of tint_unittests to 4 MB, which matches what we do in the CMake build. Required, otherwise some of my new tests stack overflow. Bug: tint:1209 Change-Id: I3b98000a989aa8b42b20cc4e2219c91887e52451 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/121360 Reviewed-by: Ben Clayton <bclayton@google.com> Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
parent
0b3400c56e
commit
6b304e9ffd
|
@ -2051,6 +2051,12 @@ if (tint_build_unittests) {
|
|||
configs += [ "//build/config/compiler:no_chromium_code" ]
|
||||
}
|
||||
|
||||
if (is_win && is_debug) {
|
||||
# TODO(crbug.com/tint/1749): both msvc and clang builds stack overflow on debug builds.
|
||||
# Increase the initial stack size to 4 MB (default is 1MB).
|
||||
ldflags = [ "/STACK:4194304" ]
|
||||
}
|
||||
|
||||
testonly = true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -99,6 +99,7 @@ namespace {
|
|||
|
||||
constexpr int64_t kMaxArrayElementCount = 65536;
|
||||
constexpr uint32_t kMaxStatementDepth = 127;
|
||||
constexpr size_t kMaxNestDepthOfCompositeType = 255;
|
||||
|
||||
} // namespace
|
||||
|
||||
|
@ -1697,7 +1698,7 @@ const type::Type* Resolver::ConcreteType(const type::Type* ty,
|
|||
target_el_ty = target_arr_ty->ElemType();
|
||||
}
|
||||
if (auto* el_ty = ConcreteType(a->ElemType(), target_el_ty, source)) {
|
||||
return Array(source, source, el_ty, a->Count(), /* explicit_stride */ 0);
|
||||
return Array(source, source, source, el_ty, a->Count(), /* explicit_stride */ 0);
|
||||
}
|
||||
return nullptr;
|
||||
},
|
||||
|
@ -2129,7 +2130,8 @@ sem::Call* Resolver::Call(const ast::CallExpression* expr) {
|
|||
}
|
||||
return nullptr;
|
||||
}
|
||||
auto* arr = Array(expr->source, expr->source, el_ty, el_count, /* explicit_stride */ 0);
|
||||
auto* arr = Array(expr->source, expr->source, expr->source, el_ty, el_count,
|
||||
/* explicit_stride */ 0);
|
||||
if (!arr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -2446,7 +2448,8 @@ type::Type* Resolver::BuiltinType(builtin::Builtin builtin_ty, const ast::Identi
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
auto* out = Array(ast_el_ty->source, //
|
||||
auto* out = Array(tmpl_ident->source, //
|
||||
ast_el_ty->source, //
|
||||
ast_count ? ast_count->source : ident->source, //
|
||||
el_ty, el_count, explicit_stride);
|
||||
if (!out) {
|
||||
|
@ -2726,6 +2729,19 @@ type::Type* Resolver::BuiltinType(builtin::Builtin builtin_ty, const ast::Identi
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
size_t Resolver::NestDepth(const type::Type* ty) const {
|
||||
return Switch(
|
||||
ty, //
|
||||
[](const type::Vector*) { return size_t{1}; },
|
||||
[](const type::Matrix*) { return size_t{2}; },
|
||||
[&](Default) {
|
||||
if (auto d = nest_depth_.Get(ty)) {
|
||||
return *d;
|
||||
}
|
||||
return size_t{0};
|
||||
});
|
||||
}
|
||||
|
||||
void Resolver::CollectTextureSamplerPairs(
|
||||
const sem::Builtin* builtin,
|
||||
utils::VectorRef<const sem::ValueExpression*> args) const {
|
||||
|
@ -3528,7 +3544,8 @@ bool Resolver::ArrayAttributes(utils::VectorRef<const ast::Attribute*> attribute
|
|||
return true;
|
||||
}
|
||||
|
||||
type::Array* Resolver::Array(const Source& el_source,
|
||||
type::Array* Resolver::Array(const Source& array_source,
|
||||
const Source& el_source,
|
||||
const Source& count_source,
|
||||
const type::Type* el_ty,
|
||||
const type::ArrayCount* el_count,
|
||||
|
@ -3555,6 +3572,17 @@ type::Array* Resolver::Array(const Source& el_source,
|
|||
el_ty, el_count, el_align, static_cast<uint32_t>(size), static_cast<uint32_t>(stride),
|
||||
static_cast<uint32_t>(implicit_stride));
|
||||
|
||||
// Maximum nesting depth of composite types
|
||||
// https://gpuweb.github.io/gpuweb/wgsl/#limits
|
||||
const size_t nest_depth = 1 + NestDepth(el_ty);
|
||||
if (nest_depth > kMaxNestDepthOfCompositeType) {
|
||||
AddError("array has nesting depth of " + std::to_string(nest_depth) + ", maximum is " +
|
||||
std::to_string(kMaxNestDepthOfCompositeType),
|
||||
array_source);
|
||||
return nullptr;
|
||||
}
|
||||
nest_depth_.Add(out, nest_depth);
|
||||
|
||||
if (!validator_.Array(out, el_source)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -3574,15 +3602,18 @@ type::Type* Resolver::Alias(const ast::Alias* alias) {
|
|||
}
|
||||
|
||||
sem::Struct* Resolver::Structure(const ast::Struct* str) {
|
||||
auto struct_name = [&] { //
|
||||
return builder_->Symbols().NameFor(str->name->symbol);
|
||||
};
|
||||
|
||||
if (validator_.IsValidationEnabled(str->attributes,
|
||||
ast::DisabledValidation::kIgnoreStructMemberLimit)) {
|
||||
// Maximum number of members in a structure type
|
||||
// https://gpuweb.github.io/gpuweb/wgsl/#limits
|
||||
const size_t kMaxNumStructMembers = 16383;
|
||||
if (str->members.Length() > kMaxNumStructMembers) {
|
||||
AddError("struct '" + builder_->Symbols().NameFor(str->name->symbol) + "' has " +
|
||||
std::to_string(str->members.Length()) + " members, maximum is " +
|
||||
std::to_string(kMaxNumStructMembers),
|
||||
AddError("struct '" + struct_name() + "' has " + std::to_string(str->members.Length()) +
|
||||
" members, maximum is " + std::to_string(kMaxNumStructMembers),
|
||||
str->source);
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -3608,6 +3639,7 @@ sem::Struct* Resolver::Structure(const ast::Struct* str) {
|
|||
uint64_t struct_align = 1;
|
||||
utils::Hashmap<Symbol, const ast::StructMember*, 8> member_map;
|
||||
|
||||
size_t members_nest_depth = 0;
|
||||
for (auto* member : str->members) {
|
||||
Mark(member);
|
||||
Mark(member->name);
|
||||
|
@ -3624,6 +3656,8 @@ sem::Struct* Resolver::Structure(const ast::Struct* str) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
members_nest_depth = std::max(members_nest_depth, NestDepth(type));
|
||||
|
||||
// validator_.Validate member type
|
||||
if (!validator_.IsPlain(type)) {
|
||||
AddError(sem_.TypeNameOf(type) + " cannot be used as the type of a structure member",
|
||||
|
@ -3821,6 +3855,18 @@ sem::Struct* Resolver::Structure(const ast::Struct* str) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
// Maximum nesting depth of composite types
|
||||
// https://gpuweb.github.io/gpuweb/wgsl/#limits
|
||||
const size_t nest_depth = 1 + members_nest_depth;
|
||||
if (nest_depth > kMaxNestDepthOfCompositeType) {
|
||||
AddError("struct '" + struct_name() + "' has nesting depth of " +
|
||||
std::to_string(nest_depth) + ", maximum is " +
|
||||
std::to_string(kMaxNestDepthOfCompositeType),
|
||||
str->source);
|
||||
return nullptr;
|
||||
}
|
||||
nest_depth_.Add(out, nest_depth);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
|
|
@ -349,6 +349,7 @@ class Resolver {
|
|||
|
||||
/// Builds and returns the semantic information for an array.
|
||||
/// @returns the semantic Array information, or nullptr if an error is raised.
|
||||
/// @param array_source the source of the array
|
||||
/// @param el_source the source of the array element, or the array if the array does not have a
|
||||
/// locally-declared element AST node.
|
||||
/// @param count_source the source of the array count, or the array if the array does not have a
|
||||
|
@ -356,7 +357,8 @@ class Resolver {
|
|||
/// @param el_ty the Array element type
|
||||
/// @param el_count the number of elements in the array.
|
||||
/// @param explicit_stride the explicit byte stride of the array. Zero means implicit stride.
|
||||
type::Array* Array(const Source& el_source,
|
||||
type::Array* Array(const Source& array_source,
|
||||
const Source& el_source,
|
||||
const Source& count_source,
|
||||
const type::Type* el_ty,
|
||||
const type::ArrayCount* el_count,
|
||||
|
@ -496,6 +498,10 @@ class Resolver {
|
|||
/// @note: Will raise an ICE if @p symbol is not a builtin type.
|
||||
type::Type* BuiltinType(builtin::Builtin builtin_ty, const ast::Identifier* ident);
|
||||
|
||||
/// @returns the nesting depth of @ty as defined in
|
||||
/// https://gpuweb.github.io/gpuweb/wgsl/#composite-types
|
||||
size_t NestDepth(const type::Type* ty) const;
|
||||
|
||||
// ArrayConstructorSig represents a unique array constructor signature.
|
||||
// It is a tuple of the array type, number of arguments provided and earliest evaluation stage.
|
||||
using ArrayConstructorSig =
|
||||
|
@ -566,6 +572,7 @@ class Resolver {
|
|||
logical_binary_lhs_to_parent_;
|
||||
utils::Hashset<const ast::Expression*, 8> skip_const_eval_;
|
||||
IdentifierResolveHint identifier_resolve_hint_;
|
||||
utils::Hashmap<const type::Type*, size_t, 8> nest_depth_;
|
||||
};
|
||||
|
||||
} // namespace tint::resolver
|
||||
|
|
|
@ -2434,5 +2434,195 @@ TEST_F(ResolverTest, MaxNumStructMembers_WithIgnoreStructMemberLimit_Valid) {
|
|||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
}
|
||||
|
||||
size_t kMaxNestDepthOfCompositeType = 255;
|
||||
|
||||
TEST_F(ResolverTest, MaxNestDepthOfCompositeType_Structs_Valid) {
|
||||
auto* s = Structure("S", utils::Vector{Member("m", ty.i32())});
|
||||
size_t depth = 1; // Depth of struct
|
||||
size_t iterations = kMaxNestDepthOfCompositeType - depth;
|
||||
for (size_t i = 0; i < iterations; ++i) {
|
||||
s = Structure("S" + std::to_string(i), utils::Vector{Member("m", ty.Of(s))});
|
||||
}
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
}
|
||||
|
||||
TEST_F(ResolverTest, MaxNestDepthOfCompositeType_Structs_Invalid) {
|
||||
auto* s = Structure("S", utils::Vector{Member("m", ty.i32())});
|
||||
size_t depth = 1; // Depth of struct
|
||||
size_t iterations = kMaxNestDepthOfCompositeType - depth + 1;
|
||||
for (size_t i = 0; i < iterations; ++i) {
|
||||
auto source = i == iterations - 1 ? Source{{12, 34}} : Source{{0, i}};
|
||||
s = Structure(source, "S" + std::to_string(i), utils::Vector{Member("m", ty.Of(s))});
|
||||
}
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(), "12:34 error: struct 'S254' has nesting depth of 256, maximum is 255");
|
||||
}
|
||||
|
||||
TEST_F(ResolverTest, MaxNestDepthOfCompositeType_StructsWithVector_Valid) {
|
||||
auto* s = Structure("S", utils::Vector{Member("m", ty.vec3<i32>())});
|
||||
size_t depth = 2; // Despth of struct + vector
|
||||
size_t iterations = kMaxNestDepthOfCompositeType - depth;
|
||||
for (size_t i = 0; i < iterations; ++i) {
|
||||
s = Structure("S" + std::to_string(i), utils::Vector{Member("m", ty.Of(s))});
|
||||
}
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
}
|
||||
|
||||
TEST_F(ResolverTest, MaxNestDepthOfCompositeType_StructsWithVector_Invalid) {
|
||||
auto* s = Structure("S", utils::Vector{Member("m", ty.vec3<i32>())});
|
||||
size_t depth = 2; // Despth of struct + vector
|
||||
size_t iterations = kMaxNestDepthOfCompositeType - depth + 1;
|
||||
for (size_t i = 0; i < iterations; ++i) {
|
||||
auto source = i == iterations - 1 ? Source{{12, 34}} : Source{{0, i}};
|
||||
s = Structure(source, "S" + std::to_string(i), utils::Vector{Member("m", ty.Of(s))});
|
||||
}
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(), "12:34 error: struct 'S253' has nesting depth of 256, maximum is 255");
|
||||
}
|
||||
|
||||
TEST_F(ResolverTest, MaxNestDepthOfCompositeType_StructsWithMatrix_Valid) {
|
||||
auto* s = Structure("S", utils::Vector{Member("m", ty.mat3x3<f32>())});
|
||||
size_t depth = 3; // Depth of struct + matrix
|
||||
size_t iterations = kMaxNestDepthOfCompositeType - depth;
|
||||
for (size_t i = 0; i < iterations; ++i) {
|
||||
s = Structure("S" + std::to_string(i), utils::Vector{Member("m", ty.Of(s))});
|
||||
}
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
}
|
||||
|
||||
TEST_F(ResolverTest, MaxNestDepthOfCompositeType_StructsWithMatrix_Invalid) {
|
||||
auto* s = Structure("S", utils::Vector{Member("m", ty.mat3x3<f32>())});
|
||||
size_t depth = 3; // Depth of struct + matrix
|
||||
size_t iterations = kMaxNestDepthOfCompositeType - depth + 1;
|
||||
for (size_t i = 0; i < iterations; ++i) {
|
||||
auto source = i == iterations - 1 ? Source{{12, 34}} : Source{{0, i}};
|
||||
s = Structure(source, "S" + std::to_string(i), utils::Vector{Member("m", ty.Of(s))});
|
||||
}
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(), "12:34 error: struct 'S252' has nesting depth of 256, maximum is 255");
|
||||
}
|
||||
|
||||
TEST_F(ResolverTest, MaxNestDepthOfCompositeType_Arrays_Valid) {
|
||||
auto a = ty.array(ty.i32(), 10_u);
|
||||
size_t depth = 1; // Depth of array
|
||||
size_t iterations = kMaxNestDepthOfCompositeType - depth;
|
||||
for (size_t i = 0; i < iterations; ++i) {
|
||||
a = ty.array(a, 1_u);
|
||||
}
|
||||
Alias("a", a);
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
}
|
||||
|
||||
TEST_F(ResolverTest, MaxNestDepthOfCompositeType_Arrays_Invalid) {
|
||||
auto a = ty.array(Source{{99, 88}}, ty.i32(), 10_u);
|
||||
size_t depth = 1; // Depth of array
|
||||
size_t iterations = kMaxNestDepthOfCompositeType - depth + 1;
|
||||
for (size_t i = 0; i < iterations; ++i) {
|
||||
auto source = (i == iterations - 1) ? Source{{12, 34}} : Source{{0, i}};
|
||||
a = ty.array(source, a, 1_u);
|
||||
}
|
||||
Alias("a", a);
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(), "12:34 error: array has nesting depth of 256, maximum is 255");
|
||||
}
|
||||
|
||||
TEST_F(ResolverTest, MaxNestDepthOfCompositeType_ArraysOfVector_Valid) {
|
||||
auto a = ty.array(ty.vec3<i32>(), 10_u);
|
||||
size_t depth = 2; // Depth of array + vector
|
||||
size_t iterations = kMaxNestDepthOfCompositeType - depth;
|
||||
for (size_t i = 0; i < iterations; ++i) {
|
||||
a = ty.array(a, 1_u);
|
||||
}
|
||||
Alias("a", a);
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
}
|
||||
|
||||
TEST_F(ResolverTest, MaxNestDepthOfCompositeType_ArraysOfVector_Invalid) {
|
||||
auto a = ty.array(Source{{99, 88}}, ty.vec3<i32>(), 10_u);
|
||||
size_t depth = 2; // Depth of array + vector
|
||||
size_t iterations = kMaxNestDepthOfCompositeType - depth + 1;
|
||||
for (size_t i = 0; i < iterations; ++i) {
|
||||
auto source = (i == iterations - 1) ? Source{{12, 34}} : Source{{0, i}};
|
||||
a = ty.array(source, a, 1_u);
|
||||
}
|
||||
Alias("a", a);
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(), "12:34 error: array has nesting depth of 256, maximum is 255");
|
||||
}
|
||||
|
||||
TEST_F(ResolverTest, MaxNestDepthOfCompositeType_ArraysOfMatrix_Valid) {
|
||||
auto a = ty.array(ty.mat3x3<f32>(), 10_u);
|
||||
size_t depth = 3; // Depth of array + matrix
|
||||
size_t iterations = kMaxNestDepthOfCompositeType - depth;
|
||||
for (size_t i = 0; i < iterations; ++i) {
|
||||
a = ty.array(a, 1_u);
|
||||
}
|
||||
Alias("a", a);
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
}
|
||||
|
||||
TEST_F(ResolverTest, MaxNestDepthOfCompositeType_ArraysOfMatrix_Invalid) {
|
||||
auto a = ty.array(ty.mat3x3<f32>(), 10_u);
|
||||
size_t depth = 3; // Depth of array + matrix
|
||||
size_t iterations = kMaxNestDepthOfCompositeType - depth + 1;
|
||||
for (size_t i = 0; i < iterations; ++i) {
|
||||
auto source = (i == iterations - 1) ? Source{{12, 34}} : Source{{0, i}};
|
||||
a = ty.array(source, a, 1_u);
|
||||
}
|
||||
Alias("a", a);
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(), "12:34 error: array has nesting depth of 256, maximum is 255");
|
||||
}
|
||||
|
||||
TEST_F(ResolverTest, MaxNestDepthOfCompositeType_StructsOfArray_Valid) {
|
||||
auto a = ty.array(ty.mat3x3<f32>(), 10_u);
|
||||
auto* s = Structure("S", utils::Vector{Member("m", a)});
|
||||
size_t depth = 4; // Depth of struct + array + matrix
|
||||
size_t iterations = kMaxNestDepthOfCompositeType - depth;
|
||||
for (size_t i = 0; i < iterations; ++i) {
|
||||
s = Structure("S" + std::to_string(i), utils::Vector{Member("m", ty.Of(s))});
|
||||
}
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
}
|
||||
|
||||
TEST_F(ResolverTest, MaxNestDepthOfCompositeType_StructsOfArray_Invalid) {
|
||||
auto a = ty.array(ty.mat3x3<f32>(), 10_u);
|
||||
auto* s = Structure("S", utils::Vector{Member("m", a)});
|
||||
size_t depth = 4; // Depth of struct + array + matrix
|
||||
size_t iterations = kMaxNestDepthOfCompositeType - depth + 1;
|
||||
for (size_t i = 0; i < iterations; ++i) {
|
||||
auto source = i == iterations - 1 ? Source{{12, 34}} : Source{{0, i}};
|
||||
s = Structure(source, "S" + std::to_string(i), utils::Vector{Member("m", ty.Of(s))});
|
||||
}
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(), "12:34 error: struct 'S251' has nesting depth of 256, maximum is 255");
|
||||
}
|
||||
|
||||
TEST_F(ResolverTest, MaxNestDepthOfCompositeType_ArraysOfStruct_Valid) {
|
||||
auto* s = Structure("S", utils::Vector{Member("m", ty.mat3x3<f32>())});
|
||||
auto a = ty.array(ty.Of(s), 10_u);
|
||||
size_t depth = 4; // Depth of array + struct + matrix
|
||||
size_t iterations = kMaxNestDepthOfCompositeType - depth;
|
||||
for (size_t i = 0; i < iterations; ++i) {
|
||||
a = ty.array(a, 1_u);
|
||||
}
|
||||
Alias("a", a);
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
}
|
||||
|
||||
TEST_F(ResolverTest, MaxNestDepthOfCompositeType_ArraysOfStruct_Invalid) {
|
||||
auto* s = Structure("S", utils::Vector{Member("m", ty.mat3x3<f32>())});
|
||||
auto a = ty.array(ty.Of(s), 10_u);
|
||||
size_t depth = 4; // Depth of array + struct + matrix
|
||||
size_t iterations = kMaxNestDepthOfCompositeType - depth + 1;
|
||||
for (size_t i = 0; i < iterations; ++i) {
|
||||
auto source = (i == iterations - 1) ? Source{{12, 34}} : Source{{0, i}};
|
||||
a = ty.array(source, a, 1_u);
|
||||
}
|
||||
Alias("a", a);
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(), "12:34 error: array has nesting depth of 256, maximum is 255");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace tint::resolver
|
||||
|
|
Loading…
Reference in New Issue