From 7547b3e1c9db78d620df8feb0c661c3b7ea403e3 Mon Sep 17 00:00:00 2001 From: Devin Carraway Date: Tue, 7 Aug 2018 23:37:30 -0700 Subject: [PATCH] Fix crasher on history underflow. From fuzz testing: getHistory() wasn't being bounds-checked, and so would crash if an @-token referenced before the first evaluation. Reproducing this requires either serialized testing or clearing the history, so the ClearHistory() needs to be called first to reproduce it reliably. --- compute/compute.go | 13 ++++++++++--- compute/compute_test.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/compute/compute.go b/compute/compute.go index b8f953a..337fd62 100644 --- a/compute/compute.go +++ b/compute/compute.go @@ -34,7 +34,11 @@ ScanLoop: _, tok, lit := s.Scan() if lit != "@" && back > -1 && len(resHistory) > 0 { - floats.Push(getHistory(back)) + if hval, err := getHistory(back); err != nil { + return 0, err + } else { + floats.Push(hval) + } if prev == token.RPAREN || constants.IsConstant(prev.String()) { evalUnprecedenced("*", ops, floats) } @@ -189,8 +193,11 @@ func parseOperator(lit string) *operators.Operator { return operators.FindOperatorFromString(lit) } -func getHistory(back int) float64 { - return resHistory[back] +func getHistory(back int) (float64, error) { + if back >= len(resHistory) { + return 0, errors.New("History underflow") + } + return resHistory[back], nil } func pushHistory(res float64) { diff --git a/compute/compute_test.go b/compute/compute_test.go index bed24cd..cf7c91c 100644 --- a/compute/compute_test.go +++ b/compute/compute_test.go @@ -50,6 +50,34 @@ func TestEvaluate(t *testing.T) { } } +func TestEvaluateInvalid(t *testing.T) { + tests := [][]string{ + {"/"}, + {"1/"}, + {"1("}, + {")("}, + {"(()"}, + {"@"}, + {"@@"}, + {"0", "@@"}, + {"0", "@@@"}, + {"@@\xa6"}, + } + for i, series := range tests { + ClearHistory() + var fail error + for _, expr := range series { + if _, err := Evaluate(expr); err != nil { + fail = err + break + } + } + if fail == nil { + t.Errorf("case %d: expected error, finished successfully", i) + } + } +} + func BenchmarkEvaluate(b *testing.B) { tests := []string{ "π",