Skip to content

Commit

Permalink
Merge pull request #118 from skx/more-fixes
Browse files Browse the repository at this point in the history
More fixes.
  • Loading branch information
skx authored Dec 29, 2022
2 parents e7e549c + f526b63 commit ff82758
Show file tree
Hide file tree
Showing 12 changed files with 96 additions and 18 deletions.
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\"\\\"\")")

0 comments on commit ff82758

Please sign in to comment.