diff options
| author | Roland Shoemaker <roland@golang.org> | 2022-02-28 05:42:11 -0800 |
|---|---|---|
| committer | Gopher Robot <gobot@golang.org> | 2022-03-04 20:02:41 +0000 |
| commit | 2b8aa2b734721487bb718ee5fb6080f51b57efd9 (patch) | |
| tree | 30f2b73b04f5d2eb090e5246e1d84d4b8c9f5114 /src/internal/fuzz/encoding.go | |
| parent | 7d7b9bbc7a37d3b83936a8caea08e0be7240a125 (diff) | |
| download | go-2b8aa2b734721487bb718ee5fb6080f51b57efd9.tar.xz | |
internal/fuzz: handle Inf/NaN float values
Fixes #51258
Change-Id: I3c8b785ac912d66e1a6e2179625e6903032b8330
Reviewed-on: https://go-review.googlesource.com/c/go/+/388354
Reviewed-by: Bryan Mills <bcmills@google.com>
Trust: Roland Shoemaker <roland@golang.org>
Run-TryBot: Roland Shoemaker <roland@golang.org>
Auto-Submit: Roland Shoemaker <roland@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Diffstat (limited to 'src/internal/fuzz/encoding.go')
| -rw-r--r-- | src/internal/fuzz/encoding.go | 115 |
1 files changed, 90 insertions, 25 deletions
diff --git a/src/internal/fuzz/encoding.go b/src/internal/fuzz/encoding.go index 2bfa02b8c0..fe070eca34 100644 --- a/src/internal/fuzz/encoding.go +++ b/src/internal/fuzz/encoding.go @@ -10,6 +10,7 @@ import ( "go/ast" "go/parser" "go/token" + "math" "strconv" ) @@ -27,8 +28,20 @@ func marshalCorpusFile(vals ...any) []byte { // instead of changing to byte and rune respectively. for _, val := range vals { switch t := val.(type) { - case int, int8, int16, int64, uint, uint16, uint32, uint64, float32, float64, bool: + case int, int8, int16, int64, uint, uint16, uint32, uint64, bool: fmt.Fprintf(b, "%T(%v)\n", t, t) + case float32: + if math.IsNaN(float64(t)) && math.Float32bits(t) != math.Float32bits(float32(math.NaN())) { + fmt.Fprintf(b, "math.Float32frombits(%v)\n", math.Float32bits(t)) + } else { + fmt.Fprintf(b, "%T(%v)\n", t, t) + } + case float64: + if math.IsNaN(t) && math.Float64bits(t) != math.Float64bits(math.NaN()) { + fmt.Fprintf(b, "math.Float64frombits(%v)\n", math.Float64bits(t)) + } else { + fmt.Fprintf(b, "%T(%v)\n", t, t) + } case string: fmt.Fprintf(b, "string(%q)\n", t) case rune: // int32 @@ -105,44 +118,78 @@ func parseCorpusValue(line []byte) (any, error) { return []byte(s), nil } - idType, ok := call.Fun.(*ast.Ident) - if !ok { - return nil, fmt.Errorf("expected []byte or primitive type") - } - if idType.Name == "bool" { - id, ok := arg.(*ast.Ident) + var idType *ast.Ident + if selector, ok := call.Fun.(*ast.SelectorExpr); ok { + xIdent, ok := selector.X.(*ast.Ident) + if !ok || xIdent.Name != "math" { + return nil, fmt.Errorf("invalid selector type") + } + switch selector.Sel.Name { + case "Float64frombits": + idType = &ast.Ident{Name: "float64-bits"} + case "Float32frombits": + idType = &ast.Ident{Name: "float32-bits"} + default: + return nil, fmt.Errorf("invalid selector type") + } + } else { + idType, ok = call.Fun.(*ast.Ident) if !ok { - return nil, fmt.Errorf("malformed bool") + return nil, fmt.Errorf("expected []byte or primitive type") } - if id.Name == "true" { - return true, nil - } else if id.Name == "false" { - return false, nil - } else { - return nil, fmt.Errorf("true or false required for type bool") + if idType.Name == "bool" { + id, ok := arg.(*ast.Ident) + if !ok { + return nil, fmt.Errorf("malformed bool") + } + if id.Name == "true" { + return true, nil + } else if id.Name == "false" { + return false, nil + } else { + return nil, fmt.Errorf("true or false required for type bool") + } } } + var ( val string kind token.Token ) if op, ok := arg.(*ast.UnaryExpr); ok { - // Special case for negative numbers. - lit, ok := op.X.(*ast.BasicLit) - if !ok || (lit.Kind != token.INT && lit.Kind != token.FLOAT) { + switch lit := op.X.(type) { + case *ast.BasicLit: + if op.Op != token.SUB { + return nil, fmt.Errorf("unsupported operation on int/float: %v", op.Op) + } + // Special case for negative numbers. + val = op.Op.String() + lit.Value // e.g. "-" + "124" + kind = lit.Kind + case *ast.Ident: + if lit.Name != "Inf" { + return nil, fmt.Errorf("expected operation on int or float type") + } + if op.Op == token.SUB { + val = "-Inf" + } else { + val = "+Inf" + } + kind = token.FLOAT + default: return nil, fmt.Errorf("expected operation on int or float type") } - if op.Op != token.SUB { - return nil, fmt.Errorf("unsupported operation on int: %v", op.Op) - } - val = op.Op.String() + lit.Value // e.g. "-" + "124" - kind = lit.Kind } else { - lit, ok := arg.(*ast.BasicLit) - if !ok { + switch lit := arg.(type) { + case *ast.BasicLit: + val, kind = lit.Value, lit.Kind + case *ast.Ident: + if lit.Name != "NaN" { + return nil, fmt.Errorf("literal value required for primitive type") + } + val, kind = "NaN", token.FLOAT + default: return nil, fmt.Errorf("literal value required for primitive type") } - val, kind = lit.Value, lit.Kind } switch typ := idType.Name; typ { @@ -191,6 +238,24 @@ func parseCorpusValue(line []byte) (any, error) { return nil, fmt.Errorf("float or integer literal required for float64 type") } return strconv.ParseFloat(val, 64) + case "float32-bits": + if kind != token.INT { + return nil, fmt.Errorf("integer literal required for math.Float32frombits type") + } + bits, err := parseUint(val, "uint32") + if err != nil { + return nil, err + } + return math.Float32frombits(bits.(uint32)), nil + case "float64-bits": + if kind != token.FLOAT && kind != token.INT { + return nil, fmt.Errorf("integer literal required for math.Float64frombits type") + } + bits, err := parseUint(val, "uint64") + if err != nil { + return nil, err + } + return math.Float64frombits(bits.(uint64)), nil default: return nil, fmt.Errorf("expected []byte or primitive type") } |
