diff --git a/promql/parse.go b/promql/parse.go index a51e0c550..c1bc1fd7d 100644 --- a/promql/parse.go +++ b/promql/parse.go @@ -321,6 +321,8 @@ func (p *parser) expectOneOf(exp1, exp2 itemType, context string) item { return token } +var errUnexpected = fmt.Errorf("unexpected error") + // recover is the handler that turns panics into returns from the top level of Parse. func (p *parser) recover(errp *error) { e := recover() @@ -331,7 +333,7 @@ func (p *parser) recover(errp *error) { buf = buf[:runtime.Stack(buf, false)] log.Errorf("parser panic: %v\n%s", e, buf) - *errp = fmt.Errorf("unexpected error") + *errp = errUnexpected } else { *errp = e.(error) } @@ -464,9 +466,6 @@ func (p *parser) recordStmt() *RecordStmt { func (p *parser) expr() Expr { // Parse the starting expression. expr := p.unaryExpr() - if expr == nil { - p.errorf("no valid expression found") - } // Loop through the operations and construct a binary operation tree based // on the operators' precedence. @@ -514,9 +513,6 @@ func (p *parser) expr() Expr { // Parse the next operand. rhs := p.unaryExpr() - if rhs == nil { - p.errorf("missing right-hand side in binary expression") - } // Assign the new root based on the precendence of the LHS and RHS operators. if lhs, ok := expr.(*BinaryExpr); ok && lhs.Op.precedence() < op.precedence() { @@ -552,6 +548,7 @@ func (p *parser) unaryExpr() Expr { case itemADD, itemSUB: p.next() e := p.unaryExpr() + // Simplify unary expressions for number literals. if nl, ok := e.(*NumberLiteral); ok { if t.typ == itemSUB { @@ -665,6 +662,9 @@ func (p *parser) primaryExpr() Expr { case t.typ.isAggregator(): p.backup() return p.aggrExpr() + + default: + p.errorf("no valid expression found") } return nil } diff --git a/promql/parse_test.go b/promql/parse_test.go index 341fe8e99..3fc1df873 100644 --- a/promql/parse_test.go +++ b/promql/parse_test.go @@ -134,7 +134,7 @@ var testExpr = []struct { }, { input: "1+", fail: true, - errMsg: "missing right-hand side in binary expression", + errMsg: "no valid expression found", }, { input: ".", fail: true, @@ -154,7 +154,7 @@ var testExpr = []struct { }, { input: "1 /", fail: true, - errMsg: "missing right-hand side in binary expression", + errMsg: "no valid expression found", }, { input: "*1", fail: true, @@ -945,6 +945,27 @@ var testExpr = []struct { fail: true, errMsg: "expected type matrix in call to function \"rate\", got vector", }, + // Fuzzing regression tests. + { + input: "-=", + fail: true, + errMsg: `no valid expression found`, + }, + { + input: "++-++-+-+-<", + fail: true, + errMsg: `no valid expression found`, + }, + { + input: "e-+=/(0)", + fail: true, + errMsg: `no valid expression found`, + }, + { + input: "-If", + fail: true, + errMsg: `no valid expression found`, + }, } func TestParseExpressions(t *testing.T) { @@ -952,6 +973,12 @@ func TestParseExpressions(t *testing.T) { parser := newParser(test.input) expr, err := parser.parseExpr() + + // Unexpected errors are always caused by a bug. + if err == errUnexpected { + t.Fatalf("unexpected error occurred") + } + if !test.fail && err != nil { t.Errorf("error in input '%s'", test.input) t.Fatalf("could not parse: %s", err) @@ -1198,6 +1225,19 @@ var testStatement = []struct { `, fail: true, }, + // Fuzzing regression tests. + { + input: `I=-/`, + fail: true, + }, + { + input: `I=3E8/-=`, + fail: true, + }, + { + input: `M=-=-0-0`, + fail: true, + }, } func TestParseStatements(t *testing.T) { @@ -1205,6 +1245,12 @@ func TestParseStatements(t *testing.T) { parser := newParser(test.input) stmts, err := parser.parseStmts() + + // Unexpected errors are always caused by a bug. + if err == errUnexpected { + t.Fatalf("unexpected error occurred") + } + if !test.fail && err != nil { t.Errorf("error in input: \n\n%s\n", test.input) t.Fatalf("could not parse: %s", err) @@ -1332,6 +1378,12 @@ func TestParseSeries(t *testing.T) { parser.lex.seriesDesc = true metric, vals, err := parser.parseSeriesDesc() + + // Unexpected errors are always caused by a bug. + if err == errUnexpected { + t.Fatalf("unexpected error occurred") + } + if !test.fail && err != nil { t.Errorf("error in input: \n\n%s\n", test.input) t.Fatalf("could not parse: %s", err) @@ -1364,8 +1416,8 @@ func TestRecoverRuntime(t *testing.T) { var a []int a[123] = 1 - if err.Error() != "unexpected error" { - t.Fatalf("wrong error message: %q, expected %q", err, "unexpected error") + if err != errUnexpected { + t.Fatalf("wrong error message: %q, expected %q", err, errUnexpected) } }