tint: validate max number of members in a struct
Bug: tint:1209
Change-Id: I248c1864d3b38d41eda56bc41d7f19fb5fdd1955
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/121220
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Antonio Maiorano <amaiorano@google.com>
diff --git a/src/tint/ast/disable_validation_attribute.cc b/src/tint/ast/disable_validation_attribute.cc
index 465a0ec..eff1c1f 100644
--- a/src/tint/ast/disable_validation_attribute.cc
+++ b/src/tint/ast/disable_validation_attribute.cc
@@ -45,6 +45,8 @@
return "disable_validation__ignore_invalid_pointer_argument";
case DisabledValidation::kIgnorePointerAliasing:
return "disable_validation__ignore_pointer_aliasing";
+ case DisabledValidation::kIgnoreStructMemberLimit:
+ return "disable_validation__ignore_struct_member";
}
return "<invalid>";
}
diff --git a/src/tint/ast/disable_validation_attribute.h b/src/tint/ast/disable_validation_attribute.h
index f52b107..9614bc1 100644
--- a/src/tint/ast/disable_validation_attribute.h
+++ b/src/tint/ast/disable_validation_attribute.h
@@ -45,6 +45,8 @@
/// When applied to a function declaration, the validator will not complain if multiple
/// pointer arguments alias when that function is called.
kIgnorePointerAliasing,
+ /// When applied to a struct, validation of max number of members is skipped.
+ kIgnoreStructMemberLimit,
};
/// An internal attribute used to tell the validator to ignore specific
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index 7471466..4292438 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -3573,6 +3573,20 @@
}
sem::Struct* Resolver::Structure(const ast::Struct* str) {
+ 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),
+ str->source);
+ return nullptr;
+ }
+ }
+
if (!validator_.NoDuplicateAttributes(str->attributes)) {
return nullptr;
}
diff --git a/src/tint/resolver/resolver_test.cc b/src/tint/resolver/resolver_test.cc
index c9ce479..25155ff 100644
--- a/src/tint/resolver/resolver_test.cc
+++ b/src/tint/resolver/resolver_test.cc
@@ -2394,5 +2394,45 @@
#endif // !defined(NDEBUG)
+const size_t kMaxNumStructMembers = 16383;
+
+TEST_F(ResolverTest, MaxNumStructMembers_Valid) {
+ utils::Vector<const ast::StructMember*, 0> members;
+ members.Reserve(kMaxNumStructMembers);
+ for (size_t i = 0; i < kMaxNumStructMembers; ++i) {
+ members.Push(Member("m" + std::to_string(i), ty.i32()));
+ }
+ Structure("S", std::move(members));
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverTest, MaxNumStructMembers_Invalid) {
+ utils::Vector<const ast::StructMember*, 0> members;
+ members.Reserve(kMaxNumStructMembers + 1);
+ for (size_t i = 0; i < kMaxNumStructMembers + 1; ++i) {
+ members.Push(Member("m" + std::to_string(i), ty.i32()));
+ }
+ Structure(Source{{12, 34}}, "S", std::move(members));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: struct 'S' has 16384 members, maximum is 16383");
+}
+
+TEST_F(ResolverTest, MaxNumStructMembers_WithIgnoreStructMemberLimit_Valid) {
+ utils::Vector<const ast::StructMember*, 0> members;
+ members.Reserve(kMaxNumStructMembers);
+ for (size_t i = 0; i < kMaxNumStructMembers; ++i) {
+ members.Push(Member("m" + std::to_string(i), ty.i32()));
+ }
+
+ // Add 10 more members, but we set the limit to be ignored on the struct
+ for (size_t i = 0; i < 10; ++i) {
+ members.Push(Member("ignored" + std::to_string(i), ty.i32()));
+ }
+
+ Structure("S", std::move(members),
+ utils::Vector{Disable(ast::DisabledValidation::kIgnoreStructMemberLimit)});
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+
} // namespace
} // namespace tint::resolver
diff --git a/src/tint/transform/pad_structs.cc b/src/tint/transform/pad_structs.cc
index 79bc631..8516aa3 100644
--- a/src/tint/transform/pad_structs.cc
+++ b/src/tint/transform/pad_structs.cc
@@ -18,6 +18,7 @@
#include <unordered_map>
#include <utility>
+#include "src/tint/ast/disable_validation_attribute.h"
#include "src/tint/ast/parameter.h"
#include "src/tint/program_builder.h"
#include "src/tint/sem/call.h"
@@ -36,7 +37,10 @@
utils::Hashset<const ast::StructMember*, 8>* padding_members,
ProgramBuilder* b,
uint32_t bytes) {
- for (uint32_t i = 0; i < bytes / 4u; ++i) {
+ const size_t count = bytes / 4u;
+ padding_members->Reserve(count);
+ new_members->Reserve(count);
+ for (uint32_t i = 0; i < count; ++i) {
auto name = b->Symbols().New("pad");
auto* member = b->Member(name, b->ty.u32());
padding_members->Add(member);
@@ -99,8 +103,15 @@
if (offset < struct_size && !has_runtime_sized_array) {
CreatePadding(&new_members, &padding_members, ctx.dst, struct_size - offset);
}
- auto* new_struct =
- b.create<ast::Struct>(ctx.Clone(ast_str->name), std::move(new_members), utils::Empty);
+
+ utils::Vector<const ast::Attribute*, 1> struct_attribs;
+ if (!padding_members.IsEmpty()) {
+ struct_attribs =
+ utils::Vector{b.Disable(ast::DisabledValidation::kIgnoreStructMemberLimit)};
+ }
+
+ auto* new_struct = b.create<ast::Struct>(ctx.Clone(ast_str->name), std::move(new_members),
+ std::move(struct_attribs));
replaced_structs[ast_str] = new_struct;
return new_struct;
});
diff --git a/src/tint/transform/pad_structs_test.cc b/src/tint/transform/pad_structs_test.cc
index 6b1a70f..0826467 100644
--- a/src/tint/transform/pad_structs_test.cc
+++ b/src/tint/transform/pad_structs_test.cc
@@ -47,6 +47,7 @@
}
)";
auto* expect = R"(
+@internal(disable_validation__ignore_struct_member)
struct S {
x : i32,
pad : u32,
@@ -81,6 +82,7 @@
}
)";
auto* expect = R"(
+@internal(disable_validation__ignore_struct_member)
struct S {
x : i32,
pad : u32,
@@ -118,6 +120,7 @@
}
)";
auto* expect = R"(
+@internal(disable_validation__ignore_struct_member)
struct S {
x : i32,
pad : u32,
@@ -158,6 +161,7 @@
}
)";
auto* expect = R"(
+@internal(disable_validation__ignore_struct_member)
struct S {
x : i32,
pad : u32,
@@ -198,6 +202,7 @@
}
)";
auto* expect = R"(
+@internal(disable_validation__ignore_struct_member)
struct S {
x : i32,
pad : u32,
@@ -273,6 +278,7 @@
}
)";
auto* expect = R"(
+@internal(disable_validation__ignore_struct_member)
struct S {
a : i32,
pad : u32,
@@ -320,6 +326,7 @@
}
)";
auto* expect = R"(
+@internal(disable_validation__ignore_struct_member)
struct S {
a : i32,
pad : u32,
@@ -367,6 +374,7 @@
}
)";
auto* expect = R"(
+@internal(disable_validation__ignore_struct_member)
struct S {
a : i32,
pad : u32,
@@ -501,6 +509,7 @@
b : i32,
}
+@internal(disable_validation__ignore_struct_member)
struct S {
a : vec4<f32>,
b : array<T, 1u>,
@@ -537,6 +546,7 @@
}
)";
auto* expect = R"(
+@internal(disable_validation__ignore_struct_member)
struct S {
a : f32,
pad : u32,
@@ -573,6 +583,7 @@
}
)";
auto* expect = R"(
+@internal(disable_validation__ignore_struct_member)
struct S {
a : f32,
pad : u32,