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:
parent
8cd5c611fc
commit
8ef7311413
|
@ -45,6 +45,8 @@ std::string DisableValidationAttribute::InternalName() const {
|
|||
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>";
|
||||
}
|
||||
|
|
|
@ -45,6 +45,8 @@ enum class DisabledValidation {
|
|||
/// 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
|
||||
|
|
|
@ -3573,6 +3573,20 @@ type::Type* Resolver::Alias(const ast::Alias* alias) {
|
|||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -2394,5 +2394,45 @@ TEST_F(ResolverTest, ScopeDepth_IfElseChain) {
|
|||
|
||||
#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
|
||||
|
|
|
@ -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 @@ void CreatePadding(utils::Vector<const ast::StructMember*, 8>* new_members,
|
|||
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 @@ Transform::ApplyResult PadStructs::Apply(const Program* src, const DataMap&, Dat
|
|||
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;
|
||||
});
|
||||
|
|
|
@ -47,6 +47,7 @@ fn main() {
|
|||
}
|
||||
)";
|
||||
auto* expect = R"(
|
||||
@internal(disable_validation__ignore_struct_member)
|
||||
struct S {
|
||||
x : i32,
|
||||
pad : u32,
|
||||
|
@ -81,6 +82,7 @@ fn main() {
|
|||
}
|
||||
)";
|
||||
auto* expect = R"(
|
||||
@internal(disable_validation__ignore_struct_member)
|
||||
struct S {
|
||||
x : i32,
|
||||
pad : u32,
|
||||
|
@ -118,6 +120,7 @@ fn main() {
|
|||
}
|
||||
)";
|
||||
auto* expect = R"(
|
||||
@internal(disable_validation__ignore_struct_member)
|
||||
struct S {
|
||||
x : i32,
|
||||
pad : u32,
|
||||
|
@ -158,6 +161,7 @@ fn main() {
|
|||
}
|
||||
)";
|
||||
auto* expect = R"(
|
||||
@internal(disable_validation__ignore_struct_member)
|
||||
struct S {
|
||||
x : i32,
|
||||
pad : u32,
|
||||
|
@ -198,6 +202,7 @@ fn main() {
|
|||
}
|
||||
)";
|
||||
auto* expect = R"(
|
||||
@internal(disable_validation__ignore_struct_member)
|
||||
struct S {
|
||||
x : i32,
|
||||
pad : u32,
|
||||
|
@ -273,6 +278,7 @@ fn main() {
|
|||
}
|
||||
)";
|
||||
auto* expect = R"(
|
||||
@internal(disable_validation__ignore_struct_member)
|
||||
struct S {
|
||||
a : i32,
|
||||
pad : u32,
|
||||
|
@ -320,6 +326,7 @@ fn main() {
|
|||
}
|
||||
)";
|
||||
auto* expect = R"(
|
||||
@internal(disable_validation__ignore_struct_member)
|
||||
struct S {
|
||||
a : i32,
|
||||
pad : u32,
|
||||
|
@ -367,6 +374,7 @@ fn main() {
|
|||
}
|
||||
)";
|
||||
auto* expect = R"(
|
||||
@internal(disable_validation__ignore_struct_member)
|
||||
struct S {
|
||||
a : i32,
|
||||
pad : u32,
|
||||
|
@ -501,6 +509,7 @@ struct T {
|
|||
b : i32,
|
||||
}
|
||||
|
||||
@internal(disable_validation__ignore_struct_member)
|
||||
struct S {
|
||||
a : vec4<f32>,
|
||||
b : array<T, 1u>,
|
||||
|
@ -537,6 +546,7 @@ fn main() {
|
|||
}
|
||||
)";
|
||||
auto* expect = R"(
|
||||
@internal(disable_validation__ignore_struct_member)
|
||||
struct S {
|
||||
a : f32,
|
||||
pad : u32,
|
||||
|
@ -573,6 +583,7 @@ fn main() {
|
|||
}
|
||||
)";
|
||||
auto* expect = R"(
|
||||
@internal(disable_validation__ignore_struct_member)
|
||||
struct S {
|
||||
a : f32,
|
||||
pad : u32,
|
||||
|
|
Loading…
Reference in New Issue