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/multisampled_texture.h"
|
||||
#include "src/tint/sem/sampled_texture.h"
|
||||
#include "src/tint/utils/reverse.h"
|
||||
#include "src/tint/utils/string.h"
|
||||
|
||||
namespace tint::reader::wgsl {
|
||||
|
@ -3239,7 +3240,35 @@ Maybe<const ast::Expression*> ParserImpl::lhs_expression() {
|
|||
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 expr = lhs_expression();
|
||||
if (expr.errored) {
|
||||
|
@ -3248,25 +3277,13 @@ Maybe<const ast::Expression*> ParserImpl::lhs_expression() {
|
|||
if (!expr.matched) {
|
||||
return add_error(t, "missing expression");
|
||||
}
|
||||
return create<ast::UnaryOpExpression>(t.source(), op, expr.value);
|
||||
};
|
||||
|
||||
// If an `&&` is encountered, split it into two `&`'s
|
||||
if (match(Token::Type::kAndAnd)) {
|
||||
// The first `&` is consumed as part of the `&&`, so this needs to run the check itself.
|
||||
split_token(Token::Type::kAnd, Token::Type::kAnd);
|
||||
return check_lhs(ast::UnaryOp::kAddressOf);
|
||||
const ast::Expression* ret = expr.value;
|
||||
// Consume the ops in reverse order so we have the correct AST ordering.
|
||||
for (auto& info : utils::Reverse(ops)) {
|
||||
ret = create<ast::UnaryOpExpression>(info.source, info.op, 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;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// variable_updating_statement
|
||||
|
|
|
@ -75,17 +75,20 @@ TEST_F(ParserImplTest, LHSExpression_InvalidCoreLHSExpr) {
|
|||
}
|
||||
|
||||
TEST_F(ParserImplTest, LHSExpression_Multiple) {
|
||||
auto p = parser("*&**&&*a");
|
||||
auto p = parser("*&********&&&&&&*a");
|
||||
auto e = p->lhs_expression();
|
||||
ASSERT_FALSE(p->has_error()) << p->error();
|
||||
ASSERT_FALSE(e.errored);
|
||||
EXPECT_TRUE(e.matched);
|
||||
ASSERT_NE(e.value, nullptr);
|
||||
|
||||
std::vector<ast::UnaryOp> results = {ast::UnaryOp::kIndirection, ast::UnaryOp::kAddressOf,
|
||||
ast::UnaryOp::kIndirection, ast::UnaryOp::kIndirection,
|
||||
ast::UnaryOp::kAddressOf, ast::UnaryOp::kAddressOf,
|
||||
ast::UnaryOp::kIndirection};
|
||||
std::vector<ast::UnaryOp> results = {
|
||||
ast::UnaryOp::kIndirection, 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;
|
||||
for (auto op : results) {
|
||||
|
|
Loading…
Reference in New Issue