Fix stack-overflow in `lhs_expression`.
Currently when parsing `*` and `&` we recursively call into ourselves to process the tokens. This can cause stack issues if there are an excessive number of `*`s and `&`s. This Cl changes `lhs_expression` to generate a list of UnaryOps to be applied and does not recursively call `lhs_expression`. Bug: chromium:1394972 Change-Id: I40caee05c9b7f71abb776d375cbf995c6a1fd36f Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/112580 Commit-Queue: Dan Sinclair <dsinclair@chromium.org> Reviewed-by: Ben Clayton <bclayton@google.com> Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
parent
6992f51ebd
commit
2939c4531e
|
@ -43,6 +43,7 @@
|
||||||
#include "src/tint/sem/external_texture.h"
|
#include "src/tint/sem/external_texture.h"
|
||||||
#include "src/tint/sem/multisampled_texture.h"
|
#include "src/tint/sem/multisampled_texture.h"
|
||||||
#include "src/tint/sem/sampled_texture.h"
|
#include "src/tint/sem/sampled_texture.h"
|
||||||
|
#include "src/tint/utils/reverse.h"
|
||||||
#include "src/tint/utils/string.h"
|
#include "src/tint/utils/string.h"
|
||||||
|
|
||||||
namespace tint::reader::wgsl {
|
namespace tint::reader::wgsl {
|
||||||
|
@ -3239,7 +3240,35 @@ Maybe<const ast::Expression*> ParserImpl::lhs_expression() {
|
||||||
return component_or_swizzle_specifier(core_expr.value);
|
return component_or_swizzle_specifier(core_expr.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto check_lhs = [&](ast::UnaryOp op) -> Maybe<const ast::Expression*> {
|
// Gather up all the `*`, `&` and `&&` tokens into a list and create all of the unary ops at
|
||||||
|
// once instead of recursing. This handles the case where the fuzzer decides >8k `*`s would be
|
||||||
|
// fun.
|
||||||
|
struct LHSData {
|
||||||
|
Source source;
|
||||||
|
ast::UnaryOp op;
|
||||||
|
};
|
||||||
|
utils::Vector<LHSData, 4> ops;
|
||||||
|
while (true) {
|
||||||
|
auto& t = peek();
|
||||||
|
if (!t.Is(Token::Type::kAndAnd) && !t.Is(Token::Type::kAnd) && !t.Is(Token::Type::kStar)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
next(); // consume the peek
|
||||||
|
|
||||||
|
if (t.Is(Token::Type::kAndAnd)) {
|
||||||
|
// The first `&` is consumed as part of the `&&`, so we only push one of the two `&`s.
|
||||||
|
split_token(Token::Type::kAnd, Token::Type::kAnd);
|
||||||
|
ops.Push({t.source(), ast::UnaryOp::kAddressOf});
|
||||||
|
} else if (t.Is(Token::Type::kAnd)) {
|
||||||
|
ops.Push({t.source(), ast::UnaryOp::kAddressOf});
|
||||||
|
} else if (t.Is(Token::Type::kStar)) {
|
||||||
|
ops.Push({t.source(), ast::UnaryOp::kIndirection});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ops.IsEmpty()) {
|
||||||
|
return Failure::kNoMatch;
|
||||||
|
}
|
||||||
|
|
||||||
auto& t = peek();
|
auto& t = peek();
|
||||||
auto expr = lhs_expression();
|
auto expr = lhs_expression();
|
||||||
if (expr.errored) {
|
if (expr.errored) {
|
||||||
|
@ -3248,25 +3277,13 @@ Maybe<const ast::Expression*> ParserImpl::lhs_expression() {
|
||||||
if (!expr.matched) {
|
if (!expr.matched) {
|
||||||
return add_error(t, "missing expression");
|
return add_error(t, "missing expression");
|
||||||
}
|
}
|
||||||
return create<ast::UnaryOpExpression>(t.source(), op, expr.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
// If an `&&` is encountered, split it into two `&`'s
|
const ast::Expression* ret = expr.value;
|
||||||
if (match(Token::Type::kAndAnd)) {
|
// Consume the ops in reverse order so we have the correct AST ordering.
|
||||||
// The first `&` is consumed as part of the `&&`, so this needs to run the check itself.
|
for (auto& info : utils::Reverse(ops)) {
|
||||||
split_token(Token::Type::kAnd, Token::Type::kAnd);
|
ret = create<ast::UnaryOpExpression>(info.source, info.op, ret);
|
||||||
return check_lhs(ast::UnaryOp::kAddressOf);
|
|
||||||
}
|
}
|
||||||
|
return ret;
|
||||||
if (match(Token::Type::kAnd)) {
|
|
||||||
return check_lhs(ast::UnaryOp::kAddressOf);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (match(Token::Type::kStar)) {
|
|
||||||
return check_lhs(ast::UnaryOp::kIndirection);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Failure::kNoMatch;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// variable_updating_statement
|
// variable_updating_statement
|
||||||
|
|
|
@ -75,17 +75,20 @@ TEST_F(ParserImplTest, LHSExpression_InvalidCoreLHSExpr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, LHSExpression_Multiple) {
|
TEST_F(ParserImplTest, LHSExpression_Multiple) {
|
||||||
auto p = parser("*&**&&*a");
|
auto p = parser("*&********&&&&&&*a");
|
||||||
auto e = p->lhs_expression();
|
auto e = p->lhs_expression();
|
||||||
ASSERT_FALSE(p->has_error()) << p->error();
|
ASSERT_FALSE(p->has_error()) << p->error();
|
||||||
ASSERT_FALSE(e.errored);
|
ASSERT_FALSE(e.errored);
|
||||||
EXPECT_TRUE(e.matched);
|
EXPECT_TRUE(e.matched);
|
||||||
ASSERT_NE(e.value, nullptr);
|
ASSERT_NE(e.value, nullptr);
|
||||||
|
|
||||||
std::vector<ast::UnaryOp> results = {ast::UnaryOp::kIndirection, ast::UnaryOp::kAddressOf,
|
std::vector<ast::UnaryOp> results = {
|
||||||
ast::UnaryOp::kIndirection, ast::UnaryOp::kIndirection,
|
ast::UnaryOp::kIndirection, ast::UnaryOp::kAddressOf, ast::UnaryOp::kIndirection,
|
||||||
ast::UnaryOp::kAddressOf, ast::UnaryOp::kAddressOf,
|
ast::UnaryOp::kIndirection, ast::UnaryOp::kIndirection, ast::UnaryOp::kIndirection,
|
||||||
ast::UnaryOp::kIndirection};
|
ast::UnaryOp::kIndirection, ast::UnaryOp::kIndirection, ast::UnaryOp::kIndirection,
|
||||||
|
ast::UnaryOp::kIndirection, ast::UnaryOp::kAddressOf, ast::UnaryOp::kAddressOf,
|
||||||
|
ast::UnaryOp::kAddressOf, ast::UnaryOp::kAddressOf, ast::UnaryOp::kAddressOf,
|
||||||
|
ast::UnaryOp::kAddressOf, ast::UnaryOp::kIndirection};
|
||||||
|
|
||||||
auto* expr = e.value;
|
auto* expr = e.value;
|
||||||
for (auto op : results) {
|
for (auto op : results) {
|
||||||
|
|
Loading…
Reference in New Issue