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>
This commit is contained in:
Antonio Maiorano 2023-02-24 21:37:44 +00:00 committed by Dawn LUCI CQ
parent 8cd5c611fc
commit 8ef7311413
6 changed files with 83 additions and 3 deletions

View File

@ -45,6 +45,8 @@ std::string DisableValidationAttribute::InternalName() const {
return "disable_validation__ignore_invalid_pointer_argument"; return "disable_validation__ignore_invalid_pointer_argument";
case DisabledValidation::kIgnorePointerAliasing: case DisabledValidation::kIgnorePointerAliasing:
return "disable_validation__ignore_pointer_aliasing"; return "disable_validation__ignore_pointer_aliasing";
case DisabledValidation::kIgnoreStructMemberLimit:
return "disable_validation__ignore_struct_member";
} }
return "<invalid>"; return "<invalid>";
} }

View File

@ -45,6 +45,8 @@ enum class DisabledValidation {
/// When applied to a function declaration, the validator will not complain if multiple /// When applied to a function declaration, the validator will not complain if multiple
/// pointer arguments alias when that function is called. /// pointer arguments alias when that function is called.
kIgnorePointerAliasing, 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 /// An internal attribute used to tell the validator to ignore specific

View File

@ -3573,6 +3573,20 @@ type::Type* Resolver::Alias(const ast::Alias* alias) {
} }
sem::Struct* Resolver::Structure(const ast::Struct* str) { 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)) { if (!validator_.NoDuplicateAttributes(str->attributes)) {
return nullptr; return nullptr;
} }

View File

@ -2394,5 +2394,45 @@ TEST_F(ResolverTest, ScopeDepth_IfElseChain) {
#endif // !defined(NDEBUG) #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
} // namespace tint::resolver } // namespace tint::resolver

View File

@ -18,6 +18,7 @@
#include <unordered_map> #include <unordered_map>
#include <utility> #include <utility>
#include "src/tint/ast/disable_validation_attribute.h"
#include "src/tint/ast/parameter.h" #include "src/tint/ast/parameter.h"
#include "src/tint/program_builder.h" #include "src/tint/program_builder.h"
#include "src/tint/sem/call.h" #include "src/tint/sem/call.h"
@ -36,7 +37,10 @@ void CreatePadding(utils::Vector<const ast::StructMember*, 8>* new_members,
utils::Hashset<const ast::StructMember*, 8>* padding_members, utils::Hashset<const ast::StructMember*, 8>* padding_members,
ProgramBuilder* b, ProgramBuilder* b,
uint32_t bytes) { 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 name = b->Symbols().New("pad");
auto* member = b->Member(name, b->ty.u32()); auto* member = b->Member(name, b->ty.u32());
padding_members->Add(member); padding_members->Add(member);
@ -99,8 +103,15 @@ Transform::ApplyResult PadStructs::Apply(const Program* src, const DataMap&, Dat
if (offset < struct_size && !has_runtime_sized_array) { if (offset < struct_size && !has_runtime_sized_array) {
CreatePadding(&new_members, &padding_members, ctx.dst, struct_size - offset); 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; replaced_structs[ast_str] = new_struct;
return new_struct; return new_struct;
}); });

View File

@ -47,6 +47,7 @@ fn main() {
} }
)"; )";
auto* expect = R"( auto* expect = R"(
@internal(disable_validation__ignore_struct_member)
struct S { struct S {
x : i32, x : i32,
pad : u32, pad : u32,
@ -81,6 +82,7 @@ fn main() {
} }
)"; )";
auto* expect = R"( auto* expect = R"(
@internal(disable_validation__ignore_struct_member)
struct S { struct S {
x : i32, x : i32,
pad : u32, pad : u32,
@ -118,6 +120,7 @@ fn main() {
} }
)"; )";
auto* expect = R"( auto* expect = R"(
@internal(disable_validation__ignore_struct_member)
struct S { struct S {
x : i32, x : i32,
pad : u32, pad : u32,
@ -158,6 +161,7 @@ fn main() {
} }
)"; )";
auto* expect = R"( auto* expect = R"(
@internal(disable_validation__ignore_struct_member)
struct S { struct S {
x : i32, x : i32,
pad : u32, pad : u32,
@ -198,6 +202,7 @@ fn main() {
} }
)"; )";
auto* expect = R"( auto* expect = R"(
@internal(disable_validation__ignore_struct_member)
struct S { struct S {
x : i32, x : i32,
pad : u32, pad : u32,
@ -273,6 +278,7 @@ fn main() {
} }
)"; )";
auto* expect = R"( auto* expect = R"(
@internal(disable_validation__ignore_struct_member)
struct S { struct S {
a : i32, a : i32,
pad : u32, pad : u32,
@ -320,6 +326,7 @@ fn main() {
} }
)"; )";
auto* expect = R"( auto* expect = R"(
@internal(disable_validation__ignore_struct_member)
struct S { struct S {
a : i32, a : i32,
pad : u32, pad : u32,
@ -367,6 +374,7 @@ fn main() {
} }
)"; )";
auto* expect = R"( auto* expect = R"(
@internal(disable_validation__ignore_struct_member)
struct S { struct S {
a : i32, a : i32,
pad : u32, pad : u32,
@ -501,6 +509,7 @@ struct T {
b : i32, b : i32,
} }
@internal(disable_validation__ignore_struct_member)
struct S { struct S {
a : vec4<f32>, a : vec4<f32>,
b : array<T, 1u>, b : array<T, 1u>,
@ -537,6 +546,7 @@ fn main() {
} }
)"; )";
auto* expect = R"( auto* expect = R"(
@internal(disable_validation__ignore_struct_member)
struct S { struct S {
a : f32, a : f32,
pad : u32, pad : u32,
@ -573,6 +583,7 @@ fn main() {
} }
)"; )";
auto* expect = R"( auto* expect = R"(
@internal(disable_validation__ignore_struct_member)
struct S { struct S {
a : f32, a : f32,
pad : u32, pad : u32,