Skip to content

Commit 71c9c37

Browse files
holimanchfast
andauthored
fuzz: add fuzzer for ternary ops (mulmod/addmod) (#95)
* fuzz: add fuzzer for mulmod * tests: improve fuzzers to run all cases * ci: reset cached fuzzing corpus Co-authored-by: Paweł Bylica <[email protected]>
1 parent 833833c commit 71c9c37

File tree

2 files changed

+157
-48
lines changed

2 files changed

+157
-48
lines changed

circle.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ jobs:
4040
command: bash <(curl -s https://codecov.io/bash)
4141
- restore_cache:
4242
keys:
43-
- corpus
43+
- corpus-v2
4444
- run:
4545
name: "Fuzzing"
4646
command: |
@@ -49,7 +49,7 @@ jobs:
4949
timeout --preserve-status --signal INT 1m go-fuzz -procs=2
5050
test ! "$(ls crashers)"
5151
- save_cache:
52-
key: corpus-{{ epoch }}
52+
key: corpus-v2-{{ epoch }}
5353
paths:
5454
- corpus
5555
- run:

fuzz.go

Lines changed: 155 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Copyright 2020 uint256 Authors
33
// SPDX-License-Identifier: BSD-3-Clause
44

5+
//go:build gofuzz
56
// +build gofuzz
67

78
package uint256
@@ -11,105 +12,180 @@ import (
1112
"math/big"
1213
"reflect"
1314
"runtime"
15+
"strings"
1416
)
1517

1618
const (
17-
opUdivrem = 0
18-
opMul = 1
19-
opLsh = 2
20-
opAdd = 4
21-
opSub = 5
19+
opUdivrem = iota
20+
opMul
21+
opLsh
22+
opAdd
23+
opSub
24+
opMulmod
2225
)
2326

24-
type opFunc func(*Int, *Int, *Int) *Int
25-
type bigFunc func(*big.Int, *big.Int, *big.Int) *big.Int
27+
type opDualArgFunc func(*Int, *Int, *Int) *Int
28+
type bigDualArgFunc func(*big.Int, *big.Int, *big.Int) *big.Int
2629

27-
func crash(op opFunc, x, y Int, msg string) {
30+
type opThreeArgFunc func(*Int, *Int, *Int, *Int) *Int
31+
type bigThreeArgFunc func(*big.Int, *big.Int, *big.Int, *big.Int) *big.Int
32+
33+
func crash(op interface{}, msg string, args ...Int) {
2834
fn := runtime.FuncForPC(reflect.ValueOf(op).Pointer())
2935
fnName := fn.Name()
3036
fnFile, fnLine := fn.FileLine(fn.Entry())
31-
panic(fmt.Sprintf("%s\nfor %s (%s:%d)\nx: %x\ny: %x", msg, fnName, fnFile, fnLine, &x, &y))
37+
var strArgs []string
38+
for i, arg := range args {
39+
strArgs = append(strArgs, fmt.Sprintf("%d: %x", i, &arg))
40+
}
41+
panic(fmt.Sprintf("%s\nfor %s (%s:%d)\n%v",
42+
msg, fnName, fnFile, fnLine, strings.Join(strArgs, "\n")))
3243
}
3344

34-
func checkOp(op opFunc, bigOp bigFunc, x, y Int) {
45+
func checkDualArgOp(op opDualArgFunc, bigOp bigDualArgFunc, x, y Int) {
3546
origX := x
3647
origY := y
3748

3849
var result Int
3950
ret := op(&result, &x, &y)
4051
if ret != &result {
41-
crash(op, x, y, "returned not the pointer receiver")
52+
crash(op, "returned not the pointer receiver", x, y)
4253
}
4354
if x != origX {
44-
crash(op, x, y, "first argument modified")
55+
crash(op, "first argument modified", x, y)
4556
}
4657
if y != origY {
47-
crash(op, x, y, "second argument modified")
58+
crash(op, "second argument modified", x, y)
4859
}
4960

5061
expected, _ := FromBig(bigOp(new(big.Int), x.ToBig(), y.ToBig()))
5162
if result != *expected {
52-
crash(op, x, y, "unexpected result")
63+
crash(op, "unexpected result", x, y)
5364
}
5465

5566
// Test again when the receiver is not zero.
5667
var garbage Int
5768
garbage.Xor(&x, &y)
5869
ret = op(&garbage, &x, &y)
5970
if ret != &garbage {
60-
crash(op, x, y, "returned not the pointer receiver")
71+
crash(op, "returned not the pointer receiver", x, y)
6172
}
6273
if garbage != *expected {
63-
crash(op, x, y, "unexpected result")
74+
crash(op, "unexpected result", x, y)
6475
}
6576
if x != origX {
66-
crash(op, x, y, "first argument modified")
77+
crash(op, "first argument modified", x, y)
6778
}
6879
if y != origY {
69-
crash(op, x, y, "second argument modified")
80+
crash(op, "second argument modified", x, y)
7081
}
7182

7283
// Test again with the receiver aliasing arguments.
7384
ret = op(&x, &x, &y)
7485
if ret != &x {
75-
crash(op, x, y, "returned not the pointer receiver")
86+
crash(op, "returned not the pointer receiver", x, y)
7687
}
7788
if x != *expected {
78-
crash(op, x, y, "unexpected result")
89+
crash(op, "unexpected result", x, y)
7990
}
8091

8192
ret = op(&y, &origX, &y)
8293
if ret != &y {
83-
crash(op, x, y, "returned not the pointer receiver")
94+
crash(op, "returned not the pointer receiver", x, y)
8495
}
8596
if y != *expected {
86-
crash(op, x, y, "unexpected result")
97+
crash(op, "unexpected result", x, y)
8798
}
8899
}
89100

90-
func Fuzz(data []byte) int {
91-
if len(data) != 65 {
92-
return 0
93-
}
101+
func checkThreeArgOp(op opThreeArgFunc, bigOp bigThreeArgFunc, x, y, z Int) {
102+
origX := x
103+
origY := y
104+
origZ := z
94105

95-
op := data[0]
106+
var result Int
107+
ret := op(&result, &x, &y, &z)
108+
if ret != &result {
109+
crash(op, "returned not the pointer receiver", x, y, z)
110+
}
111+
switch {
112+
case x != origX:
113+
crash(op, "first argument modified", x, y, z)
114+
case y != origY:
115+
crash(op, "second argument modified", x, y, z)
116+
case z != origZ:
117+
crash(op, "third argument modified", x, y, z)
118+
}
119+
expected, _ := FromBig(bigOp(new(big.Int), x.ToBig(), y.ToBig(), z.ToBig()))
120+
if have, want := result, *expected; have != want {
121+
crash(op, fmt.Sprintf("unexpected result: have %v want %v", have, want), x, y, z)
122+
}
96123

97-
var x, y Int
98-
x.SetBytes(data[1:33])
99-
y.SetBytes(data[33:])
124+
// Test again when the receiver is not zero.
125+
var garbage Int
126+
garbage.Xor(&x, &y)
127+
ret = op(&garbage, &x, &y, &z)
128+
if ret != &garbage {
129+
crash(op, "returned not the pointer receiver", x, y, z)
130+
}
131+
if have, want := garbage, *expected; have != want {
132+
crash(op, fmt.Sprintf("unexpected result: have %v want %v", have, want), x, y, z)
133+
}
134+
switch {
135+
case x != origX:
136+
crash(op, "first argument modified", x, y, z)
137+
case y != origY:
138+
crash(op, "second argument modified", x, y, z)
139+
case z != origZ:
140+
crash(op, "third argument modified", x, y, z)
141+
}
100142

101-
switch op {
102-
case opUdivrem:
103-
if y.IsZero() {
104-
return 0
105-
}
106-
checkOp((*Int).Div, (*big.Int).Div, x, y)
107-
checkOp((*Int).Mod, (*big.Int).Mod, x, y)
143+
// Test again with the receiver aliasing arguments.
144+
ret = op(&x, &x, &y, &z)
145+
if ret != &x {
146+
crash(op, "returned not the pointer receiver", x, y, z)
147+
}
148+
if have, want := x, *expected; have != want {
149+
crash(op, fmt.Sprintf("unexpected result: have %v want %v", have, want), x, y, z)
150+
}
108151

109-
case opMul:
110-
checkOp((*Int).Mul, (*big.Int).Mul, x, y)
152+
ret = op(&y, &origX, &y, &z)
153+
if ret != &y {
154+
crash(op, "returned not the pointer receiver", x, y, z)
155+
}
156+
if y != *expected {
157+
crash(op, "unexpected result", x, y, z)
158+
}
159+
ret = op(&z, &origX, &origY, &z)
160+
if ret != &z {
161+
crash(op, "returned not the pointer receiver", x, y, z)
162+
}
163+
if z != *expected {
164+
crash(op, fmt.Sprintf("unexpected result: have %v want %v", z.ToBig(), expected), x, y, z)
165+
}
166+
}
111167

112-
case opLsh:
168+
func Fuzz(data []byte) int {
169+
switch len(data) {
170+
case 64:
171+
return fuzzBinaryOp(data)
172+
case 96:
173+
return fuzzTernaryOp(data)
174+
}
175+
return -1
176+
}
177+
func fuzzBinaryOp(data []byte) int {
178+
var x, y Int
179+
x.SetBytes(data[0:32])
180+
y.SetBytes(data[32:])
181+
if !y.IsZero() { // uDivrem
182+
checkDualArgOp((*Int).Div, (*big.Int).Div, x, y)
183+
checkDualArgOp((*Int).Mod, (*big.Int).Mod, x, y)
184+
}
185+
{ // opMul
186+
checkDualArgOp((*Int).Mul, (*big.Int).Mul, x, y)
187+
}
188+
{ // opLsh
113189
lsh := func(z, x, y *Int) *Int {
114190
return z.Lsh(x, uint(y[0]))
115191
}
@@ -120,14 +196,47 @@ func Fuzz(data []byte) int {
120196
}
121197
return z.Lsh(x, n)
122198
}
123-
checkOp(lsh, bigLsh, x, y)
199+
checkDualArgOp(lsh, bigLsh, x, y)
200+
}
201+
{ // opAdd
202+
checkDualArgOp((*Int).Add, (*big.Int).Add, x, y)
203+
}
204+
{ // opSub
205+
checkDualArgOp((*Int).Sub, (*big.Int).Sub, x, y)
206+
}
207+
return 1
208+
}
124209

125-
case opAdd:
126-
checkOp((*Int).Add, (*big.Int).Add, x, y)
210+
func bigMulMod(b1, b2, b3, b4 *big.Int) *big.Int {
211+
return b1.Mod(big.NewInt(0).Mul(b2, b3), b4)
212+
}
127213

128-
case opSub:
129-
checkOp((*Int).Sub, (*big.Int).Sub, x, y)
214+
func intMulMod(f1, f2, f3, f4 *Int) *Int {
215+
return f1.MulMod(f2, f3, f4)
216+
}
217+
218+
func bigAddMod(b1, b2, b3, b4 *big.Int) *big.Int {
219+
return b1.Mod(big.NewInt(0).Add(b2, b3), b4)
220+
}
221+
222+
func intAddMod(f1, f2, f3, f4 *Int) *Int {
223+
return f1.AddMod(f2, f3, f4)
224+
}
225+
226+
func fuzzTernaryOp(data []byte) int {
227+
var x, y, z Int
228+
x.SetBytes(data[:32])
229+
y.SetBytes(data[32:64])
230+
z.SetBytes(data[64:])
231+
if z.IsZero() {
232+
return 0
130233
}
131234

132-
return 0
235+
{ // mulMod
236+
checkThreeArgOp(intMulMod, bigMulMod, x, y, z)
237+
}
238+
{ // addMod
239+
checkThreeArgOp(intAddMod, bigAddMod, x, y, z)
240+
}
241+
return 1
133242
}

0 commit comments

Comments
 (0)