Skip to content

Commit

Permalink
feat: fuzz gnovm/pkg/transpiler.Transpile (#3457)
Browse files Browse the repository at this point in the history
Adds a fuzzer for Transpile, which found bugs:
* #3425 in which this following Go program crashed the transpiler
```go
package A
import(""//"
""/***/)
```
* #3426 which generated an input that revealed the fact that Gno
deviates from Go by allowing unused variables yet Go's standard is
strict on unused variables like this program
```go
package main

func main() {
	const c1 = 1 < 8
	main()
	1
}
```
which we shouldn't allow
* partially #3428 which revealed the discrepancy in Gno that the
overflow detection is still lacking as the following program is invalid
Go but Gno allowed it to run
```go
package main

func main() {
	const c1 = int8(1) << 8
	println(c1)
}
```
because 1<<8 (256) is higher than the range of int8 for which the
maximum is `(1<<7) - 1 aka 127`

Updates #3087

Co-authored-by: Nathan Toups <[email protected]>
  • Loading branch information
odeke-em and n2p5 authored Jan 9, 2025
1 parent b60f864 commit 41f8763
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 0 deletions.
80 changes: 80 additions & 0 deletions gnovm/pkg/transpiler/fuzz_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package transpiler

import (
"fmt"
"io"
"io/fs"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
"time"
)

func FuzzTranspiling(f *testing.F) {
if testing.Short() {
f.Skip("Running in -short mode")
}

// 1. Derive the seeds from our seedGnoFiles.
breakRoot := filepath.Join("gnolang", "gno")
pc, thisFile, _, _ := runtime.Caller(0)
index := strings.Index(thisFile, breakRoot)
_ = pc // to silence the pedantic golangci linter.
rootPath := thisFile[:index+len(breakRoot)]
examplesDir := filepath.Join(rootPath, "examples")
ffs := os.DirFS(examplesDir)
fs.WalkDir(ffs, ".", func(path string, d fs.DirEntry, err error) error {
if err != nil {
panic(err)
}
if !strings.HasSuffix(path, ".gno") {
return nil
}
file, err := ffs.Open(path)
if err != nil {
panic(err)
}
blob, err := io.ReadAll(file)
file.Close()
if err != nil {
panic(err)
}
f.Add(blob)
return nil
})

// 2. Run the fuzzers.
f.Fuzz(func(t *testing.T, gnoSourceCode []byte) {
// 3. Add timings to ensure that if transpiling takes a long time
// to run, that we report this as problematic.
doneCh := make(chan bool, 1)
readyCh := make(chan bool)
go func() {
defer func() {
r := recover()
if r == nil {
return
}

sr := fmt.Sprintf("%s", r)
if !strings.Contains(sr, "invalid line number ") {
panic(r)
}
}()
close(readyCh)
defer close(doneCh)
_, _ = Transpile(string(gnoSourceCode), "gno", "in.gno")
doneCh <- true
}()

<-readyCh

select {
case <-time.After(2 * time.Second):
t.Fatalf("took more than 2 seconds to transpile\n\n%s", gnoSourceCode)
case <-doneCh:
}
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
go test fuzz v1
[]byte("package\tA\nimport(\"\"//\"\n\"\"/***/)")
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
go test fuzz v1
[]byte("package A\nimport(\"\"//\"\n\"\"/***/)")
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
go test fuzz v1
[]byte("package A\ncon\x12\n\xec|b\x80\xddQst(\n/*\n\n\n\n\n\n\nka\n*/A)")
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
go test fuzz v1
[]byte("//\"\npackage\tA\nimport(\"\"//\"\n\"\"/***/)")
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
go test fuzz v1
[]byte("//0000\x170000000000:0\npackage A")

0 comments on commit 41f8763

Please sign in to comment.