wgsl/parser: Avoid stack overflows
Most recursive control flow passes through Sync(). Error out if the Sync() function is recursively called too many times. This replaces the more specific kMaxConstExprDepth, which also passes through Sync(). Fixed: chromium:1178436 Change-Id: I64a05f9f6a4fe6d2b53a3ca75642b30e98c7a35f Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/41724 Commit-Queue: Ben Clayton <bclayton@google.com> Reviewed-by: dan sinclair <dsinclair@chromium.org>
This commit is contained in:
parent
52296de528
commit
34ae7d2ce7
|
@ -83,10 +83,10 @@ using Expect = ParserImpl::Expect<T>;
|
||||||
template <typename T>
|
template <typename T>
|
||||||
using Maybe = ParserImpl::Maybe<T>;
|
using Maybe = ParserImpl::Maybe<T>;
|
||||||
|
|
||||||
/// Controls the maximum number of times we'll call into the const_expr function
|
/// Controls the maximum number of times we'll call into the sync() function
|
||||||
/// from itself. This is to guard against stack overflow when there is an
|
/// from itself. This is to guard against stack overflow when there is an
|
||||||
/// excessive number of type constructors inside the const_expr.
|
/// excessive number of blocks.
|
||||||
constexpr uint32_t kMaxConstExprDepth = 128;
|
constexpr uint32_t kMaxSyncDepth = 128;
|
||||||
|
|
||||||
/// The maximum number of tokens to look ahead to try and sync the
|
/// The maximum number of tokens to look ahead to try and sync the
|
||||||
/// parser on error.
|
/// parser on error.
|
||||||
|
@ -2709,38 +2709,29 @@ Maybe<ast::Literal*> ParserImpl::const_literal() {
|
||||||
// : type_decl PAREN_LEFT (const_expr COMMA)? const_expr PAREN_RIGHT
|
// : type_decl PAREN_LEFT (const_expr COMMA)? const_expr PAREN_RIGHT
|
||||||
// | const_literal
|
// | const_literal
|
||||||
Expect<ast::ConstructorExpression*> ParserImpl::expect_const_expr() {
|
Expect<ast::ConstructorExpression*> ParserImpl::expect_const_expr() {
|
||||||
return expect_const_expr_internal(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
Expect<ast::ConstructorExpression*> ParserImpl::expect_const_expr_internal(
|
|
||||||
uint32_t depth) {
|
|
||||||
auto t = peek();
|
auto t = peek();
|
||||||
|
|
||||||
if (depth > kMaxConstExprDepth) {
|
|
||||||
return add_error(t, "max const_expr depth reached");
|
|
||||||
}
|
|
||||||
|
|
||||||
auto source = t.source();
|
auto source = t.source();
|
||||||
|
|
||||||
auto type = type_decl();
|
auto type = type_decl();
|
||||||
if (type.errored)
|
if (type.errored)
|
||||||
return Failure::kErrored;
|
return Failure::kErrored;
|
||||||
if (type.matched) {
|
if (type.matched) {
|
||||||
auto params = expect_paren_block(
|
auto params = expect_paren_block("type constructor",
|
||||||
"type constructor", [&]() -> Expect<ast::ExpressionList> {
|
[&]() -> Expect<ast::ExpressionList> {
|
||||||
ast::ExpressionList list;
|
ast::ExpressionList list;
|
||||||
auto param = expect_const_expr_internal(depth + 1);
|
auto param = expect_const_expr();
|
||||||
if (param.errored)
|
if (param.errored)
|
||||||
return Failure::kErrored;
|
return Failure::kErrored;
|
||||||
list.emplace_back(param.value);
|
list.emplace_back(param.value);
|
||||||
while (match(Token::Type::kComma)) {
|
while (match(Token::Type::kComma)) {
|
||||||
param = expect_const_expr_internal(depth + 1);
|
param = expect_const_expr();
|
||||||
if (param.errored)
|
if (param.errored)
|
||||||
return Failure::kErrored;
|
return Failure::kErrored;
|
||||||
list.emplace_back(param.value);
|
list.emplace_back(param.value);
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (params.errored)
|
if (params.errored)
|
||||||
return Failure::kErrored;
|
return Failure::kErrored;
|
||||||
|
@ -3170,9 +3161,15 @@ T ParserImpl::expect_lt_gt_block(const std::string& use, F&& body) {
|
||||||
|
|
||||||
template <typename F, typename T>
|
template <typename F, typename T>
|
||||||
T ParserImpl::sync(Token::Type tok, F&& body) {
|
T ParserImpl::sync(Token::Type tok, F&& body) {
|
||||||
|
if (sync_depth_ >= kMaxSyncDepth) {
|
||||||
|
return add_error(peek(), "maximum parser recursive depth reached");
|
||||||
|
}
|
||||||
|
|
||||||
sync_tokens_.push_back(tok);
|
sync_tokens_.push_back(tok);
|
||||||
|
|
||||||
|
++sync_depth_;
|
||||||
auto result = body();
|
auto result = body();
|
||||||
|
--sync_depth_;
|
||||||
|
|
||||||
assert(sync_tokens_.back() == tok);
|
assert(sync_tokens_.back() == tok);
|
||||||
sync_tokens_.pop_back();
|
sync_tokens_.pop_back();
|
||||||
|
|
|
@ -812,9 +812,6 @@ class ParserImpl {
|
||||||
Expect<type::Type*> expect_type_decl_array(ast::ArrayDecorationList decos);
|
Expect<type::Type*> expect_type_decl_array(ast::ArrayDecorationList decos);
|
||||||
Expect<type::Type*> expect_type_decl_matrix(Token t);
|
Expect<type::Type*> expect_type_decl_matrix(Token t);
|
||||||
|
|
||||||
Expect<ast::ConstructorExpression*> expect_const_expr_internal(
|
|
||||||
uint32_t depth);
|
|
||||||
|
|
||||||
Expect<type::Type*> expect_type(const std::string& use);
|
Expect<type::Type*> expect_type(const std::string& use);
|
||||||
|
|
||||||
Maybe<ast::Statement*> non_block_statement();
|
Maybe<ast::Statement*> non_block_statement();
|
||||||
|
@ -834,6 +831,7 @@ class ParserImpl {
|
||||||
std::unique_ptr<Lexer> lexer_;
|
std::unique_ptr<Lexer> lexer_;
|
||||||
std::deque<Token> token_queue_;
|
std::deque<Token> token_queue_;
|
||||||
bool synchronized_ = true;
|
bool synchronized_ = true;
|
||||||
|
uint32_t sync_depth_ = 0;
|
||||||
std::vector<Token::Type> sync_tokens_;
|
std::vector<Token::Type> sync_tokens_;
|
||||||
int silence_errors_ = 0;
|
int silence_errors_ = 0;
|
||||||
std::unordered_map<std::string, type::Type*> registered_constructs_;
|
std::unordered_map<std::string, type::Type*> registered_constructs_;
|
||||||
|
|
|
@ -144,7 +144,7 @@ TEST_F(ParserImplTest, ConstExpr_Recursion) {
|
||||||
ASSERT_TRUE(p->has_error());
|
ASSERT_TRUE(p->has_error());
|
||||||
ASSERT_TRUE(e.errored);
|
ASSERT_TRUE(e.errored);
|
||||||
ASSERT_EQ(e.value, nullptr);
|
ASSERT_EQ(e.value, nullptr);
|
||||||
EXPECT_EQ(p->error(), "1:517: max const_expr depth reached");
|
EXPECT_EQ(p->error(), "1:517: maximum parser recursive depth reached");
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -526,17 +526,17 @@ TEST_F(ParserImplErrorTest, GlobalDeclConstBadConstLiteral) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplErrorTest, GlobalDeclConstExprMaxDepth) {
|
TEST_F(ParserImplErrorTest, GlobalDeclConstExprMaxDepth) {
|
||||||
uint32_t kMaxConstExprDepth = 128;
|
uint32_t kMaxDepth = 128;
|
||||||
|
|
||||||
std::stringstream src;
|
std::stringstream src;
|
||||||
std::stringstream mkr;
|
std::stringstream mkr;
|
||||||
src << "const i : i32 = ";
|
src << "const i : i32 = ";
|
||||||
mkr << " ";
|
mkr << " ";
|
||||||
for (size_t i = 0; i < kMaxConstExprDepth + 8; i++) {
|
for (size_t i = 0; i < kMaxDepth + 8; i++) {
|
||||||
src << "f32(";
|
src << "f32(";
|
||||||
if (i < kMaxConstExprDepth + 1) {
|
if (i < kMaxDepth) {
|
||||||
mkr << " ";
|
mkr << " ";
|
||||||
} else if (i == kMaxConstExprDepth + 1) {
|
} else if (i == kMaxDepth) {
|
||||||
mkr << "^^^";
|
mkr << "^^^";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -546,7 +546,7 @@ TEST_F(ParserImplErrorTest, GlobalDeclConstExprMaxDepth) {
|
||||||
}
|
}
|
||||||
src << ";";
|
src << ";";
|
||||||
std::stringstream err;
|
std::stringstream err;
|
||||||
err << "test.wgsl:1:533 error: max const_expr depth reached\n"
|
err << "test.wgsl:1:529 error: maximum parser recursive depth reached\n"
|
||||||
<< src.str() << "\n"
|
<< src.str() << "\n"
|
||||||
<< mkr.str() << "\n";
|
<< mkr.str() << "\n";
|
||||||
EXPECT(src.str().c_str(), err.str().c_str());
|
EXPECT(src.str().c_str(), err.str().c_str());
|
||||||
|
|
Loading…
Reference in New Issue