blob: bad6b41349bb57fa6348759ea5d086f9b8c4b793 [file] [log] [blame]
Yuxuan 'fishy' Wang022d0272023-11-22 09:09:57 -08001//go:build gofuzz
Philippe Antoine65ea7522021-03-15 09:34:58 +01002// +build gofuzz
3
4/*
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
20 * under the License.
21 */
22
23package fuzz
24
25import (
26 "context"
27 "fmt"
Philippe Antoine65ea7522021-03-15 09:34:58 +010028 "strconv"
Philippe Antoine65ea7522021-03-15 09:34:58 +010029
Yuxuan 'fishy' Wang022d0272023-11-22 09:09:57 -080030 "github.com/apache/thrift/lib/go/test/fuzz/gen-go/shared"
31 "github.com/apache/thrift/lib/go/test/fuzz/gen-go/tutorial"
Hasnain Lakhani9b136682025-08-25 11:54:23 -070032 "github.com/apache/thrift/lib/go/test/fuzz/gen-go/fuzztest"
Philippe Antoine65ea7522021-03-15 09:34:58 +010033 "github.com/apache/thrift/lib/go/thrift"
34)
35
36const nbFuzzedProtocols = 2
37
Hasnain Lakhani9b136682025-08-25 11:54:23 -070038// 10MB message size limit to prevent over-allocation during fuzzing
39const FUZZ_MAX_MESSAGE_SIZE = 10 * 1024 * 1024
40
Philippe Antoine65ea7522021-03-15 09:34:58 +010041func fuzzChooseProtocol(d byte, t thrift.TTransport) thrift.TProtocol {
42 switch d % nbFuzzedProtocols {
43 default:
44 fallthrough
45 case 0:
46 return thrift.NewTBinaryProtocolFactoryConf(nil).GetProtocol(t)
47 case 1:
48 return thrift.NewTCompactProtocolFactoryConf(nil).GetProtocol(t)
Hasnain Lakhani9b136682025-08-25 11:54:23 -070049 case 2:
50 return thrift.NewTJSONProtocolFactory().GetProtocol(t)
Philippe Antoine65ea7522021-03-15 09:34:58 +010051 }
52}
53
Hasnain Lakhani9b136682025-08-25 11:54:23 -070054func FuzzTutorial(data []byte) int {
Philippe Antoine65ea7522021-03-15 09:34:58 +010055 if len(data) < 2 {
56 return 0
57 }
58 inputTransport := thrift.NewTMemoryBuffer()
59 inputTransport.Buffer.Write(data[2:])
60 outputTransport := thrift.NewTMemoryBuffer()
61 outputProtocol := fuzzChooseProtocol(data[0], outputTransport)
62 inputProtocol := fuzzChooseProtocol(data[1], inputTransport)
63 ctx := thrift.SetResponseHelper(
64 context.Background(),
65 thrift.TResponseHelper{
66 THeaderResponseHelper: thrift.NewTHeaderResponseHelper(outputProtocol),
67 },
68 )
69 handler := NewCalculatorHandler()
70 processor := tutorial.NewCalculatorProcessor(handler)
71 ok := true
72 var err error
73 for ok {
74 ok, err = processor.Process(ctx, inputProtocol, outputProtocol)
75 if err != nil {
76 // Handle parse error
77 return 0
78 }
79 res := make([]byte, 1024)
80 n, err := outputTransport.Buffer.Read(res)
81 fmt.Printf("lol %d %s %v\n", n, err, res)
82 }
83 return 1
84}
85
86type CalculatorHandler struct {
87 log map[int]*shared.SharedStruct
88}
89
90func NewCalculatorHandler() *CalculatorHandler {
91 return &CalculatorHandler{log: make(map[int]*shared.SharedStruct)}
92}
93
94func (p *CalculatorHandler) Ping(ctx context.Context) (err error) {
95 fmt.Print("ping()\n")
96 return nil
97}
98
99func (p *CalculatorHandler) Add(ctx context.Context, num1 int32, num2 int32) (retval17 int32, err error) {
100 fmt.Print("add(", num1, ",", num2, ")\n")
101 return num1 + num2, nil
102}
103
104func (p *CalculatorHandler) Calculate(ctx context.Context, logid int32, w *tutorial.Work) (val int32, err error) {
105 fmt.Print("calculate(", logid, ", {", w.Op, ",", w.Num1, ",", w.Num2, "})\n")
106 switch w.Op {
107 case tutorial.Operation_ADD:
108 val = w.Num1 + w.Num2
109 break
110 case tutorial.Operation_SUBTRACT:
111 val = w.Num1 - w.Num2
112 break
113 case tutorial.Operation_MULTIPLY:
114 val = w.Num1 * w.Num2
115 break
116 case tutorial.Operation_DIVIDE:
117 if w.Num2 == 0 {
118 ouch := tutorial.NewInvalidOperation()
119 ouch.WhatOp = int32(w.Op)
120 ouch.Why = "Cannot divide by 0"
121 err = ouch
122 return
123 }
124 val = w.Num1 / w.Num2
125 break
126 default:
127 ouch := tutorial.NewInvalidOperation()
128 ouch.WhatOp = int32(w.Op)
129 ouch.Why = "Unknown operation"
130 err = ouch
131 return
132 }
133 entry := shared.NewSharedStruct()
134 entry.Key = logid
135 entry.Value = strconv.Itoa(int(val))
136 k := int(logid)
137 p.log[k] = entry
138 return val, err
139}
140
141func (p *CalculatorHandler) GetStruct(ctx context.Context, key int32) (*shared.SharedStruct, error) {
142 fmt.Print("getStruct(", key, ")\n")
143 v, _ := p.log[int(key)]
144 return v, nil
145}
146
147func (p *CalculatorHandler) Zip(ctx context.Context) (err error) {
148 fmt.Print("zip()\n")
149 return nil
150}
Hasnain Lakhani9b136682025-08-25 11:54:23 -0700151
152func FuzzParseBinary(data []byte) int {
153 // Skip if input is too small
154 if len(data) < 1 {
155 return 0
156 }
157
158 // Create transport and protocol
159 transport := thrift.NewTMemoryBufferLen(len(data))
160 defer func() {
161 transport.Close()
162 // Reset the buffer to release memory
163 transport.Buffer.Reset()
164 }()
165 transport.Write(data)
166 config := &thrift.TConfiguration{
167 MaxMessageSize: FUZZ_MAX_MESSAGE_SIZE,
168 }
169 protocol := thrift.NewTBinaryProtocolFactoryConf(config).GetProtocol(transport)
170
171 // Try to read the FuzzTest structure
172 fuzzTest := fuzztest.NewFuzzTest()
173 err := fuzzTest.Read(context.Background(), protocol)
174 if err != nil {
175 // Invalid input, but not a crash
176 return 0
177 }
178
179 // Successfully parsed
180 return 1
181}
182
183func FuzzParseCompact(data []byte) int {
184 // Skip if input is too small
185 if len(data) < 1 {
186 return 0
187 }
188
189 // Create transport and protocol
190 transport := thrift.NewTMemoryBufferLen(len(data))
191 defer func() {
192 transport.Close()
193 // Reset the buffer to release memory
194 transport.Buffer.Reset()
195 }()
196 transport.Write(data)
197 config := &thrift.TConfiguration{
198 MaxMessageSize: FUZZ_MAX_MESSAGE_SIZE,
199 }
200 protocol := thrift.NewTCompactProtocolFactoryConf(config).GetProtocol(transport)
201
202 // Try to read the FuzzTest structure
203 fuzzTest := fuzztest.NewFuzzTest()
204 err := fuzzTest.Read(context.Background(), protocol)
205 if err != nil {
206 // Invalid input, but not a crash
207 return 0
208 }
209
210 // Successfully parsed
211 return 1
212}
213
214func FuzzParseJson(data []byte) int {
215 // Skip if input is too small
216 if len(data) < 1 {
217 return 0
218 }
219
220 // Create transport and protocol
221 transport := thrift.NewTMemoryBufferLen(len(data))
222 defer func() {
223 transport.Close()
224 // Reset the buffer to release memory
225 transport.Buffer.Reset()
226 }()
227 transport.Write(data)
228 protocol := thrift.NewTJSONProtocolFactory().GetProtocol(transport)
229
230 // Try to read the FuzzTest structure
231 fuzzTest := fuzztest.NewFuzzTest()
232 err := fuzzTest.Read(context.Background(), protocol)
233 if err != nil {
234 // Invalid input, but not a crash
235 return 0
236 }
237
238 // Successfully parsed
239 return 1
240}
241
242func FuzzRoundtripBinary(data []byte) int {
243 // Skip if input is too small
244 if len(data) < 1 {
245 return 0
246 }
247
248 config := &thrift.TConfiguration{
249 MaxMessageSize: FUZZ_MAX_MESSAGE_SIZE,
250 }
251
252 // First parse
253 transport := thrift.NewTMemoryBufferLen(len(data))
254 transport.Write(data)
255 protocol := thrift.NewTBinaryProtocolFactoryConf(config).GetProtocol(transport)
256
257 // Try to read the FuzzTest structure
258 test1 := fuzztest.NewFuzzTest()
259 err := test1.Read(context.Background(), protocol)
260 if err != nil {
261 // Invalid input, but not a crash
262 return 0
263 }
264
265 // Serialize back
266 outTransport := thrift.NewTMemoryBuffer()
267 outProtocol := thrift.NewTBinaryProtocolFactoryConf(config).GetProtocol(outTransport)
268 err = test1.Write(context.Background(), outProtocol)
269 if err != nil {
270 return 0
271 }
272
273 // Get serialized data and deserialize again
274 serialized := outTransport.Bytes()
275 reTransport := thrift.NewTMemoryBufferLen(len(serialized))
276 reTransport.Write(serialized)
277 reProtocol := thrift.NewTBinaryProtocolFactoryConf(config).GetProtocol(reTransport)
278
279 test2 := fuzztest.NewFuzzTest()
280 err = test2.Read(context.Background(), reProtocol)
281 if err != nil {
282 return 0
283 }
284
285 // Verify equality
286 if !test1.Equals(test2) {
287 panic("Roundtrip failed: objects not equal after deserialization")
288 }
289
290 return 1
291}
292
293func FuzzRoundtripCompact(data []byte) int {
294 // Skip if input is too small
295 if len(data) < 1 {
296 return 0
297 }
298
299 config := &thrift.TConfiguration{
300 MaxMessageSize: FUZZ_MAX_MESSAGE_SIZE,
301 }
302
303 // First parse
304 transport := thrift.NewTMemoryBufferLen(len(data))
305 transport.Write(data)
306 protocol := thrift.NewTCompactProtocolFactoryConf(config).GetProtocol(transport)
307
308 // Try to read the FuzzTest structure
309 test1 := fuzztest.NewFuzzTest()
310 err := test1.Read(context.Background(), protocol)
311 if err != nil {
312 // Invalid input, but not a crash
313 return 0
314 }
315
316 // Serialize back
317 outTransport := thrift.NewTMemoryBuffer()
318 outProtocol := thrift.NewTCompactProtocolFactoryConf(config).GetProtocol(outTransport)
319 err = test1.Write(context.Background(), outProtocol)
320 if err != nil {
321 return 0
322 }
323
324 // Get serialized data and deserialize again
325 serialized := outTransport.Bytes()
326 reTransport := thrift.NewTMemoryBufferLen(len(serialized))
327 reTransport.Write(serialized)
328 reProtocol := thrift.NewTCompactProtocolFactoryConf(config).GetProtocol(reTransport)
329
330 test2 := fuzztest.NewFuzzTest()
331 err = test2.Read(context.Background(), reProtocol)
332 if err != nil {
333 return 0
334 }
335
336 // Verify equality
337 if !test1.Equals(test2) {
338 panic("Roundtrip failed: objects not equal after deserialization")
339 }
340
341 return 1
342}
343
344func FuzzRoundtripJson(data []byte) int {
345 // Skip if input is too small
346 if len(data) < 1 {
347 return 0
348 }
349
350 // First parse
351 transport := thrift.NewTMemoryBufferLen(len(data))
352 transport.Write(data)
353 protocol := thrift.NewTJSONProtocolFactory().GetProtocol(transport)
354
355 // Try to read the FuzzTest structure
356 test1 := fuzztest.NewFuzzTest()
357 err := test1.Read(context.Background(), protocol)
358 if err != nil {
359 // Invalid input, but not a crash
360 return 0
361 }
362
363 // Serialize back
364 outTransport := thrift.NewTMemoryBuffer()
365 outProtocol := thrift.NewTJSONProtocolFactory().GetProtocol(outTransport)
366 err = test1.Write(context.Background(), outProtocol)
367 if err != nil {
368 return 0
369 }
370
371 // Get serialized data and deserialize again
372 serialized := outTransport.Bytes()
373 reTransport := thrift.NewTMemoryBufferLen(len(serialized))
374 reTransport.Write(serialized)
375 reProtocol := thrift.NewTJSONProtocolFactory().GetProtocol(reTransport)
376
377 test2 := fuzztest.NewFuzzTest()
378 err = test2.Read(context.Background(), reProtocol)
379 if err != nil {
380 return 0
381 }
382
383 // Verify equality
384 if !test1.Equals(test2) {
385 panic("Roundtrip failed: objects not equal after deserialization")
386 }
387
388 return 1
389}