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:
Antonio Maiorano 2023-02-27 19:04:23 +00:00
parent 0b3400c56e
commit 6b304e9ffd
4 changed files with 257 additions and 8 deletions

View File

@ -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
}
}

View File

@ -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;
}

View File

@ -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

View File

@ -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