reader/wgsl: Prevent stack overflow in unary_expression()

Fixed: chromium:1209237
Change-Id: I14b4777aaee3ee7a34baf8a218db28f54b81af84
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/55253
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: James Price <jrprice@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
This commit is contained in:
Ben Clayton 2021-06-21 19:36:26 +00:00 committed by Tint LUCI CQ
parent 055901b3cd
commit e7257eb2fb
3 changed files with 33 additions and 8 deletions

View File

@ -52,10 +52,10 @@ using Expect = ParserImpl::Expect<T>;
template <typename T>
using Maybe = ParserImpl::Maybe<T>;
/// 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
/// excessive number of blocks.
constexpr uint32_t kMaxSyncDepth = 128;
/// Controls the maximum number of times we'll call into the sync() and
/// unary_expression() functions from themselves. This is to guard against stack
/// overflow when there is an excessive number of blocks.
constexpr uint32_t kMaxParseDepth = 128;
/// The maximum number of tokens to look ahead to try and sync the
/// parser on error.
@ -2327,7 +2327,18 @@ Maybe<ast::Expression*> ParserImpl::unary_expression() {
return singular_expression();
}
if (parse_depth_ >= kMaxParseDepth) {
// We've hit a maximum parser recursive depth.
// We can't call into unary_expression() as we might stack overflow.
// Instead, report an error
add_error(peek(), "maximum parser recursive depth reached");
return Failure::kErrored;
}
++parse_depth_;
auto expr = unary_expression();
--parse_depth_;
if (expr.errored) {
return Failure::kErrored;
}
@ -3268,7 +3279,7 @@ T ParserImpl::expect_lt_gt_block(const std::string& use, F&& body) {
template <typename F, typename T>
T ParserImpl::sync(Token::Type tok, F&& body) {
if (sync_depth_ >= kMaxSyncDepth) {
if (parse_depth_ >= kMaxParseDepth) {
// We've hit a maximum parser recursive depth.
// We can't call into body() as we might stack overflow.
// Instead, report an error...
@ -3282,9 +3293,9 @@ T ParserImpl::sync(Token::Type tok, F&& body) {
sync_tokens_.push_back(tok);
++sync_depth_;
++parse_depth_;
auto result = body();
--sync_depth_;
--parse_depth_;
if (sync_tokens_.back() != tok) {
TINT_ICE(builder_.Diagnostics()) << "sync_tokens is out of sync";

View File

@ -871,7 +871,7 @@ class ParserImpl {
std::deque<Token> token_queue_;
Token last_token_;
bool synchronized_ = true;
uint32_t sync_depth_ = 0;
uint32_t parse_depth_ = 0;
std::vector<Token::Type> sync_tokens_;
int silence_errors_ = 0;
std::unordered_map<std::string, const ast::TypeDecl*> registered_types_;

View File

@ -154,6 +154,20 @@ TEST_F(ParserImplTest, ConstExpr_Recursion) {
EXPECT_EQ(p->error(), "1:517: maximum parser recursive depth reached");
}
TEST_F(ParserImplTest, UnaryOp_Recursion) {
std::stringstream out;
for (size_t i = 0; i < 200; i++) {
out << "!";
}
out << "1.0";
auto p = parser(out.str());
auto e = p->unary_expression();
ASSERT_TRUE(p->has_error());
ASSERT_TRUE(e.errored);
ASSERT_EQ(e.value, nullptr);
EXPECT_EQ(p->error(), "1:130: maximum parser recursive depth reached");
}
} // namespace
} // namespace wgsl
} // namespace reader