Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More fixes. #118

Merged
merged 3 commits into from
Dec 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions builtins/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,9 @@ func baseFn(env *env.Environment, args []primitive.Primitive) primitive.Primitiv
return primitive.Error("argument not a number")
}

if int(base) < 2 || int(base) > 36 {
return primitive.Error("invalid base - must be >=2 and <=36")
}
return primitive.String(strconv.FormatInt(int64(n), int(base)))
}

Expand Down Expand Up @@ -1498,6 +1501,10 @@ func randomFn(env *env.Environment, args []primitive.Primitive) primitive.Primit
return primitive.Error("argument not a number")
}

if int(num) <= 0 {
return primitive.Error("argument must be greater than zero")
}

return primitive.Number(rand.Intn(int(num)))

}
Expand Down
34 changes: 33 additions & 1 deletion builtins/builtins_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,33 @@ func TestBase(t *testing.T) {
primitive.Number(2),
})

// Will lead to an error
// Will lead to a string
r, ok2 := result.(primitive.String)
if !ok2 {
t.Fatalf("expected string, got %v", result)
}
if !strings.Contains(string(r), "11111111") {
t.Fatalf("got string, but wrong one %v", r)
}

// However bases must be 2-36 inclusive, so outside that range we
// expect errors
for _, bs := range []int{0, 1, 40, 100, 200} {

res := baseFn(ENV, []primitive.Primitive{
primitive.Number(255),
primitive.Number(bs),
})

// Will lead to an error
r, ok3 := res.(primitive.Error)
if !ok3 {
t.Fatalf("expected error, got %v", res)
}
if !strings.Contains(string(r), "invalid base") {
t.Fatalf("got error, but wrong one %v", r)
}
}
}

// Test (car
Expand Down Expand Up @@ -3065,6 +3084,19 @@ func TestRandom(t *testing.T) {
if !ok2 {
t.Fatalf("expected string, got %v", out)
}

// Calling with a number less than zero returns an error
out = randomFn(ENV, []primitive.Primitive{
primitive.Number(-3),
})

e, ok = out.(primitive.Error)
if !ok {
t.Fatalf("expected error, got %v", out)
}
if !strings.Contains(string(e), "greater than zero") {
t.Fatalf("got error, but wrong one %v", out)
}
}

// TestSet tests set
Expand Down
55 changes: 38 additions & 17 deletions fuzz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package main
import (
"context"
"os"
"path"
"strings"
"testing"
"time"
Expand Down Expand Up @@ -109,34 +110,63 @@ func FuzzYAL(f *testing.F) {
f.Add([]byte(`define blah (lambda (a:any) (print "I received the arg %s" a)))`))
f.Add([]byte(`define blah (lambda (a) (print "I received the arg %s" a)))`))

// Find each of our examples, as these are valid code samples
files, err := os.ReadDir("examples")
if err != nil {
f.Fatalf("failed to read examples/ directory %s", err)
}

// Load each example as a fuzz-source
for _, file := range files {
path := path.Join("examples", file.Name())

data, err := os.ReadFile(path)
if err != nil {
f.Fatalf("Failed to load %s %s", path, err)
}
f.Add(data)
}

// Known errors are listed here.
//
// The purpose of fuzzing is to find panics, or unexpected errors.
//
// Some programs are obviously invalid though, so we don't want to
// Some programs are obviously invalid though, and we don't want to
// report those known-bad things.
//
known := []string{
"arityerror",
"deadline exceeded", // context timeout
"catch list should begin with 'catch'", // try/catch
"deadline exceeded", // context timeout
"division by zero",
"error expanding argument",
"expected a function body",
"expected a list",
"expected a symbol",
"failed to compile regexp",
"failed to open", // file:lines
"invalid character literal",
"is not a symbol",
"list should have three elements", // try
"must be greater than zero", // random
"must have even length",
"not a character",
"not a function",
"not a hash",
"not a list",
"not a number",
"not a procedure",
"not a string",
"out of bounds", // nth
"recursion limit",
"syntax error in pattern", // glob
"tried to set a non-symbol",
"typeerror - ",
"unexpected type",
}

// Read the standard library only once.
std := string(stdlib.Contents()) + "\n"

f.Fuzz(func(t *testing.T, input []byte) {

// Timeout after a second
Expand All @@ -149,13 +179,10 @@ func FuzzYAL(f *testing.F) {
// Populate the default primitives
builtins.PopulateEnvironment(environment)

// Read the standard library
pre := stdlib.Contents()

// Prepend that to the users' script
src := string(pre) + "\n" + string(input)
// Prepend the standard-library to the users' script
src := std + string(input)

// Create a new interpreter with that source
// Create a new interpreter with the combined source
interpreter := eval.New(src)

// Ensure we timeout after 1 second
Expand All @@ -164,23 +191,17 @@ func FuzzYAL(f *testing.F) {
// Now evaluate the input using the specified environment
out := interpreter.Evaluate(environment)

found := false

switch out.(type) {
case *primitive.Error, primitive.Error:
str := strings.ToLower(out.ToString())

// does it look familiar?
for _, v := range known {
if strings.Contains(str, v) {
found = true
return
}
}

// raise an error
if !found {
t.Fatalf("error parsing %s:%v", input, out)
}
t.Fatalf("error processing input %s:%v", input, out)
}
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
go test fuzz v1
[]byte("(try()())")
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
go test fuzz v1
[]byte("(set!()00)")
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
go test fuzz v1
[]byte("(nth()00)")
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
go test fuzz v1
[]byte("(help 00)")
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
go test fuzz v1
[]byte("(random 0)")
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
go test fuzz v1
[]byte("(file:lines\"\")")
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
go test fuzz v1
[]byte("(try()(0 0()))")
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
go test fuzz v1
[]byte("(match\"(\"0)")
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
go test fuzz v1
[]byte("(glob\"\\\"\")")