Repository: jhump/protoreflect (go)

Issue:
protoparse: panic in (*basicCompositeNode).start
Hi!

I tried fuzzing 1.7.0 with the help of @bradleyjkemp's excellent fuzzing method from https://github.com/jhump/protoreflect/issues/271 and ran into a pretty interesting panic. It is reproduced by using a file with a single `0`. This reproduces the panic:

```go
package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"strings"

	"github.com/jhump/protoreflect/desc/protoparse"
)

func main() {
	parser := &protoparse.Parser{
		Accessor: func(_ string) (closer io.ReadCloser, e error) {
			return ioutil.NopCloser(strings.NewReader("0")), nil
		},
	}

	_, err := parser.ParseFiles("foo")
	if err != nil {
		fmt.Println("Error!", err)
	}
	fmt.Println("Success!")
}
```

I'd like to take this moment to re-state the case for continuous fuzzing of `protoparse`. It's a prime example of a library that will benefit from it, since it fundamentally works on arbitrary user-supplied bytes. As already mentioned by Bradley, there are platforms that provide free fuzzing for open source projects, and it'd be great to integrate it with protoreflect.

Pull request description:
fix panic when protoparse consuming non-proto file
If the parsing fails immediately due to first token being invalid, it can result in a `*file` AST node result that has no range set (e.g. no `first` and `last` tokens set). This results in a nil-pointer dereference panic later.

Fixes #334 

Patch:
diff --git a/desc/protoparse/parser.go b/desc/protoparse/parser.go
index ef27aff..d9d74ed 100644
--- a/desc/protoparse/parser.go
+++ b/desc/protoparse/parser.go
@@ -717,6 +717,8 @@ func createParseResult(filename string, file *fileNode, errs *errorHandler) *par
 		// nil AST means there was an error that prevented any parsing
 		// or the file was empty; synthesize empty non-nil AST
 		file = &fileNode{}
+	}
+	if file.first == nil {
 		n := noSourceNode{pos: unknownPos(filename)}
 		file.setRange(&n, &n)
 	}


Test patch:
diff --git a/desc/protoparse/validate_test.go b/desc/protoparse/validate_test.go
index 0c0e39f..3963f97 100644
--- a/desc/protoparse/validate_test.go
+++ b/desc/protoparse/validate_test.go
@@ -244,6 +244,30 @@ func TestBasicValidation(t *testing.T) {
 			contents: `message Foo { oneof foo { group bar = 1 { } } }`,
 			errMsg:   `test.proto:1:33: group bar should have a name that starts with a capital letter`,
 		},
+		{
+			contents: ``,
+			succeeds: true,
+		},
+		{
+			contents: `0`,
+			errMsg:   `test.proto:1:1: syntax error: unexpected int literal`,
+		},
+		{
+			contents: `foobar`,
+			errMsg:   `test.proto:1:1: syntax error: unexpected identifier`,
+		},
+		{
+			contents: `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.`,
+			errMsg:   `test.proto:1:1: syntax error: unexpected identifier`,
+		},
+		{
+			contents: `"abc"`,
+			errMsg:   `test.proto:1:1: syntax error: unexpected string literal`,
+		},
+		{
+			contents: `0.0.0.0.0`,
+			errMsg:   `test.proto:1:1: syntax error: unexpected float literal`,
+		},
 	}
 
 	for i, tc := range testCases {