Skip to content

Commit 2b8aa2b

Browse files
rolandshoemakergopherbot
authored andcommitted
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 <[email protected]> Trust: Roland Shoemaker <[email protected]> Run-TryBot: Roland Shoemaker <[email protected]> Auto-Submit: Roland Shoemaker <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent 7d7b9bb commit 2b8aa2b

File tree

2 files changed

+104
-25
lines changed

2 files changed

+104
-25
lines changed

src/internal/fuzz/encoding.go

Lines changed: 90 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"go/ast"
1111
"go/parser"
1212
"go/token"
13+
"math"
1314
"strconv"
1415
)
1516

@@ -27,8 +28,20 @@ func marshalCorpusFile(vals ...any) []byte {
2728
// instead of changing to byte and rune respectively.
2829
for _, val := range vals {
2930
switch t := val.(type) {
30-
case int, int8, int16, int64, uint, uint16, uint32, uint64, float32, float64, bool:
31+
case int, int8, int16, int64, uint, uint16, uint32, uint64, bool:
3132
fmt.Fprintf(b, "%T(%v)\n", t, t)
33+
case float32:
34+
if math.IsNaN(float64(t)) && math.Float32bits(t) != math.Float32bits(float32(math.NaN())) {
35+
fmt.Fprintf(b, "math.Float32frombits(%v)\n", math.Float32bits(t))
36+
} else {
37+
fmt.Fprintf(b, "%T(%v)\n", t, t)
38+
}
39+
case float64:
40+
if math.IsNaN(t) && math.Float64bits(t) != math.Float64bits(math.NaN()) {
41+
fmt.Fprintf(b, "math.Float64frombits(%v)\n", math.Float64bits(t))
42+
} else {
43+
fmt.Fprintf(b, "%T(%v)\n", t, t)
44+
}
3245
case string:
3346
fmt.Fprintf(b, "string(%q)\n", t)
3447
case rune: // int32
@@ -105,44 +118,78 @@ func parseCorpusValue(line []byte) (any, error) {
105118
return []byte(s), nil
106119
}
107120

108-
idType, ok := call.Fun.(*ast.Ident)
109-
if !ok {
110-
return nil, fmt.Errorf("expected []byte or primitive type")
111-
}
112-
if idType.Name == "bool" {
113-
id, ok := arg.(*ast.Ident)
121+
var idType *ast.Ident
122+
if selector, ok := call.Fun.(*ast.SelectorExpr); ok {
123+
xIdent, ok := selector.X.(*ast.Ident)
124+
if !ok || xIdent.Name != "math" {
125+
return nil, fmt.Errorf("invalid selector type")
126+
}
127+
switch selector.Sel.Name {
128+
case "Float64frombits":
129+
idType = &ast.Ident{Name: "float64-bits"}
130+
case "Float32frombits":
131+
idType = &ast.Ident{Name: "float32-bits"}
132+
default:
133+
return nil, fmt.Errorf("invalid selector type")
134+
}
135+
} else {
136+
idType, ok = call.Fun.(*ast.Ident)
114137
if !ok {
115-
return nil, fmt.Errorf("malformed bool")
138+
return nil, fmt.Errorf("expected []byte or primitive type")
116139
}
117-
if id.Name == "true" {
118-
return true, nil
119-
} else if id.Name == "false" {
120-
return false, nil
121-
} else {
122-
return nil, fmt.Errorf("true or false required for type bool")
140+
if idType.Name == "bool" {
141+
id, ok := arg.(*ast.Ident)
142+
if !ok {
143+
return nil, fmt.Errorf("malformed bool")
144+
}
145+
if id.Name == "true" {
146+
return true, nil
147+
} else if id.Name == "false" {
148+
return false, nil
149+
} else {
150+
return nil, fmt.Errorf("true or false required for type bool")
151+
}
123152
}
124153
}
154+
125155
var (
126156
val string
127157
kind token.Token
128158
)
129159
if op, ok := arg.(*ast.UnaryExpr); ok {
130-
// Special case for negative numbers.
131-
lit, ok := op.X.(*ast.BasicLit)
132-
if !ok || (lit.Kind != token.INT && lit.Kind != token.FLOAT) {
160+
switch lit := op.X.(type) {
161+
case *ast.BasicLit:
162+
if op.Op != token.SUB {
163+
return nil, fmt.Errorf("unsupported operation on int/float: %v", op.Op)
164+
}
165+
// Special case for negative numbers.
166+
val = op.Op.String() + lit.Value // e.g. "-" + "124"
167+
kind = lit.Kind
168+
case *ast.Ident:
169+
if lit.Name != "Inf" {
170+
return nil, fmt.Errorf("expected operation on int or float type")
171+
}
172+
if op.Op == token.SUB {
173+
val = "-Inf"
174+
} else {
175+
val = "+Inf"
176+
}
177+
kind = token.FLOAT
178+
default:
133179
return nil, fmt.Errorf("expected operation on int or float type")
134180
}
135-
if op.Op != token.SUB {
136-
return nil, fmt.Errorf("unsupported operation on int: %v", op.Op)
137-
}
138-
val = op.Op.String() + lit.Value // e.g. "-" + "124"
139-
kind = lit.Kind
140181
} else {
141-
lit, ok := arg.(*ast.BasicLit)
142-
if !ok {
182+
switch lit := arg.(type) {
183+
case *ast.BasicLit:
184+
val, kind = lit.Value, lit.Kind
185+
case *ast.Ident:
186+
if lit.Name != "NaN" {
187+
return nil, fmt.Errorf("literal value required for primitive type")
188+
}
189+
val, kind = "NaN", token.FLOAT
190+
default:
143191
return nil, fmt.Errorf("literal value required for primitive type")
144192
}
145-
val, kind = lit.Value, lit.Kind
146193
}
147194

148195
switch typ := idType.Name; typ {
@@ -191,6 +238,24 @@ func parseCorpusValue(line []byte) (any, error) {
191238
return nil, fmt.Errorf("float or integer literal required for float64 type")
192239
}
193240
return strconv.ParseFloat(val, 64)
241+
case "float32-bits":
242+
if kind != token.INT {
243+
return nil, fmt.Errorf("integer literal required for math.Float32frombits type")
244+
}
245+
bits, err := parseUint(val, "uint32")
246+
if err != nil {
247+
return nil, err
248+
}
249+
return math.Float32frombits(bits.(uint32)), nil
250+
case "float64-bits":
251+
if kind != token.FLOAT && kind != token.INT {
252+
return nil, fmt.Errorf("integer literal required for math.Float64frombits type")
253+
}
254+
bits, err := parseUint(val, "uint64")
255+
if err != nil {
256+
return nil, err
257+
}
258+
return math.Float64frombits(bits.(uint64)), nil
194259
default:
195260
return nil, fmt.Errorf("expected []byte or primitive type")
196261
}

src/internal/fuzz/encoding_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,20 @@ float64(-12.5)
103103
float32(2.5)`,
104104
ok: true,
105105
},
106+
{
107+
in: `go test fuzz v1
108+
float32(-0)
109+
float64(-0)
110+
float32(+Inf)
111+
float32(-Inf)
112+
float32(NaN)
113+
float64(+Inf)
114+
float64(-Inf)
115+
float64(NaN)
116+
math.Float64frombits(9221120237041090560)
117+
math.Float32frombits(2143289343)`,
118+
ok: true,
119+
},
106120
}
107121
for _, test := range tests {
108122
t.Run(test.in, func(t *testing.T) {

0 commit comments

Comments
 (0)