1
2
3
4
5
6
7
8 package fuzz
9
10 import (
11 "bytes"
12 "context"
13 "crypto/sha256"
14 "errors"
15 "fmt"
16 "internal/godebug"
17 "io"
18 "math/bits"
19 "os"
20 "path/filepath"
21 "reflect"
22 "runtime"
23 "strings"
24 "time"
25 )
26
27
28
29 type CoordinateFuzzingOpts struct {
30
31
32 Log io.Writer
33
34
35
36 Timeout time.Duration
37
38
39
40 Limit int64
41
42
43
44
45
46 MinimizeTimeout time.Duration
47
48
49
50
51
52
53 MinimizeLimit int64
54
55
56
57 Parallel int
58
59
60
61 Seed []CorpusEntry
62
63
64
65 Types []reflect.Type
66
67
68
69 CorpusDir string
70
71
72
73 CacheDir string
74 }
75
76
77
78
79
80
81
82
83
84
85 func CoordinateFuzzing(ctx context.Context, opts CoordinateFuzzingOpts) (err error) {
86 if err := ctx.Err(); err != nil {
87 return err
88 }
89 if opts.Log == nil {
90 opts.Log = io.Discard
91 }
92 if opts.Parallel == 0 {
93 opts.Parallel = runtime.GOMAXPROCS(0)
94 }
95 if opts.Limit > 0 && int64(opts.Parallel) > opts.Limit {
96
97 opts.Parallel = int(opts.Limit)
98 }
99
100 c, err := newCoordinator(opts)
101 if err != nil {
102 return err
103 }
104
105 if opts.Timeout > 0 {
106 var cancel func()
107 ctx, cancel = context.WithTimeout(ctx, opts.Timeout)
108 defer cancel()
109 }
110
111
112 fuzzCtx, cancelWorkers := context.WithCancel(ctx)
113 defer cancelWorkers()
114 doneC := ctx.Done()
115
116
117 var fuzzErr error
118 stopping := false
119 stop := func(err error) {
120 if err == fuzzCtx.Err() || isInterruptError(err) {
121
122
123
124 err = nil
125 }
126 if err != nil && (fuzzErr == nil || fuzzErr == ctx.Err()) {
127 fuzzErr = err
128 }
129 if stopping {
130 return
131 }
132 stopping = true
133 cancelWorkers()
134 doneC = nil
135 }
136
137
138
139 crashWritten := false
140 defer func() {
141 if c.crashMinimizing == nil || crashWritten {
142 return
143 }
144 werr := writeToCorpus(&c.crashMinimizing.entry, opts.CorpusDir)
145 if werr != nil {
146 err = fmt.Errorf("%w\n%v", err, werr)
147 return
148 }
149 if err == nil {
150 err = &crashError{
151 path: c.crashMinimizing.entry.Path,
152 err: errors.New(c.crashMinimizing.crasherMsg),
153 }
154 }
155 }()
156
157
158
159 dir := ""
160 binPath := os.Args[0]
161 args := append([]string{"-test.fuzzworker"}, os.Args[1:]...)
162 env := os.Environ()
163
164 errC := make(chan error)
165 workers := make([]*worker, opts.Parallel)
166 for i := range workers {
167 var err error
168 workers[i], err = newWorker(c, dir, binPath, args, env)
169 if err != nil {
170 return err
171 }
172 }
173 for i := range workers {
174 w := workers[i]
175 go func() {
176 err := w.coordinate(fuzzCtx)
177 if fuzzCtx.Err() != nil || isInterruptError(err) {
178 err = nil
179 }
180 cleanErr := w.cleanup()
181 if err == nil {
182 err = cleanErr
183 }
184 errC <- err
185 }()
186 }
187
188
189
190
191 activeWorkers := len(workers)
192 statTicker := time.NewTicker(3 * time.Second)
193 defer statTicker.Stop()
194 defer c.logStats()
195
196 c.logStats()
197 for {
198 var inputC chan fuzzInput
199 input, ok := c.peekInput()
200 if ok && c.crashMinimizing == nil && !stopping {
201 inputC = c.inputC
202 }
203
204 var minimizeC chan fuzzMinimizeInput
205 minimizeInput, ok := c.peekMinimizeInput()
206 if ok && !stopping {
207 minimizeC = c.minimizeC
208 }
209
210 select {
211 case <-doneC:
212
213
214 stop(ctx.Err())
215
216 case err := <-errC:
217
218 stop(err)
219 activeWorkers--
220 if activeWorkers == 0 {
221 return fuzzErr
222 }
223
224 case result := <-c.resultC:
225
226 if stopping {
227 break
228 }
229 c.updateStats(result)
230
231 if result.crasherMsg != "" {
232 if c.warmupRun() && result.entry.IsSeed {
233 target := filepath.Base(c.opts.CorpusDir)
234 fmt.Fprintf(c.opts.Log, "failure while testing seed corpus entry: %s/%s\n", target, testName(result.entry.Parent))
235 stop(errors.New(result.crasherMsg))
236 break
237 }
238 if c.canMinimize() && result.canMinimize {
239 if c.crashMinimizing != nil {
240
241
242 break
243 }
244
245
246
247 c.crashMinimizing = &result
248 fmt.Fprintf(c.opts.Log, "fuzz: minimizing %d-byte failing input file\n", len(result.entry.Data))
249 c.queueForMinimization(result, nil)
250 } else if !crashWritten {
251
252
253 err := writeToCorpus(&result.entry, opts.CorpusDir)
254 if err == nil {
255 crashWritten = true
256 err = &crashError{
257 path: result.entry.Path,
258 err: errors.New(result.crasherMsg),
259 }
260 }
261 if shouldPrintDebugInfo() {
262 fmt.Fprintf(
263 c.opts.Log,
264 "DEBUG new crasher, elapsed: %s, id: %s, parent: %s, gen: %d, size: %d, exec time: %s\n",
265 c.elapsed(),
266 result.entry.Path,
267 result.entry.Parent,
268 result.entry.Generation,
269 len(result.entry.Data),
270 result.entryDuration,
271 )
272 }
273 stop(err)
274 }
275 } else if result.coverageData != nil {
276 if c.warmupRun() {
277 if shouldPrintDebugInfo() {
278 fmt.Fprintf(
279 c.opts.Log,
280 "DEBUG processed an initial input, elapsed: %s, id: %s, new bits: %d, size: %d, exec time: %s\n",
281 c.elapsed(),
282 result.entry.Parent,
283 countBits(diffCoverage(c.coverageMask, result.coverageData)),
284 len(result.entry.Data),
285 result.entryDuration,
286 )
287 }
288 c.updateCoverage(result.coverageData)
289 c.warmupInputLeft--
290 if c.warmupInputLeft == 0 {
291 fmt.Fprintf(c.opts.Log, "fuzz: elapsed: %s, gathering baseline coverage: %d/%d completed, now fuzzing with %d workers\n", c.elapsed(), c.warmupInputCount, c.warmupInputCount, c.opts.Parallel)
292 if shouldPrintDebugInfo() {
293 fmt.Fprintf(
294 c.opts.Log,
295 "DEBUG finished processing input corpus, elapsed: %s, entries: %d, initial coverage bits: %d\n",
296 c.elapsed(),
297 len(c.corpus.entries),
298 countBits(c.coverageMask),
299 )
300 }
301 }
302 } else if keepCoverage := diffCoverage(c.coverageMask, result.coverageData); keepCoverage != nil {
303
304
305
306
307
308
309
310
311 if c.canMinimize() && result.canMinimize && c.crashMinimizing == nil {
312
313
314 c.queueForMinimization(result, keepCoverage)
315 } else {
316
317 inputSize := len(result.entry.Data)
318 entryNew, err := c.addCorpusEntries(true, result.entry)
319 if err != nil {
320 stop(err)
321 break
322 }
323 if !entryNew {
324 continue
325 }
326 c.updateCoverage(keepCoverage)
327 c.inputQueue.enqueue(result.entry)
328 c.interestingCount++
329 if shouldPrintDebugInfo() {
330 fmt.Fprintf(
331 c.opts.Log,
332 "DEBUG new interesting input, elapsed: %s, id: %s, parent: %s, gen: %d, new bits: %d, total bits: %d, size: %d, exec time: %s\n",
333 c.elapsed(),
334 result.entry.Path,
335 result.entry.Parent,
336 result.entry.Generation,
337 countBits(keepCoverage),
338 countBits(c.coverageMask),
339 inputSize,
340 result.entryDuration,
341 )
342 }
343 }
344 } else {
345 if shouldPrintDebugInfo() {
346 fmt.Fprintf(
347 c.opts.Log,
348 "DEBUG worker reported interesting input that doesn't expand coverage, elapsed: %s, id: %s, parent: %s, canMinimize: %t\n",
349 c.elapsed(),
350 result.entry.Path,
351 result.entry.Parent,
352 result.canMinimize,
353 )
354 }
355 }
356 } else if c.warmupRun() {
357
358
359 c.warmupInputLeft--
360 if c.warmupInputLeft == 0 {
361 fmt.Fprintf(c.opts.Log, "fuzz: elapsed: %s, testing seed corpus: %d/%d completed, now fuzzing with %d workers\n", c.elapsed(), c.warmupInputCount, c.warmupInputCount, c.opts.Parallel)
362 if shouldPrintDebugInfo() {
363 fmt.Fprintf(
364 c.opts.Log,
365 "DEBUG finished testing-only phase, elapsed: %s, entries: %d\n",
366 time.Since(c.startTime),
367 len(c.corpus.entries),
368 )
369 }
370 }
371 }
372
373
374
375 if c.opts.Limit > 0 && c.count >= c.opts.Limit {
376 stop(nil)
377 }
378
379 case inputC <- input:
380
381 c.sentInput(input)
382
383 case minimizeC <- minimizeInput:
384
385 c.sentMinimizeInput(minimizeInput)
386
387 case <-statTicker.C:
388 c.logStats()
389 }
390 }
391
392
393
394 }
395
396
397
398
399 type crashError struct {
400 path string
401 err error
402 }
403
404 func (e *crashError) Error() string {
405 return e.err.Error()
406 }
407
408 func (e *crashError) Unwrap() error {
409 return e.err
410 }
411
412 func (e *crashError) CrashPath() string {
413 return e.path
414 }
415
416 type corpus struct {
417 entries []CorpusEntry
418 hashes map[[sha256.Size]byte]bool
419 }
420
421
422
423
424
425 func (c *coordinator) addCorpusEntries(addToCache bool, entries ...CorpusEntry) (bool, error) {
426 noDupes := true
427 for _, e := range entries {
428 data, err := corpusEntryData(e)
429 if err != nil {
430 return false, err
431 }
432 h := sha256.Sum256(data)
433 if c.corpus.hashes[h] {
434 noDupes = false
435 continue
436 }
437 if addToCache {
438 if err := writeToCorpus(&e, c.opts.CacheDir); err != nil {
439 return false, err
440 }
441
442
443
444 e.Data = nil
445 }
446 c.corpus.hashes[h] = true
447 c.corpus.entries = append(c.corpus.entries, e)
448 }
449 return noDupes, nil
450 }
451
452
453
454
455
456
457
458 type CorpusEntry = struct {
459 Parent string
460
461
462
463
464 Path string
465
466
467
468
469 Data []byte
470
471
472 Values []any
473
474 Generation int
475
476
477 IsSeed bool
478 }
479
480
481
482 func corpusEntryData(ce CorpusEntry) ([]byte, error) {
483 if ce.Data != nil {
484 return ce.Data, nil
485 }
486
487 return os.ReadFile(ce.Path)
488 }
489
490 type fuzzInput struct {
491
492
493 entry CorpusEntry
494
495
496
497 timeout time.Duration
498
499
500
501
502
503 limit int64
504
505
506
507 warmup bool
508
509
510 coverageData []byte
511 }
512
513 type fuzzResult struct {
514
515 entry CorpusEntry
516
517
518 crasherMsg string
519
520
521
522 canMinimize bool
523
524
525 coverageData []byte
526
527
528
529 limit int64
530
531
532 count int64
533
534
535 totalDuration time.Duration
536
537
538 entryDuration time.Duration
539 }
540
541 type fuzzMinimizeInput struct {
542
543 entry CorpusEntry
544
545
546
547
548 crasherMsg string
549
550
551
552
553 limit int64
554
555
556
557 timeout time.Duration
558
559
560
561
562
563 keepCoverage []byte
564 }
565
566
567
568 type coordinator struct {
569 opts CoordinateFuzzingOpts
570
571
572
573 startTime time.Time
574
575
576
577 inputC chan fuzzInput
578
579
580
581 minimizeC chan fuzzMinimizeInput
582
583
584
585 resultC chan fuzzResult
586
587
588 count int64
589
590
591
592 countLastLog int64
593
594
595 timeLastLog time.Time
596
597
598
599 interestingCount int
600
601
602
603
604
605 warmupInputCount int
606
607
608
609
610 warmupInputLeft int
611
612
613
614 duration time.Duration
615
616
617
618 countWaiting int64
619
620
621
622 corpus corpus
623
624
625
626 minimizationAllowed bool
627
628
629
630
631 inputQueue queue
632
633
634
635
636 minimizeQueue queue
637
638
639 crashMinimizing *fuzzResult
640
641
642
643
644
645
646
647 coverageMask []byte
648 }
649
650 func newCoordinator(opts CoordinateFuzzingOpts) (*coordinator, error) {
651
652 for i := range opts.Seed {
653 if opts.Seed[i].Data == nil && opts.Seed[i].Values != nil {
654 opts.Seed[i].Data = marshalCorpusFile(opts.Seed[i].Values...)
655 }
656 }
657 c := &coordinator{
658 opts: opts,
659 startTime: time.Now(),
660 inputC: make(chan fuzzInput),
661 minimizeC: make(chan fuzzMinimizeInput),
662 resultC: make(chan fuzzResult),
663 timeLastLog: time.Now(),
664 corpus: corpus{hashes: make(map[[sha256.Size]byte]bool)},
665 }
666 if err := c.readCache(); err != nil {
667 return nil, err
668 }
669 if opts.MinimizeLimit > 0 || opts.MinimizeTimeout > 0 {
670 for _, t := range opts.Types {
671 if isMinimizable(t) {
672 c.minimizationAllowed = true
673 break
674 }
675 }
676 }
677
678 covSize := len(coverage())
679 if covSize == 0 {
680 fmt.Fprintf(c.opts.Log, "warning: the test binary was not built with coverage instrumentation, so fuzzing will run without coverage guidance and may be inefficient\n")
681
682
683
684 c.warmupInputCount = len(c.opts.Seed)
685 for _, e := range c.opts.Seed {
686 c.inputQueue.enqueue(e)
687 }
688 } else {
689 c.warmupInputCount = len(c.corpus.entries)
690 for _, e := range c.corpus.entries {
691 c.inputQueue.enqueue(e)
692 }
693
694 c.coverageMask = make([]byte, covSize)
695 }
696 c.warmupInputLeft = c.warmupInputCount
697
698 if len(c.corpus.entries) == 0 {
699 fmt.Fprintf(c.opts.Log, "warning: starting with empty corpus\n")
700 var vals []any
701 for _, t := range opts.Types {
702 vals = append(vals, zeroValue(t))
703 }
704 data := marshalCorpusFile(vals...)
705 h := sha256.Sum256(data)
706 name := fmt.Sprintf("%x", h[:4])
707 c.addCorpusEntries(false, CorpusEntry{Path: name, Data: data})
708 }
709
710 return c, nil
711 }
712
713 func (c *coordinator) updateStats(result fuzzResult) {
714 c.count += result.count
715 c.countWaiting -= result.limit
716 c.duration += result.totalDuration
717 }
718
719 func (c *coordinator) logStats() {
720 now := time.Now()
721 if c.warmupRun() {
722 runSoFar := c.warmupInputCount - c.warmupInputLeft
723 if coverageEnabled {
724 fmt.Fprintf(c.opts.Log, "fuzz: elapsed: %s, gathering baseline coverage: %d/%d completed\n", c.elapsed(), runSoFar, c.warmupInputCount)
725 } else {
726 fmt.Fprintf(c.opts.Log, "fuzz: elapsed: %s, testing seed corpus: %d/%d completed\n", c.elapsed(), runSoFar, c.warmupInputCount)
727 }
728 } else if c.crashMinimizing != nil {
729 fmt.Fprintf(c.opts.Log, "fuzz: elapsed: %s, minimizing\n", c.elapsed())
730 } else {
731 rate := float64(c.count-c.countLastLog) / now.Sub(c.timeLastLog).Seconds()
732 if coverageEnabled {
733 total := c.warmupInputCount + c.interestingCount
734 fmt.Fprintf(c.opts.Log, "fuzz: elapsed: %s, execs: %d (%.0f/sec), new interesting: %d (total: %d)\n", c.elapsed(), c.count, rate, c.interestingCount, total)
735 } else {
736 fmt.Fprintf(c.opts.Log, "fuzz: elapsed: %s, execs: %d (%.0f/sec)\n", c.elapsed(), c.count, rate)
737 }
738 }
739 c.countLastLog = c.count
740 c.timeLastLog = now
741 }
742
743
744
745
746
747
748
749
750
751
752
753 func (c *coordinator) peekInput() (fuzzInput, bool) {
754 if c.opts.Limit > 0 && c.count+c.countWaiting >= c.opts.Limit {
755
756
757 return fuzzInput{}, false
758 }
759 if c.inputQueue.len == 0 {
760 if c.warmupRun() {
761
762
763 return fuzzInput{}, false
764 }
765 c.refillInputQueue()
766 }
767
768 entry, ok := c.inputQueue.peek()
769 if !ok {
770 panic("input queue empty after refill")
771 }
772 input := fuzzInput{
773 entry: entry.(CorpusEntry),
774 timeout: workerFuzzDuration,
775 warmup: c.warmupRun(),
776 }
777 if c.coverageMask != nil {
778 input.coverageData = bytes.Clone(c.coverageMask)
779 }
780 if input.warmup {
781
782
783 input.limit = 1
784 return input, true
785 }
786
787 if c.opts.Limit > 0 {
788 input.limit = c.opts.Limit / int64(c.opts.Parallel)
789 if c.opts.Limit%int64(c.opts.Parallel) > 0 {
790 input.limit++
791 }
792 remaining := c.opts.Limit - c.count - c.countWaiting
793 if input.limit > remaining {
794 input.limit = remaining
795 }
796 }
797 return input, true
798 }
799
800
801 func (c *coordinator) sentInput(input fuzzInput) {
802 c.inputQueue.dequeue()
803 c.countWaiting += input.limit
804 }
805
806
807
808 func (c *coordinator) refillInputQueue() {
809 for _, e := range c.corpus.entries {
810 c.inputQueue.enqueue(e)
811 }
812 }
813
814
815
816 func (c *coordinator) queueForMinimization(result fuzzResult, keepCoverage []byte) {
817 if result.crasherMsg != "" {
818 c.minimizeQueue.clear()
819 }
820
821 input := fuzzMinimizeInput{
822 entry: result.entry,
823 crasherMsg: result.crasherMsg,
824 keepCoverage: keepCoverage,
825 }
826 c.minimizeQueue.enqueue(input)
827 }
828
829
830
831 func (c *coordinator) peekMinimizeInput() (fuzzMinimizeInput, bool) {
832 if !c.canMinimize() {
833
834
835 return fuzzMinimizeInput{}, false
836 }
837 v, ok := c.minimizeQueue.peek()
838 if !ok {
839 return fuzzMinimizeInput{}, false
840 }
841 input := v.(fuzzMinimizeInput)
842
843 if c.opts.MinimizeTimeout > 0 {
844 input.timeout = c.opts.MinimizeTimeout
845 }
846 if c.opts.MinimizeLimit > 0 {
847 input.limit = c.opts.MinimizeLimit
848 } else if c.opts.Limit > 0 {
849 if input.crasherMsg != "" {
850 input.limit = c.opts.Limit
851 } else {
852 input.limit = c.opts.Limit / int64(c.opts.Parallel)
853 if c.opts.Limit%int64(c.opts.Parallel) > 0 {
854 input.limit++
855 }
856 }
857 }
858 if c.opts.Limit > 0 {
859 remaining := c.opts.Limit - c.count - c.countWaiting
860 if input.limit > remaining {
861 input.limit = remaining
862 }
863 }
864 return input, true
865 }
866
867
868
869 func (c *coordinator) sentMinimizeInput(input fuzzMinimizeInput) {
870 c.minimizeQueue.dequeue()
871 c.countWaiting += input.limit
872 }
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887 func (c *coordinator) warmupRun() bool {
888 return c.warmupInputLeft > 0
889 }
890
891
892
893
894 func (c *coordinator) updateCoverage(newCoverage []byte) int {
895 if len(newCoverage) != len(c.coverageMask) {
896 panic(fmt.Sprintf("number of coverage counters changed at runtime: %d, expected %d", len(newCoverage), len(c.coverageMask)))
897 }
898 newBitCount := 0
899 for i := range newCoverage {
900 diff := newCoverage[i] &^ c.coverageMask[i]
901 newBitCount += bits.OnesCount8(diff)
902 c.coverageMask[i] |= newCoverage[i]
903 }
904 return newBitCount
905 }
906
907
908
909 func (c *coordinator) canMinimize() bool {
910 return c.minimizationAllowed &&
911 (c.opts.Limit == 0 || c.count+c.countWaiting < c.opts.Limit)
912 }
913
914 func (c *coordinator) elapsed() time.Duration {
915 return time.Since(c.startTime).Round(1 * time.Second)
916 }
917
918
919
920
921
922
923 func (c *coordinator) readCache() error {
924 if _, err := c.addCorpusEntries(false, c.opts.Seed...); err != nil {
925 return err
926 }
927 entries, err := ReadCorpus(c.opts.CacheDir, c.opts.Types)
928 if err != nil {
929 if _, ok := err.(*MalformedCorpusError); !ok {
930
931
932 return err
933 }
934
935
936
937 }
938 if _, err := c.addCorpusEntries(false, entries...); err != nil {
939 return err
940 }
941 return nil
942 }
943
944
945
946
947 type MalformedCorpusError struct {
948 errs []error
949 }
950
951 func (e *MalformedCorpusError) Error() string {
952 var msgs []string
953 for _, s := range e.errs {
954 msgs = append(msgs, s.Error())
955 }
956 return strings.Join(msgs, "\n")
957 }
958
959
960
961
962
963 func ReadCorpus(dir string, types []reflect.Type) ([]CorpusEntry, error) {
964 files, err := os.ReadDir(dir)
965 if os.IsNotExist(err) {
966 return nil, nil
967 } else if err != nil {
968 return nil, fmt.Errorf("reading seed corpus from testdata: %v", err)
969 }
970 var corpus []CorpusEntry
971 var errs []error
972 for _, file := range files {
973
974
975
976
977
978 if file.IsDir() {
979 continue
980 }
981 filename := filepath.Join(dir, file.Name())
982 data, err := os.ReadFile(filename)
983 if err != nil {
984 return nil, fmt.Errorf("failed to read corpus file: %v", err)
985 }
986 var vals []any
987 vals, err = readCorpusData(data, types)
988 if err != nil {
989 errs = append(errs, fmt.Errorf("%q: %v", filename, err))
990 continue
991 }
992 corpus = append(corpus, CorpusEntry{Path: filename, Values: vals})
993 }
994 if len(errs) > 0 {
995 return corpus, &MalformedCorpusError{errs: errs}
996 }
997 return corpus, nil
998 }
999
1000 func readCorpusData(data []byte, types []reflect.Type) ([]any, error) {
1001 vals, err := unmarshalCorpusFile(data)
1002 if err != nil {
1003 return nil, fmt.Errorf("unmarshal: %v", err)
1004 }
1005 if err = CheckCorpus(vals, types); err != nil {
1006 return nil, err
1007 }
1008 return vals, nil
1009 }
1010
1011
1012
1013 func CheckCorpus(vals []any, types []reflect.Type) error {
1014 if len(vals) != len(types) {
1015 return fmt.Errorf("wrong number of values in corpus entry: %d, want %d", len(vals), len(types))
1016 }
1017 valsT := make([]reflect.Type, len(vals))
1018 for valsI, v := range vals {
1019 valsT[valsI] = reflect.TypeOf(v)
1020 }
1021 for i := range types {
1022 if valsT[i] != types[i] {
1023 return fmt.Errorf("mismatched types in corpus entry: %v, want %v", valsT, types)
1024 }
1025 }
1026 return nil
1027 }
1028
1029
1030
1031
1032
1033 func writeToCorpus(entry *CorpusEntry, dir string) (err error) {
1034 sum := fmt.Sprintf("%x", sha256.Sum256(entry.Data))[:16]
1035 entry.Path = filepath.Join(dir, sum)
1036 if err := os.MkdirAll(dir, 0777); err != nil {
1037 return err
1038 }
1039 if err := os.WriteFile(entry.Path, entry.Data, 0666); err != nil {
1040 os.Remove(entry.Path)
1041 return err
1042 }
1043 return nil
1044 }
1045
1046 func testName(path string) string {
1047 return filepath.Base(path)
1048 }
1049
1050 func zeroValue(t reflect.Type) any {
1051 for _, v := range zeroVals {
1052 if reflect.TypeOf(v) == t {
1053 return v
1054 }
1055 }
1056 panic(fmt.Sprintf("unsupported type: %v", t))
1057 }
1058
1059 var zeroVals []any = []any{
1060 []byte(""),
1061 string(""),
1062 false,
1063 byte(0),
1064 rune(0),
1065 float32(0),
1066 float64(0),
1067 int(0),
1068 int8(0),
1069 int16(0),
1070 int32(0),
1071 int64(0),
1072 uint(0),
1073 uint8(0),
1074 uint16(0),
1075 uint32(0),
1076 uint64(0),
1077 }
1078
1079 var debugInfo = godebug.New("fuzzdebug").Value() == "1"
1080
1081 func shouldPrintDebugInfo() bool {
1082 return debugInfo
1083 }
1084
View as plain text