From 77418b70b4856ba0b55497a016041b8a34efb54a Mon Sep 17 00:00:00 2001 From: Sasha Smundak Date: Tue, 21 Jan 2020 13:31:06 -0800 Subject: [PATCH] Fix null pointer dereference printing an expression. Parser.parseVariable method should always set the value of the variable it creates. Failure to do so may end up in the following: ``` $ androidmk <(printf "FOO:=(X)\nFOO:=bar\n") parse error: :3:1: variable already set, previous assignment: FOO@:1:5 = %!s(PANIC=String method: runtime error: invalid memory address or nil pointer dereference) (%!s(PANIC=String method: runtime error: invalid memory address or nil pointer dereference)) false ``` The cause is that calling Parser.Parse to parse `FOO=abc` created a Variable instance with nil value, causing panic on print attempt. Test: m androidmk && androidmk <(printf "FOO:=(X)\nFOO:=bar\n") (should print: ERROR: parse error: :3:1: variable already set, previous assignment: FOO@:1:5 = X = Not Evaluated (X = Not Evaluated) false) Change-Id: I296d7984df6d8796e0075f9eb692b234f8c94f08 --- parser/ast.go | 26 ++++++++++++++++++++++++++ parser/parser.go | 2 ++ parser/parser_test.go | 21 +++++++++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/parser/ast.go b/parser/ast.go index b5053bb..57c4948 100644 --- a/parser/ast.go +++ b/parser/ast.go @@ -164,6 +164,7 @@ const ( Int64Type ListType MapType + NotEvaluatedType ) func (t Type) String() string { @@ -178,6 +179,8 @@ func (t Type) String() string { return "list" case MapType: return "map" + case NotEvaluatedType: + return "notevaluated" default: panic(fmt.Errorf("Unknown type %d", t)) } @@ -476,6 +479,29 @@ func (c Comment) Text() string { return string(buf) } +type NotEvaluated struct { + Position scanner.Position +} + +func (n NotEvaluated) Copy() Expression { + return NotEvaluated{Position: n.Position} +} + +func (n NotEvaluated) String() string { + return "Not Evaluated" +} + +func (n NotEvaluated) Type() Type { + return NotEvaluatedType +} + +func (n NotEvaluated) Eval() Expression { + return NotEvaluated{Position: n.Position} +} + +func (n NotEvaluated) Pos() scanner.Position { return n.Position } +func (n NotEvaluated) End() scanner.Position { return n.Position } + func endPos(pos scanner.Position, n int) scanner.Position { pos.Offset += n pos.Column += n diff --git a/parser/parser.go b/parser/parser.go index cb86246..6ae5df3 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -484,6 +484,8 @@ func (p *parser) parseVariable() Expression { } value = assignment.Value } + } else { + value = &NotEvaluated{} } value = &Variable{ Name: text, diff --git a/parser/parser_test.go b/parser/parser_test.go index 6377dc1..70151ad 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -1122,3 +1122,24 @@ func TestParserEndPos(t *testing.T) { } } } + +func TestParserNotEvaluated(t *testing.T) { + // When parsing without evaluation, create variables correctly + scope := NewScope(nil) + input := "FOO=abc\n" + _, errs := Parse("", bytes.NewBufferString(input), scope) + if errs != nil { + t.Errorf("unexpected errors:") + for _, err := range errs { + t.Errorf(" %s", err) + } + t.FailNow() + } + assignment, found := scope.Get("FOO") + if !found { + t.Fatalf("Expected to find FOO after parsing %s", input) + } + if s := assignment.String(); strings.Contains(s, "PANIC") { + t.Errorf("Attempt to print FOO returned %s", s) + } +} -- GitLab