├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── gen └── main.go ├── go-fuzz-build ├── cover.go └── main.go ├── go-fuzz-defs └── defs.go ├── go-fuzz-dep ├── cover.go ├── doc.go ├── main.go ├── main_libFuzzer.go ├── sonar.go ├── sys_posix.go └── sys_windows.go ├── go-fuzz ├── assets │ ├── bootstrap-theme.min.css │ ├── bootstrap.min.css │ ├── bootstrap.min.js │ ├── jquery.min.js │ └── stats.html ├── bindata_assetfs.go ├── compare.go ├── compare_amd64.go ├── compare_amd64.s ├── coordinator.go ├── cover.go ├── cover_test.go ├── cpu_amd64.go ├── cpu_amd64.s ├── exectype_string.go ├── hub.go ├── internal │ └── pcg │ │ └── pcg.go ├── main.go ├── mutator.go ├── persistent.go ├── sonar.go ├── sys_posix.go ├── sys_windows.go ├── testee.go ├── vendor │ └── github.com │ │ ├── elazarl │ │ └── go-bindata-assetfs │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── assetfs.go │ │ │ ├── doc.go │ │ │ └── go-bindata-assetfs │ │ │ └── main.go │ │ └── stephens2424 │ │ └── writerset │ │ ├── readme.md │ │ └── writerset.go ├── versifier │ ├── versifier.go │ └── versifier_test.go ├── worker.go └── worker_test.go ├── internal └── go-fuzz-types │ └── types.go ├── slides ├── README.md ├── algo.png ├── bugs.png ├── crash.go ├── fuzzing.slide ├── go-fuzz.png ├── go-fuzz.slide ├── gob.go └── regexp.go ├── test ├── corpus │ ├── 0 │ ├── 1 │ └── 2 ├── internal │ └── test │ │ └── test.go ├── test.go ├── testdep │ └── testdep.go └── vendor │ └── non.existent.com │ └── foo │ └── foo.go └── testscripts ├── fuzz_help.txt ├── mod_go_fuzz_dep.txt ├── mod_inside_gopath.txt ├── mod_outside_gopath.txt ├── mod_v2.txt └── mod_vendor.txt /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *-fuzz$ 3 | 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # There are currently two main sets of tests 2 | # 1. Running go-fuzz-build and go-fuzz on dvyukov/go-fuzz-corpus/png starting with an empty corpus, 3 | # and it validates >1 entry appears in the corpus. This uses what was formerly the required 4 | # arguments like -workdir and -bin. 5 | # 2. Running go-fuzz-build and go-fuzz on dvyukov/go-fuzz/test, which contains some regression tests, etc. 6 | # This uses the newer ability to not supply any required arguments to go-fuzz-build and go-fuzz. 7 | 8 | language: go 9 | 10 | # Test Go 1.16 and 1.15 across linux, osx, windows. 11 | # Test Go tip on linux, osx (and explicitly set GO111MODULE=auto for tip, given default might change). 12 | # SET_GO111MODULE is used because (at least early on) setting GO111MODULE directly interfered with the setup performed by travis. 13 | matrix: 14 | include: 15 | - os: linux 16 | go: "1.16.x" 17 | - os: linux 18 | go: "1.15.x" 19 | - os: linux 20 | go: tip 21 | - os: linux 22 | go: tip 23 | env: SET_GO111MODULE=auto 24 | - os: osx 25 | go: "1.16.x" 26 | - os: osx 27 | go: "1.15.x" 28 | - os: osx 29 | go: tip 30 | - os: osx 31 | go: tip 32 | env: SET_GO111MODULE=auto 33 | - os: windows 34 | go: "1.16.x" 35 | - os: windows 36 | go: "1.15.x" 37 | 38 | # Install coreutils for the 'timeout(1)' utility on windows and osx. 39 | before_install: 40 | - if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then choco install gnuwin32-coreutils.install; fi 41 | - go get -u github.com/rogpeppe/go-internal/cmd/testscript 42 | 43 | # Set the import path (including to help with forks). 44 | go_import_path: github.com/dvyukov/go-fuzz 45 | 46 | script: 47 | # Sanity check 'timeout(1)'. 48 | - which timeout 49 | - "echo 'verify the timeout utility works, including that it exits with status 124 on timeout.'" 50 | - "(timeout 2 sleep 10; ret=$?; echo timeout ret=$ret; if [[ $ret -eq 124 ]]; then exit 0; fi; exit $ret)" 51 | 52 | # Sanity check that the 'testscript' cmd seems to function at all. 53 | # If all the testscripts fail, this is a good one to troubleshoot first. 54 | - cd $GOPATH/src/github.com/dvyukov/go-fuzz 55 | - testscript -v testscripts/fuzz_help.txt 56 | 57 | # Run our tests for fuzzing modules. 58 | # If multiple modules testcripts fail, probably makes sense to start by troubleshooting 59 | # the earliest failing testscript. 60 | # TODO: probably makes more sense to move this further down, but place here for now for faster iterations. 61 | - testscript -v testscripts/mod_go_fuzz_dep.txt 62 | - testscript -v testscripts/mod_outside_gopath.txt 63 | - testscript -v testscripts/mod_inside_gopath.txt 64 | - testscript -v testscripts/mod_v2.txt 65 | - testscript -v testscripts/mod_vendor.txt 66 | 67 | # Prepare to test the png example from dvyukov/go-fuzz-corpus. 68 | - git clone --depth=1 https://github.com/dvyukov/go-fuzz-corpus $GOPATH/src/github.com/dvyukov/go-fuzz-corpus 69 | - cd $GOPATH/src/github.com/dvyukov/go-fuzz-corpus/ 70 | - cd png 71 | # Create a small module to test the png example. 72 | - go mod init github.com/dvyukov/go-fuzz-corpus/png 73 | - go mod tidy 74 | - go get -d github.com/dvyukov/go-fuzz/go-fuzz-dep 75 | - ls -l 76 | 77 | # Reduce chances of future surprises due to any caching. 78 | - rm -rf fuzz.zip ./freshworkdir 79 | 80 | # Explicitly set GO111MODULE if requested. 81 | # Travis and/or tip might change the default value of GO111MODULE at some point. 82 | # As of 2019-08-31, travis sets GO111MODULE=auto, and tip defaults to 'auto' if 83 | # GO111MODULE is unset. 84 | - if [[ ! -z "$SET_GO111MODULE" ]]; then export GO111MODULE="$SET_GO111MODULE"; fi 85 | - echo "GO111MODULE=$GO111MODULE" 86 | 87 | # Instrument using go-fuzz-build on the png example Fuzz function. 88 | - which go-fuzz-build 89 | - go-fuzz-build -o=./fuzz.zip github.com/dvyukov/go-fuzz-corpus/png 90 | 91 | # Run go-fuzz on the result of instrumenting the png example Fuzz function. 92 | # Stop after 20 sec of fuzzing. 93 | - which go-fuzz 94 | - "(timeout 20 go-fuzz -bin=./fuzz.zip -workdir=./freshworkdir; ret=$?; echo timeout ret=$ret; if [[ $ret -eq 124 ]]; then exit 0; fi; exit $ret)" 95 | 96 | # Lightly validate that we have more than 1 result in the corpus. 97 | # Some chance this could fail if unlucky, but so far seems unlikely to have a false failure on this validation. 98 | - ls -lrt ./freshworkdir/corpus | tail -30 99 | - find ./freshworkdir/corpus/ -type f | wc -l 100 | - "(if [[ $(find ./freshworkdir/corpus/ -type f | wc -l) -gt 1 ]]; then exit 0; fi; exit 1)" 101 | 102 | # Instrument the test package from dvyukov/go-fuzz/test. 103 | - cd $GOPATH/src/github.com/dvyukov/go-fuzz 104 | - cd test 105 | - ls -l 106 | - rm -rf test-fuzz.zip 107 | - go-fuzz-build 108 | 109 | # End early for Windows. 'timeout' does not seem to kill this fuzzing session on Windows. 110 | # Presumably we could solve that with an alternative timeout/kill mechanism, but for 111 | # now workaround by skipping that last test on Windows. 112 | - if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then travis_terminate "${TRAVIS_TEST_RESULT}"; fi 113 | 114 | # Run go-fuzz on the result. Stop after 20 sec of fuzzing. 115 | - which go-fuzz 116 | - "(timeout 20 go-fuzz; ret=$?; echo timeout ret=$ret; if [[ $ret -eq 124 ]]; then exit 0; fi; exit $ret)" 117 | 118 | # Windows seems a bit flakey about capturing output on failure. This seems to help. 119 | after_failure: 120 | - "echo 'sleep 10 sec to help capture output. On Windows, failing output might be in wrong section, including this one.'" 121 | - sleep 10 122 | 123 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | -------------------------------------------------------------------------------- /gen/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-fuzz project authors. All rights reserved. 2 | // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 | 4 | package gen 5 | 6 | import ( 7 | "flag" 8 | "fmt" 9 | "math/rand" 10 | "os" 11 | "path/filepath" 12 | "time" 13 | ) 14 | 15 | var ( 16 | flagOut = flag.String("out", "", "output dir") 17 | flagN = flag.Int("n", 1000, "number of inputs to generate") 18 | seq = 0 19 | ) 20 | 21 | func init() { 22 | flag.Parse() 23 | if *flagOut == "" { 24 | fmt.Fprintf(os.Stderr, "output directory is not set\n") 25 | os.Exit(1) 26 | } 27 | if err := os.MkdirAll(*flagOut, 0760); err != nil { 28 | fmt.Fprintf(os.Stderr, "mkdir failed: %v\n", err) 29 | os.Exit(1) 30 | } 31 | rand.Seed(time.Now().UnixNano()) 32 | } 33 | 34 | func Rand(n int) int { 35 | return rand.Intn(n) 36 | } 37 | 38 | func Emit(data, hint []byte, valid bool) { 39 | f, err := os.Create(filepath.Join(*flagOut, fmt.Sprintf("%d", seq))) 40 | if err != nil { 41 | fmt.Fprintf(os.Stderr, "failed to create file: %v\n", err) 42 | os.Exit(1) 43 | } 44 | defer f.Close() 45 | 46 | n, err := f.Write(data) 47 | if err != nil { 48 | fmt.Fprintf(os.Stderr, "failed to write to file: %v\n", err) 49 | os.Exit(1) 50 | } else if n != len(data) { 51 | fmt.Fprint(os.Stderr, "failed to write data to file\n") 52 | os.Exit(1) 53 | } 54 | 55 | if seq++; seq == *flagN { 56 | os.Exit(0) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /go-fuzz-defs/defs.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-fuzz project authors. All rights reserved. 2 | // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 | 4 | // Package defs provides constants required by go-fuzz-build, go-fuzz, and instrumented code. 5 | package base 6 | 7 | // This package has a special interaction with go-fuzz-dep: 8 | // It is copied into a package with it by go-fuzz-build. 9 | // Only things that can be safely duplicated without confusion, 10 | // like constants, should be added to this package. 11 | // And any additions should be tested carefully. :) 12 | 13 | const ( 14 | CoverSize = 64 << 10 15 | MaxInputSize = 1 << 20 16 | SonarRegionSize = 1 << 20 17 | ) 18 | 19 | const ( 20 | SonarEQL = iota 21 | SonarNEQ 22 | SonarLSS 23 | SonarGTR 24 | SonarLEQ 25 | SonarGEQ 26 | 27 | SonarOpMask = 7 28 | SonarLength = 1 << 3 29 | SonarSigned = 1 << 4 30 | SonarString = 1 << 5 31 | SonarConst1 = 1 << 6 32 | SonarConst2 = 1 << 7 33 | 34 | SonarHdrLen = 6 35 | SonarMaxLen = 20 36 | ) 37 | -------------------------------------------------------------------------------- /go-fuzz-dep/cover.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 go-fuzz project authors. All rights reserved. 2 | // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 | 4 | // +build gofuzz 5 | 6 | package gofuzzdep 7 | 8 | import ( 9 | . "github.com/dvyukov/go-fuzz/go-fuzz-defs" 10 | ) 11 | 12 | // Bool is just a bool. 13 | // It is used by code autogenerated by go-fuzz-build 14 | // to avoid compilation errors when a user's code shadows the built-in bool. 15 | type Bool = bool 16 | 17 | // CoverTab holds code coverage. 18 | // It is initialized to a new array so that instrumentation 19 | // executed during process initialization has somewhere to write to. 20 | // It is replaced by a newly initialized array when it is 21 | // time for actual instrumentation to commence. 22 | var CoverTab = new([CoverSize]byte) 23 | -------------------------------------------------------------------------------- /go-fuzz-dep/doc.go: -------------------------------------------------------------------------------- 1 | // Package gofuzzdep contains the business logic used to monitor the fuzzing. 2 | // 3 | // It is handled specially by go-fuzz-build; see the comments in package go-fuzz-defs. 4 | // 5 | // Be particularly careful about adding imports to go-fuzz-dep: 6 | // Any package imported by go-fuzz-dep cannot be instrumented (on pain of import cycles), 7 | // which reduces the effectiveness of go-fuzz on any other package that imports it. 8 | // That is why (e.g.) there are hand-rolled serialization functions instead of using encoding/binary, 9 | // and hand-rolled syscall-based communication instead of using package net or os. 10 | package gofuzzdep 11 | -------------------------------------------------------------------------------- /go-fuzz-dep/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-fuzz project authors. All rights reserved. 2 | // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 | 4 | // +build gofuzz 5 | // +build !gofuzz_libfuzzer 6 | 7 | package gofuzzdep 8 | 9 | import ( 10 | "runtime" 11 | "sync/atomic" 12 | "syscall" 13 | "time" 14 | "unsafe" 15 | 16 | . "github.com/dvyukov/go-fuzz/go-fuzz-defs" 17 | ) 18 | 19 | func Main(fns []func([]byte) int) { 20 | mem, inFD, outFD := setupCommFile() 21 | CoverTab = (*[CoverSize]byte)(unsafe.Pointer(&mem[0])) 22 | input := mem[CoverSize : CoverSize+MaxInputSize] 23 | sonarRegion = mem[CoverSize+MaxInputSize:] 24 | runtime.GOMAXPROCS(1) // makes coverage more deterministic, we parallelize on higher level 25 | for { 26 | fnidx, n := read(inFD) 27 | if n > uint64(len(input)) { 28 | println("invalid input length") 29 | syscall.Exit(1) 30 | } 31 | for i := range CoverTab { 32 | CoverTab[i] = 0 33 | } 34 | atomic.StoreUint32(&sonarPos, 0) 35 | t0 := time.Now() 36 | res := fns[fnidx](input[:n:n]) 37 | ns := time.Since(t0) 38 | write(outFD, uint64(res), uint64(ns), uint64(atomic.LoadUint32(&sonarPos))) 39 | } 40 | } 41 | 42 | // read reads little-endian-encoded uint8+uint64 from fd. 43 | func read(fd FD) (uint8, uint64) { 44 | rd := 0 45 | var buf [9]byte 46 | for rd != len(buf) { 47 | n, err := fd.read(buf[rd:]) 48 | if err == syscall.EINTR { 49 | continue 50 | } 51 | if n == 0 { 52 | syscall.Exit(1) 53 | } 54 | if err != nil { 55 | println("failed to read fd =", fd, "errno =", err.(syscall.Errno)) 56 | syscall.Exit(1) 57 | } 58 | rd += n 59 | } 60 | return buf[0], deserialize64(buf[1:]) 61 | } 62 | 63 | // write writes little-endian-encoded vals... to fd. 64 | func write(fd FD, vals ...uint64) { 65 | var tmp [3 * 8]byte 66 | buf := tmp[:len(vals)*8] 67 | for i, v := range vals { 68 | serialize64(buf[i*8:], v) 69 | } 70 | wr := 0 71 | for wr != len(buf) { 72 | n, err := fd.write(buf[wr:]) 73 | if err == syscall.EINTR { 74 | continue 75 | } 76 | if err != nil { 77 | println("failed to read fd =", fd, "errno =", err.(syscall.Errno)) 78 | syscall.Exit(1) 79 | } 80 | wr += n 81 | } 82 | } 83 | 84 | // writeStr writes strings s to fd. 85 | func writeStr(fd FD, s string) { 86 | buf := []byte(s) 87 | wr := 0 88 | for wr != len(buf) { 89 | n, err := fd.write(buf[wr:]) 90 | if err == syscall.EINTR { 91 | continue 92 | } 93 | if err != nil { 94 | println("failed to read fd =", fd, "errno =", err.(syscall.Errno)) 95 | syscall.Exit(1) 96 | } 97 | wr += n 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /go-fuzz-dep/main_libFuzzer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-fuzz project authors. All rights reserved. 2 | // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 | 4 | // +build gofuzz 5 | // +build gofuzz_libfuzzer 6 | 7 | package gofuzzdep 8 | 9 | import ( 10 | "unsafe" 11 | 12 | . "github.com/dvyukov/go-fuzz/go-fuzz-defs" 13 | ) 14 | 15 | func Initialize(coverTabPtr unsafe.Pointer, coverTabSize uint64) { 16 | if coverTabSize != CoverSize { 17 | panic("Incorrect cover tab size") 18 | } 19 | CoverTab = (*[CoverSize]byte)(coverTabPtr) 20 | } 21 | -------------------------------------------------------------------------------- /go-fuzz-dep/sonar.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-fuzz project authors. All rights reserved. 2 | // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 | 4 | // +build gofuzz 5 | 6 | package gofuzzdep 7 | 8 | import ( 9 | "sync/atomic" 10 | "unsafe" 11 | 12 | . "github.com/dvyukov/go-fuzz/go-fuzz-defs" 13 | ) 14 | 15 | var ( 16 | sonarRegion []byte 17 | sonarPos uint32 18 | ) 19 | 20 | const failure = ^uint8(0) 21 | 22 | type iface struct { 23 | typ unsafe.Pointer 24 | val unsafe.Pointer 25 | } 26 | 27 | // Sonar is called by instrumentation code to notify go-fuzz about comparisons. 28 | // Low 8 bits of id are flags, the rest is unique id of a comparison. 29 | func Sonar(v1, v2 interface{}, id uint32) { 30 | var buf [SonarHdrLen + 2*SonarMaxLen]byte 31 | n1, f1 := serialize(v1, v2, buf[SonarHdrLen:]) 32 | if n1 == failure { 33 | return 34 | } 35 | n2, f2 := serialize(v2, v1, buf[SonarHdrLen+n1:]) 36 | if n2 == failure { 37 | return 38 | } 39 | // Ideal const operands are converted to signed int, 40 | // but it does not mean that the comparison is signed 41 | // unless the other operand is signed. 42 | if id&SonarConst1 != 0 { 43 | f1 &^= SonarSigned 44 | } 45 | if id&SonarConst2 != 0 { 46 | f2 &^= SonarSigned 47 | } 48 | id |= uint32(f1 | f2) 49 | serialize32(buf[:], id) 50 | buf[4] = n1 51 | buf[5] = n2 52 | n := uint32(SonarHdrLen + n1 + n2) 53 | pos := atomic.LoadUint32(&sonarPos) 54 | for { 55 | if pos+n > uint32(len(sonarRegion)) { 56 | return 57 | } 58 | if atomic.CompareAndSwapUint32(&sonarPos, pos, pos+n) { 59 | break 60 | } 61 | pos = atomic.LoadUint32(&sonarPos) 62 | } 63 | copy(sonarRegion[pos:pos+n], buf[:]) 64 | } 65 | 66 | func serialize(v, v2 interface{}, buf []byte) (n, flags uint8) { 67 | switch vv := v.(type) { 68 | case int8: 69 | buf[0] = byte(vv) 70 | return 1, SonarSigned 71 | case uint8: 72 | buf[0] = byte(vv) 73 | return 1, 0 74 | case int16: 75 | return serialize16(buf, uint16(vv)), SonarSigned 76 | case uint16: 77 | return serialize16(buf, vv), 0 78 | case int32: 79 | return serialize32(buf, uint32(vv)), SonarSigned 80 | case uint32: 81 | return serialize32(buf, vv), 0 82 | case int64: 83 | return serialize64(buf, uint64(vv)), SonarSigned 84 | case uint64: 85 | return serialize64(buf, vv), 0 86 | case int: 87 | if unsafe.Sizeof(vv) == 4 { 88 | return serialize32(buf, uint32(vv)), SonarSigned 89 | } else { 90 | return serialize64(buf, uint64(vv)), SonarSigned 91 | } 92 | case uint: 93 | if unsafe.Sizeof(vv) == 4 { 94 | return serialize32(buf, uint32(vv)), 0 95 | } else { 96 | return serialize64(buf, uint64(vv)), 0 97 | } 98 | case string: 99 | if len(vv) > SonarMaxLen { 100 | return failure, 0 101 | } 102 | return uint8(copy(buf, vv)), SonarString 103 | case [1]byte: 104 | return uint8(copy(buf, vv[:])), SonarString 105 | case [2]byte: 106 | return uint8(copy(buf, vv[:])), SonarString 107 | case [3]byte: 108 | return uint8(copy(buf, vv[:])), SonarString 109 | case [4]byte: 110 | return uint8(copy(buf, vv[:])), SonarString 111 | case [5]byte: 112 | return uint8(copy(buf, vv[:])), SonarString 113 | case [6]byte: 114 | return uint8(copy(buf, vv[:])), SonarString 115 | case [7]byte: 116 | return uint8(copy(buf, vv[:])), SonarString 117 | case [8]byte: 118 | return uint8(copy(buf, vv[:])), SonarString 119 | case [9]byte: 120 | return uint8(copy(buf, vv[:])), SonarString 121 | case [10]byte: 122 | return uint8(copy(buf, vv[:])), SonarString 123 | case [11]byte: 124 | return uint8(copy(buf, vv[:])), SonarString 125 | case [12]byte: 126 | return uint8(copy(buf, vv[:])), SonarString 127 | case [13]byte: 128 | return uint8(copy(buf, vv[:])), SonarString 129 | case [14]byte: 130 | return uint8(copy(buf, vv[:])), SonarString 131 | case [15]byte: 132 | return uint8(copy(buf, vv[:])), SonarString 133 | case [16]byte: 134 | return uint8(copy(buf, vv[:])), SonarString 135 | case [17]byte: 136 | return uint8(copy(buf, vv[:])), SonarString 137 | case [18]byte: 138 | return uint8(copy(buf, vv[:])), SonarString 139 | case [19]byte: 140 | return uint8(copy(buf, vv[:])), SonarString 141 | case [20]byte: 142 | return uint8(copy(buf, vv[:])), SonarString 143 | default: 144 | // Special case: string literal is compared with a variable of 145 | // user type with string underlying type: 146 | // type Name string 147 | // var name Name 148 | // if name == "foo" { ... } 149 | if _, ok := v2.(string); ok { 150 | s := *(*string)((*iface)(unsafe.Pointer(&v)).val) 151 | if len(s) <= SonarMaxLen { 152 | return uint8(copy(buf[:], s)), SonarString 153 | } 154 | } 155 | return failure, 0 156 | } 157 | } 158 | 159 | // The serialization routines here match those of encoding/binary.LittleEndian. 160 | // They are copied here because importing encoding/binary creates import cycles. 161 | 162 | func serialize16(buf []byte, v uint16) uint8 { 163 | _ = buf[1] 164 | buf[0] = byte(v >> 0) 165 | buf[1] = byte(v >> 8) 166 | return 2 167 | } 168 | 169 | func serialize32(buf []byte, v uint32) uint8 { 170 | _ = buf[3] 171 | buf[0] = byte(v >> 0) 172 | buf[1] = byte(v >> 8) 173 | buf[2] = byte(v >> 16) 174 | buf[3] = byte(v >> 24) 175 | return 4 176 | } 177 | 178 | func serialize64(buf []byte, v uint64) uint8 { 179 | _ = buf[7] 180 | buf[0] = byte(v >> 0) 181 | buf[1] = byte(v >> 8) 182 | buf[2] = byte(v >> 16) 183 | buf[3] = byte(v >> 24) 184 | buf[4] = byte(v >> 32) 185 | buf[5] = byte(v >> 40) 186 | buf[6] = byte(v >> 48) 187 | buf[7] = byte(v >> 56) 188 | return 8 189 | } 190 | 191 | func deserialize64(buf []byte) uint64 { 192 | _ = buf[7] 193 | return uint64(buf[0])<<0 | 194 | uint64(buf[1])<<8 | 195 | uint64(buf[2])<<16 | 196 | uint64(buf[3])<<24 | 197 | uint64(buf[4])<<32 | 198 | uint64(buf[5])<<40 | 199 | uint64(buf[6])<<48 | 200 | uint64(buf[7])<<56 201 | } 202 | -------------------------------------------------------------------------------- /go-fuzz-dep/sys_posix.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-fuzz project authors. All rights reserved. 2 | // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 | 4 | // +build darwin linux freebsd dragonfly openbsd netbsd 5 | // +build gofuzz 6 | 7 | package gofuzzdep 8 | 9 | import ( 10 | "syscall" 11 | 12 | . "github.com/dvyukov/go-fuzz/go-fuzz-defs" 13 | ) 14 | 15 | type FD int 16 | 17 | func setupCommFile() ([]byte, FD, FD) { 18 | mem, err := syscall.Mmap(3, 0, CoverSize+MaxInputSize+SonarRegionSize, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED) 19 | if err != nil { 20 | println("failed to mmap fd = 3 errno =", err.(syscall.Errno)) 21 | syscall.Exit(1) 22 | } 23 | return mem, 4, 5 24 | } 25 | 26 | func (fd FD) read(buf []byte) (int, error) { 27 | return syscall.Read(int(fd), buf) 28 | } 29 | 30 | func (fd FD) write(buf []byte) (int, error) { 31 | return syscall.Write(int(fd), buf) 32 | } 33 | -------------------------------------------------------------------------------- /go-fuzz-dep/sys_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-fuzz project authors. All rights reserved. 2 | // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 | 4 | // +build gofuzz 5 | 6 | package gofuzzdep 7 | 8 | import ( 9 | "syscall" 10 | "unsafe" 11 | 12 | . "github.com/dvyukov/go-fuzz/go-fuzz-defs" 13 | ) 14 | 15 | // Can't import reflect because of import cycles. 16 | type sliceHeader struct { 17 | addr uintptr 18 | l, c int 19 | } 20 | 21 | type FD syscall.Handle 22 | 23 | func setupCommFile() ([]byte, FD, FD) { 24 | const ( 25 | size = CoverSize + MaxInputSize + SonarRegionSize 26 | FILE_MAP_ALL_ACCESS = 0xF001F 27 | ) 28 | mapping := readEnvParam("GO_FUZZ_COMM_FD") 29 | addr, err := syscall.MapViewOfFile(mapping, FILE_MAP_ALL_ACCESS, 0, 0, size) 30 | if err != nil { 31 | println("failed to mmap comm file:", err.Error()) 32 | syscall.Exit(1) 33 | } 34 | hdr := sliceHeader{addr, size, size} 35 | mem := *(*[]byte)(unsafe.Pointer(&hdr)) 36 | in := FD(readEnvParam("GO_FUZZ_IN_FD")) 37 | out := FD(readEnvParam("GO_FUZZ_OUT_FD")) 38 | return mem, in, out 39 | } 40 | 41 | func readEnvParam(name string) syscall.Handle { 42 | v, _ := syscall.Getenv(name) 43 | var x uintptr 44 | for i := 0; i < len(v); i++ { 45 | x = x*10 + uintptr(v[i]-'0') 46 | } 47 | return syscall.Handle(x) 48 | } 49 | 50 | func (fd FD) read(buf []byte) (int, error) { 51 | return syscall.Read(syscall.Handle(fd), buf) 52 | } 53 | 54 | func (fd FD) write(buf []byte) (int, error) { 55 | return syscall.Write(syscall.Handle(fd), buf) 56 | } 57 | -------------------------------------------------------------------------------- /go-fuzz/assets/stats.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 |
9 |
10 |

Go Fuzz

11 |
12 |
13 |

14 | Workers 15 |
16 |
17 |

18 | Corpus 19 |
20 |
21 |

22 | Crashers 23 |
24 |
25 |

26 | Restarts 27 |
28 |
29 |

30 | Execs 31 |
32 |
33 |

34 | Cover 35 |
36 |
37 |

38 | Uptime 39 |
40 |
41 | 42 |

History

43 |
44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 |
WorkersCorpusCrashersRestartsExecsCoverUptime
58 |
59 |
60 |
61 |
62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 72 | 79 | 92 | 93 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /go-fuzz/compare.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-fuzz project authors. All rights reserved. 2 | // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 | 4 | // +build !amd64 5 | 6 | package main 7 | 8 | func compareCoverBody(base, cur []byte) bool { 9 | return compareCoverDump(base, cur) 10 | } 11 | -------------------------------------------------------------------------------- /go-fuzz/compare_amd64.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-fuzz project authors. All rights reserved. 2 | // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 | 4 | package main 5 | 6 | func compareCoverBody(base, cur []byte) bool { 7 | if hasAVX2 { 8 | return compareCoverBodyAVX2(&base[0], &cur[0]) 9 | } 10 | return compareCoverBodySSE2(&base[0], &cur[0]) 11 | } 12 | 13 | func compareCoverBodySSE2(base, cur *byte) bool // in compare_amd64.s 14 | func compareCoverBodyAVX2(base, cur *byte) bool // in compare_amd64.s 15 | -------------------------------------------------------------------------------- /go-fuzz/compare_amd64.s: -------------------------------------------------------------------------------- 1 | // Copyright 2019 go-fuzz project authors. All rights reserved. 2 | // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 | 4 | #include "textflag.h" 5 | 6 | // ·compareCoverBodySSE2 compares every corresponding byte of base and cur, and 7 | // reports whether cur has any entries bigger than base. 8 | // func ·compareCoverBodySSE2(base, cur *byte) bool 9 | TEXT ·compareCoverBodySSE2(SB), NOSPLIT, $0-17 10 | MOVQ base+0(FP), SI 11 | MOVQ cur+8(FP), DI 12 | XORQ CX, CX // loop counter 13 | XORQ R10, R10 // ret 14 | 15 | // Fill X0 with 128. 16 | MOVL $128, AX 17 | MOVD AX, X0 18 | PUNPCKLBW X0, X0 19 | PUNPCKLBW X0, X0 20 | PSHUFL $0, X0, X0 21 | 22 | // Align loop. 23 | // There is not enough control to align to 16 bytes; 24 | // the function itself might be at any 2 byte offset. 25 | // But we can at least get the loop offset to be even. 26 | BYTE $0x90 27 | loop: 28 | MOVOU (SI)(CX*1), X1 29 | MOVOU (DI)(CX*1), X2 30 | // Add -128 to each byte. 31 | // This lets us use signed comparison below to implement unsigned comparison. 32 | PSUBB X0, X1 33 | PSUBB X0, X2 34 | // Compare each byte 35 | PCMPGTB X1, X2 // X2 > X1 36 | // Extract top bit of each elem. 37 | PMOVMSKB X2, AX 38 | // If any bits were set, then some elem of X2 (cur) was bigger than some elem of X1 (base). 39 | TESTL AX, AX 40 | JNZ yes 41 | LEAQ 16(CX), CX // CX += 16 42 | BTL $16, CX // have we reached 65536 (CoverSize)? 43 | JCS ret 44 | JMP loop 45 | yes: 46 | MOVQ $1, R10 47 | ret: 48 | MOVB R10, ret+16(FP) 49 | RET 50 | 51 | // compareCoverBodyAVX2 compares every corresponding byte of base and cur, and 52 | // reports whether cur has any entries bigger than base. 53 | // func ·compareCoverBodyAVX2(base, cur *byte) bool 54 | TEXT ·compareCoverBodyAVX2(SB), NOSPLIT, $0-17 55 | MOVQ base+0(FP), SI 56 | MOVQ cur+8(FP), DI 57 | XORQ CX, CX // loop counter 58 | XORQ R10, R10 // ret 59 | MOVL $128, AX 60 | MOVD AX, X0 61 | VPBROADCASTB X0, Y0 62 | 63 | // align loop. 64 | // See comment in compareCoverBodySSE2. 65 | BYTE $0x90 66 | loop: 67 | VMOVDQU (SI)(CX*1), Y1 68 | VMOVDQU (DI)(CX*1), Y2 69 | VPSUBB Y0, Y1, Y3 70 | VPSUBB Y0, Y2, Y4 71 | VPCMPGTB Y3, Y4, Y5 72 | // Extract top bit of each elem. 73 | VPMOVMSKB Y5, AX 74 | // If any bits were set, then some elem of X2 (cur) was bigger than some elem of X1 (base). 75 | TESTL AX, AX 76 | JNZ yes 77 | LEAQ 32(CX), CX 78 | BTL $16, CX // have we reached 65536 (CoverSize)? 79 | JCS ret 80 | JMP loop 81 | yes: 82 | MOVQ $1, R10 83 | ret: 84 | VZEROUPPER 85 | MOVB R10, ret+16(FP) 86 | RET 87 | -------------------------------------------------------------------------------- /go-fuzz/coordinator.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-fuzz project authors. All rights reserved. 2 | // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 | 4 | package main 5 | 6 | import ( 7 | "bytes" 8 | "encoding/json" 9 | "errors" 10 | "fmt" 11 | "log" 12 | "net" 13 | "net/http" 14 | _ "net/http/pprof" 15 | "net/rpc" 16 | "path/filepath" 17 | "runtime" 18 | "sync" 19 | "sync/atomic" 20 | "time" 21 | 22 | "github.com/stephens2424/writerset" 23 | ) 24 | 25 | // Coordinator manages persistent fuzzer state like input corpus and crashers. 26 | type Coordinator struct { 27 | mu sync.Mutex 28 | idSeq int 29 | workers map[int]*CoordinatorWorker 30 | corpus *PersistentSet 31 | suppressions *PersistentSet 32 | crashers *PersistentSet 33 | 34 | startTime time.Time 35 | lastInput time.Time 36 | statExecs uint64 37 | statRestarts uint64 38 | coverFullness int 39 | 40 | statsWriters *writerset.WriterSet 41 | } 42 | 43 | // CoordinatorWorker represents coordinator's view of a worker. 44 | type CoordinatorWorker struct { 45 | id int 46 | procs int 47 | pending []CoordinatorInput 48 | lastSync time.Time 49 | } 50 | 51 | // coordinatorMain is entry function for coordinator. 52 | func coordinatorMain(ln net.Listener) { 53 | m := &Coordinator{} 54 | m.statsWriters = writerset.New() 55 | m.startTime = time.Now() 56 | m.lastInput = time.Now() 57 | m.suppressions = newPersistentSet(filepath.Join(*flagWorkdir, "suppressions")) 58 | m.crashers = newPersistentSet(filepath.Join(*flagWorkdir, "crashers")) 59 | m.corpus = newPersistentSet(filepath.Join(*flagWorkdir, "corpus")) 60 | if len(m.corpus.m) == 0 { 61 | m.corpus.add(Artifact{[]byte{}, 0, false}) 62 | } 63 | 64 | m.workers = make(map[int]*CoordinatorWorker) 65 | coordinatorListen(m) 66 | 67 | go coordinatorLoop(m) 68 | 69 | s := rpc.NewServer() 70 | s.Register(m) 71 | s.Accept(ln) 72 | } 73 | 74 | func coordinatorListen(c *Coordinator) { 75 | if *flagHTTP != "" { 76 | http.HandleFunc("/eventsource", c.eventSource) 77 | http.HandleFunc("/", c.index) 78 | 79 | go func() { 80 | fmt.Printf("Serving statistics on http://%s/\n", *flagHTTP) 81 | panic(http.ListenAndServe(*flagHTTP, nil)) 82 | }() 83 | } else { 84 | runtime.MemProfileRate = 0 85 | } 86 | } 87 | 88 | func coordinatorLoop(c *Coordinator) { 89 | for range time.NewTicker(3 * time.Second).C { 90 | if atomic.LoadUint32(&shutdown) != 0 { 91 | return 92 | } 93 | c.mu.Lock() 94 | // Nuke dead workers. 95 | for id, s := range c.workers { 96 | if time.Since(s.lastSync) < syncDeadline { 97 | continue 98 | } 99 | log.Printf("worker %v died", s.id) 100 | delete(c.workers, id) 101 | } 102 | c.mu.Unlock() 103 | 104 | c.broadcastStats() 105 | } 106 | } 107 | 108 | func (c *Coordinator) broadcastStats() { 109 | stats := c.coordinatorStats() 110 | 111 | // log to stdout 112 | log.Println(stats.String()) 113 | 114 | // write to any http clients 115 | b, err := json.Marshal(stats) 116 | if err != nil { 117 | panic(err) 118 | } 119 | 120 | fmt.Fprintf(c.statsWriters, "event: ping\ndata: %s\n\n", string(b)) 121 | c.statsWriters.Flush() 122 | } 123 | 124 | func (c *Coordinator) eventSource(w http.ResponseWriter, r *http.Request) { 125 | w.Header().Set("Content-Type", "text/event-stream") 126 | w.WriteHeader(http.StatusOK) 127 | <-c.statsWriters.Add(w) 128 | } 129 | 130 | func (c *Coordinator) index(w http.ResponseWriter, r *http.Request) { 131 | if r.URL.Path == "/" { 132 | r.URL.Path = "/stats.html" 133 | } 134 | http.FileServer(assetFS()).ServeHTTP(w, r) 135 | } 136 | 137 | func (c *Coordinator) coordinatorStats() coordinatorStats { 138 | c.mu.Lock() 139 | defer c.mu.Unlock() 140 | 141 | stats := coordinatorStats{ 142 | Corpus: uint64(len(c.corpus.m)), 143 | Crashers: uint64(len(c.crashers.m)), 144 | Uptime: fmtDuration(time.Since(c.startTime)), 145 | StartTime: c.startTime, 146 | LastNewInputTime: c.lastInput, 147 | Execs: c.statExecs, 148 | Cover: uint64(c.coverFullness), 149 | } 150 | 151 | // Print stats line. 152 | if c.statExecs != 0 && c.statRestarts != 0 { 153 | stats.RestartsDenom = c.statExecs / c.statRestarts 154 | } 155 | 156 | for _, w := range c.workers { 157 | stats.Workers += uint64(w.procs) 158 | } 159 | 160 | return stats 161 | } 162 | 163 | type coordinatorStats struct { 164 | Workers, Corpus, Crashers, Execs, Cover, RestartsDenom uint64 165 | LastNewInputTime, StartTime time.Time 166 | Uptime string 167 | } 168 | 169 | func (s coordinatorStats) String() string { 170 | return fmt.Sprintf("workers: %v, corpus: %v (%v ago), crashers: %v,"+ 171 | " restarts: 1/%v, execs: %v (%.0f/sec), cover: %v, uptime: %v", 172 | s.Workers, s.Corpus, fmtDuration(time.Since(s.LastNewInputTime)), 173 | s.Crashers, s.RestartsDenom, s.Execs, s.ExecsPerSec(), s.Cover, 174 | s.Uptime, 175 | ) 176 | } 177 | 178 | func (s coordinatorStats) ExecsPerSec() float64 { 179 | return float64(s.Execs) * 1e9 / float64(time.Since(s.StartTime)) 180 | } 181 | 182 | func fmtDuration(d time.Duration) string { 183 | if d.Hours() >= 1 { 184 | return fmt.Sprintf("%vh%vm", int(d.Hours()), int(d.Minutes())%60) 185 | } else if d.Minutes() >= 1 { 186 | return fmt.Sprintf("%vm%vs", int(d.Minutes()), int(d.Seconds())%60) 187 | } else { 188 | return fmt.Sprintf("%vs", int(d.Seconds())) 189 | } 190 | } 191 | 192 | type ConnectArgs struct { 193 | Procs int 194 | } 195 | 196 | type ConnectRes struct { 197 | ID int 198 | Corpus []CoordinatorInput 199 | } 200 | 201 | // CoordinatorInput is description of input that is passed between coordinator and worker. 202 | type CoordinatorInput struct { 203 | Data []byte 204 | Prio uint64 205 | Type execType 206 | Minimized bool 207 | Smashed bool 208 | } 209 | 210 | // Connect attaches new worker to coordinator. 211 | func (c *Coordinator) Connect(a *ConnectArgs, r *ConnectRes) error { 212 | c.mu.Lock() 213 | defer c.mu.Unlock() 214 | 215 | c.idSeq++ 216 | w := &CoordinatorWorker{ 217 | id: c.idSeq, 218 | procs: a.Procs, 219 | lastSync: time.Now(), 220 | } 221 | c.workers[w.id] = w 222 | r.ID = w.id 223 | // Give the worker initial corpus. 224 | for _, a := range c.corpus.m { 225 | r.Corpus = append(r.Corpus, CoordinatorInput{a.data, a.meta, execCorpus, !a.user, true}) 226 | } 227 | return nil 228 | } 229 | 230 | type NewInputArgs struct { 231 | ID int 232 | Data []byte 233 | Prio uint64 234 | } 235 | 236 | // NewInput saves new interesting input on coordinator. 237 | func (c *Coordinator) NewInput(a *NewInputArgs, r *int) error { 238 | c.mu.Lock() 239 | defer c.mu.Unlock() 240 | 241 | w := c.workers[a.ID] 242 | if w == nil { 243 | return errors.New("unknown worker") 244 | } 245 | 246 | art := Artifact{a.Data, a.Prio, false} 247 | if !c.corpus.add(art) { 248 | return nil 249 | } 250 | c.lastInput = time.Now() 251 | // Queue the input for sending to every worker. 252 | for _, w1 := range c.workers { 253 | w1.pending = append(w1.pending, CoordinatorInput{a.Data, a.Prio, execCorpus, true, w1 != w}) 254 | } 255 | 256 | return nil 257 | } 258 | 259 | type NewCrasherArgs struct { 260 | Data []byte 261 | Error []byte 262 | Suppression []byte 263 | Hanging bool 264 | } 265 | 266 | // NewCrasher saves new crasher input on coordinator. 267 | func (c *Coordinator) NewCrasher(a *NewCrasherArgs, r *int) error { 268 | c.mu.Lock() 269 | defer c.mu.Unlock() 270 | 271 | if !*flagDup && !c.suppressions.add(Artifact{a.Suppression, 0, false}) { 272 | return nil // Already have this. 273 | } 274 | if !c.crashers.add(Artifact{a.Data, 0, false}) { 275 | return nil // Already have this. 276 | } 277 | 278 | // Prepare quoted version of input to simplify creation of standalone reproducers. 279 | var buf bytes.Buffer 280 | for i := 0; i < len(a.Data); i += 20 { 281 | e := i + 20 282 | if e > len(a.Data) { 283 | e = len(a.Data) 284 | } 285 | fmt.Fprintf(&buf, "\t%q", a.Data[i:e]) 286 | if e != len(a.Data) { 287 | fmt.Fprintf(&buf, " +") 288 | } 289 | fmt.Fprintf(&buf, "\n") 290 | } 291 | c.crashers.addDescription(a.Data, buf.Bytes(), "quoted") 292 | c.crashers.addDescription(a.Data, a.Error, "output") 293 | 294 | return nil 295 | } 296 | 297 | type SyncArgs struct { 298 | ID int 299 | Execs uint64 300 | Restarts uint64 301 | CoverFullness int 302 | } 303 | 304 | type SyncRes struct { 305 | Inputs []CoordinatorInput // new interesting inputs 306 | } 307 | 308 | var errUnkownWorker = errors.New("unknown worker") 309 | 310 | // Sync is a periodic sync with a worker. 311 | // Worker sends statistics. Coordinator returns new inputs. 312 | func (c *Coordinator) Sync(a *SyncArgs, r *SyncRes) error { 313 | c.mu.Lock() 314 | defer c.mu.Unlock() 315 | 316 | w := c.workers[a.ID] 317 | if w == nil { 318 | return errUnkownWorker 319 | } 320 | c.statExecs += a.Execs 321 | c.statRestarts += a.Restarts 322 | if c.coverFullness < a.CoverFullness { 323 | c.coverFullness = a.CoverFullness 324 | } 325 | w.lastSync = time.Now() 326 | r.Inputs = w.pending 327 | w.pending = nil 328 | return nil 329 | } 330 | -------------------------------------------------------------------------------- /go-fuzz/cover.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-fuzz project authors. All rights reserved. 2 | // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 | 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | "log" 9 | "os" 10 | 11 | . "github.com/dvyukov/go-fuzz/go-fuzz-defs" 12 | . "github.com/dvyukov/go-fuzz/internal/go-fuzz-types" 13 | ) 14 | 15 | func makeCopy(data []byte) []byte { 16 | return append([]byte{}, data...) 17 | } 18 | 19 | func compareCover(base, cur []byte) bool { 20 | if len(base) != CoverSize || len(cur) != CoverSize { 21 | log.Fatalf("bad cover table size (%v, %v)", len(base), len(cur)) 22 | } 23 | res := compareCoverBody(base, cur) 24 | if false { 25 | // This check can legitimately fail if the test process has 26 | // some background goroutines that continue to write to the 27 | // cover array (cur storage is in shared memory). 28 | if compareCoverDump(base, cur) != res { 29 | panic("bad") 30 | } 31 | } 32 | return res 33 | } 34 | 35 | func compareCoverDump(base, cur []byte) bool { 36 | for i, v := range base { 37 | if cur[i] > v { 38 | return true 39 | } 40 | } 41 | return false 42 | } 43 | 44 | func updateMaxCover(base, cur []byte) int { 45 | if len(base) != CoverSize || len(cur) != CoverSize { 46 | log.Fatalf("bad cover table size (%v, %v)", len(base), len(cur)) 47 | } 48 | cnt := 0 49 | for i, x := range cur { 50 | x = roundUpCover(x) 51 | v := base[i] 52 | if v != 0 || x > 0 { 53 | cnt++ 54 | } 55 | if v < x { 56 | base[i] = x 57 | } 58 | } 59 | return cnt 60 | } 61 | 62 | // Quantize the counters. Otherwise we get too inflated corpus. 63 | func roundUpCover(x byte) byte { 64 | if !*flagCoverCounters && x > 0 { 65 | return 255 66 | } 67 | 68 | if x <= 5 { 69 | return x 70 | } else if x <= 8 { 71 | return 8 72 | } else if x <= 16 { 73 | return 16 74 | } else if x <= 32 { 75 | return 32 76 | } else if x <= 64 { 77 | return 64 78 | } 79 | return 255 80 | } 81 | 82 | func findNewCover(base, cover []byte) (res []byte, notEmpty bool) { 83 | res = make([]byte, CoverSize) 84 | for i, b := range base { 85 | c := cover[i] 86 | if c > b { 87 | res[i] = c 88 | notEmpty = true 89 | } 90 | } 91 | return 92 | } 93 | 94 | func worseCover(base, cover []byte) bool { 95 | for i, b := range base { 96 | c := cover[i] 97 | if c < b { 98 | return true 99 | } 100 | } 101 | return false 102 | } 103 | 104 | func dumpCover(outf string, blocks map[int][]CoverBlock, cover []byte) { 105 | // Exclude files that have no coverage at all. 106 | files := make(map[string]bool) 107 | for i, v := range cover { 108 | if v == 0 { 109 | continue 110 | } 111 | for _, b := range blocks[i] { 112 | files[b.File] = true 113 | } 114 | } 115 | 116 | out, err := os.Create(outf) 117 | if err != nil { 118 | log.Fatalf("failed to create coverage file: %v", err) 119 | } 120 | defer out.Close() 121 | const showCounters = false 122 | if showCounters { 123 | fmt.Fprintf(out, "mode: count\n") 124 | } else { 125 | fmt.Fprintf(out, "mode: set\n") 126 | } 127 | for i, v := range cover { 128 | for _, b := range blocks[i] { 129 | if !files[b.File] { 130 | continue 131 | } 132 | if !showCounters && v != 0 { 133 | v = 1 134 | } 135 | fmt.Fprintf(out, "%s:%v.%v,%v.%v %v %v\n", 136 | b.File, b.StartLine, b.StartCol, b.EndLine, b.EndCol, b.NumStmt, v) 137 | } 138 | } 139 | } 140 | 141 | func dumpSonar(outf string, sites []SonarSite) { 142 | out, err := os.Create(outf) 143 | if err != nil { 144 | log.Fatalf("failed to create coverage file: %v", err) 145 | } 146 | defer out.Close() 147 | fmt.Fprintf(out, "mode: set\n") 148 | for i := range sites { 149 | s := &sites[i] 150 | cnt := 0 // red color 151 | stmt := 1 // account in percentage calculation 152 | if s.takenTotal[0] == 0 && s.takenTotal[1] == 0 { 153 | stmt = 0 // don't account in percentage calculation 154 | cnt = 1 // grey color 155 | } else if s.takenTotal[0] > 0 && s.takenTotal[1] > 0 { 156 | cnt = 100 // green color 157 | } 158 | fmt.Fprintf(out, "%v %v %v\n", s.loc, stmt, cnt) 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /go-fuzz/cover_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 go-fuzz project authors. All rights reserved. 2 | // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 | 4 | package main 5 | 6 | import ( 7 | "testing" 8 | 9 | . "github.com/dvyukov/go-fuzz/go-fuzz-defs" 10 | ) 11 | 12 | func BenchmarkCompareCoverBody(b *testing.B) { 13 | base := make([]byte, CoverSize) 14 | cur := make([]byte, CoverSize) 15 | 16 | // Set 1 at both ends, so that it is easy regardless of which end compareCoverBody starts from. 17 | cur[0] = 1 18 | cur[CoverSize-1] = 1 19 | b.Run("easy", func(b *testing.B) { 20 | for i := 0; i < b.N; i++ { 21 | if !compareCoverBody(base, cur) { 22 | b.Fatalf("cur should have increased coverage") 23 | } 24 | } 25 | }) 26 | cur[0] = 0 27 | cur[CoverSize-1] = 0 28 | 29 | // Set 1 in the middle, so that it is hard regardless of which end compareCoverBody starts from. 30 | cur[CoverSize/2] = 1 31 | b.Run("hard", func(b *testing.B) { 32 | for i := 0; i < b.N; i++ { 33 | if !compareCoverBody(base, cur) { 34 | b.Fatalf("cur should have increased coverage") 35 | } 36 | } 37 | }) 38 | } 39 | -------------------------------------------------------------------------------- /go-fuzz/cpu_amd64.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-fuzz project authors. All rights reserved. 2 | // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 | 4 | package main 5 | 6 | // Adapted from GOROOT/src/internal/cpu/cpu_x86.go. 7 | 8 | var hasAVX2 bool 9 | 10 | func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32) 11 | func xgetbv() (eax, edx uint32) 12 | 13 | const ( 14 | // ecx bits 15 | cpuid_OSXSAVE = 1 << 27 16 | 17 | // ebx bits 18 | cpuid_AVX2 = 1 << 5 19 | ) 20 | 21 | func init() { 22 | _, _, ecx1, _ := cpuid(1, 0) 23 | hasOSXSAVE := cpuBitIsSet(ecx1, cpuid_OSXSAVE) 24 | 25 | osSupportsAVX := false 26 | // For XGETBV, OSXSAVE bit is required and sufficient. 27 | if hasOSXSAVE { 28 | eax, _ := xgetbv() 29 | // Check if XMM and YMM registers have OS support. 30 | osSupportsAVX = cpuBitIsSet(eax, 1<<1) && cpuBitIsSet(eax, 1<<2) 31 | } 32 | 33 | _, ebx7, _, _ := cpuid(7, 0) 34 | hasAVX2 = cpuBitIsSet(ebx7, cpuid_AVX2) && osSupportsAVX 35 | } 36 | 37 | func cpuBitIsSet(hwc uint32, value uint32) bool { 38 | return hwc&value != 0 39 | } 40 | -------------------------------------------------------------------------------- /go-fuzz/cpu_amd64.s: -------------------------------------------------------------------------------- 1 | // Copyright 2019 go-fuzz project authors. All rights reserved. 2 | // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 | 4 | #include "textflag.h" 5 | 6 | // Adapted from GOROOT/src/internal/cpu/cpu_x86.s 7 | 8 | // func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32) 9 | TEXT ·cpuid(SB), NOSPLIT, $0-24 10 | MOVL eaxArg+0(FP), AX 11 | MOVL ecxArg+4(FP), CX 12 | CPUID 13 | MOVL AX, eax+8(FP) 14 | MOVL BX, ebx+12(FP) 15 | MOVL CX, ecx+16(FP) 16 | MOVL DX, edx+20(FP) 17 | RET 18 | 19 | // func xgetbv() (eax, edx uint32) 20 | TEXT ·xgetbv(SB),NOSPLIT,$0-8 21 | #ifdef GOOS_nacl 22 | // nacl does not support XGETBV. 23 | MOVL $0, eax+0(FP) 24 | MOVL $0, edx+4(FP) 25 | #else 26 | MOVL $0, CX 27 | XGETBV 28 | MOVL AX, eax+0(FP) 29 | MOVL DX, edx+4(FP) 30 | #endif 31 | RET 32 | -------------------------------------------------------------------------------- /go-fuzz/exectype_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -type execType -trimprefix exec"; DO NOT EDIT. 2 | 3 | package main 4 | 5 | import "strconv" 6 | 7 | const _execType_name = "BootstrapCorpusMinimizeInputMinimizeCrasherTriageInputFuzzVersifierSmashSonarSonarHintTotalCount" 8 | 9 | var _execType_index = [...]uint8{0, 9, 15, 28, 43, 54, 58, 67, 72, 77, 86, 91, 96} 10 | 11 | func (i execType) String() string { 12 | if i >= execType(len(_execType_index)-1) { 13 | return "execType(" + strconv.FormatInt(int64(i), 10) + ")" 14 | } 15 | return _execType_name[_execType_index[i]:_execType_index[i+1]] 16 | } 17 | -------------------------------------------------------------------------------- /go-fuzz/hub.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-fuzz project authors. All rights reserved. 2 | // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 | 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | "log" 9 | "net/rpc" 10 | "path/filepath" 11 | "sync" 12 | "sync/atomic" 13 | "time" 14 | 15 | "github.com/dvyukov/go-fuzz/go-fuzz/versifier" 16 | 17 | . "github.com/dvyukov/go-fuzz/go-fuzz-defs" 18 | . "github.com/dvyukov/go-fuzz/internal/go-fuzz-types" 19 | ) 20 | 21 | const ( 22 | syncPeriod = 3 * time.Second 23 | syncDeadline = 100 * syncPeriod 24 | connectionPollInterval = 100 * time.Millisecond 25 | 26 | minScore = 1.0 27 | maxScore = 1000.0 28 | defScore = 10.0 29 | ) 30 | 31 | // Hub contains data shared between all workers in the process (e.g. corpus). 32 | // This reduces memory consumption for highly parallel workers. 33 | // Hub also handles communication with the coordinator. 34 | type Hub struct { 35 | id int 36 | coordinator *rpc.Client 37 | 38 | ro atomic.Value // *ROData 39 | 40 | maxCoverMu sync.Mutex 41 | maxCover atomic.Value // []byte 42 | 43 | initialTriage uint32 44 | 45 | corpusCoverSize int 46 | corpusSigs map[Sig]struct{} 47 | corpusStale bool 48 | triageQueue []CoordinatorInput 49 | 50 | triageC chan CoordinatorInput 51 | newInputC chan Input 52 | newCrasherC chan NewCrasherArgs 53 | syncC chan Stats 54 | 55 | stats Stats 56 | corpusOrigins [execCount]uint64 57 | } 58 | 59 | type ROData struct { 60 | corpus []Input 61 | corpusCover []byte 62 | badInputs map[Sig]struct{} 63 | suppressions map[Sig]struct{} 64 | strLits [][]byte // string literals in testee 65 | intLits [][]byte // int literals in testee 66 | coverBlocks map[int][]CoverBlock 67 | sonarSites []SonarSite 68 | verse *versifier.Verse 69 | } 70 | 71 | type Stats struct { 72 | execs uint64 73 | restarts uint64 74 | } 75 | 76 | func newHub(metadata MetaData) *Hub { 77 | procs := *flagProcs 78 | hub := &Hub{ 79 | corpusSigs: make(map[Sig]struct{}), 80 | triageC: make(chan CoordinatorInput, procs), 81 | newInputC: make(chan Input, procs), 82 | newCrasherC: make(chan NewCrasherArgs, procs), 83 | syncC: make(chan Stats, procs), 84 | } 85 | 86 | if err := hub.connect(); err != nil { 87 | log.Fatalf("failed to connect to coordinator: %v", err) 88 | } 89 | 90 | coverBlocks := make(map[int][]CoverBlock) 91 | for _, b := range metadata.Blocks { 92 | coverBlocks[b.ID] = append(coverBlocks[b.ID], b) 93 | } 94 | sonarSites := make([]SonarSite, len(metadata.Sonar)) 95 | for i, b := range metadata.Sonar { 96 | if i != b.ID { 97 | log.Fatalf("corrupted sonar metadata") 98 | } 99 | sonarSites[i].id = b.ID 100 | sonarSites[i].loc = fmt.Sprintf("%v:%v.%v,%v.%v", b.File, b.StartLine, b.StartCol, b.EndLine, b.EndCol) 101 | } 102 | hub.maxCover.Store(make([]byte, CoverSize)) 103 | 104 | ro := &ROData{ 105 | corpusCover: make([]byte, CoverSize), 106 | badInputs: make(map[Sig]struct{}), 107 | suppressions: make(map[Sig]struct{}), 108 | coverBlocks: coverBlocks, 109 | sonarSites: sonarSites, 110 | } 111 | // Prepare list of string and integer literals. 112 | for _, lit := range metadata.Literals { 113 | if lit.IsStr { 114 | ro.strLits = append(ro.strLits, []byte(lit.Val)) 115 | } else { 116 | ro.intLits = append(ro.intLits, []byte(lit.Val)) 117 | } 118 | } 119 | hub.ro.Store(ro) 120 | 121 | go hub.loop() 122 | 123 | return hub 124 | } 125 | 126 | func (hub *Hub) connect() error { 127 | var c *rpc.Client 128 | var err error 129 | 130 | t := time.Now() 131 | for { 132 | c, err = rpc.Dial("tcp", *flagWorker) 133 | if err == nil || time.Since(t) > *flagConnectionTimeout { 134 | break 135 | } 136 | time.Sleep(connectionPollInterval) 137 | } 138 | if err != nil { 139 | return err 140 | } 141 | var res ConnectRes 142 | if err := c.Call("Coordinator.Connect", &ConnectArgs{Procs: *flagProcs}, &res); err != nil { 143 | return err 144 | } 145 | 146 | hub.coordinator = c 147 | hub.id = res.ID 148 | hub.initialTriage = uint32(len(res.Corpus)) 149 | hub.triageQueue = res.Corpus 150 | return nil 151 | } 152 | 153 | func (hub *Hub) loop() { 154 | // Local buffer helps to avoid deadlocks on chan overflows. 155 | var triageC chan CoordinatorInput 156 | var triageInput CoordinatorInput 157 | 158 | syncTicker := time.NewTicker(syncPeriod).C 159 | for { 160 | if len(hub.triageQueue) > 0 && triageC == nil { 161 | n := len(hub.triageQueue) - 1 162 | triageInput = hub.triageQueue[n] 163 | hub.triageQueue[n] = CoordinatorInput{} 164 | hub.triageQueue = hub.triageQueue[:n] 165 | triageC = hub.triageC 166 | } 167 | 168 | select { 169 | case <-syncTicker: 170 | // Sync with the coordinator. 171 | if *flagV >= 1 { 172 | ro := hub.ro.Load().(*ROData) 173 | log.Printf("hub: corpus=%v bootstrap=%v fuzz=%v minimize=%v versifier=%v smash=%v sonar=%v", 174 | len(ro.corpus), hub.corpusOrigins[execBootstrap]+hub.corpusOrigins[execCorpus], 175 | hub.corpusOrigins[execFuzz]+hub.corpusOrigins[execSonar], 176 | hub.corpusOrigins[execMinimizeInput]+hub.corpusOrigins[execMinimizeCrasher], 177 | hub.corpusOrigins[execVersifier], hub.corpusOrigins[execSmash], 178 | hub.corpusOrigins[execSonarHint]) 179 | } 180 | args := &SyncArgs{ 181 | ID: hub.id, 182 | Execs: hub.stats.execs, 183 | Restarts: hub.stats.restarts, 184 | CoverFullness: hub.corpusCoverSize, 185 | } 186 | hub.stats.execs = 0 187 | hub.stats.restarts = 0 188 | var res SyncRes 189 | if err := hub.coordinator.Call("Coordinator.Sync", args, &res); err != nil { 190 | log.Printf("sync call failed: %v, reconnection to coordinator", err) 191 | if err := hub.connect(); err != nil { 192 | log.Printf("failed to connect to coordinator: %v, killing worker", err) 193 | return 194 | } 195 | } 196 | if len(res.Inputs) > 0 { 197 | hub.triageQueue = append(hub.triageQueue, res.Inputs...) 198 | } 199 | if hub.corpusStale { 200 | hub.updateScores() 201 | hub.corpusStale = false 202 | } 203 | 204 | case triageC <- triageInput: 205 | // Send new input to workers for triage. 206 | if len(hub.triageQueue) > 0 { 207 | n := len(hub.triageQueue) - 1 208 | triageInput = hub.triageQueue[n] 209 | hub.triageQueue[n] = CoordinatorInput{} 210 | hub.triageQueue = hub.triageQueue[:n] 211 | } else { 212 | triageC = nil 213 | triageInput = CoordinatorInput{} 214 | } 215 | 216 | case s := <-hub.syncC: 217 | // Sync from a worker. 218 | hub.stats.execs += s.execs 219 | hub.stats.restarts += s.restarts 220 | 221 | case input := <-hub.newInputC: 222 | // New interesting input from workers. 223 | ro := hub.ro.Load().(*ROData) 224 | if !compareCover(ro.corpusCover, input.cover) { 225 | break 226 | } 227 | sig := hash(input.data) 228 | if _, ok := hub.corpusSigs[sig]; ok { 229 | break 230 | } 231 | 232 | // Passed deduplication, taking it. 233 | if *flagV >= 2 { 234 | log.Printf("hub received new input [%v]%v mine=%v", len(input.data), hash(input.data), input.mine) 235 | } 236 | hub.corpusSigs[sig] = struct{}{} 237 | ro1 := new(ROData) 238 | *ro1 = *ro 239 | // Assign it the default score, but mark corpus for score recalculation. 240 | hub.corpusStale = true 241 | scoreSum := 0 242 | if len(ro1.corpus) > 0 { 243 | scoreSum = ro1.corpus[len(ro1.corpus)-1].runningScoreSum 244 | } 245 | input.score = defScore 246 | input.runningScoreSum = scoreSum + defScore 247 | ro1.corpus = append(ro1.corpus, input) 248 | hub.updateMaxCover(input.cover) 249 | ro1.corpusCover = makeCopy(ro.corpusCover) 250 | hub.corpusCoverSize = updateMaxCover(ro1.corpusCover, input.cover) 251 | if input.res > 0 || input.typ == execBootstrap { 252 | ro1.verse = versifier.BuildVerse(ro.verse, input.data) 253 | } 254 | hub.ro.Store(ro1) 255 | hub.corpusOrigins[input.typ]++ 256 | 257 | if input.mine { 258 | if err := hub.coordinator.Call("Coordinator.NewInput", NewInputArgs{hub.id, input.data, uint64(input.depth)}, nil); err != nil { 259 | log.Printf("new input call failed: %v, reconnecting to coordinator", err) 260 | if err := hub.connect(); err != nil { 261 | log.Printf("failed to connect to coordinator: %v, killing worker", err) 262 | return 263 | } 264 | } 265 | } 266 | 267 | if *flagDumpCover { 268 | dumpCover(filepath.Join(*flagWorkdir, "coverprofile"), ro.coverBlocks, ro.corpusCover) 269 | } 270 | 271 | case crash := <-hub.newCrasherC: 272 | // New crasher from workers. Woohoo! 273 | if crash.Hanging || !*flagDup { 274 | ro := hub.ro.Load().(*ROData) 275 | ro1 := new(ROData) 276 | *ro1 = *ro 277 | if crash.Hanging { 278 | ro1.badInputs = make(map[Sig]struct{}) 279 | for k, v := range ro.badInputs { 280 | ro1.badInputs[k] = v 281 | } 282 | ro1.badInputs[hash(crash.Data)] = struct{}{} 283 | } 284 | if !*flagDup { 285 | ro1.suppressions = make(map[Sig]struct{}) 286 | for k, v := range ro.suppressions { 287 | ro1.suppressions[k] = v 288 | } 289 | ro1.suppressions[hash(crash.Suppression)] = struct{}{} 290 | } 291 | hub.ro.Store(ro1) 292 | } 293 | if err := hub.coordinator.Call("Coordinator.NewCrasher", crash, nil); err != nil { 294 | log.Printf("new crasher call failed: %v", err) 295 | } 296 | } 297 | } 298 | } 299 | 300 | // Preliminary cover update to prevent new input thundering herd. 301 | // This function is synchronous to reduce latency. 302 | func (hub *Hub) updateMaxCover(cover []byte) bool { 303 | oldMaxCover := hub.maxCover.Load().([]byte) 304 | if !compareCover(oldMaxCover, cover) { 305 | return false 306 | } 307 | hub.maxCoverMu.Lock() 308 | defer hub.maxCoverMu.Unlock() 309 | oldMaxCover = hub.maxCover.Load().([]byte) 310 | if !compareCover(oldMaxCover, cover) { 311 | return false 312 | } 313 | maxCover := makeCopy(oldMaxCover) 314 | updateMaxCover(maxCover, cover) 315 | hub.maxCover.Store(maxCover) 316 | return true 317 | } 318 | 319 | func (hub *Hub) updateScores() { 320 | ro := hub.ro.Load().(*ROData) 321 | ro1 := new(ROData) 322 | *ro1 = *ro 323 | corpus := make([]Input, len(ro.corpus)) 324 | copy(corpus, ro.corpus) 325 | ro1.corpus = corpus 326 | 327 | var sumExecTime, sumCoverSize uint64 328 | for _, inp := range corpus { 329 | sumExecTime += inp.execTime 330 | sumCoverSize += uint64(inp.coverSize) 331 | } 332 | n := uint64(len(corpus)) 333 | avgExecTime := sumExecTime / n 334 | avgCoverSize := sumCoverSize / n 335 | 336 | // Phase 1: calculate score for each input independently. 337 | for i, inp := range corpus { 338 | score := defScore 339 | 340 | // Execution time multiplier 0.1-3x. 341 | // Fuzzing faster inputs increases efficiency. 342 | execTime := float64(inp.execTime) / float64(avgExecTime) 343 | if execTime > 10 { 344 | score /= 10 345 | } else if execTime > 4 { 346 | score /= 4 347 | } else if execTime > 2 { 348 | score /= 2 349 | } else if execTime < 0.25 { 350 | score *= 3 351 | } else if execTime < 0.33 { 352 | score *= 2 353 | } else if execTime < 0.5 { 354 | score *= 1.5 355 | } 356 | 357 | // Coverage size multiplier 0.25-3x. 358 | // Inputs with larger coverage are more interesting. 359 | coverSize := float64(inp.coverSize) / float64(avgCoverSize) 360 | if coverSize > 3 { 361 | score *= 3 362 | } else if coverSize > 2 { 363 | score *= 2 364 | } else if coverSize > 1.5 { 365 | score *= 1.5 366 | } else if coverSize < 0.3 { 367 | score /= 4 368 | } else if coverSize < 0.5 { 369 | score /= 2 370 | } else if coverSize < 0.75 { 371 | score /= 1.5 372 | } 373 | 374 | // Input depth multiplier 1-5x. 375 | // Deeper inputs have higher chances of digging deeper into code. 376 | if inp.depth < 10 { 377 | // no boost for you 378 | } else if inp.depth < 20 { 379 | score *= 2 380 | } else if inp.depth < 40 { 381 | score *= 3 382 | } else if inp.depth < 80 { 383 | score *= 4 384 | } else { 385 | score *= 5 386 | } 387 | 388 | // User boost (Fuzz function return value) multiplier 1-2x. 389 | // We don't know what it is, but user said so. 390 | if inp.res > 0 { 391 | // Assuming this is a correct input (e.g. deserialized successfully). 392 | score *= 2 393 | } 394 | 395 | if score < minScore { 396 | score = minScore 397 | } else if score > maxScore { 398 | score = maxScore 399 | } 400 | corpus[i].score = int(score) 401 | } 402 | 403 | // Phase 2: Choose a minimal set of (favored) inputs that give full coverage. 404 | // Non-favored inputs receive minimal score. 405 | type Candidate struct { 406 | index int 407 | score int 408 | chosen bool 409 | } 410 | candidates := make([]Candidate, CoverSize) 411 | for idx, inp := range corpus { 412 | corpus[idx].favored = false 413 | for i, c := range inp.cover { 414 | if c == 0 { 415 | continue 416 | } 417 | c = roundUpCover(c) 418 | if c != ro.corpusCover[i] { 419 | continue 420 | } 421 | if c > ro.corpusCover[i] { 422 | log.Fatalf("bad") 423 | } 424 | if candidates[i].score < inp.score { 425 | candidates[i].index = idx 426 | candidates[i].score = inp.score 427 | } 428 | } 429 | } 430 | for ci, cand := range candidates { 431 | if cand.score == 0 { 432 | continue 433 | } 434 | inp := &corpus[cand.index] 435 | inp.favored = true 436 | for i := ci + 1; i < CoverSize; i++ { 437 | c := inp.cover[i] 438 | if c == 0 { 439 | continue 440 | } 441 | c = roundUpCover(c) 442 | if c != ro.corpusCover[i] { 443 | continue 444 | } 445 | candidates[i].score = 0 446 | } 447 | } 448 | scoreSum := 0 449 | for i, inp := range corpus { 450 | if !inp.favored { 451 | inp.score = minScore 452 | } 453 | scoreSum += inp.score 454 | corpus[i].runningScoreSum = scoreSum 455 | } 456 | 457 | hub.ro.Store(ro1) 458 | } 459 | -------------------------------------------------------------------------------- /go-fuzz/internal/pcg/pcg.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 go-fuzz project authors. All rights reserved. 2 | // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 | 4 | // Package pcg implements a 32 bit PRNG with a 64 bit period: pcg xsh rr 64 32. 5 | // See https://www.pcg-random.org/ for more information. 6 | // This implementation is geared specifically towards go-fuzz's needs: 7 | // Simple creation and use, no reproducibility, no concurrency safety, 8 | // just the methods go-fuzz needs, optimized for speed. 9 | package pcg 10 | 11 | import ( 12 | "math/bits" 13 | "sync/atomic" 14 | "time" 15 | ) 16 | 17 | var globalInc uint64 // PCG stream 18 | 19 | const multiplier uint64 = 6364136223846793005 20 | 21 | // Rand is a PRNG. 22 | // It should not be copied or shared. 23 | // No Rand methods are concurrency safe. 24 | // They are small, and cheap to create. 25 | // If in doubt: Just make another one. 26 | type Rand struct { 27 | noCopy noCopy // help avoid mistakes: ask vet to ensure that we don't make a copy 28 | state uint64 29 | inc uint64 30 | } 31 | 32 | // New generates a new, seeded Rand, ready for use. 33 | func New() *Rand { 34 | r := new(Rand) 35 | now := uint64(time.Now().UnixNano()) 36 | inc := atomic.AddUint64(&globalInc, 1) 37 | r.state = now 38 | r.inc = (inc << 1) | 1 39 | r.step() 40 | r.state += now 41 | r.step() 42 | return r 43 | } 44 | 45 | func (r *Rand) step() { 46 | r.state *= multiplier 47 | r.state += r.inc 48 | } 49 | 50 | // Uint32 returns a pseudo-random uint32. 51 | func (r *Rand) Uint32() uint32 { 52 | x := r.state 53 | r.step() 54 | return bits.RotateLeft32(uint32(((x>>18)^x)>>27), -int(x>>59)) 55 | } 56 | 57 | // Intn returns a pseudo-random number in [0, n). 58 | // n must fit in a uint32. 59 | func (r *Rand) Intn(n int) int { 60 | if int(uint32(n)) != n { 61 | panic("large Intn") 62 | } 63 | return int(r.Uint32n(uint32(n))) 64 | } 65 | 66 | // Uint32n returns a pseudo-random number in [0, n). 67 | // 68 | // For implementation details, see: 69 | // https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction 70 | // https://lemire.me/blog/2016/06/30/fast-random-shuffling 71 | func (r *Rand) Uint32n(n uint32) uint32 { 72 | v := r.Uint32() 73 | prod := uint64(v) * uint64(n) 74 | low := uint32(prod) 75 | if low < n { 76 | thresh := uint32(-int32(n)) % n 77 | for low < thresh { 78 | v = r.Uint32() 79 | prod = uint64(v) * uint64(n) 80 | low = uint32(prod) 81 | } 82 | } 83 | return uint32(prod >> 32) 84 | } 85 | 86 | // Exp2 generates n with probability 1/2^(n+1). 87 | func (r *Rand) Exp2() int { 88 | return bits.TrailingZeros32(r.Uint32()) 89 | } 90 | 91 | // Bool generates a random bool. 92 | func (r *Rand) Bool() bool { 93 | return r.Uint32()&1 == 0 94 | } 95 | 96 | // noCopy may be embedded into structs which must not be copied 97 | // after the first use. 98 | // 99 | // See https://golang.org/issues/8005#issuecomment-190753527 100 | // for details. 101 | type noCopy struct{} 102 | 103 | // Lock is a no-op used by -copylocks checker from `go vet`. 104 | func (*noCopy) Lock() {} 105 | func (*noCopy) Unlock() {} 106 | -------------------------------------------------------------------------------- /go-fuzz/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-fuzz project authors. All rights reserved. 2 | // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 | 4 | package main 5 | 6 | import ( 7 | "flag" 8 | "log" 9 | "net" 10 | "os" 11 | "os/signal" 12 | "os/user" 13 | "path/filepath" 14 | "runtime" 15 | "runtime/debug" 16 | "sync/atomic" 17 | "syscall" 18 | "time" 19 | 20 | "golang.org/x/tools/go/packages" 21 | ) 22 | 23 | //go:generate go build github.com/dvyukov/go-fuzz/go-fuzz/vendor/github.com/elazarl/go-bindata-assetfs/go-bindata-assetfs 24 | //go:generate ./go-bindata-assetfs assets/... 25 | //go:generate goimports -w bindata_assetfs.go 26 | //go:generate rm go-bindata-assetfs 27 | 28 | var ( 29 | flagWorkdir = flag.String("workdir", ".", "dir with persistent work data") 30 | flagProcs = flag.Int("procs", runtime.NumCPU(), "parallelism level") 31 | flagTimeout = flag.Int("timeout", 10, "test timeout, in seconds") 32 | flagMinimize = flag.Duration("minimize", 1*time.Minute, "time limit for input minimization") 33 | flagCoordinator = flag.String("coordinator", "", "coordinator mode (value is coordinator address)") 34 | flagWorker = flag.String("worker", "", "worker mode (value is coordinator address)") 35 | flagConnectionTimeout = flag.Duration("connectiontimeout", 1*time.Minute, "time limit for worker to try to connect coordinator") 36 | flagBin = flag.String("bin", "", "test binary built with go-fuzz-build") 37 | flagFunc = flag.String("func", "", "function to fuzz") 38 | flagDumpCover = flag.Bool("dumpcover", false, "dump coverage profile into workdir") 39 | flagDup = flag.Bool("dup", false, "collect duplicate crashers") 40 | flagTestOutput = flag.Bool("testoutput", false, "print test binary output to stdout (for debugging only)") 41 | flagCoverCounters = flag.Bool("covercounters", true, "use coverage hit counters") 42 | flagSonar = flag.Bool("sonar", true, "use sonar hints") 43 | flagV = flag.Int("v", 0, "verbosity level") 44 | flagHTTP = flag.String("http", "", "HTTP server listen address (coordinator mode only)") 45 | 46 | shutdown uint32 47 | shutdownC = make(chan struct{}) 48 | shutdownCleanup []func() 49 | ) 50 | 51 | func main() { 52 | flag.Parse() 53 | if *flagCoordinator != "" && *flagWorker != "" { 54 | log.Fatalf("both -coordinator and -worker are specified") 55 | } 56 | if *flagHTTP != "" && *flagWorker != "" { 57 | log.Fatalf("both -http and -worker are specified") 58 | } 59 | 60 | go func() { 61 | c := make(chan os.Signal, 1) 62 | signal.Notify(c, syscall.SIGINT) 63 | <-c 64 | atomic.StoreUint32(&shutdown, 1) 65 | close(shutdownC) 66 | log.Printf("shutting down...") 67 | time.Sleep(2 * time.Second) 68 | for _, f := range shutdownCleanup { 69 | f() 70 | } 71 | os.Exit(0) 72 | }() 73 | 74 | runtime.GOMAXPROCS(min(*flagProcs, runtime.NumCPU())) 75 | debug.SetGCPercent(50) // most memory is in large binary blobs 76 | lowerProcessPrio() 77 | 78 | *flagWorkdir = expandHomeDir(*flagWorkdir) 79 | *flagBin = expandHomeDir(*flagBin) 80 | 81 | if *flagCoordinator != "" || *flagWorker == "" { 82 | if *flagWorkdir == "" { 83 | log.Fatalf("-workdir is not set") 84 | } 85 | if *flagCoordinator == "" { 86 | *flagCoordinator = "localhost:0" 87 | } 88 | ln, err := net.Listen("tcp", *flagCoordinator) 89 | if err != nil { 90 | log.Fatalf("failed to listen: %v", err) 91 | } 92 | if *flagCoordinator == "localhost:0" && *flagWorker == "" { 93 | *flagWorker = ln.Addr().String() 94 | } 95 | go coordinatorMain(ln) 96 | } 97 | 98 | if *flagWorker != "" { 99 | if *flagBin == "" { 100 | // Try the default. Best effort only. 101 | var bin string 102 | cfg := new(packages.Config) 103 | // Note that we do not set GO111MODULE here in order to respect any GO111MODULE 104 | // setting by the user as we are finding dependencies. See modules support 105 | // comments in go-fuzz-build/main.go for more details. 106 | cfg.Env = os.Environ() 107 | pkgs, err := packages.Load(cfg, ".") 108 | if err == nil && len(pkgs) == 1 { 109 | bin = pkgs[0].Name + "-fuzz.zip" 110 | _, err := os.Stat(bin) 111 | if err != nil { 112 | bin = "" 113 | } 114 | } 115 | if bin == "" { 116 | log.Fatalf("-bin is not set") 117 | } 118 | *flagBin = bin 119 | } 120 | go workerMain() 121 | } 122 | 123 | select {} 124 | } 125 | 126 | // expandHomeDir expands the tilde sign and replaces it 127 | // with current users home directory and returns it. 128 | func expandHomeDir(path string) string { 129 | if len(path) > 2 && path[:2] == "~/" { 130 | usr, _ := user.Current() 131 | path = filepath.Join(usr.HomeDir, path[2:]) 132 | } 133 | return path 134 | } 135 | -------------------------------------------------------------------------------- /go-fuzz/mutator.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-fuzz project authors. All rights reserved. 2 | // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 | 4 | package main 5 | 6 | import ( 7 | "encoding/binary" 8 | "sort" 9 | "strconv" 10 | 11 | . "github.com/dvyukov/go-fuzz/go-fuzz-defs" 12 | "github.com/dvyukov/go-fuzz/go-fuzz/internal/pcg" 13 | ) 14 | 15 | type Mutator struct { 16 | r *pcg.Rand 17 | } 18 | 19 | func newMutator() *Mutator { 20 | return &Mutator{r: pcg.New()} 21 | } 22 | 23 | func (m *Mutator) rand(n int) int { 24 | return m.r.Intn(n) 25 | } 26 | 27 | // randbig generates a number in [0, 2³⁰). 28 | func (m *Mutator) randbig() int64 { 29 | return int64(m.r.Uint32() >> 2) 30 | } 31 | 32 | func (m *Mutator) randByteOrder() binary.ByteOrder { 33 | if m.r.Bool() { 34 | return binary.LittleEndian 35 | } 36 | return binary.BigEndian 37 | } 38 | 39 | func (m *Mutator) generate(ro *ROData) ([]byte, int) { 40 | corpus := ro.corpus 41 | scoreSum := corpus[len(corpus)-1].runningScoreSum 42 | weightedIdx := m.rand(scoreSum) 43 | idx := sort.Search(len(corpus), func(i int) bool { 44 | return corpus[i].runningScoreSum > weightedIdx 45 | }) 46 | input := &corpus[idx] 47 | return m.mutate(input.data, ro), input.depth + 1 48 | } 49 | 50 | func (m *Mutator) mutate(data []byte, ro *ROData) []byte { 51 | corpus := ro.corpus 52 | res := make([]byte, len(data)) 53 | copy(res, data) 54 | nm := 1 + m.r.Exp2() 55 | for iter := 0; iter < nm; iter++ { 56 | switch m.rand(20) { 57 | case 0: 58 | // Remove a range of bytes. 59 | if len(res) <= 1 { 60 | iter-- 61 | continue 62 | } 63 | pos0 := m.rand(len(res)) 64 | pos1 := pos0 + m.chooseLen(len(res)-pos0) 65 | copy(res[pos0:], res[pos1:]) 66 | res = res[:len(res)-(pos1-pos0)] 67 | case 1: 68 | // Insert a range of random bytes. 69 | pos := m.rand(len(res) + 1) 70 | n := m.chooseLen(10) 71 | for i := 0; i < n; i++ { 72 | res = append(res, 0) 73 | } 74 | copy(res[pos+n:], res[pos:]) 75 | for i := 0; i < n; i++ { 76 | res[pos+i] = byte(m.rand(256)) 77 | } 78 | case 2: 79 | // Duplicate a range of bytes. 80 | if len(res) <= 1 { 81 | iter-- 82 | continue 83 | } 84 | src := m.rand(len(res)) 85 | dst := m.rand(len(res)) 86 | for dst == src { 87 | dst = m.rand(len(res)) 88 | } 89 | n := m.chooseLen(len(res) - src) 90 | tmp := make([]byte, n) 91 | copy(tmp, res[src:]) 92 | for i := 0; i < n; i++ { 93 | res = append(res, 0) 94 | } 95 | copy(res[dst+n:], res[dst:]) 96 | for i := 0; i < n; i++ { 97 | res[dst+i] = tmp[i] 98 | } 99 | case 3: 100 | // Copy a range of bytes. 101 | if len(res) <= 1 { 102 | iter-- 103 | continue 104 | } 105 | src := m.rand(len(res)) 106 | dst := m.rand(len(res)) 107 | for dst == src { 108 | dst = m.rand(len(res)) 109 | } 110 | n := m.chooseLen(len(res) - src) 111 | if dst > len(res) || src+n > len(res) { 112 | println(len(res), dst, src, n) 113 | } 114 | copy(res[dst:], res[src:src+n]) 115 | case 4: 116 | // Bit flip. Spooky! 117 | if len(res) == 0 { 118 | iter-- 119 | continue 120 | } 121 | pos := m.rand(len(res)) 122 | res[pos] ^= 1 << uint(m.rand(8)) 123 | case 5: 124 | // Set a byte to a random value. 125 | if len(res) == 0 { 126 | iter-- 127 | continue 128 | } 129 | pos := m.rand(len(res)) 130 | res[pos] ^= byte(m.rand(255)) + 1 131 | case 6: 132 | // Swap 2 bytes. 133 | if len(res) <= 1 { 134 | iter-- 135 | continue 136 | } 137 | src := m.rand(len(res)) 138 | dst := m.rand(len(res)) 139 | for dst == src { 140 | dst = m.rand(len(res)) 141 | } 142 | res[src], res[dst] = res[dst], res[src] 143 | case 7: 144 | // Add/subtract from a byte. 145 | if len(res) == 0 { 146 | iter-- 147 | continue 148 | } 149 | pos := m.rand(len(res)) 150 | v := byte(m.rand(35) + 1) 151 | if m.r.Bool() { 152 | res[pos] += v 153 | } else { 154 | res[pos] -= v 155 | } 156 | case 8: 157 | // Add/subtract from a uint16. 158 | if len(res) < 2 { 159 | iter-- 160 | continue 161 | } 162 | pos := m.rand(len(res) - 1) 163 | buf := res[pos:] 164 | v := uint16(m.rand(35) + 1) 165 | if m.r.Bool() { 166 | v = 0 - v 167 | } 168 | enc := m.randByteOrder() 169 | enc.PutUint16(buf, enc.Uint16(buf)+v) 170 | case 9: 171 | // Add/subtract from a uint32. 172 | if len(res) < 4 { 173 | iter-- 174 | continue 175 | } 176 | pos := m.rand(len(res) - 3) 177 | buf := res[pos:] 178 | v := uint32(m.rand(35) + 1) 179 | if m.r.Bool() { 180 | v = 0 - v 181 | } 182 | enc := m.randByteOrder() 183 | enc.PutUint32(buf, enc.Uint32(buf)+v) 184 | case 10: 185 | // Add/subtract from a uint64. 186 | if len(res) < 8 { 187 | iter-- 188 | continue 189 | } 190 | pos := m.rand(len(res) - 7) 191 | buf := res[pos:] 192 | v := uint64(m.rand(35) + 1) 193 | if m.r.Bool() { 194 | v = 0 - v 195 | } 196 | enc := m.randByteOrder() 197 | enc.PutUint64(buf, enc.Uint64(buf)+v) 198 | case 11: 199 | // Replace a byte with an interesting value. 200 | if len(res) == 0 { 201 | iter-- 202 | continue 203 | } 204 | pos := m.rand(len(res)) 205 | res[pos] = byte(interesting8[m.rand(len(interesting8))]) 206 | case 12: 207 | // Replace an uint16 with an interesting value. 208 | if len(res) < 2 { 209 | iter-- 210 | continue 211 | } 212 | pos := m.rand(len(res) - 1) 213 | buf := res[pos:] 214 | v := uint16(interesting16[m.rand(len(interesting16))]) 215 | m.randByteOrder().PutUint16(buf, v) 216 | case 13: 217 | // Replace an uint32 with an interesting value. 218 | if len(res) < 4 { 219 | iter-- 220 | continue 221 | } 222 | pos := m.rand(len(res) - 3) 223 | buf := res[pos:] 224 | v := uint32(interesting32[m.rand(len(interesting32))]) 225 | m.randByteOrder().PutUint32(buf, v) 226 | case 14: 227 | // Replace an ascii digit with another digit. 228 | var digits []int 229 | for i, v := range res { 230 | if v >= '0' && v <= '9' { 231 | digits = append(digits, i) 232 | } 233 | } 234 | if len(digits) == 0 { 235 | iter-- 236 | continue 237 | } 238 | pos := m.rand(len(digits)) 239 | was := res[digits[pos]] 240 | now := was 241 | for was == now { 242 | now = byte(m.rand(10)) + '0' 243 | } 244 | res[digits[pos]] = now 245 | case 15: 246 | // Replace a multi-byte ASCII number with another number. 247 | type arange struct { 248 | start int 249 | end int 250 | } 251 | var numbers []arange 252 | start := -1 253 | for i, v := range res { 254 | if (v >= '0' && v <= '9') || (start == -1 && v == '-') { 255 | if start == -1 { 256 | start = i 257 | } else if i == len(res)-1 { 258 | // At final byte of input. 259 | if i-start > 0 { 260 | numbers = append(numbers, arange{start, i + 1}) 261 | } 262 | } 263 | } else { 264 | if start != -1 && i-start > 1 { 265 | numbers = append(numbers, arange{start, i}) 266 | start = -1 267 | } 268 | } 269 | } 270 | if len(numbers) == 0 { 271 | iter-- 272 | continue 273 | } 274 | var v int64 275 | switch m.rand(4) { 276 | case 0: 277 | v = int64(m.rand(1000)) 278 | case 1: 279 | v = m.randbig() 280 | case 2: 281 | v = m.randbig() * m.randbig() 282 | case 3: 283 | v = -m.randbig() 284 | } 285 | r := numbers[m.rand(len(numbers))] 286 | if res[r.start] == '-' { 287 | // If we started with a negative number, invert the sign of v. 288 | // The idea here is that negative numbers will mostly stay negative; 289 | // we only generate a negative (positive) replacement 1/4th of the time. 290 | v *= -1 291 | } 292 | str := strconv.FormatInt(v, 10) 293 | tmp := make([]byte, len(res)-(r.end-r.start)+len(str)) 294 | copy(tmp, res[:r.start]) 295 | copy(tmp[r.start:], str) 296 | copy(tmp[r.start+len(str):], res[r.end:]) 297 | res = tmp 298 | case 16: 299 | // Splice another input. 300 | if len(res) < 4 || len(corpus) < 2 { 301 | iter-- 302 | continue 303 | } 304 | other := corpus[m.rand(len(corpus))].data 305 | if len(other) < 4 || &res[0] == &other[0] { 306 | iter-- 307 | continue 308 | } 309 | // Find common prefix and suffix. 310 | idx0 := 0 311 | for idx0 < len(res) && idx0 < len(other) && res[idx0] == other[idx0] { 312 | idx0++ 313 | } 314 | idx1 := 0 315 | for idx1 < len(res) && idx1 < len(other) && res[len(res)-idx1-1] == other[len(other)-idx1-1] { 316 | idx1++ 317 | } 318 | // If diffing parts are too small, there is no sense in splicing, rely on byte flipping. 319 | diff := min(len(res)-idx0-idx1, len(other)-idx0-idx1) 320 | if diff < 4 { 321 | iter-- 322 | continue 323 | } 324 | copy(res[idx0:idx0+m.rand(diff-2)+1], other[idx0:]) 325 | case 17: 326 | // Insert a part of another input. 327 | if len(res) < 4 || len(corpus) < 2 { 328 | iter-- 329 | continue 330 | } 331 | other := corpus[m.rand(len(corpus))].data 332 | if len(other) < 4 || &res[0] == &other[0] { 333 | iter-- 334 | continue 335 | } 336 | pos0 := m.rand(len(res) + 1) 337 | pos1 := m.rand(len(other) - 2) 338 | n := m.chooseLen(len(other)-pos1-2) + 2 339 | for i := 0; i < n; i++ { 340 | res = append(res, 0) 341 | } 342 | copy(res[pos0+n:], res[pos0:]) 343 | for i := 0; i < n; i++ { 344 | res[pos0+i] = other[pos1+i] 345 | } 346 | case 18: 347 | // Insert a literal. 348 | // TODO: encode int literals in big-endian, base-128, etc. 349 | if len(ro.intLits) == 0 && len(ro.strLits) == 0 { 350 | iter-- 351 | continue 352 | } 353 | var lit []byte 354 | if len(ro.strLits) != 0 && m.r.Bool() { 355 | lit = []byte(ro.strLits[m.rand(len(ro.strLits))]) 356 | } else { 357 | lit = ro.intLits[m.rand(len(ro.intLits))] 358 | if m.rand(3) == 0 { 359 | lit = reverse(lit) 360 | } 361 | } 362 | pos := m.rand(len(res) + 1) 363 | for i := 0; i < len(lit); i++ { 364 | res = append(res, 0) 365 | } 366 | copy(res[pos+len(lit):], res[pos:]) 367 | copy(res[pos:], lit) 368 | case 19: 369 | // Replace with literal. 370 | if len(ro.intLits) == 0 && len(ro.strLits) == 0 { 371 | iter-- 372 | continue 373 | } 374 | var lit []byte 375 | if len(ro.strLits) != 0 && m.r.Bool() { 376 | lit = []byte(ro.strLits[m.rand(len(ro.strLits))]) 377 | } else { 378 | lit = ro.intLits[m.rand(len(ro.intLits))] 379 | if m.rand(3) == 0 { 380 | lit = reverse(lit) 381 | } 382 | } 383 | if len(lit) >= len(res) { 384 | iter-- 385 | continue 386 | } 387 | pos := m.rand(len(res) - len(lit)) 388 | copy(res[pos:], lit) 389 | } 390 | } 391 | if len(res) > MaxInputSize { 392 | res = res[:MaxInputSize] 393 | } 394 | return res 395 | } 396 | 397 | // chooseLen chooses length of range mutation. 398 | // It gives preference to shorter ranges. 399 | func (m *Mutator) chooseLen(n int) int { 400 | switch x := m.rand(100); { 401 | case x < 90: 402 | return m.rand(min(8, n)) + 1 403 | case x < 99: 404 | return m.rand(min(32, n)) + 1 405 | default: 406 | return m.rand(n) + 1 407 | } 408 | } 409 | 410 | func min(a, b int) int { 411 | if a < b { 412 | return a 413 | } 414 | return b 415 | } 416 | 417 | var ( 418 | interesting8 = []int8{-128, -1, 0, 1, 16, 32, 64, 100, 127} 419 | interesting16 = []int16{-32768, -129, 128, 255, 256, 512, 1000, 1024, 4096, 32767} 420 | interesting32 = []int32{-2147483648, -100663046, -32769, 32768, 65535, 65536, 100663045, 2147483647} 421 | ) 422 | 423 | func init() { 424 | for _, v := range interesting8 { 425 | interesting16 = append(interesting16, int16(v)) 426 | } 427 | for _, v := range interesting16 { 428 | interesting32 = append(interesting32, int32(v)) 429 | } 430 | } 431 | -------------------------------------------------------------------------------- /go-fuzz/persistent.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-fuzz project authors. All rights reserved. 2 | // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 | 4 | package main 5 | 6 | import ( 7 | "crypto/sha1" 8 | "encoding/hex" 9 | "fmt" 10 | "io/ioutil" 11 | "log" 12 | "os" 13 | "path/filepath" 14 | "strconv" 15 | ) 16 | 17 | // PersistentSet is a set of binary blobs with a persistent mirror on disk. 18 | type PersistentSet struct { 19 | dir string 20 | m map[Sig]Artifact 21 | } 22 | 23 | type Artifact struct { 24 | data []byte 25 | meta uint64 // arbitrary user payload 26 | user bool // file created by user 27 | } 28 | 29 | type Sig [sha1.Size]byte 30 | 31 | func hash(data []byte) Sig { 32 | return Sig(sha1.Sum(data)) 33 | } 34 | 35 | func newPersistentSet(dir string) *PersistentSet { 36 | ps := &PersistentSet{ 37 | dir: dir, 38 | m: make(map[Sig]Artifact), 39 | } 40 | os.MkdirAll(dir, 0770) 41 | ps.readInDir(dir) 42 | return ps 43 | } 44 | 45 | func (ps *PersistentSet) readInDir(dir string) { 46 | filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { 47 | if err != nil { 48 | log.Printf("error during dir walk: %v\n", err) 49 | return nil 50 | } 51 | if info.IsDir() { 52 | return nil 53 | } 54 | data, err := ioutil.ReadFile(path) 55 | if err != nil { 56 | log.Printf("error during file read: %v\n", err) 57 | return nil 58 | } 59 | sig := hash(data) 60 | if _, ok := ps.m[sig]; ok { 61 | return nil 62 | } 63 | name := info.Name() 64 | const hexLen = 2 * sha1.Size 65 | if len(name) > hexLen+1 && isHexString(name[:hexLen]) && name[hexLen] == '.' { 66 | return nil // description file 67 | } 68 | var meta uint64 69 | if len(name) > hexLen+1 && isHexString(name[:hexLen]) && name[hexLen] == '-' { 70 | meta, _ = strconv.ParseUint(name[2*sha1.Size+1:], 10, 64) 71 | } 72 | a := Artifact{data, meta, len(name) < hexLen || !isHexString(name[:hexLen])} 73 | ps.m[sig] = a 74 | return nil 75 | }) 76 | } 77 | 78 | func persistentFilename(dir string, a Artifact, sig Sig) string { 79 | fname := filepath.Join(dir, hex.EncodeToString(sig[:])) 80 | if a.meta != 0 { 81 | fname += fmt.Sprintf("-%v", a.meta) 82 | } 83 | return fname 84 | } 85 | 86 | func isHexString(s string) bool { 87 | for _, v := range []byte(s) { 88 | if v >= '0' && v <= '9' || v >= 'a' && v <= 'f' { 89 | continue 90 | } 91 | return false 92 | } 93 | return true 94 | } 95 | 96 | func (ps *PersistentSet) add(a Artifact) bool { 97 | sig := hash(a.data) 98 | if _, ok := ps.m[sig]; ok { 99 | return false 100 | } 101 | ps.m[sig] = a 102 | fname := persistentFilename(ps.dir, a, sig) 103 | if err := ioutil.WriteFile(fname, a.data, 0660); err != nil { 104 | log.Printf("failed to write file: %v", err) 105 | } 106 | return true 107 | } 108 | 109 | // addDescription creates a complementary to data file on disk. 110 | func (ps *PersistentSet) addDescription(data []byte, desc []byte, typ string) { 111 | sig := hash(data) 112 | fname := filepath.Join(ps.dir, fmt.Sprintf("%v.%v", hex.EncodeToString(sig[:]), typ)) 113 | if err := ioutil.WriteFile(fname, desc, 0660); err != nil { 114 | log.Printf("failed to write file: %v", err) 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /go-fuzz/sonar.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-fuzz project authors. All rights reserved. 2 | // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 | 4 | package main 5 | 6 | import ( 7 | "bytes" 8 | "encoding/binary" 9 | "encoding/hex" 10 | "log" 11 | "path/filepath" 12 | "strconv" 13 | "sync" 14 | "unicode" 15 | 16 | . "github.com/dvyukov/go-fuzz/go-fuzz-defs" 17 | ) 18 | 19 | type SonarSite struct { 20 | id int // unique site id (const) 21 | loc string // file:line.pos,line.pos (const) 22 | sync.Mutex 23 | dynamic bool // both operands are not constant 24 | takenFuzz [2]int // number of times condition evaluated to false/true during fuzzing 25 | takenTotal [2]int // number of times condition evaluated to false/true in total 26 | val [2][]byte 27 | } 28 | 29 | type SonarSample struct { 30 | site *SonarSite 31 | flags byte 32 | val [2][]byte 33 | } 34 | 35 | func (w *Worker) parseSonarData(sonar []byte) (res []SonarSample) { 36 | ro := w.hub.ro.Load().(*ROData) 37 | sonar = makeCopy(sonar) 38 | for len(sonar) > SonarHdrLen { 39 | id := binary.LittleEndian.Uint32(sonar) 40 | flags := byte(id) 41 | id >>= 8 42 | n1 := sonar[4] 43 | n2 := sonar[5] 44 | sonar = sonar[SonarHdrLen:] 45 | if n1 > SonarMaxLen || n2 > SonarMaxLen || len(sonar) < int(n1)+int(n2) { 46 | log.Fatalf("corrupted sonar data: hdr=[%v/%v/%v] data=%v", flags, n1, n2, len(sonar)) 47 | } 48 | v1 := makeCopy(sonar[:n1]) 49 | v2 := makeCopy(sonar[n1 : n1+n2]) 50 | sonar = sonar[n1+n2:] 51 | 52 | // Trim trailing 0x00 and 0xff bytes (we don't know exact size of operands). 53 | if flags&SonarString == 0 { 54 | for len(v1) > 0 || len(v2) > 0 { 55 | i := len(v1) - 1 56 | if len(v2) > len(v1) { 57 | i = len(v2) - 1 58 | } 59 | var c1, c2 byte 60 | if i < len(v1) { 61 | c1 = v1[i] 62 | } 63 | if i < len(v2) { 64 | c2 = v2[i] 65 | } 66 | if (c1 == 0 || c1 == 0xff) && (c2 == 0 || c2 == 0xff) { 67 | if i < len(v1) { 68 | v1 = v1[:i] 69 | } 70 | if i < len(v2) { 71 | v2 = v2[:i] 72 | } 73 | continue 74 | } 75 | break 76 | } 77 | } 78 | 79 | res = append(res, SonarSample{&ro.sonarSites[id], flags, [2][]byte{v1, v2}}) 80 | } 81 | return res 82 | } 83 | 84 | func (w *Worker) processSonarData(data, sonar []byte, depth int, smash bool) { 85 | ro := w.hub.ro.Load().(*ROData) 86 | updated := false 87 | checked := make(map[string]struct{}) 88 | samples := w.parseSonarData(sonar) 89 | for _, sam := range samples { 90 | // TODO: extract literal corpus from sonar instead of from source. 91 | // This should give smaller, better corpus which does not contain literals from dead code. 92 | 93 | // TODO: detect loop counters (small incrementing/decrementing values on the same site). 94 | // Either ignore them or handle differently (e.g. alter a string length). 95 | 96 | site := sam.site 97 | flags := sam.flags 98 | v1 := sam.val[0] 99 | v2 := sam.val[1] 100 | res := sam.evaluate() 101 | // Ignore sites that has at least one const operand and 102 | // are already taken both ways enough times. 103 | upd, skip := site.update(sam, smash, res) 104 | if upd { 105 | updated = true 106 | } 107 | if skip { 108 | continue 109 | } 110 | if smash && bytes.Equal(v1, v2) { 111 | // We systematically mutate all bytes during smashing, 112 | // no point in trying to break equality here. 113 | continue 114 | } 115 | testInput := func(tmp []byte) { 116 | w.testInput(tmp, depth+1, execSonarHint) 117 | } 118 | check := func(indexdata, v1, v2 []byte) { 119 | if len(v1) == 0 || bytes.Equal(v1, v2) || !bytes.Contains(indexdata, v1) { 120 | return 121 | } 122 | if len(indexdata) != len(data) { 123 | // We use indices into indexdata to construct indices into data below. 124 | // If they don't have the same size, this operation is nonsensical and may panic. 125 | panic("len(indexdata) != len(data)") 126 | } 127 | vv := string(v1) + "\t|\t" + string(v2) 128 | if _, ok := checked[vv]; ok { 129 | return 130 | } 131 | checked[vv] = struct{}{} 132 | pos := 0 133 | for { 134 | i := bytes.Index(indexdata[pos:], v1) 135 | if i == -1 { 136 | break 137 | } 138 | i += pos 139 | pos = i + 1 140 | tmp := make([]byte, len(data)-len(v1)+len(v2)) 141 | copy(tmp, data[:i]) 142 | copy(tmp[i:], v2) 143 | copy(tmp[i+len(v2):], data[i+len(v1):]) 144 | if len(tmp) > CoverSize { 145 | tmp = tmp[:CoverSize] 146 | } 147 | testInput(tmp) 148 | if flags&SonarString != 0 && len(v1) != len(v2) && len(tmp) < CoverSize { 149 | // Update length field. 150 | // TODO: handle multi-byte/big-endian/base-128 length fields. 151 | diff := byte(len(v2) - len(v1)) 152 | for idx := i - 1; idx >= 0 && idx+5 >= i; idx-- { 153 | tmp[idx] += diff 154 | testInput(tmp) 155 | tmp[idx] -= diff 156 | } 157 | } 158 | } 159 | } 160 | check1 := func(v1, v2 []byte) { 161 | check(data, v1, v2) 162 | // TODO: for strings check upper/lower case. 163 | if flags&SonarString != 0 { 164 | if bytes.Equal(v1, bytes.ToLower(v1)) && bytes.Equal(v2, bytes.ToLower(v2)) { 165 | if lower := bytes.ToLower(data); len(lower) == len(data) { 166 | check(lower, v1, v2) 167 | } 168 | } 169 | if bytes.Equal(v1, bytes.ToUpper(v1)) && bytes.Equal(v2, bytes.ToUpper(v2)) { 170 | if upper := bytes.ToUpper(data); len(upper) == len(data) { 171 | check(upper, v1, v2) 172 | } 173 | } 174 | } else { 175 | // Try several common wire encodings of the values: 176 | // network format (big endian), hex, base-128. 177 | // TODO: try more encodings if it proves to be useful: 178 | // base-64, quoted-printable, xml-escaping, hex+increment/decrement. 179 | 180 | if len(v1) == 1 && len(v2) == 1 && unicode.IsLower(rune(v1[0])) && unicode.IsLower(rune(v2[0])) { 181 | if lower := bytes.ToLower(data); len(lower) == len(data) { 182 | check(lower, v1, v2) 183 | } 184 | } 185 | if len(v1) == 1 && len(v2) == 1 && unicode.IsUpper(rune(v1[0])) && unicode.IsUpper(rune(v2[0])) { 186 | if upper := bytes.ToUpper(data); len(upper) == len(data) { 187 | check(upper, v1, v2) 188 | } 189 | } 190 | 191 | // Increment and decrement take care of less and greater comparison operators 192 | // as well as of off-by-one bugs. 193 | check(data, v1, increment(v2)) 194 | check(data, v1, decrement(v2)) 195 | 196 | // Also try big-endian increments/decrements. 197 | if len(v1) > 1 { 198 | check(data, reverse(v1), reverse(v2)) 199 | check(data, reverse(v1), reverse(increment(v2))) 200 | check(data, reverse(v1), reverse(decrement(v2))) 201 | check(data, v1, reverse(increment(reverse(v2)))) 202 | check(data, v1, reverse(decrement(reverse(v2)))) 203 | } 204 | 205 | // Base-128. 206 | // TODO: try to treat the value as negative. 207 | var u1, u2 uint64 208 | for i := 0; i < len(v1); i++ { 209 | u1 += uint64(v1[i]) << uint(i*8) 210 | } 211 | for i := 0; i < len(v2); i++ { 212 | u2 += uint64(v2[i]) << uint(i*8) 213 | } 214 | if u1 > 127 || u2 > 127 { // otherwise it's the same as byte replacement 215 | var vv1, vv2 [10]byte 216 | n1 := binary.PutUvarint(vv1[:], u1) 217 | n2 := binary.PutUvarint(vv2[:], u2) 218 | check(data, vv1[:n1], vv2[:n2]) 219 | 220 | // Increment/decrement in base-128. 221 | n1 = binary.PutUvarint(vv1[:], u1+1) 222 | n2 = binary.PutUvarint(vv2[:], u2+1) 223 | check(data, vv1[:n1], vv2[:n2]) 224 | n1 = binary.PutUvarint(vv1[:], u1-1) 225 | n2 = binary.PutUvarint(vv2[:], u2-1) 226 | check(data, vv1[:n1], vv2[:n2]) 227 | } 228 | 229 | // Ascii-encoding. 230 | // TODO: try to treat the value as negative. 231 | s1 := strconv.FormatUint(u1, 10) 232 | s2 := strconv.FormatUint(u2, 10) 233 | check(data, []byte(s1), []byte(s2)) 234 | } 235 | check(data, []byte(hex.EncodeToString(v1)), []byte(hex.EncodeToString(v2))) 236 | } 237 | if flags&SonarConst1 == 0 { 238 | check1(v1, v2) 239 | } 240 | if flags&SonarConst2 == 0 { 241 | check1(v2, v1) 242 | } 243 | } 244 | if updated && *flagDumpCover { 245 | dumpMu.Lock() 246 | defer dumpMu.Unlock() 247 | dumpSonar(filepath.Join(*flagWorkdir, "sonarprofile"), ro.sonarSites) 248 | } 249 | } 250 | 251 | var dumpMu sync.Mutex 252 | 253 | func (site *SonarSite) update(sam SonarSample, smash, resb bool) (updated, skip bool) { 254 | res := 0 255 | if resb { 256 | res = 1 257 | } 258 | site.Lock() 259 | defer site.Unlock() 260 | v1 := sam.val[0] 261 | v2 := sam.val[1] 262 | if !site.dynamic && sam.flags&SonarConst1+sam.flags&SonarConst2 == 0 { 263 | if site.val[0] == nil { 264 | site.val[0] = makeCopy(v1) 265 | } 266 | if site.val[1] == nil { 267 | site.val[1] = makeCopy(v2) 268 | } 269 | if !bytes.Equal(site.val[0], v1) && !bytes.Equal(site.val[1], v2) { 270 | site.val[0] = nil 271 | site.val[1] = nil 272 | site.dynamic = true 273 | } 274 | } 275 | if site.takenTotal[res] == 0 { 276 | updated = true 277 | } 278 | site.takenTotal[res]++ 279 | if !smash { 280 | site.takenFuzz[res]++ 281 | } 282 | if !site.dynamic && !smash { 283 | // Skip this site if it has at least one const operand 284 | // and is taken both ways enough times. 285 | // Check sites that don't have const operands always, 286 | // because it can be a CRC-like verification, which 287 | // won't be cracked otherwise. 288 | if site.takenFuzz[0] > 10 && site.takenFuzz[1] > 10 || site.takenFuzz[0]+site.takenFuzz[1] > 100 { 289 | skip = true 290 | return 291 | } 292 | } 293 | return 294 | } 295 | 296 | func (sam *SonarSample) evaluate() bool { 297 | v1 := sam.val[0] 298 | v2 := sam.val[1] 299 | if sam.flags&SonarString != 0 { 300 | s1 := string(v1) 301 | s2 := string(v2) 302 | switch sam.flags & SonarOpMask { 303 | case SonarEQL: 304 | return s1 == s2 305 | case SonarNEQ: 306 | return s1 != s2 307 | case SonarLSS: 308 | return s1 < s2 309 | case SonarGTR: 310 | return s1 > s2 311 | case SonarLEQ: 312 | return s1 <= s2 313 | case SonarGEQ: 314 | return s1 >= s2 315 | default: 316 | panic("bad") 317 | } 318 | } 319 | if len(v1) == 0 || len(v2) == 0 || len(v1) > 8 || len(v2) > 8 || len(v1) != len(v2) { 320 | return false 321 | } 322 | v1 = makeCopy(v1) 323 | for len(v1) < 8 { 324 | if int8(v1[len(v1)-1]) >= 0 { 325 | v1 = append(v1, 0) 326 | } else { 327 | v1 = append(v1, 0xff) 328 | } 329 | } 330 | v2 = makeCopy(v2) 331 | for len(v2) < 8 { 332 | if int8(v2[len(v2)-1]) >= 0 { 333 | v2 = append(v2, 0) 334 | } else { 335 | v2 = append(v2, 0xff) 336 | } 337 | } 338 | // Note: assuming le machine. 339 | if sam.flags&SonarSigned == 0 { 340 | s1 := binary.LittleEndian.Uint64(v1) 341 | s2 := binary.LittleEndian.Uint64(v2) 342 | switch sam.flags & SonarOpMask { 343 | case SonarEQL: 344 | return s1 == s2 345 | case SonarNEQ: 346 | return s1 != s2 347 | case SonarLSS: 348 | return s1 < s2 349 | case SonarGTR: 350 | return s1 > s2 351 | case SonarLEQ: 352 | return s1 <= s2 353 | case SonarGEQ: 354 | return s1 >= s2 355 | default: 356 | panic("bad") 357 | } 358 | } else { 359 | s1 := int64(binary.LittleEndian.Uint64(v1)) 360 | s2 := int64(binary.LittleEndian.Uint64(v2)) 361 | switch sam.flags & SonarOpMask { 362 | case SonarEQL: 363 | return s1 == s2 364 | case SonarNEQ: 365 | return s1 != s2 366 | case SonarLSS: 367 | return s1 < s2 368 | case SonarGTR: 369 | return s1 > s2 370 | case SonarLEQ: 371 | return s1 <= s2 372 | case SonarGEQ: 373 | return s1 >= s2 374 | default: 375 | panic("bad") 376 | } 377 | } 378 | } 379 | 380 | func dumpSonarData(site *SonarSite, flags byte, v1, v2 []byte) { 381 | // Debug output. 382 | op := "" 383 | switch flags & SonarOpMask { 384 | case SonarEQL: 385 | op = "==" 386 | case SonarNEQ: 387 | op = "!=" 388 | case SonarLSS: 389 | op = "<" 390 | case SonarGTR: 391 | op = ">" 392 | case SonarLEQ: 393 | op = "<=" 394 | case SonarGEQ: 395 | op = ">=" 396 | default: 397 | log.Fatalf("bad") 398 | } 399 | sign := "" 400 | if flags&SonarSigned != 0 { 401 | sign = "(signed)" 402 | } 403 | isstr := "" 404 | if flags&SonarString != 0 { 405 | isstr = "(string)" 406 | } 407 | const1 := "" 408 | if flags&SonarConst1 != 0 { 409 | const1 = "c" 410 | } 411 | const2 := "" 412 | if flags&SonarConst2 != 0 { 413 | const2 = "c" 414 | } 415 | log.Printf("SONAR %v%v %v %v%v %v%v %v", 416 | hex.EncodeToString(v1), const1, op, hex.EncodeToString(v2), const2, sign, isstr, site.loc) 417 | } 418 | -------------------------------------------------------------------------------- /go-fuzz/sys_posix.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-fuzz project authors. All rights reserved. 2 | // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 | 4 | // +build darwin linux freebsd dragonfly openbsd netbsd 5 | 6 | package main 7 | 8 | import ( 9 | "log" 10 | "os" 11 | "os/exec" 12 | "syscall" 13 | ) 14 | 15 | func lowerProcessPrio() { 16 | syscall.Setpriority(syscall.PRIO_PROCESS, 0, 19) 17 | } 18 | 19 | type Mapping struct { 20 | f *os.File 21 | } 22 | 23 | func createMapping(name string, size int) (*Mapping, []byte) { 24 | f, err := os.OpenFile(name, os.O_RDWR, 0) 25 | if err != nil { 26 | log.Fatalf("failed to open comm file: %v", err) 27 | } 28 | mem, err := syscall.Mmap(int(f.Fd()), 0, size, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED) 29 | if err != nil { 30 | log.Fatalf("failed to mmap comm file: %v", err) 31 | } 32 | return &Mapping{f}, mem 33 | } 34 | 35 | func (m *Mapping) destroy() { 36 | m.f.Close() 37 | } 38 | 39 | func setupCommMapping(cmd *exec.Cmd, comm *Mapping, rOut, wIn *os.File) { 40 | cmd.ExtraFiles = append(cmd.ExtraFiles, comm.f) 41 | cmd.ExtraFiles = append(cmd.ExtraFiles, rOut) 42 | cmd.ExtraFiles = append(cmd.ExtraFiles, wIn) 43 | } 44 | -------------------------------------------------------------------------------- /go-fuzz/sys_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-fuzz project authors. All rights reserved. 2 | // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 | 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | "log" 9 | "os" 10 | "os/exec" 11 | "reflect" 12 | "syscall" 13 | "unsafe" 14 | ) 15 | 16 | func lowerProcessPrio() { 17 | // TODO: implement me 18 | } 19 | 20 | type Mapping struct { 21 | mapping syscall.Handle 22 | addr uintptr 23 | } 24 | 25 | func createMapping(name string, size int) (*Mapping, []byte) { 26 | f, err := os.OpenFile(name, os.O_RDWR, 0) 27 | if err != nil { 28 | log.Fatalf("failed to open comm file: %v", err) 29 | } 30 | defer f.Close() 31 | mapping, err := syscall.CreateFileMapping(syscall.InvalidHandle, nil, syscall.PAGE_READWRITE, 0, uint32(size), nil) 32 | if err != nil { 33 | log.Fatalf("failed to create file mapping: %v", err) 34 | } 35 | const FILE_MAP_ALL_ACCESS = 0xF001F 36 | addr, err := syscall.MapViewOfFile(mapping, FILE_MAP_ALL_ACCESS, 0, 0, uintptr(size)) 37 | if err != nil { 38 | log.Fatalf("failed to mmap comm file: %v", err) 39 | } 40 | hdr := reflect.SliceHeader{addr, size, size} 41 | mem := *(*[]byte)(unsafe.Pointer(&hdr)) 42 | mem[0] = 1 // test access 43 | return &Mapping{mapping, addr}, mem 44 | } 45 | 46 | func (m *Mapping) destroy() { 47 | syscall.UnmapViewOfFile(m.addr) 48 | syscall.CloseHandle(m.mapping) 49 | } 50 | 51 | func setupCommMapping(cmd *exec.Cmd, comm *Mapping, rOut, wIn *os.File) { 52 | syscall.SetHandleInformation(syscall.Handle(comm.mapping), syscall.HANDLE_FLAG_INHERIT, 1) 53 | syscall.SetHandleInformation(syscall.Handle(rOut.Fd()), syscall.HANDLE_FLAG_INHERIT, 1) 54 | syscall.SetHandleInformation(syscall.Handle(wIn.Fd()), syscall.HANDLE_FLAG_INHERIT, 1) 55 | cmd.Env = append(cmd.Env, fmt.Sprintf("GO_FUZZ_COMM_FD=%v", comm.mapping)) 56 | cmd.Env = append(cmd.Env, fmt.Sprintf("GO_FUZZ_IN_FD=%v", rOut.Fd())) 57 | cmd.Env = append(cmd.Env, fmt.Sprintf("GO_FUZZ_OUT_FD=%v", wIn.Fd())) 58 | } 59 | -------------------------------------------------------------------------------- /go-fuzz/testee.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-fuzz project authors. All rights reserved. 2 | // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 | 4 | package main 5 | 6 | import ( 7 | "encoding/binary" 8 | "fmt" 9 | "io" 10 | "io/ioutil" 11 | "log" 12 | "os" 13 | "os/exec" 14 | "sync/atomic" 15 | "syscall" 16 | "time" 17 | "unsafe" 18 | 19 | . "github.com/dvyukov/go-fuzz/go-fuzz-defs" 20 | ) 21 | 22 | // Testee is a wrapper around one testee subprocess. 23 | // It manages communication with the testee, timeouts and output collection. 24 | type Testee struct { 25 | coverRegion []byte 26 | inputRegion []byte 27 | sonarRegion []byte 28 | cmd *exec.Cmd 29 | inPipe *os.File 30 | outPipe *os.File 31 | stdoutPipe *os.File 32 | writebuf [9]byte // reusable write buffer 33 | resbuf [24]byte // reusable results buffer 34 | startTime int64 35 | execs int 36 | outputC chan []byte 37 | downC chan bool 38 | down bool 39 | fnidx uint8 40 | } 41 | 42 | // TestBinary handles communication with and restring of testee subprocesses. 43 | type TestBinary struct { 44 | fileName string 45 | commFile string 46 | comm *Mapping 47 | periodicCheck func() 48 | 49 | coverRegion []byte 50 | inputRegion []byte 51 | sonarRegion []byte 52 | 53 | testee *Testee 54 | testeeBuffer []byte // reusable buffer for collecting testee output 55 | 56 | stats *Stats 57 | 58 | fnidx uint8 59 | } 60 | 61 | func init() { 62 | if unsafe.Offsetof(Testee{}.startTime)%8 != 0 { 63 | println(unsafe.Offsetof(Testee{}.startTime)) 64 | panic("bad atomic field offset") 65 | } 66 | } 67 | 68 | // testeeBufferSize is how much output a test binary can emit 69 | // before we start to overwrite old output. 70 | const testeeBufferSize = 1 << 20 71 | 72 | func newTestBinary(fileName string, periodicCheck func(), stats *Stats, fnidx uint8) *TestBinary { 73 | comm, err := ioutil.TempFile("", "go-fuzz-comm") 74 | if err != nil { 75 | log.Fatalf("failed to create comm file: %v", err) 76 | } 77 | comm.Truncate(CoverSize + MaxInputSize + SonarRegionSize) 78 | comm.Close() 79 | mapping, mem := createMapping(comm.Name(), CoverSize+MaxInputSize+SonarRegionSize) 80 | return &TestBinary{ 81 | fileName: fileName, 82 | commFile: comm.Name(), 83 | comm: mapping, 84 | periodicCheck: periodicCheck, 85 | coverRegion: mem[:CoverSize], 86 | inputRegion: mem[CoverSize : CoverSize+MaxInputSize], 87 | sonarRegion: mem[CoverSize+MaxInputSize:], 88 | stats: stats, 89 | fnidx: fnidx, 90 | testeeBuffer: make([]byte, testeeBufferSize), 91 | } 92 | } 93 | 94 | func (bin *TestBinary) close() { 95 | if bin.testee != nil { 96 | bin.testee.shutdown() 97 | bin.testee = nil 98 | } 99 | bin.comm.destroy() 100 | os.Remove(bin.commFile) 101 | } 102 | 103 | func (bin *TestBinary) test(data []byte) (res int, ns uint64, cover, sonar, output []byte, crashed, hanged bool) { 104 | if len(data) > MaxInputSize { 105 | panic("input is too large") 106 | } 107 | for { 108 | // This is the only function that is executed regularly, 109 | // so we tie some periodic checks to it. 110 | bin.periodicCheck() 111 | 112 | bin.stats.execs++ 113 | if bin.testee == nil { 114 | bin.stats.restarts++ 115 | bin.testee = newTestee(bin.fileName, bin.comm, bin.coverRegion, bin.inputRegion, bin.sonarRegion, bin.fnidx, bin.testeeBuffer) 116 | } 117 | var retry bool 118 | res, ns, cover, sonar, crashed, hanged, retry = bin.testee.test(data) 119 | if retry { 120 | bin.testee.shutdown() 121 | bin.testee = nil 122 | continue 123 | } 124 | if crashed { 125 | output = bin.testee.shutdown() 126 | if hanged { 127 | hdr := fmt.Sprintf("program hanged (timeout %v seconds)\n\n", *flagTimeout) 128 | output = append([]byte(hdr), output...) 129 | } 130 | bin.testee = nil 131 | return 132 | } 133 | return 134 | } 135 | } 136 | 137 | func newTestee(bin string, comm *Mapping, coverRegion, inputRegion, sonarRegion []byte, fnidx uint8, buffer []byte) *Testee { 138 | retry: 139 | rIn, wIn, err := os.Pipe() 140 | if err != nil { 141 | log.Fatalf("failed to pipe: %v", err) 142 | } 143 | rOut, wOut, err := os.Pipe() 144 | if err != nil { 145 | log.Fatalf("failed to pipe: %v", err) 146 | } 147 | rStdout, wStdout, err := os.Pipe() 148 | if err != nil { 149 | log.Fatalf("failed to pipe: %v", err) 150 | } 151 | cmd := exec.Command(bin) 152 | if *flagTestOutput { 153 | // For debugging of testee failures. 154 | cmd.Stdout = os.Stdout 155 | cmd.Stderr = os.Stdout 156 | } else { 157 | cmd.Stdout = wStdout 158 | cmd.Stderr = wStdout 159 | } 160 | cmd.Env = append([]string{}, os.Environ()...) 161 | cmd.Env = append(cmd.Env, "GOTRACEBACK=1") 162 | setupCommMapping(cmd, comm, rOut, wIn) 163 | if err = cmd.Start(); err != nil { 164 | // This can be a transient failure like "cannot allocate memory" or "text file is busy". 165 | log.Printf("failed to start test binary: %v", err) 166 | rIn.Close() 167 | wIn.Close() 168 | rOut.Close() 169 | wOut.Close() 170 | rStdout.Close() 171 | wStdout.Close() 172 | time.Sleep(time.Second) 173 | goto retry 174 | } 175 | rOut.Close() 176 | wIn.Close() 177 | wStdout.Close() 178 | t := &Testee{ 179 | coverRegion: coverRegion, 180 | inputRegion: inputRegion, 181 | sonarRegion: sonarRegion, 182 | cmd: cmd, 183 | inPipe: rIn, 184 | outPipe: wOut, 185 | stdoutPipe: rStdout, 186 | outputC: make(chan []byte), 187 | downC: make(chan bool), 188 | fnidx: fnidx, 189 | } 190 | // Stdout reader goroutine. 191 | go func() { 192 | // The testee should not output unless it crashes. 193 | // But there are still chances that it does. If so, it can overflow 194 | // the stdout pipe during testing and deadlock. To prevent the 195 | // deadlock we periodically read out stdout. 196 | // This goroutine also collects crash output. 197 | ticker := time.NewTicker(time.Second) 198 | data := buffer 199 | filled := 0 200 | for { 201 | select { 202 | case <-ticker.C: 203 | case <-t.downC: 204 | } 205 | n, err := t.stdoutPipe.Read(data[filled:]) 206 | if *flagV >= 3 { 207 | log.Printf("testee: %v\n", string(data[filled:filled+n])) 208 | } 209 | filled += n 210 | if filled > testeeBufferSize/4*3 { 211 | copy(data, data[testeeBufferSize/2:filled]) 212 | filled -= testeeBufferSize / 2 213 | } 214 | if err != nil { 215 | break 216 | } 217 | } 218 | ticker.Stop() 219 | trimmed := make([]byte, filled) 220 | copy(trimmed, data) 221 | t.outputC <- trimmed 222 | }() 223 | // Hang watcher goroutine. 224 | go func() { 225 | timeout := time.Duration(*flagTimeout) * time.Second 226 | ticker := time.NewTicker(timeout / 2) 227 | for { 228 | select { 229 | case <-ticker.C: 230 | start := atomic.LoadInt64(&t.startTime) 231 | if start != 0 && time.Now().UnixNano()-start > int64(timeout) { 232 | atomic.StoreInt64(&t.startTime, -1) 233 | t.cmd.Process.Signal(syscall.SIGABRT) 234 | time.Sleep(time.Second) 235 | t.cmd.Process.Signal(syscall.SIGKILL) 236 | ticker.Stop() 237 | return 238 | } 239 | case <-t.downC: 240 | ticker.Stop() 241 | return 242 | } 243 | 244 | } 245 | }() 246 | // Shutdown watcher goroutine. 247 | go func() { 248 | select { 249 | case <-t.downC: 250 | case <-shutdownC: 251 | t.cmd.Process.Signal(syscall.SIGKILL) 252 | } 253 | }() 254 | return t 255 | } 256 | 257 | // test passes data for testing. 258 | func (t *Testee) test(data []byte) (res int, ns uint64, cover, sonar []byte, crashed, hanged, retry bool) { 259 | if t.down { 260 | log.Fatalf("cannot test: testee is already shutdown") 261 | } 262 | 263 | // The test binary can accumulate significant amount of memory, 264 | // so we recreate it periodically. 265 | t.execs++ 266 | if t.execs > 10000 { 267 | t.cmd.Process.Signal(syscall.SIGKILL) 268 | retry = true 269 | return 270 | } 271 | 272 | copy(t.inputRegion[:], data) 273 | atomic.StoreInt64(&t.startTime, time.Now().UnixNano()) 274 | t.writebuf[0] = t.fnidx 275 | binary.LittleEndian.PutUint64(t.writebuf[1:], uint64(len(data))) 276 | if _, err := t.outPipe.Write(t.writebuf[:]); err != nil { 277 | if *flagV >= 1 { 278 | log.Printf("write to testee failed: %v", err) 279 | } 280 | retry = true 281 | return 282 | } 283 | // Once we do the write, the test is running. 284 | // Once we read the reply below, the test is done. 285 | type Reply struct { 286 | Res uint64 287 | Ns uint64 288 | Sonar uint64 289 | } 290 | _, err := io.ReadFull(t.inPipe, t.resbuf[:]) 291 | r := Reply{ 292 | Res: binary.LittleEndian.Uint64(t.resbuf[:]), 293 | Ns: binary.LittleEndian.Uint64(t.resbuf[8:]), 294 | Sonar: binary.LittleEndian.Uint64(t.resbuf[16:]), 295 | } 296 | hanged = atomic.LoadInt64(&t.startTime) == -1 297 | atomic.StoreInt64(&t.startTime, 0) 298 | if err != nil || hanged { 299 | // Should have been crashed. 300 | crashed = true 301 | return 302 | } 303 | res = int(r.Res) 304 | ns = r.Ns 305 | cover = t.coverRegion 306 | sonar = t.sonarRegion[:r.Sonar] 307 | return 308 | } 309 | 310 | func (t *Testee) shutdown() (output []byte) { 311 | if t.down { 312 | log.Fatalf("cannot shutdown: testee is already shutdown") 313 | } 314 | t.down = true 315 | t.cmd.Process.Kill() // it is probably already dead, but kill it again to be sure 316 | close(t.downC) // wakeup stdout reader 317 | out := <-t.outputC 318 | if err := t.cmd.Wait(); err != nil { 319 | out = append(out, err.Error()...) 320 | } 321 | t.inPipe.Close() 322 | t.outPipe.Close() 323 | t.stdoutPipe.Close() 324 | return out 325 | } 326 | -------------------------------------------------------------------------------- /go-fuzz/vendor/github.com/elazarl/go-bindata-assetfs/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Elazar Leibovich 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /go-fuzz/vendor/github.com/elazarl/go-bindata-assetfs/README.md: -------------------------------------------------------------------------------- 1 | # go-bindata-assetfs 2 | 3 | Serve embedded files from [jteeuwen/go-bindata](https://github.com/jteeuwen/go-bindata) with `net/http`. 4 | 5 | [GoDoc](http://godoc.org/github.com/elazarl/go-bindata-assetfs) 6 | 7 | ### Installation 8 | 9 | Install with 10 | 11 | $ go get github.com/jteeuwen/go-bindata/... 12 | $ go get github.com/elazarl/go-bindata-assetfs/... 13 | 14 | ### Creating embedded data 15 | 16 | Usage is identical to [jteeuwen/go-bindata](https://github.com/jteeuwen/go-bindata) usage, 17 | instead of running `go-bindata` run `go-bindata-assetfs`. 18 | 19 | The tool will create a `bindata_assetfs.go` file, which contains the embedded data. 20 | 21 | A typical use case is 22 | 23 | $ go-bindata-assetfs data/... 24 | 25 | ### Using assetFS in your code 26 | 27 | The generated file provides an `assetFS()` function that returns a `http.Filesystem` 28 | wrapping the embedded files. What you usually want to do is: 29 | 30 | http.Handle("/", http.FileServer(assetFS())) 31 | 32 | This would run an HTTP server serving the embedded files. 33 | 34 | ## Without running binary tool 35 | 36 | You can always just run the `go-bindata` tool, and then 37 | 38 | use 39 | 40 | import "github.com/elazarl/go-bindata-assetfs" 41 | ... 42 | http.Handle("/", 43 | http.FileServer( 44 | &assetfs.AssetFS{Asset: Asset, AssetDir: AssetDir, AssetInfo: AssetInfo, Prefix: "data"})) 45 | 46 | to serve files embedded from the `data` directory. 47 | -------------------------------------------------------------------------------- /go-fuzz/vendor/github.com/elazarl/go-bindata-assetfs/assetfs.go: -------------------------------------------------------------------------------- 1 | package assetfs 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "io" 7 | "io/ioutil" 8 | "net/http" 9 | "os" 10 | "path" 11 | "path/filepath" 12 | "time" 13 | ) 14 | 15 | var ( 16 | defaultFileTimestamp = time.Now() 17 | ) 18 | 19 | // FakeFile implements os.FileInfo interface for a given path and size 20 | type FakeFile struct { 21 | // Path is the path of this file 22 | Path string 23 | // Dir marks of the path is a directory 24 | Dir bool 25 | // Len is the length of the fake file, zero if it is a directory 26 | Len int64 27 | // Timestamp is the ModTime of this file 28 | Timestamp time.Time 29 | } 30 | 31 | func (f *FakeFile) Name() string { 32 | _, name := filepath.Split(f.Path) 33 | return name 34 | } 35 | 36 | func (f *FakeFile) Mode() os.FileMode { 37 | mode := os.FileMode(0644) 38 | if f.Dir { 39 | return mode | os.ModeDir 40 | } 41 | return mode 42 | } 43 | 44 | func (f *FakeFile) ModTime() time.Time { 45 | return f.Timestamp 46 | } 47 | 48 | func (f *FakeFile) Size() int64 { 49 | return f.Len 50 | } 51 | 52 | func (f *FakeFile) IsDir() bool { 53 | return f.Mode().IsDir() 54 | } 55 | 56 | func (f *FakeFile) Sys() interface{} { 57 | return nil 58 | } 59 | 60 | // AssetFile implements http.File interface for a no-directory file with content 61 | type AssetFile struct { 62 | *bytes.Reader 63 | io.Closer 64 | FakeFile 65 | } 66 | 67 | func NewAssetFile(name string, content []byte, timestamp time.Time) *AssetFile { 68 | if timestamp.IsZero() { 69 | timestamp = defaultFileTimestamp 70 | } 71 | return &AssetFile{ 72 | bytes.NewReader(content), 73 | ioutil.NopCloser(nil), 74 | FakeFile{name, false, int64(len(content)), timestamp}} 75 | } 76 | 77 | func (f *AssetFile) Readdir(count int) ([]os.FileInfo, error) { 78 | return nil, errors.New("not a directory") 79 | } 80 | 81 | func (f *AssetFile) Size() int64 { 82 | return f.FakeFile.Size() 83 | } 84 | 85 | func (f *AssetFile) Stat() (os.FileInfo, error) { 86 | return f, nil 87 | } 88 | 89 | // AssetDirectory implements http.File interface for a directory 90 | type AssetDirectory struct { 91 | AssetFile 92 | ChildrenRead int 93 | Children []os.FileInfo 94 | } 95 | 96 | func NewAssetDirectory(name string, children []string, fs *AssetFS) *AssetDirectory { 97 | fileinfos := make([]os.FileInfo, 0, len(children)) 98 | for _, child := range children { 99 | _, err := fs.AssetDir(filepath.Join(name, child)) 100 | fileinfos = append(fileinfos, &FakeFile{child, err == nil, 0, time.Time{}}) 101 | } 102 | return &AssetDirectory{ 103 | AssetFile{ 104 | bytes.NewReader(nil), 105 | ioutil.NopCloser(nil), 106 | FakeFile{name, true, 0, time.Time{}}, 107 | }, 108 | 0, 109 | fileinfos} 110 | } 111 | 112 | func (f *AssetDirectory) Readdir(count int) ([]os.FileInfo, error) { 113 | if count <= 0 { 114 | return f.Children, nil 115 | } 116 | if f.ChildrenRead+count > len(f.Children) { 117 | count = len(f.Children) - f.ChildrenRead 118 | } 119 | rv := f.Children[f.ChildrenRead : f.ChildrenRead+count] 120 | f.ChildrenRead += count 121 | return rv, nil 122 | } 123 | 124 | func (f *AssetDirectory) Stat() (os.FileInfo, error) { 125 | return f, nil 126 | } 127 | 128 | // AssetFS implements http.FileSystem, allowing 129 | // embedded files to be served from net/http package. 130 | type AssetFS struct { 131 | // Asset should return content of file in path if exists 132 | Asset func(path string) ([]byte, error) 133 | // AssetDir should return list of files in the path 134 | AssetDir func(path string) ([]string, error) 135 | // AssetInfo should return the info of file in path if exists 136 | AssetInfo func(path string) (os.FileInfo, error) 137 | // Prefix would be prepended to http requests 138 | Prefix string 139 | } 140 | 141 | func (fs *AssetFS) Open(name string) (http.File, error) { 142 | name = path.Join(fs.Prefix, name) 143 | if len(name) > 0 && name[0] == '/' { 144 | name = name[1:] 145 | } 146 | if b, err := fs.Asset(name); err == nil { 147 | timestamp := defaultFileTimestamp 148 | if info, err := fs.AssetInfo(name); err == nil { 149 | timestamp = info.ModTime() 150 | } 151 | return NewAssetFile(name, b, timestamp), nil 152 | } 153 | if children, err := fs.AssetDir(name); err == nil { 154 | return NewAssetDirectory(name, children, fs), nil 155 | } else { 156 | return nil, err 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /go-fuzz/vendor/github.com/elazarl/go-bindata-assetfs/doc.go: -------------------------------------------------------------------------------- 1 | // assetfs allows packages to serve static content embedded 2 | // with the go-bindata tool with the standard net/http package. 3 | // 4 | // See https://github.com/jteeuwen/go-bindata for more information 5 | // about embedding binary data with go-bindata. 6 | // 7 | // Usage example, after running 8 | // $ go-bindata data/... 9 | // use: 10 | // http.Handle("/", 11 | // http.FileServer( 12 | // &assetfs.AssetFS{Asset: Asset, AssetDir: AssetDir, Prefix: "data"})) 13 | package assetfs 14 | -------------------------------------------------------------------------------- /go-fuzz/vendor/github.com/elazarl/go-bindata-assetfs/go-bindata-assetfs/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "flag" 7 | "fmt" 8 | "os" 9 | "os/exec" 10 | "strings" 11 | ) 12 | 13 | const bindatafile = "bindata.go" 14 | 15 | func isDebug(args []string) bool { 16 | flagset := flag.NewFlagSet("", flag.ContinueOnError) 17 | debug := flagset.Bool("debug", false, "") 18 | debugArgs := make([]string, 0) 19 | for _, arg := range args { 20 | if strings.HasPrefix(arg, "-debug") { 21 | debugArgs = append(debugArgs, arg) 22 | } 23 | } 24 | flagset.Parse(debugArgs) 25 | if debug == nil { 26 | return false 27 | } 28 | return *debug 29 | } 30 | 31 | func main() { 32 | if _, err := exec.LookPath("go-bindata"); err != nil { 33 | fmt.Println("Cannot find go-bindata executable in path") 34 | fmt.Println("Maybe you need: go get github.com/elazarl/go-bindata-assetfs/...") 35 | os.Exit(1) 36 | } 37 | cmd := exec.Command("go-bindata", os.Args[1:]...) 38 | cmd.Stdin = os.Stdin 39 | cmd.Stdout = os.Stdout 40 | cmd.Stderr = os.Stderr 41 | if err := cmd.Run(); err != nil { 42 | os.Exit(1) 43 | } 44 | in, err := os.Open(bindatafile) 45 | if err != nil { 46 | fmt.Fprintln(os.Stderr, "Cannot read", bindatafile, err) 47 | return 48 | } 49 | out, err := os.Create("bindata_assetfs.go") 50 | if err != nil { 51 | fmt.Fprintln(os.Stderr, "Cannot write 'bindata_assetfs.go'", err) 52 | return 53 | } 54 | debug := isDebug(os.Args[1:]) 55 | r := bufio.NewReader(in) 56 | done := false 57 | for line, isPrefix, err := r.ReadLine(); err == nil; line, isPrefix, err = r.ReadLine() { 58 | if !isPrefix { 59 | line = append(line, '\n') 60 | } 61 | if _, err := out.Write(line); err != nil { 62 | fmt.Fprintln(os.Stderr, "Cannot write to 'bindata_assetfs.go'", err) 63 | return 64 | } 65 | if !done && !isPrefix && bytes.HasPrefix(line, []byte("import (")) { 66 | if debug { 67 | fmt.Fprintln(out, "\t\"net/http\"") 68 | } else { 69 | fmt.Fprintln(out, "\t\"github.com/elazarl/go-bindata-assetfs\"") 70 | } 71 | done = true 72 | } 73 | } 74 | if debug { 75 | fmt.Fprintln(out, ` 76 | func assetFS() http.FileSystem { 77 | for k := range _bintree.Children { 78 | return http.Dir(k) 79 | } 80 | panic("unreachable") 81 | }`) 82 | } else { 83 | fmt.Fprintln(out, ` 84 | func assetFS() *assetfs.AssetFS { 85 | for k := range _bintree.Children { 86 | return &assetfs.AssetFS{Asset: Asset, AssetDir: AssetDir, AssetInfo: AssetInfo, Prefix: k} 87 | } 88 | panic("unreachable") 89 | }`) 90 | } 91 | // Close files BEFORE remove calls (don't use defer). 92 | in.Close() 93 | out.Close() 94 | if err := os.Remove(bindatafile); err != nil { 95 | fmt.Fprintln(os.Stderr, "Cannot remove", bindatafile, err) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /go-fuzz/vendor/github.com/stephens2424/writerset/readme.md: -------------------------------------------------------------------------------- 1 | # writerset 2 | -- 3 | import "github.com/stephens2424/writerset" 4 | 5 | Package writerset implements a mechanism to add and remove writers from a 6 | construct similar to io.MultiWriter. 7 | 8 | ## Usage 9 | 10 | #### type WriterSet 11 | 12 | ```go 13 | type WriterSet struct { 14 | } 15 | ``` 16 | 17 | WriterSet wraps multiple writers like io.MultiWriter, but such that individual 18 | writers are easy to add or remove as necessary. 19 | 20 | #### func New 21 | 22 | ```go 23 | func New() *WriterSet 24 | ``` 25 | New initializes a new empty writer set. 26 | 27 | #### func (*WriterSet) Add 28 | 29 | ```go 30 | func (ws *WriterSet) Add(w io.Writer) <-chan error 31 | ``` 32 | Add ensures w is in the set. 33 | 34 | #### func (*WriterSet) Contains 35 | 36 | ```go 37 | func (ws *WriterSet) Contains(w io.Writer) bool 38 | ``` 39 | Contains determines if w is in the set. 40 | 41 | #### func (*WriterSet) Flush 42 | 43 | ```go 44 | func (ws *WriterSet) Flush() 45 | ``` 46 | Flush implements http.Flusher by calling flush on all the underlying writers if 47 | they are also http.Flushers. 48 | 49 | #### func (*WriterSet) Remove 50 | 51 | ```go 52 | func (ws *WriterSet) Remove(w io.Writer) 53 | ``` 54 | Remove ensures w is not in the set. 55 | 56 | #### func (*WriterSet) Write 57 | 58 | ```go 59 | func (ws *WriterSet) Write(b []byte) (int, error) 60 | ``` 61 | Write writes data to each underlying writer. If an error occurs on an underlying 62 | writer, that writer is removed from the set. The error will be sent on the 63 | channel created when the writer was added. 64 | -------------------------------------------------------------------------------- /go-fuzz/vendor/github.com/stephens2424/writerset/writerset.go: -------------------------------------------------------------------------------- 1 | // Package writerset implements a mechanism to add and remove writers from a construct 2 | // similar to io.MultiWriter. 3 | package writerset 4 | 5 | import ( 6 | "io" 7 | "net/http" 8 | "sync" 9 | ) 10 | 11 | // ErrPartialWrite encapsulates an error from a WriterSet. 12 | type ErrPartialWrite struct { 13 | Writer io.Writer 14 | Err error 15 | Expected, Wrote int 16 | } 17 | 18 | // Error returns the error string from the underlying error. 19 | func (e ErrPartialWrite) Error() string { 20 | return e.Err.Error() 21 | } 22 | 23 | // WriterSet wraps multiple writers like io.MultiWriter, but such that individual 24 | // writers are easy to add or remove as necessary. 25 | type WriterSet struct { 26 | m map[io.Writer]chan error 27 | mu sync.Mutex 28 | } 29 | 30 | // New initializes a new empty writer set. 31 | func New() *WriterSet { 32 | return &WriterSet{ 33 | m: make(map[io.Writer]chan error), 34 | mu: sync.Mutex{}, 35 | } 36 | } 37 | 38 | // Add ensures w is in the set. 39 | func (ws *WriterSet) Add(w io.Writer) <-chan error { 40 | ws.mu.Lock() 41 | defer ws.mu.Unlock() 42 | 43 | c, ok := ws.m[w] 44 | if ok { 45 | return c 46 | } 47 | 48 | c = make(chan error, 1) 49 | ws.m[w] = c 50 | return c 51 | } 52 | 53 | // Contains determines if w is in the set. 54 | func (ws *WriterSet) Contains(w io.Writer) bool { 55 | ws.mu.Lock() 56 | defer ws.mu.Unlock() 57 | 58 | _, ok := ws.m[w] 59 | return ok 60 | } 61 | 62 | // Remove ensures w is not in the set. 63 | func (ws *WriterSet) Remove(w io.Writer) { 64 | ws.mu.Lock() 65 | defer ws.mu.Unlock() 66 | delete(ws.m, w) 67 | } 68 | 69 | // Write writes data to each underlying writer. If an error occurs on an underlying writer, 70 | // that writer is removed from the set. The error will be wrapped as an ErrPartialWrite and 71 | // sent on the channel created when the writer was added. 72 | func (ws *WriterSet) Write(b []byte) (int, error) { 73 | ws.mu.Lock() 74 | defer ws.mu.Unlock() 75 | 76 | for w, c := range ws.m { 77 | bs, err := w.Write(b) 78 | if err != nil { 79 | c <- ErrPartialWrite{ 80 | Err: err, 81 | Wrote: bs, 82 | Expected: len(b), 83 | Writer: w, 84 | } 85 | close(c) 86 | delete(ws.m, w) 87 | } 88 | } 89 | 90 | return len(b), nil 91 | } 92 | 93 | // Flush implements http.Flusher by calling flush on all the underlying writers if they are 94 | // also http.Flushers. 95 | func (ws *WriterSet) Flush() { 96 | ws.mu.Lock() 97 | defer ws.mu.Unlock() 98 | 99 | for w := range ws.m { 100 | if w, ok := w.(http.Flusher); ok { 101 | w.Flush() 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /go-fuzz/versifier/versifier_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-fuzz project authors. All rights reserved. 2 | // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 | 4 | package versifier 5 | 6 | import ( 7 | "os" 8 | "testing" 9 | ) 10 | 11 | func dump(data string) { 12 | v := BuildVerse(nil, []byte(data)) 13 | v.Print(os.Stdout) 14 | } 15 | 16 | func TestNumber(t *testing.T) { 17 | dump(`abc -10 def 0xab1 0x123 1e10 asd 1e2 22e-78 -11e72`) 18 | } 19 | 20 | func TestList1(t *testing.T) { 21 | dump(`{"f1": "v1", "f2": "v2", "f3": "v3"}`) 22 | } 23 | 24 | func TestList2(t *testing.T) { 25 | dump(`1,2.0,3e3`) 26 | } 27 | 28 | func TestBracket(t *testing.T) { 29 | dump(`[] [afal] ( ) (afaf)`) 30 | } 31 | 32 | func TestKeyValue(t *testing.T) { 33 | dump(`a=1 a=b 2 (aa=bb) a bb:cc:dd,a=b,c=d,e=f`) 34 | dump(`:a`) 35 | } 36 | -------------------------------------------------------------------------------- /go-fuzz/worker.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-fuzz project authors. All rights reserved. 2 | // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 | 4 | package main 5 | 6 | import ( 7 | "archive/zip" 8 | "bufio" 9 | "bytes" 10 | "encoding/json" 11 | "io" 12 | "io/ioutil" 13 | "log" 14 | "math/bits" 15 | "os" 16 | "strings" 17 | "sync/atomic" 18 | "time" 19 | "unsafe" 20 | 21 | . "github.com/dvyukov/go-fuzz/go-fuzz-defs" 22 | . "github.com/dvyukov/go-fuzz/internal/go-fuzz-types" 23 | ) 24 | 25 | type execType byte 26 | 27 | //go:generate stringer -type execType -trimprefix exec 28 | const ( 29 | execBootstrap execType = iota 30 | execCorpus 31 | execMinimizeInput 32 | execMinimizeCrasher 33 | execTriageInput 34 | execFuzz 35 | execVersifier 36 | execSmash 37 | execSonar 38 | execSonarHint 39 | execTotal 40 | execCount 41 | ) 42 | 43 | // Worker manages one testee. 44 | type Worker struct { 45 | id int 46 | hub *Hub 47 | mutator *Mutator 48 | 49 | coverBin *TestBinary 50 | sonarBin *TestBinary 51 | 52 | triageQueue []CoordinatorInput 53 | crasherQueue []NewCrasherArgs 54 | 55 | lastSync time.Time 56 | stats Stats 57 | execs [execCount]uint64 58 | } 59 | 60 | type Input struct { 61 | mine bool 62 | data []byte 63 | cover []byte 64 | coverSize int 65 | res int 66 | depth int 67 | typ execType 68 | execTime uint64 69 | favored bool 70 | score int 71 | runningScoreSum int 72 | } 73 | 74 | func workerMain() { 75 | zipr, err := zip.OpenReader(*flagBin) 76 | if err != nil { 77 | log.Fatalf("failed to open bin file: %v", err) 78 | } 79 | var coverBin, sonarBin string 80 | var metadata MetaData 81 | for _, zipf := range zipr.File { 82 | r, err := zipf.Open() 83 | if err != nil { 84 | log.Fatalf("failed to unzip file from input archive: %v", err) 85 | } 86 | if zipf.Name == "metadata" { 87 | if err := json.NewDecoder(r).Decode(&metadata); err != nil { 88 | log.Fatalf("failed to decode metadata: %v", err) 89 | } 90 | } else { 91 | f, err := ioutil.TempFile("", "go-fuzz") 92 | if err != nil { 93 | log.Fatalf("failed to create temp file: %v", err) 94 | } 95 | f.Close() 96 | os.Remove(f.Name()) 97 | f, err = os.OpenFile(f.Name()+".exe", os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0700) 98 | if err != nil { 99 | log.Fatalf("failed to create temp file: %v", err) 100 | } 101 | if _, err := io.Copy(f, r); err != nil { 102 | log.Fatalf("failed to uzip bin file: %v", err) 103 | } 104 | f.Close() 105 | switch zipf.Name { 106 | case "cover.exe": 107 | coverBin = f.Name() 108 | case "sonar.exe": 109 | sonarBin = f.Name() 110 | default: 111 | log.Fatalf("unknown file '%v' in input archive", f.Name()) 112 | } 113 | } 114 | r.Close() 115 | } 116 | zipr.Close() 117 | if coverBin == "" || sonarBin == "" || len(metadata.Blocks) == 0 || len(metadata.Funcs) == 0 { 118 | log.Fatalf("bad input archive: missing file") 119 | } 120 | 121 | cleanup := func() { 122 | os.Remove(coverBin) 123 | os.Remove(sonarBin) 124 | } 125 | 126 | // Which function should we fuzz? 127 | fnname := *flagFunc 128 | if fnname == "" { 129 | fnname = metadata.DefaultFunc 130 | } 131 | if fnname == "" && len(metadata.Funcs) == 1 { 132 | fnname = metadata.Funcs[0] 133 | } 134 | if fnname == "" { 135 | cleanup() 136 | log.Fatalf("-func flag not provided, but multiple fuzz functions available: %v", strings.Join(metadata.Funcs, ", ")) 137 | } 138 | fnidx := -1 139 | for i, n := range metadata.Funcs { 140 | if n == fnname { 141 | fnidx = i 142 | break 143 | } 144 | } 145 | if fnidx == -1 { 146 | cleanup() 147 | log.Fatalf("function %v not found, available functions are: %v", fnname, strings.Join(metadata.Funcs, ", ")) 148 | } 149 | if int(uint8(fnidx)) != fnidx { 150 | cleanup() 151 | log.Fatalf("internal consistency error, please file an issue: too many fuzz functions: %v", metadata.Funcs) 152 | } 153 | 154 | shutdownCleanup = append(shutdownCleanup, cleanup) 155 | 156 | hub := newHub(metadata) 157 | for i := 0; i < *flagProcs; i++ { 158 | w := &Worker{ 159 | id: i, 160 | hub: hub, 161 | mutator: newMutator(), 162 | } 163 | w.coverBin = newTestBinary(coverBin, w.periodicCheck, &w.stats, uint8(fnidx)) 164 | w.sonarBin = newTestBinary(sonarBin, w.periodicCheck, &w.stats, uint8(fnidx)) 165 | go w.loop() 166 | } 167 | } 168 | 169 | func (w *Worker) loop() { 170 | iter, fuzzSonarIter, versifierSonarIter := 0, 0, 0 171 | for atomic.LoadUint32(&shutdown) == 0 { 172 | if len(w.crasherQueue) > 0 { 173 | n := len(w.crasherQueue) - 1 174 | crash := w.crasherQueue[n] 175 | w.crasherQueue[n] = NewCrasherArgs{} 176 | w.crasherQueue = w.crasherQueue[:n] 177 | if *flagV >= 2 { 178 | log.Printf("worker %v processes crasher [%v]%v", w.id, len(crash.Data), hash(crash.Data)) 179 | } 180 | w.processCrasher(crash) 181 | continue 182 | } 183 | 184 | select { 185 | case input := <-w.hub.triageC: 186 | if *flagV >= 2 { 187 | log.Printf("worker %v triages coordinator input [%v]%v minimized=%v smashed=%v", w.id, len(input.Data), hash(input.Data), input.Minimized, input.Smashed) 188 | } 189 | w.triageInput(input) 190 | for { 191 | x := atomic.LoadUint32(&w.hub.initialTriage) 192 | if x == 0 || atomic.CompareAndSwapUint32(&w.hub.initialTriage, x, x-1) { 193 | break 194 | } 195 | } 196 | continue 197 | default: 198 | } 199 | 200 | if atomic.LoadUint32(&w.hub.initialTriage) != 0 { 201 | // Other workers are still triaging initial inputs. 202 | // Wait until they finish, otherwise we can generate 203 | // as if new interesting inputs that are not actually new 204 | // and thus unnecessary inflate corpus on every run. 205 | time.Sleep(100 * time.Millisecond) 206 | continue 207 | } 208 | 209 | if len(w.triageQueue) > 0 { 210 | n := len(w.triageQueue) - 1 211 | input := w.triageQueue[n] 212 | w.triageQueue[n] = CoordinatorInput{} 213 | w.triageQueue = w.triageQueue[:n] 214 | if *flagV >= 2 { 215 | log.Printf("worker %v triages local input [%v]%v minimized=%v smashed=%v", w.id, len(input.Data), hash(input.Data), input.Minimized, input.Smashed) 216 | } 217 | w.triageInput(input) 218 | continue 219 | } 220 | 221 | ro := w.hub.ro.Load().(*ROData) 222 | if len(ro.corpus) == 0 { 223 | // Some other worker triages corpus inputs. 224 | time.Sleep(100 * time.Millisecond) 225 | continue 226 | } 227 | 228 | // 9 out of 10 iterations are random fuzzing. 229 | iter++ 230 | if iter%10 != 0 || ro.verse == nil { 231 | data, depth := w.mutator.generate(ro) 232 | // Every 1000-th iteration goes to sonar. 233 | fuzzSonarIter++ 234 | if *flagSonar && fuzzSonarIter%1000 == 0 { 235 | // TODO: ensure that generated hint inputs does not actually take 99% of time. 236 | sonar := w.testInputSonar(data, depth) 237 | w.processSonarData(data, sonar, depth, false) 238 | } else { 239 | // Plain old blind fuzzing. 240 | w.testInput(data, depth, execFuzz) 241 | } 242 | } else { 243 | // 1 out of 10 iterations goes to versifier. 244 | data := ro.verse.Rhyme() 245 | const maxSize = MaxInputSize - 5*SonarMaxLen // need some gap for sonar replacements 246 | if len(data) > maxSize { 247 | data = data[:maxSize] 248 | } 249 | // Every 100-th versifier input goes to sonar. 250 | versifierSonarIter++ 251 | if *flagSonar && versifierSonarIter%100 == 0 { 252 | sonar := w.testInputSonar(data, 0) 253 | w.processSonarData(data, sonar, 0, false) 254 | } else { 255 | w.testInput(data, 0, execVersifier) 256 | } 257 | } 258 | } 259 | w.shutdown() 260 | } 261 | 262 | // triageInput processes every new input. 263 | // It calculates per-input metrics like execution time, coverage mask, 264 | // and minimizes the input to the minimal input with the same coverage. 265 | func (w *Worker) triageInput(input CoordinatorInput) { 266 | if len(input.Data) > MaxInputSize { 267 | input.Data = input.Data[:MaxInputSize] 268 | } 269 | inp := Input{ 270 | data: input.Data, 271 | depth: int(input.Prio), 272 | typ: input.Type, 273 | execTime: 1 << 60, 274 | } 275 | // Calculate min exec time, min coverage and max result of 3 runs. 276 | for i := 0; i < 3; i++ { 277 | w.execs[execTriageInput]++ 278 | res, ns, cover, _, output, crashed, hanged := w.coverBin.test(inp.data) 279 | if crashed { 280 | // Inputs in corpus should not crash. 281 | w.noteCrasher(inp.data, output, hanged) 282 | return 283 | } 284 | if inp.cover == nil { 285 | inp.cover = make([]byte, CoverSize) 286 | copy(inp.cover, cover) 287 | } else { 288 | for i, v := range cover { 289 | x := inp.cover[i] 290 | if v > x { 291 | inp.cover[i] = v 292 | } 293 | } 294 | } 295 | if inp.res < res { 296 | inp.res = res 297 | } 298 | if inp.execTime > ns { 299 | inp.execTime = ns 300 | } 301 | } 302 | if !input.Minimized { 303 | inp.mine = true 304 | ro := w.hub.ro.Load().(*ROData) 305 | // When minimizing new inputs we don't pursue exactly the same coverage, 306 | // instead we pursue just the "novelty" in coverage. 307 | // Here we use corpusCover, because maxCover already includes the input coverage. 308 | newCover, ok := findNewCover(ro.corpusCover, inp.cover) 309 | if !ok { 310 | return // covered by somebody else 311 | } 312 | inp.data = w.minimizeInput(inp.data, false, func(candidate, cover, output []byte, res int, crashed, hanged bool) bool { 313 | if crashed { 314 | w.noteCrasher(candidate, output, hanged) 315 | return false 316 | } 317 | if inp.res != res || worseCover(newCover, cover) { 318 | w.noteNewInput(candidate, cover, res, inp.depth+1, execMinimizeInput) 319 | return false 320 | } 321 | return true 322 | }) 323 | } else if !input.Smashed { 324 | w.smash(inp.data, inp.depth) 325 | } 326 | inp.coverSize = 0 327 | for _, v := range inp.cover { 328 | if v != 0 { 329 | inp.coverSize++ 330 | } 331 | } 332 | w.hub.newInputC <- inp 333 | } 334 | 335 | // processCrasher minimizes new crashers and sends them to the hub. 336 | func (w *Worker) processCrasher(crash NewCrasherArgs) { 337 | // Hanging inputs can take very long time to minimize. 338 | if !crash.Hanging { 339 | crash.Data = w.minimizeInput(crash.Data, true, func(candidate, cover, output []byte, res int, crashed, hanged bool) bool { 340 | if !crashed { 341 | return false 342 | } 343 | supp := extractSuppression(output) 344 | if hanged || !bytes.Equal(crash.Suppression, supp) { 345 | w.noteCrasher(candidate, output, hanged) 346 | return false 347 | } 348 | crash.Error = output 349 | return true 350 | }) 351 | } 352 | w.hub.newCrasherC <- crash 353 | } 354 | 355 | // minimizeInput applies series of minimizing transformations to data 356 | // and asks pred whether the input is equivalent to the original one or not. 357 | func (w *Worker) minimizeInput(data []byte, canonicalize bool, pred func(candidate, cover, output []byte, result int, crashed, hanged bool) bool) []byte { 358 | res := make([]byte, len(data)) 359 | copy(res, data) 360 | start := time.Now() 361 | stat := &w.execs[execMinimizeInput] 362 | if canonicalize { 363 | stat = &w.execs[execMinimizeCrasher] 364 | } 365 | 366 | // First, try to cut tail. 367 | for n := 1024; n != 0; n /= 2 { 368 | for len(res) > n { 369 | if time.Since(start) > *flagMinimize { 370 | return res 371 | } 372 | candidate := res[:len(res)-n] 373 | *stat++ 374 | result, _, cover, _, output, crashed, hanged := w.coverBin.test(candidate) 375 | if !pred(candidate, cover, output, result, crashed, hanged) { 376 | break 377 | } 378 | res = candidate 379 | } 380 | } 381 | 382 | // Then, try to remove each individual byte. 383 | tmp := make([]byte, len(res)) 384 | for i := 0; i < len(res); i++ { 385 | if time.Since(start) > *flagMinimize { 386 | return res 387 | } 388 | candidate := tmp[:len(res)-1] 389 | copy(candidate[:i], res[:i]) 390 | copy(candidate[i:], res[i+1:]) 391 | *stat++ 392 | result, _, cover, _, output, crashed, hanged := w.coverBin.test(candidate) 393 | if !pred(candidate, cover, output, result, crashed, hanged) { 394 | continue 395 | } 396 | res = makeCopy(candidate) 397 | i-- 398 | } 399 | 400 | // Then, try to remove each possible subset of bytes. 401 | for i := 0; i < len(res)-1; i++ { 402 | copy(tmp, res[:i]) 403 | for j := len(res); j > i+1; j-- { 404 | if time.Since(start) > *flagMinimize { 405 | return res 406 | } 407 | candidate := tmp[:len(res)-j+i] 408 | copy(candidate[i:], res[j:]) 409 | *stat++ 410 | result, _, cover, _, output, crashed, hanged := w.coverBin.test(candidate) 411 | if !pred(candidate, cover, output, result, crashed, hanged) { 412 | continue 413 | } 414 | res = makeCopy(candidate) 415 | j = len(res) 416 | } 417 | } 418 | 419 | // Then, try to replace each individual byte with '0'. 420 | if canonicalize { 421 | for i := 0; i < len(res); i++ { 422 | if res[i] == '0' { 423 | continue 424 | } 425 | if time.Since(start) > *flagMinimize { 426 | return res 427 | } 428 | candidate := tmp[:len(res)] 429 | copy(candidate, res) 430 | candidate[i] = '0' 431 | *stat++ 432 | result, _, cover, _, output, crashed, hanged := w.coverBin.test(candidate) 433 | if !pred(candidate, cover, output, result, crashed, hanged) { 434 | continue 435 | } 436 | res = makeCopy(candidate) 437 | } 438 | } 439 | 440 | return res 441 | } 442 | 443 | // smash gives some minimal attention to every new input. 444 | func (w *Worker) smash(data []byte, depth int) { 445 | ro := w.hub.ro.Load().(*ROData) 446 | 447 | // Pass it through sonar. 448 | if *flagSonar { 449 | sonar := w.testInputSonar(data, depth) 450 | w.processSonarData(data, sonar, depth, true) 451 | } 452 | 453 | // Flip each bit one-by-one. 454 | for i := 0; i < len(data)*8; i++ { 455 | data[i/8] ^= 1 << uint(i%8) 456 | w.testInput(data, depth, execSmash) 457 | data[i/8] ^= 1 << uint(i%8) 458 | } 459 | 460 | // Two walking bits. 461 | for i := 0; i < len(data)*8-1; i++ { 462 | data[i/8] ^= 1 << uint(i%8) 463 | data[(i+1)/8] ^= 1 << uint((i+1)%8) 464 | w.testInput(data, depth, execSmash) 465 | data[i/8] ^= 1 << uint(i%8) 466 | data[(i+1)/8] ^= 1 << uint((i+1)%8) 467 | } 468 | 469 | // Four walking bits. 470 | for i := 0; i < len(data)*8-3; i++ { 471 | data[i/8] ^= 1 << uint(i%8) 472 | data[(i+1)/8] ^= 1 << uint((i+1)%8) 473 | data[(i+2)/8] ^= 1 << uint((i+2)%8) 474 | data[(i+3)/8] ^= 1 << uint((i+3)%8) 475 | w.testInput(data, depth, execSmash) 476 | data[i/8] ^= 1 << uint(i%8) 477 | data[(i+1)/8] ^= 1 << uint((i+1)%8) 478 | data[(i+2)/8] ^= 1 << uint((i+2)%8) 479 | data[(i+3)/8] ^= 1 << uint((i+3)%8) 480 | } 481 | 482 | // Byte flip. 483 | for i := 0; i < len(data); i++ { 484 | data[i] ^= 0xff 485 | w.testInput(data, depth, execSmash) 486 | data[i] ^= 0xff 487 | } 488 | 489 | // Two walking bytes. 490 | for i := 0; i < len(data)-1; i++ { 491 | data[i] ^= 0xff 492 | data[i+1] ^= 0xff 493 | w.testInput(data, depth, execSmash) 494 | data[i] ^= 0xff 495 | data[i+1] ^= 0xff 496 | } 497 | 498 | // Four walking bytes. 499 | for i := 0; i < len(data)-3; i++ { 500 | data[i] ^= 0xff 501 | data[i+1] ^= 0xff 502 | data[i+2] ^= 0xff 503 | data[i+3] ^= 0xff 504 | w.testInput(data, depth, execSmash) 505 | data[i] ^= 0xff 506 | data[i+1] ^= 0xff 507 | data[i+2] ^= 0xff 508 | data[i+3] ^= 0xff 509 | } 510 | 511 | // Increment/decrement every byte. 512 | for i := 0; i < len(data); i++ { 513 | for j := uint8(1); j <= 4; j++ { 514 | data[i] += j 515 | w.testInput(data, depth, execSmash) 516 | data[i] -= j 517 | data[i] -= j 518 | w.testInput(data, depth, execSmash) 519 | data[i] += j 520 | } 521 | } 522 | 523 | // Set bytes to interesting values. 524 | for i := 0; i < len(data); i++ { 525 | v := data[i] 526 | for _, x := range interesting8 { 527 | data[i] = uint8(x) 528 | w.testInput(data, depth, execSmash) 529 | } 530 | data[i] = v 531 | } 532 | 533 | // Set words to interesting values. 534 | for i := 0; i < len(data)-1; i++ { 535 | p := (*int16)(unsafe.Pointer(&data[i])) 536 | v := *p 537 | for _, x := range interesting16 { 538 | *p = x 539 | w.testInput(data, depth, execSmash) 540 | if x != 0 && x != -1 { 541 | *p = int16(bits.ReverseBytes16(uint16(x))) 542 | w.testInput(data, depth, execSmash) 543 | } 544 | } 545 | *p = v 546 | } 547 | 548 | // Set double-words to interesting values. 549 | for i := 0; i < len(data)-3; i++ { 550 | p := (*int32)(unsafe.Pointer(&data[i])) 551 | v := *p 552 | for _, x := range interesting32 { 553 | *p = x 554 | w.testInput(data, depth, execSmash) 555 | if x != 0 && x != -1 { 556 | *p = int32(bits.ReverseBytes32(uint32(x))) 557 | w.testInput(data, depth, execSmash) 558 | } 559 | } 560 | *p = v 561 | } 562 | 563 | // Trim after every byte. 564 | for i := 1; i < len(data); i++ { 565 | tmp := data[:i] 566 | w.testInput(tmp, depth, execSmash) 567 | } 568 | 569 | // Insert a byte after every byte. 570 | tmp := make([]byte, len(data)+1) 571 | if len(tmp) > MaxInputSize { 572 | tmp = tmp[:MaxInputSize] 573 | } 574 | for i := 0; i <= len(data) && i < MaxInputSize-1; i++ { 575 | copy(tmp, data[:i]) 576 | copy(tmp[i+1:], data[i:]) 577 | tmp[i] = 0 578 | w.testInput(tmp, depth, execSmash) 579 | tmp[i] = 'a' 580 | w.testInput(tmp, depth, execSmash) 581 | } 582 | 583 | // Do a bunch of random mutations so that this input catches up with the rest. 584 | for i := 0; i < 1e4; i++ { 585 | tmp := w.mutator.mutate(data, ro) 586 | w.testInput(tmp, depth+1, execFuzz) 587 | } 588 | } 589 | 590 | func (w *Worker) testInput(data []byte, depth int, typ execType) { 591 | w.testInputImpl(w.coverBin, data, depth, typ) 592 | } 593 | 594 | func (w *Worker) testInputSonar(data []byte, depth int) (sonar []byte) { 595 | return w.testInputImpl(w.sonarBin, data, depth, execSonar) 596 | } 597 | 598 | func (w *Worker) testInputImpl(bin *TestBinary, data []byte, depth int, typ execType) (sonar []byte) { 599 | ro := w.hub.ro.Load().(*ROData) 600 | if len(ro.badInputs) > 0 { 601 | if _, ok := ro.badInputs[hash(data)]; ok { 602 | return nil // no, thanks 603 | } 604 | } 605 | w.execs[typ]++ 606 | res, _, cover, sonar, output, crashed, hanged := bin.test(data) 607 | if crashed { 608 | w.noteCrasher(data, output, hanged) 609 | return nil 610 | } 611 | w.noteNewInput(data, cover, res, depth, typ) 612 | return sonar 613 | } 614 | 615 | func (w *Worker) noteNewInput(data, cover []byte, res, depth int, typ execType) { 616 | if res < 0 { 617 | // User said to not add this input to corpus. 618 | return 619 | } 620 | if w.hub.updateMaxCover(cover) { 621 | w.triageQueue = append(w.triageQueue, CoordinatorInput{makeCopy(data), uint64(depth), typ, false, false}) 622 | } 623 | } 624 | 625 | func (w *Worker) noteCrasher(data, output []byte, hanged bool) { 626 | ro := w.hub.ro.Load().(*ROData) 627 | supp := extractSuppression(output) 628 | if _, ok := ro.suppressions[hash(supp)]; ok { 629 | return 630 | } 631 | w.crasherQueue = append(w.crasherQueue, NewCrasherArgs{ 632 | Data: makeCopy(data), 633 | Error: output, 634 | Suppression: supp, 635 | Hanging: hanged, 636 | }) 637 | } 638 | 639 | func (w *Worker) periodicCheck() { 640 | if atomic.LoadUint32(&shutdown) != 0 { 641 | w.shutdown() 642 | select {} 643 | } 644 | if time.Since(w.lastSync) < syncPeriod { 645 | return 646 | } 647 | w.execs[execTotal] += w.stats.execs 648 | w.lastSync = time.Now() 649 | w.hub.syncC <- w.stats 650 | w.stats.execs = 0 651 | w.stats.restarts = 0 652 | if *flagV >= 2 { 653 | log.Printf("worker %v: triageq=%v execs=%v mininp=%v mincrash=%v triage=%v fuzz=%v versifier=%v smash=%v sonar=%v hint=%v", 654 | w.id, len(w.triageQueue), 655 | w.execs[execTotal], w.execs[execMinimizeInput], w.execs[execMinimizeCrasher], 656 | w.execs[execTriageInput], w.execs[execFuzz], w.execs[execVersifier], w.execs[execSmash], 657 | w.execs[execSonar], w.execs[execSonarHint]) 658 | } 659 | } 660 | 661 | // shutdown cleanups after worker, it is not guaranteed to be called. 662 | func (w *Worker) shutdown() { 663 | w.coverBin.close() 664 | w.sonarBin.close() 665 | } 666 | 667 | func extractSuppression(out []byte) []byte { 668 | var supp []byte 669 | seenPanic := false 670 | collect := false 671 | s := bufio.NewScanner(bytes.NewReader(out)) 672 | for s.Scan() { 673 | line := s.Text() 674 | if !seenPanic && (strings.HasPrefix(line, "panic: ") || 675 | strings.HasPrefix(line, "fatal error: ") || 676 | strings.HasPrefix(line, "SIG") && strings.Index(line, ": ") != 0) { 677 | // Start of a crash message. 678 | seenPanic = true 679 | supp = append(supp, line...) 680 | supp = append(supp, '\n') 681 | if line == "SIGABRT: abort" || line == "signal: killed" { 682 | return supp // timeout stacks are flaky 683 | } 684 | } 685 | if collect && line == "runtime stack:" { 686 | // Skip runtime stack. 687 | // Unless it is a runtime bug, user stack is more descriptive. 688 | collect = false 689 | } 690 | if collect && len(line) > 0 && (line[0] >= 'a' && line[0] <= 'z' || 691 | line[0] >= 'A' && line[0] <= 'Z') { 692 | // Function name line. 693 | idx := strings.LastIndex(line, "(") 694 | if idx != -1 { 695 | supp = append(supp, line[:idx]...) 696 | supp = append(supp, '\n') 697 | } 698 | } 699 | if collect && line == "" { 700 | // End of first goroutine stack. 701 | break 702 | } 703 | if seenPanic && !collect && line == "" { 704 | // Start of first goroutine stack. 705 | collect = true 706 | } 707 | } 708 | if len(supp) == 0 { 709 | supp = out 710 | } 711 | return supp 712 | } 713 | 714 | func reverse(data []byte) []byte { 715 | tmp := make([]byte, len(data)) 716 | for i, v := range data { 717 | tmp[len(data)-i-1] = v 718 | } 719 | return tmp 720 | } 721 | 722 | func increment(data []byte) []byte { 723 | tmp := make([]byte, len(data)) 724 | copy(tmp, data) 725 | for i, v := range data { 726 | tmp[i] = v + 1 727 | if v != 0xff { 728 | break 729 | } 730 | } 731 | return tmp 732 | } 733 | 734 | func decrement(data []byte) []byte { 735 | tmp := make([]byte, len(data)) 736 | copy(tmp, data) 737 | for i, v := range data { 738 | tmp[i] = v - 1 739 | if v != 0 { 740 | break 741 | } 742 | } 743 | return tmp 744 | } 745 | -------------------------------------------------------------------------------- /go-fuzz/worker_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/binary" 5 | "math" 6 | "testing" 7 | ) 8 | 9 | func TestIncrementDecrement(t *testing.T) { 10 | b := make([]byte, 2) 11 | for i := 0; i < math.MaxUint16; i++ { 12 | u := uint16(i) 13 | binary.LittleEndian.PutUint16(b, u) 14 | 15 | b1 := increment(b) 16 | u1 := binary.LittleEndian.Uint16(b1) 17 | if u+1 != u1 { 18 | t.Fatalf("increment(%d) = %d, want %d", u, u1, u+1) 19 | } 20 | 21 | b1 = decrement(b) 22 | u1 = binary.LittleEndian.Uint16(b1) 23 | if u-1 != u1 { 24 | t.Fatalf("decrement(%d) = %d, want %d", u, u1, u-1) 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /internal/go-fuzz-types/types.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-fuzz project authors. All rights reserved. 2 | // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 | 4 | // Package types provides types shared between go-fuzz-build and go-fuzz. 5 | package types 6 | 7 | type CoverBlock struct { 8 | ID int 9 | File string 10 | StartLine int 11 | StartCol int 12 | EndLine int 13 | EndCol int 14 | NumStmt int 15 | } 16 | 17 | type Literal struct { 18 | Val string 19 | IsStr bool 20 | } 21 | 22 | type MetaData struct { 23 | Literals []Literal 24 | Blocks []CoverBlock 25 | Sonar []CoverBlock 26 | Funcs []string // fuzz function names; must have length > 0 27 | DefaultFunc string // default function to fuzz 28 | } 29 | -------------------------------------------------------------------------------- /slides/README.md: -------------------------------------------------------------------------------- 1 | # go-fuzz presentations 2 | 3 | - [go-fuzz: randomized testing system for Go](http://go-talks.appspot.com/github.com/dvyukov/go-fuzz/slides/go-fuzz.slide) (extended version of GopherCon 2015 talk) 4 | - [Fuzzing: the new unit testing](http://go-talks.appspot.com/github.com/dvyukov/go-fuzz/slides/fuzzing.slide) (GopherCon Russia 2018) 5 | -------------------------------------------------------------------------------- /slides/algo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dvyukov/go-fuzz/e577bee5275c42f9a3ff598ade77fe9806191345/slides/algo.png -------------------------------------------------------------------------------- /slides/bugs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dvyukov/go-fuzz/e577bee5275c42f9a3ff598ade77fe9806191345/slides/bugs.png -------------------------------------------------------------------------------- /slides/crash.go: -------------------------------------------------------------------------------- 1 | // Prevent go install ./... from complaining about different packages in the same dir. 2 | // +build 3 | 4 | package n 5 | 6 | type R interface{ S } 7 | type S = interface{ R } 8 | -------------------------------------------------------------------------------- /slides/fuzzing.slide: -------------------------------------------------------------------------------- 1 | Fuzzing 2 | the new unit testing 3 | 17 Mar 2018 4 | 5 | Dmitry Vyukov 6 | Bug Slaughterer, Google 7 | @dvyukov 8 | 9 | * Agenda 10 | 11 | - What is fuzzing? 12 | - Coverage-guided fuzzing 13 | - go-fuzz tutorial 14 | - Effective fuzzers 15 | 16 | * Wikipedia 17 | 18 | *Fuzzing* is a software *testing*technique*, often automated or semi-automated, 19 | that involves providing *invalid*, *unexpected*, or *random* data to the inputs 20 | of a computer program. 21 | 22 | * A brief history of fuzzing 23 | 24 | *1950s*: 25 | 26 | We didn't call it fuzzing back in the 1950s, but it was our standard practice 27 | to test programs by inputting decks of punch cards taken from the trash. 28 | This type of testing was so common that it had no name. 29 | 30 | [[http://secretsofconsulting.blogspot.de/2017/02/fuzz-testing-and-fuzz-history.html][_Gerald_M._Weinberg_]] 31 | 32 | : Some called it monkey testing or random_testing 33 | 34 | *1988*: term *fuzzing* is coined by Barton Miller 35 | 36 | *1991*: *crashme*: generate random bytes and jump to them 37 | 38 | *2012*: ClusterFuzz 39 | 40 | *2014-2015*: AFL, libFuzzer, go-fuzz 41 | 42 | *2016*: Project Springfield, OSS-Fuzz 43 | 44 | * What can be fuzzed? 45 | 46 | Anything that consumes complex inputs: 47 | 48 | - deserialization (xml, json, gob, asn.1) 49 | - network protocols (HTTP, SMTP, MIME) 50 | - media codecs (audio, video, raster & vector images, pdf) 51 | - crypto (boringssl, openssl) 52 | - compression (zip, gzip, bzip2, brotli) 53 | - text/UTF/font processing (fmt, regexp, text/template, icu, truetype) 54 | - compilers, interpreters, databases, kernels, supervisors, etc 55 | 56 | *Must*have* for everything that consumes untrusted inputs, open to internet or 57 | is otherwise *security* sensitive. 58 | 59 | * Why fuzz? 60 | 61 | - more code coverage 62 | - no bias 63 | - cheap to use 64 | 65 | Complementary to unit-testing! 66 | 67 | * But who cares? 68 | 69 | - We are not testing/checking anything! 70 | - Random data will not trigger any bugs! 71 | 72 | * Types of bugs fuzzing can find 73 | 74 | - nil derefs 75 | - out-of-bounds accesses 76 | - division by 0 77 | - floating-point exceptions 78 | - huge slice allocation requests 79 | - infinite recursion 80 | - infinite loops 81 | - deadlocks 82 | - data races 83 | - send on closed chans 84 | - chan double close 85 | - logical bugs (asserts, but stay tuned for more) 86 | 87 | * Types of fuzzers 88 | 89 | - generation-based or mutation-based 90 | - aware of input structure or not 91 | - aware of program structure or not 92 | 93 | : Mutation-based generally require a pre-collected corpus of inputs. 94 | : Input structure can be provided or learned. 95 | : Program structure can be learned dynamically (code coverage) 96 | : or statically (code analysis) or via symbolic execution. 97 | 98 | * Coverage-guided fuzzing 99 | 100 | Instrument program for code coverage 101 | Collect initial corpus of inputs 102 | for { 103 | Randomly mutate an input from the corpus 104 | Execute and collect coverage 105 | if the input gives new coverage { 106 | Add the input to corpus 107 | } 108 | } 109 | 110 | * Coverage-guiding in action 111 | 112 | The following code wants "ABCD" input: 113 | 114 | if input[0] == 'A' { 115 | if input[1] == 'B' { 116 | if input[2] == 'C' { 117 | if input[3] == 'D' { 118 | slice[input[4]] = 1 // out-of-bounds here 119 | }}}} 120 | 121 | Blind generation needs O(2^8^4) = O(2^32) tries. 122 | 123 | Corpus progression: 124 | 125 | 0. {} 126 | 1. {"A"} 127 | 2. {"A", "AB"} 128 | 3. {"A", "AB", "ABC"} 129 | 4. {"A", "AB", "ABC", "ABCD"} 130 | 131 | Coverage-guided fuzzer needs O(4 * 2^8) = O(2^10) tries. 132 | 133 | : Coverage-guiding turns exponential problem into polynomial. 134 | : + incremental progress 135 | : + persisted progress 136 | 137 | * go-fuzz tutorial 138 | 139 | * go-fuzz tutorial 140 | 141 | 1. Write fuzz function: 142 | 143 | func Fuzz(data []byte) int { 144 | gob.NewDecoder(bytes.NewReader(data)).Decode(new(interface{})) 145 | return 0 146 | } 147 | 148 | 2. Build: 149 | 150 | go get github.com/dvyukov/go-fuzz/... 151 | go-fuzz-build github.com/dvyukov/go-fuzz-corpus/gob 152 | 153 | 3. Run: 154 | 155 | go-fuzz -bin gob-fuzz.zip -workdir ./workdir 156 | 157 | workers: 8, corpus: 1525 (6s ago), crashers: 6, execs: 0 (0/sec), cover: 1651, uptime: 6s 158 | workers: 8, corpus: 1525 (9s ago), crashers: 6, execs: 16787 (1860/sec), cover: 1651, uptime: 9s 159 | workers: 8, corpus: 1525 (12s ago), crashers: 6, execs: 29840 (2482/sec), cover: 1651, uptime: 12s 160 | 161 | * [Some] bugs in standard library 162 | 163 | .image bugs.png 550 _ 164 | 165 | * Quick Quiz: what std lib package did not contain a bug? 166 | 167 | - compress/flate 168 | - fmt 169 | - go/format 170 | - html/template 171 | - image/bmp 172 | - image/png 173 | - time 174 | 175 | * [This slide intentionally left blank] 176 | 177 | * Quick quiz: what std lib package did not contain a bug? 178 | 179 | - compress/flate 180 | - fmt 181 | - go/format 182 | - html/template 183 | - *image/bmp* _(_because_it_does_not_exist_)_ 184 | - image/png 185 | - time 186 | 187 | * encoding/gob DoS 188 | 189 | .play gob.go /START OMIT/,/END OMIT/ 190 | 191 | * encoding/gob crash 192 | 193 | fatal error: runtime: out of memory 194 | 195 | goroutine 1 [running]: 196 | runtime.mallocgc(0x3740000000, ...) 197 | runtime/malloc.go:895 198 | runtime.newarray(..., 0x44000000, ...) 199 | runtime/malloc.go:1030 200 | reflect.MakeMapWithSize(..., 0xff003030, ...) 201 | reflect/value.go:2136 202 | encoding/gob.(*Decoder).decodeMap(...) 203 | encoding/gob/decode.go:562 204 | encoding/gob.(*Decoder).decodeStruct(...) 205 | encoding/gob/decode.go:471 206 | encoding/gob.(*Decoder).Decode(...) 207 | encoding/gob/decoder.go:187 208 | main.main() 209 | gob.go:26 210 | 211 | * Go compiler crash 212 | 213 | .play crash.go 214 | 215 | panic: runtime error: invalid memory address or nil pointer dereference 216 | 217 | goroutine 1 [running]: 218 | cmd/compile/internal/gc.dowidth() 219 | cmd/compile/internal/types.(*Type).Fields() 220 | cmd/compile/internal/gc.expandiface() 221 | cmd/compile/internal/gc.dowidth() 222 | cmd/compile/internal/types.(*Type).Fields() 223 | cmd/compile/internal/gc.expandiface() 224 | cmd/compile/internal/gc.dowidth() 225 | cmd/compile/internal/gc.resumecheckwidth() 226 | cmd/compile/internal/gc.Main() 227 | main.main() 228 | 229 | * How to write effective fuzzers 230 | 231 | * Involve as much code as possble 232 | 233 | The more code fuzzer runs, the more bugs it finds: 234 | 235 | - just bugs in the additional code 236 | - additional code provides coverage-guidance help 237 | 238 | For example: 239 | 240 | - run code in different modes (compression levels, syntax variations) 241 | - run auxiliary functions (code formatting, protobuf text serialization) 242 | - run functions that act on parsing result (regexp match) 243 | 244 | * regexp example 245 | 246 | for _, ctor := range []func(string) (*regexp.Regexp, error){ 247 | regexp.Compile, 248 | regexp.CompilePOSIX, 249 | CompileLongest, 250 | CompilePOSIXLongest 251 | } { 252 | re, err := ctor(restr) 253 | if err != nil { continue } 254 | re.LiteralPrefix() 255 | re.SubexpNames() 256 | re.NumSubexp() 257 | re.Split(sstr, 1) 258 | re.Split(sstr, -1) 259 | re.FindAll(str, 1) 260 | re.FindAll(str, -1) 261 | re.FindAllSubmatch(str, 1) 262 | re.FindAllSubmatch(str, -1) 263 | str1, str2 := str[:len(str)/2], str[len(str)/2:] 264 | match := re.FindSubmatchIndex(str1) 265 | re.Expand(nil, str2, str1, match) 266 | re.ReplaceAll(str1, str2) 267 | re.ReplaceAllLiteral(str1, str2) 268 | } 269 | 270 | * Logical bugs 271 | 272 | Not only security/stability bugs. But we don't know the right result! 273 | 274 | Sanity checks on results: 275 | 276 | - know that some substring must be present in output, but it is not 277 | - result must be in `[0.0, 1.0)` range 278 | - uncompressed image decoder: 100 byte input -> 100 MB output? 279 | - encrypt, check that decryption with wrong key fails 280 | - function returns both error and object, or no error and no object 281 | - any sorting: check that each element is present, check that it's not descending 282 | - building a trie: check size, all elements are present 283 | 284 | * Round-trip test 285 | 286 | Pair functions like: 287 | 288 | - serialize <-> deserialize 289 | - compress <-> decompress 290 | - encrypt <-> decrypt 291 | 292 | Round-trip is: 293 | 294 | deserialize -> serialize -> deserialize 295 | 296 | Can check that: 297 | 298 | - `serialize` does not fail 299 | - second `deserialize` does not fail 300 | - `deserialize` results are equal 301 | 302 | Finds tons of bugs! 303 | 304 | * Golden implementation 305 | 306 | Comparing two (or more) implementations gives phenomenal results: 307 | 308 | - check that output is equal 309 | - or at least check that ok/fail result is the same 310 | 311 | But I don't want to write the second impl! 312 | 313 | - there can be several packages already (`json` vs `jsonfast` vs `jsonveryfast`) 314 | - implementation in a different language (Go's `regexp` vs C++ `re2`) 315 | - compare "fast but complex" with "slow but dumb" (sometimes easy to write) 316 | - compare different functions (`marshalBinary` vs `marshalText`) 317 | 318 | * Corpus 319 | 320 | Providing a good corpus helps: 321 | 322 | - collect inputs from tests 323 | - samples from production 324 | - examples from web (HTTP/XML) 325 | - hand write 326 | - write a generator (PNGs with different sizes/contents/palettes) 327 | 328 | * Complex inputs 329 | 330 | What if input is not `[]byte`? 331 | 332 | .code regexp.go /START OMIT/,/END OMIT/ 333 | 334 | * Complex inputs (2) 335 | 336 | Unmarshalling a complex struct: 337 | 338 | var input struct { 339 | X float32 340 | Y float32 341 | Z int64 342 | F bool 343 | } 344 | binary.Read(bytes.NewReader([]byte(data)), binary.LittleEndian, &input) 345 | 346 | Without changing input format: 347 | 348 | x := crc32.ChecksumIEEE(data) 349 | f1 := x&1 != 0 350 | f2 := x&2 != 0 351 | 352 | * Quick Quiz: how to fuzz go fmt? 353 | 354 | Let's imaging destiny of mankind depends on correctness of go fmt! 355 | 356 | How would you fuzz test it? 357 | 358 | * [This slide intentionally left blank] 359 | 360 | * Quick Quiz: how to fuzz go fmt? 361 | 362 | - format twice, compare results (e.g. relies on map order) 363 | - format, then format result (must be idempotent) 364 | - strip all whitespaces, compare before/after 365 | - parse with go/ast, compare AST before/after 366 | - compile before/after (formatting breaks/unbreaks code) 367 | - also parse with go/types to improve coverage feedback 368 | - provide corpus (compiler tests) 369 | 370 | * go tool support 371 | 372 | Issue #19109: proposal: cmd/go: make fuzzing a first class citizen 373 | 374 | encoding/hex/fuzz_test.go: 375 | 376 | package hex 377 | 378 | import "testing" 379 | 380 | func FuzzDecodeString(f *testing.F, data []byte) { 381 | DecodeString(string(data)) 382 | } 383 | 384 | Running: 385 | 386 | go test -fuzz=FuzzDecodeString 387 | 388 | Regression testing (runs all inputs from corpus): 389 | 390 | go test ./... 391 | 392 | * The bottom line 393 | 394 | - fuzzing is complimentary to any other testing technique 395 | - fuzzing is mandatory for anything security-related 396 | - fuzzing finds LOTS of bugs 397 | - fuzzing is easy to use 398 | 399 | * Call to action 400 | 401 | - choose 1 package that uses complex inputs 402 | - write a fuzzer using go-fuzz 403 | - run it and see what happens 404 | -------------------------------------------------------------------------------- /slides/go-fuzz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dvyukov/go-fuzz/e577bee5275c42f9a3ff598ade77fe9806191345/slides/go-fuzz.png -------------------------------------------------------------------------------- /slides/go-fuzz.slide: -------------------------------------------------------------------------------- 1 | go-fuzz 2 | Randomized testing system for Go 3 | 4 | Dmitry Vyukov 5 | Google 6 | dvyukov@ 7 | 8 | * Agenda 9 | 10 | - High-level architecture 11 | - Driver/program interface 12 | - Coverage 13 | - Mutations 14 | - Corpus management 15 | - Sonar 16 | - Versifier 17 | 18 | .image go-fuzz.png 19 | 20 | * Introduction 21 | 22 | A different appraoch to testing that finds [lots of] bugs that other testing approaches do not. Intended mostly for programs that parse complex inputs. 23 | 24 | Generate random blob -> feed into program -> see if it crashes -> profit! 25 | 26 | - cheap to use 27 | - does not have any bias 28 | 29 | Completely random blobs won't uncover lots of bugs. 30 | 31 | How can we generate diverse but meaningful inputs that will trigger 32 | nil derefs, off-by-ones, etc? 33 | 34 | * Coverage-guided fuzzing 35 | 36 | Genetic algorithms to the rescue! 37 | 38 | Instrument program for code coverage 39 | Collect initial corpus of inputs 40 | for { 41 | Randomly mutate an input from the corpus 42 | Execute and collect coverage 43 | If the input gives new coverage, add it to corpus 44 | } 45 | 46 | * Example 47 | 48 | The following code wants "ABCD" input: 49 | 50 | if input[0] == 'A' { 51 | if input[1] == 'B' { 52 | if input[2] == 'C' { 53 | if input[3] == 'D' { 54 | panic("input must not be ABCD") 55 | } 56 | } 57 | } 58 | } 59 | 60 | Corpus progression: 61 | 62 | "" 63 | "", "A" 64 | "", "A", "AB" 65 | "", "A", "AB", "ABC" 66 | "", "A", "AB", "ABC", "ABCD" 67 | 68 | * High-level architecture 69 | 70 | - Coordinator: manages persistent artifacts 71 | - Hub: manages corpus in memory 72 | - Worker: manages a single testee subprocess 73 | - N workers are collocated with Hub in a single process. 74 | - Coordinator and Hub communicate via RPC 75 | - go-fuzz-build builds test binary 76 | 77 | * go-fuzz-build 78 | 79 | Creates a zip archive with: 80 | 81 | - coverage instrumented binary 82 | - sonar instrumented binary 83 | - coverage and sonar metadata 84 | - int and string literals 85 | 86 | Also generates a bit of code to glue test and driver. 87 | 88 | * Coordinator process 89 | 90 | - Persistent corpus (SHA-1 + input depth) 91 | - Persistent suppressions (SHA-1) 92 | - Persistent crashers (input, quoted input, output) 93 | - Final deduplication 94 | - Total stats 95 | 96 | * Hub/worker process 97 | 98 | - Does actual fuzzing 99 | 100 | * Driver/program interface 101 | 102 | - Input shared region (fd 3) 103 | - Coverage shared region (fixed size, 64K) (fd 4) 104 | - Output sonar data shared region (fd 5) 105 | - Input pipe: (input length) 106 | - Output pipe: (result, exec time, sonar data length) 107 | 108 | * Coverage 109 | 110 | - 64K of 8-bit counters 111 | - Counters are rounded up to: 0, 1, 2, 3, 4, 5, 8, 16, 32, 64, 255 112 | - Signal ID is hashed using SHA-1 113 | - Currently uses basic block ID (but adds missing else and default blocks, instruments || and &&) 114 | 115 | * Mutations 116 | 117 | Does N mutations with probability 1/2^N: 118 | 119 | - Insert/remove/duplicate/copy a random range of random bytes. 120 | - Bit flip. 121 | - Swap 2 bytes. 122 | - Set a byte to a random value. 123 | - Add/subtract from a byte/uint16/uint32/uint64 (le/be). 124 | - Replace a byte/uint16/uint32 with an interesting value (le/be). 125 | - Replace an ascii digit/number with another digit/number. 126 | - Splice another input. 127 | - Insert a part of another input. 128 | - Insert a string/int literal. 129 | - Replace with string/int literal. 130 | 131 | * Corpus management 132 | 133 | For input we know: 134 | 135 | - input data 136 | - coverage 137 | - result 138 | - mutation depth 139 | - execution time 140 | 141 | Inputs are minimized and prioritized. 142 | 143 | * Input prioritization 144 | 145 | Each input receives score 1..1000. 146 | 147 | - start with score 10 148 | - execution time multiplier 0.1-3x 149 | - coverage size multiplier 0.25-3x 150 | - input depth multiplier 1-5x 151 | - user boost multiplier 1-4x 152 | 153 | Then choose favored inputs: minimal set of highest-score inputs that give full counter coverage. The rest receive score 1 (can be discarded as well). 154 | During mutations inputs are selected according to the priority. 155 | 156 | 157 | * Game over 158 | 159 | CRC32 checksum verification in `image/png/reader.go` 160 | 161 | func (d *decoder) verifyChecksum() error { 162 | if binary.BigEndian.Uint32(d.tmp[:4]) != d.crc.Sum32() { 163 | return FormatError("invalid checksum") 164 | } 165 | return nil 166 | } 167 | 168 | Probability that random mutations will alter input in an interesting way and 169 | guess CRC32 at the same time is basically ZERO. 170 | 171 | * Sonar 172 | 173 | Don't need to guess, program knows it! 174 | 175 | + v1 := binary.BigEndian.Uint32(d.tmp[:4]) 176 | + v2 := d.crc.Sum32() 177 | + __go_fuzz.Sonar(v1, v2, id, flags) 178 | if v1 != v2 { 179 | return FormatError("invalid checksum") 180 | } 181 | 182 | Then, find v1 in the input and replace it with v2. Done! 183 | 184 | * Sonar (2) 185 | 186 | ID allows to identify a particular code site (and match with metadata). 187 | Flags: SonarEQL, SonarNEQ, SonarLSS, SonarGTR, SonarLEQ, SonarGEQ, SonarLength, SonarSigned, SonarString, SonarConst1, SonarConst2. 188 | 189 | Understands lower/upper-case, big-endian, base-128, ascii, hex. 190 | Also tried to increment/decrement the value. 191 | 192 | Evalues the comparison and counts number of times it is taken one way or another. 193 | Marks sites where both operands are dynamic (e.g. CRC check). 194 | Skips sites that are taken both ways enough times. 195 | 196 | Tried to match causes and effects. Turned out to be non-trivial. 197 | 198 | * Game over 2 199 | 200 | Mutations and sonar do low-level changes ("bit-flipping"): 201 | 202 | Original: 203 | 204 | `100` 205 | 206 | Mutated: 207 | 208 | `100<` 209 | 210 | Also want high-level changes! 211 | 212 | * Versifier 213 | 214 | Versifier reverse-engineers [text] protocol and learns its _structure_. 215 | 216 | abc -> alphanum token 217 | 123, 1e-2 -> number 218 | "..." -> quoted 219 | [...] -> parenthesized 220 | ...,...,... -> list 221 | ...\n...\n -> lines 222 | 223 | Then, applies _structural_ mutations to inputs. 224 | 225 | * Versifier example 226 | 227 | Original: 228 | 229 | `100` 230 | 231 | Versified (all valid xml): 232 | 233 | 234 | = 235 | -026023767521520230564132665e0333302100 236 | 510-9e-07036514 237 | prop name="p"/}01e-6 238 | 1008 239 | 240 | * Algorithm 241 | 242 | .image algo.png 243 | 244 | * Cover profile demo 245 | 246 | [DEMO] 247 | 248 | * Achievements 249 | 250 | - 60 tests 251 | - 137 bugs in std lib (70 fixed) 252 | - 165 elsewhere (47 in gccgo, 30 in golang.org/x, 42 in freetype-go, protobuf, http2, bson) 253 | 254 | * Achievements 255 | 256 | fmt.Sprintf("%.[]") 257 | panic: runtime error: index out of range 258 | 259 | regexp.MustCompile("((0(0){0}))").ReplaceAllString("00000", "00$00") 260 | panic: runtime error: slice bounds out of range 261 | 262 | ioutil.ReadAll(flate.NewReader(strings.NewReader("4LJNIMK\a\x00\x00\xff..\xff.....\xff"))) 263 | runs forever 264 | 265 | var x = 1/"."[0] 266 | crashes compiler 267 | 268 | archive/tar: hang 269 | archive/zip: cap out of range 270 | encoding/gob: stack overflow 271 | encoding/asn1: index out of range 272 | image/jpeg: Decode hangs 273 | image/png: nil deref 274 | math/big: incorrect string->Float conversion 275 | crypto/x509: division by zero 276 | ... 277 | 278 | * Usage 279 | 280 | - go get github.com/dvyukov/go-fuzz/... 281 | - write test: 282 | 283 | func Fuzz(data []byte) int { 284 | gob.NewDecoder(bytes.NewReader(data)) 285 | return 0 286 | } 287 | 288 | - build 289 | 290 | $ go-fuzz-build github.com/dvyukov/go-fuzz/examples/gob 291 | 292 | - collect corpus 293 | - run 294 | 295 | $ go-fuzz -bin=gob-fuzz.zip -workdir=examples/gob 296 | 297 | * Areas for improvement 298 | 299 | - Better reverse engineering of text protocols (more constructs, recursive decomposition, several guesses) 300 | - Reverse engineering of binary protos (fields, message type, length, encoding of data, etc) 301 | - Better structural mutations 302 | - More compiler assists for sonar (x/3 > 100 --> x > 300, index expressions) 303 | - More encodings in sonar 304 | - Match causes and effects in sonar 305 | - Detect sonar sites that we don't affect 306 | - Mark fields and their meaning in inputs 307 | - Find non-meaningful bytes in inputs 308 | - Better corpus prioritization 309 | - What is a signal (bb, edge, path, stack+bb, type, index) 310 | 311 | 312 | 313 | -------------------------------------------------------------------------------- /slides/gob.go: -------------------------------------------------------------------------------- 1 | // Prevent go install ./... from complaining about different packages in the same dir. 2 | // +build 3 | 4 | package main 5 | 6 | import ( 7 | "bytes" 8 | "encoding/gob" 9 | "encoding/hex" 10 | ) 11 | 12 | // START OMIT 13 | const data = "4dffb503010102303001ff30000109010130010800010130010800010130" + 14 | "01ffb80001014a01ffb60001014b01ff860001013001ff860001013001ff" + 15 | "860001013001ff860001013001ffb80000001eff850401010e3030303030" + 16 | "30303030303030303001ff3000010c0104000016ffb70201010830303030" + 17 | "3030303001ff3000010c000030ffb6040405fcff00303030303030303030" + 18 | "303030303030303030303030303030303030303030303030303030303030" + 19 | "303030303030303030303030303030303030303030303030303030303030" + 20 | "30303030303030" 21 | 22 | type X struct { 23 | J *X 24 | K map[string]int 25 | } 26 | 27 | func main() { 28 | raw, _ := hex.DecodeString(data) 29 | gob.NewDecoder(bytes.NewReader(raw)).Decode(new(X)) 30 | } 31 | 32 | // END OMIT 33 | -------------------------------------------------------------------------------- /slides/regexp.go: -------------------------------------------------------------------------------- 1 | // Prevent go install ./... from complaining about different packages in the same dir. 2 | // +build 3 | 4 | package regexp 5 | 6 | import ( 7 | "regexp" 8 | ) 9 | 10 | // START OMIT 11 | func FuzzRegexp(data []byte) int { 12 | if len(data) < 3 { 13 | return 0 14 | } 15 | longestMode := data[0]%2 != 0 // first byte as "longest" flag 16 | reStr := data[1 : len(data)/2] // half as regular expression 17 | matchStr := data[len(data)/2:] // the rest is string to match 18 | 19 | re, err := regexp.Compile(string(reStr)) 20 | if err != nil { 21 | return 0 22 | } 23 | if longestMode { 24 | re.Longest() 25 | } 26 | re.FindAll(matchStr, -1) 27 | return 0 28 | } 29 | 30 | // END OMIT 31 | -------------------------------------------------------------------------------- /test/corpus/0: -------------------------------------------------------------------------------- 1 | - 2 | -------------------------------------------------------------------------------- /test/corpus/1: -------------------------------------------------------------------------------- 1 | 0123456789abcd 2 | -------------------------------------------------------------------------------- /test/corpus/2: -------------------------------------------------------------------------------- 1 | foo input [x] minimization ---- test 2 | ------------------------------------ 3 | ------------------------------------ 4 | ------------------------------------ 5 | ------------------------------------ 6 | ------------------------------------ 7 | ------------------------------------ 8 | ------------------------------------ 9 | ------------------------------------ 10 | ------------------------------------ 11 | ------------------------------------ 12 | ------------------------------------ 13 | ------------------------------------ 14 | ------------------------------------ 15 | ------------------------------------ 16 | ------------------------------------ 17 | 18 | -------------------------------------------------------------------------------- /test/internal/test/test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 go-fuzz project authors. All rights reserved. 2 | // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 | 4 | // Test for fuzzing of internal packages. 5 | 6 | package test 7 | 8 | func Fuzz(data []byte) int { 9 | return 0 10 | } 11 | -------------------------------------------------------------------------------- /test/test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 go-fuzz project authors. All rights reserved. 2 | // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 | 4 | package test 5 | 6 | import ( 7 | "bytes" 8 | "runtime" 9 | 10 | // Test vendoring support. 11 | vendored_foo "non.existent.com/foo" 12 | 13 | "github.com/dvyukov/go-fuzz/test/testdep" 14 | ) 15 | 16 | func init() { 17 | vendored_foo.Foo() 18 | // Test that background goroutines don't break sonar. 19 | // https://github.com/dvyukov/go-fuzz/issues/145 20 | // Sonar code is racy (see the issue), but the test don't crash 21 | // unless runtime.GOMAXPROCS is uncommented below. 22 | go func() { 23 | x := 0 24 | s := "foobarbazqux" 25 | for i := 0; ; i++ { 26 | runtime.Gosched() 27 | if i == x { 28 | s = "foobarbazquz" 29 | x -= 1 30 | } 31 | if s == "foo" { 32 | x -= 1 33 | } 34 | } 35 | }() 36 | } 37 | 38 | func Fuzz(data []byte) int { 39 | if len(data) != cap(data) { 40 | panic("should not happen") 41 | } 42 | if len(data) == 1 { 43 | if data[0] == '!' || data[0] == '#' { 44 | panic("bingo 0") 45 | } 46 | if data[0] == '0' || data[0] == '9' { 47 | for { 48 | c := make(chan bool) 49 | close(c) 50 | } 51 | } 52 | if data[0] == 'a' || data[0] == 'z' { 53 | data := make([]byte, 128<<30-1) 54 | _ = data 55 | } 56 | if data[0] == 'b' { 57 | // new coverage 58 | } 59 | if data[0] == 'c' { 60 | // new coverage 61 | } 62 | } 63 | 64 | // Test for crash minimization. 65 | if bytes.IndexByte(data, 'x') != -1 { 66 | if bytes.IndexByte(data, 'y') != -1 { 67 | panic("xy") 68 | } 69 | } 70 | 71 | // Test for input minimization. 72 | if bytes.Index(data, []byte("input ")) != -1 { 73 | if bytes.Index(data, []byte("minimization ")) != -1 { 74 | if bytes.Index(data, []byte("test")) != -1 { 75 | } 76 | } 77 | } 78 | 79 | if len(data) >= 14 && bytes.HasPrefix(data, []byte("0123456789")) { 80 | x := int(data[10]) + int(data[11])<<8 + int(data[12])<<16 + int(data[13])<<24 81 | if x == 0 || x == -1 { 82 | panic("bingo 1") 83 | } 84 | if x == 255 || x == 256 { 85 | for { 86 | c := make(chan bool) 87 | close(c) 88 | } 89 | } 90 | if x == 1<<16-1 || x == 1<<16 { 91 | data := make([]byte, 128<<30-1) 92 | _ = data 93 | } 94 | if x == '1' { 95 | // new coverage 96 | } 97 | if x == '2' { 98 | // new coverage 99 | } 100 | } 101 | if len(data) == 7 && data[7:8][0] == 'a' { 102 | // The above should cause OOB. 103 | } 104 | return 0 105 | } 106 | 107 | // Compilation tests, go-fuzz-build previously failed on these code patterns. 108 | 109 | // Test for issue #35. 110 | const X = 1 << 129 111 | 112 | func foo(x float64) bool { 113 | return x < X 114 | } 115 | 116 | func test1() bool { 117 | var x uint64 118 | var y uint 119 | return x == 1< ../../../../bar 82 | 83 | -- gopath/src/example.com/foo/fuzz.go -- 84 | package foo 85 | 86 | import "example.com/bar" 87 | 88 | func FuzzMod(data []byte) int { 89 | bar.Bar() 90 | return 0 91 | } 92 | 93 | -- bar/go.mod -- 94 | module example.com/bar 95 | 96 | -- bar/bar.go -- 97 | package bar 98 | 99 | func Bar() string { 100 | return "hello from bar" 101 | } 102 | 103 | -------------------------------------------------------------------------------- /testscripts/mod_outside_gopath.txt: -------------------------------------------------------------------------------- 1 | # These steps validate we can fuzz outside of GOPATH 2 | # with GO111MODULE auto, on, unset, and off. 3 | 4 | # The foo module being fuzzed depends on a 'replace' 5 | # in its go.mod to find one of its dependencies, 6 | # which is a way to check that module-mode is required to compile successfully. 7 | 8 | # Enter a simple module with a fuzz function. 9 | # This is outside of GOPATH. 10 | cd foo 11 | 12 | # Copy a pristine go.mod file. 13 | cp go.mod_PRISTINE go.mod 14 | 15 | # First, we test with GO111MODULE=auto, which is the default 16 | # in Go 1.11-1.13. (In 1.14, the default will likely be GO111MODULE=on). 17 | env GO111MODULE=auto 18 | 19 | # Because cmd/go now defaults to -mod=readonly, we need to explicitly add go-fuzz-dep. 20 | go get github.com/dvyukov/go-fuzz/go-fuzz-dep 21 | 22 | # Sanity check the module seems well formed. 23 | exec go list -m all 24 | stdout '^example.com/foo$' 25 | exec go build 26 | 27 | # Because we are outside GOPATH, the presence of the 'go.mod' here will 28 | # enable module-module for cmd/go with GO111MODULE=auto. 29 | # This is true in Go 1.11-1.13, and likely will be true in 1.14 as well. 30 | # Ask go-fuzz-build to build, including specifying the fuzz function for mod. 31 | exec go-fuzz-build -func=FuzzMod 32 | exists foo-fuzz.zip 33 | 34 | # Validate we can start fuzzing. 35 | # Note that 'timeout(1)' will error here, so we preface the invocation with '!'. 36 | # For travis on Windows, we install 'timeout(1)' as part of our travis setup steps. 37 | # To test this locally on Windows, you might need to change 'timeout' to '\cygwin64\bin\timeout' or similar. 38 | ! exec timeout 5 go-fuzz -procs=1 -func=FuzzMod 39 | stderr 'workers: \d+, corpus: ' 40 | 41 | # Clean up. 42 | cp go.mod_PRISTINE go.mod 43 | rm foo-fuzz.zip 44 | go get github.com/dvyukov/go-fuzz/go-fuzz-dep 45 | 46 | # Second, we test with GO111MODULE=on, which will likely be the default in Go 1.14. 47 | env GO111MODULE=on 48 | 49 | # Sanity check we can still interact with the module. 50 | exec go list -m all 51 | stdout '^example.com/foo$' 52 | exec go build 53 | 54 | # Ask go-fuzz-build to build, including specifying the fuzz function for mod. 55 | exec go-fuzz-build -func=FuzzMod 56 | exists foo-fuzz.zip 57 | 58 | # Validate we can start fuzzing. 59 | ! exec timeout 5 go-fuzz -procs=1 -func=FuzzMod 60 | stderr 'workers: \d+, corpus: ' 61 | 62 | # Clean up. 63 | cp go.mod_PRISTINE go.mod 64 | rm foo-fuzz.zip 65 | go get github.com/dvyukov/go-fuzz/go-fuzz-dep 66 | 67 | # Third, we test with GO111MODULE unset. 68 | # The meaning of this will likely change in Go 1.14, but given we are 69 | # outside of GOPATH, in theory these steps will still work even in Go 1.14. 70 | env GO111MODULE= 71 | 72 | # Sanity check we can still interact with the module. 73 | exec go list -m all 74 | stdout '^example.com/foo$' 75 | exec go build 76 | 77 | # Ask go-fuzz-build to build, including specifying the fuzz function for mod. 78 | exec go-fuzz-build -func=FuzzMod 79 | exists foo-fuzz.zip 80 | 81 | # Validate we can start fuzzing. 82 | ! exec timeout 5 go-fuzz -procs=1 -func=FuzzMod 83 | stderr 'workers: \d+, corpus: ' 84 | 85 | # Clean up. 86 | cp go.mod_PRISTINE go.mod 87 | rm foo-fuzz.zip 88 | go get github.com/dvyukov/go-fuzz/go-fuzz-dep 89 | 90 | # Fourth, we test with GO111MODULE=off. 91 | # The meaning of this is unlikely to change in Go 1.14, 92 | # altough in some (distant?) future, GO111MODULE=off might 93 | # no longer be supported. 94 | env GO111MODULE=off 95 | 96 | # Confirm 'go list -m' and 'go build' fail. 97 | ! exec go list -m all 98 | ! exec go build 99 | 100 | # Confirm go-fuzz-build fails. 101 | ! exec go-fuzz-build -func=FuzzMod 102 | ! exists foo-fuzz.zip 103 | 104 | # Clean up (mainly in case we later add another test below). 105 | cp go.mod_PRISTINE go.mod 106 | rm foo-fuzz.zip 107 | go get github.com/dvyukov/go-fuzz/go-fuzz-dep 108 | 109 | # Define two modules. 110 | # example.com/foo has a fuzz function, and depends on example.com/bar. 111 | 112 | -- foo/go.mod_PRISTINE -- 113 | module example.com/foo 114 | 115 | require example.com/bar v0.0.0 116 | 117 | replace example.com/bar => ../bar 118 | 119 | -- foo/fuzz.go -- 120 | package foo 121 | 122 | import "example.com/bar" 123 | 124 | func FuzzMod(data []byte) int { 125 | bar.Bar() 126 | return 0 127 | } 128 | 129 | -- bar/go.mod -- 130 | module example.com/bar 131 | 132 | -- bar/bar.go -- 133 | package bar 134 | 135 | func Bar() string { 136 | return "hello from bar" 137 | } 138 | 139 | -------------------------------------------------------------------------------- /testscripts/mod_v2.txt: -------------------------------------------------------------------------------- 1 | # These steps validate we can fuzz a module that depends on a v2 module. 2 | # v2 modules are particularly problematic if not handled properly. 3 | # We validate that 'go-fuzz' and 'go-fuzz-build' work with and without 4 | # the package path and fuzz function being specified. 5 | 6 | # Enter a module that depends on a v2+ module. 7 | # Note that we are outside of GOPATH, so the presence of the 'go.mod' here will 8 | # enable module-module for cmd/go (which is true in Go 1.11-1.13, and likely will be true in 1.14 as well). 9 | cd foo 10 | 11 | # Sanity check the module seems well formed. 12 | exec go list -m all 13 | stdout '^example.com/foo$' 14 | stdout '^example.com/bar/v2 v2.0.0 => ../bar$' 15 | exec go build 16 | 17 | # Because cmd/go now defaults to -mod=readonly, we need to explicitly add go-fuzz-dep. 18 | go get github.com/dvyukov/go-fuzz/go-fuzz-dep 19 | 20 | # Ask go-fuzz-build to build, first specifying the package path and fuzz function for foo. 21 | # foo is a module itself, and foo also depends on a v2 module bar. 22 | exec go-fuzz-build -func=FuzzDependOnV2Mod example.com/foo 23 | exists foo-fuzz.zip 24 | 25 | # Validate we can start fuzzing, first with the fuzz function specified. 26 | # Note that 'timeout(1)' will error here, so we preface the invocation with '!'. 27 | # For travis on Windows, we install 'timeout(1)' as part of our travis setup steps. 28 | # To test this locally on Windows, you might need to change 'timeout' to '\cygwin64\bin\timeout' or similar. 29 | ! exec timeout 5 go-fuzz -procs=1 -func=FuzzDependOnV2Mod 30 | stderr 'workers: \d+, corpus: ' 31 | 32 | # Validate we can start fuzzing, now without a fuzz function specified. 33 | ! exec timeout 5 go-fuzz -procs=1 34 | stderr 'workers: \d+, corpus: ' 35 | 36 | # Ask go-fuzz-build to build again, but now do not specify the fuzz function. 37 | rm foo-fuzz.zip 38 | exec go-fuzz-build 39 | exists foo-fuzz.zip 40 | 41 | # Validate we can start fuzzing with the new zip, first with a fuzz function specified. 42 | ! exec timeout 5 go-fuzz -procs=1 -func=FuzzDependOnV2Mod 43 | stderr 'workers: \d+, corpus: ' 44 | 45 | # Validate we can start fuzzing with the new zip, now without a fuzz function specified. 46 | ! exec timeout 5 go-fuzz -procs=1 47 | stderr 'workers: \d+, corpus: ' 48 | 49 | # Define two modules. 50 | # example.com/foo has a fuzz function, and depends on example.com/bar/v2. 51 | # The v2 module is following the 'major branch' approach for v2+ modules, 52 | # not 'major subdirectory' approach. (Details: https://github.com/golang/go/wiki/Modules#releasing-modules-v2-or-higher) 53 | 54 | -- foo/go.mod -- 55 | module example.com/foo 56 | 57 | require example.com/bar/v2 v2.0.0 58 | 59 | replace example.com/bar/v2 => ../bar 60 | 61 | -- foo/fuzz.go -- 62 | package foo 63 | 64 | import "example.com/bar/v2" 65 | 66 | func FuzzDependOnV2Mod(data []byte) int { 67 | bar.Bar() 68 | return 0 69 | } 70 | 71 | -- bar/go.mod -- 72 | module example.com/bar/v2 73 | 74 | -- bar/bar.go -- 75 | package bar 76 | 77 | func Bar() string { 78 | return "hello from bar" 79 | } 80 | -------------------------------------------------------------------------------- /testscripts/mod_vendor.txt: -------------------------------------------------------------------------------- 1 | # These steps validate we get a proper error 2 | # if we try to fuzz a module using GOFLAGS=-mod=vendor. 3 | # It then stops. 4 | 5 | # For the future, we have some unexecuted steps after the stop. 6 | # These don't run now, but attempt to validate fuzzing a module using -mod=vendor, 7 | # including validating that the actual code in the 'vendor' directory is used. 8 | 9 | # TODO: -mod=vendor is not yet supported with go-fuzz, so the majority of this test is disabled with a 'stop' command. 10 | # If you change go-fuzz-build to remove the check for -mod=vendor, it currently fails with error: 11 | # > exec go-fuzz-build -func=FuzzDependOnVendor 12 | # [stderr] 13 | # could not load packages: go [list -e -json -compiled=true -test=false -export=false -deps=true -find=false -tags gofuzz -- . 14 | # github.com/dvyukov/go-fuzz/go-fuzz-dep]: exit status 1: build github.com/dvyukov/go-fuzz/go-fuzz-dep: 15 | # cannot load github.com/dvyukov/go-fuzz/go-fuzz-dep: 16 | # open $WORK\foo\vendor\github.com\dvyukov\go-fuzz\go-fuzz-dep: The system cannot find the path specified. 17 | 18 | # TODO: this test currently relies on latest go-fuzz/go-fuzz-dep and go-fuzz-defs 19 | # versions on master being downloaded from github as part of the tests. 20 | # If this test is run in a go-fuzz branch and there have been signficant 21 | # changes to go-fuzz/go-fuzz-dep and go-fuzz-defs in the branch, this test 22 | # will use the versions from master and might incorrectly fail. 23 | # If that happens, the foo/go.mod below can temporarily be modified to point 24 | # to the branch, or this test can temporarily be ignored or removed. 25 | 26 | # Enter a module that has a vendor directory, where the code in the vendor 27 | # directory has been edited compared (so that we can validate the vendored 28 | # code is in fact used). 29 | # Note that we are outside of GOPATH, so the presence of the 'go.mod' here will 30 | # enable module-module for cmd/go (which is true in Go 1.11-1.13, and likely will be true in 1.14 as well). 31 | cd foo 32 | 33 | # Because cmd/go now defaults to -mod=readonly and requires a valid go.sum entry, we need to explicitly add go-fuzz-dep. 34 | go get github.com/dvyukov/go-fuzz/go-fuzz-dep 35 | 36 | # Sanity check the module seems well formed. 37 | exec go list -m all 38 | stdout '^example.com/foo$' 39 | stdout '^example.com/bar v0.0.0 => ../bar$' 40 | exec go build 41 | 42 | # Do an intial 'go mod vendor' 43 | exec go mod vendor 44 | 45 | # Overwrite the original bar with bad code that will not compile, 46 | # while leaving the vendored version of bar alone 47 | # so that vendored code still compiles. 48 | cp ../bar/bar.go_BAD ../bar/bar.go 49 | 50 | # Sanity check the good code compiles, and bad code fails. 51 | # Successful compilation here relies on -mod=vendor being enabled. 52 | env GOFLAGS=-mod=vendor 53 | exec go build 54 | env GOFLAGS= 55 | ! exec go build 56 | 57 | # Validate we get the expected error if we try to run go-fuzz-build 58 | # with -mod=vendor in GOFLAGS. 59 | env GOFLAGS=-mod=vendor 60 | ! exec go-fuzz-build -func=FuzzDependOnVendor 61 | stderr 'GOFLAGS with -mod=vendor is not supported' 62 | env GOFLAGS='-v -mod=vendor' 63 | ! exec go-fuzz-build -func=FuzzDependOnVendor 64 | stderr 'GOFLAGS with -mod=vendor is not supported' 65 | 66 | # This is as far as we can currently go here, given go-fuzz does not support -mod=vendor. 67 | # The remainder of the test has never passed, but we will leave here in case useful 68 | # in the future. The rest of the tests are set to pass as if GOFLAGS=-mod=vendor worked, 69 | # but they have never run, so might have a typo or other mistake. 70 | stop 'do not run actual vendoring test. -mod=vendor not yet supported.' 71 | 72 | # Ask go-fuzz-build to build, specifying the fuzz function for foo. 73 | # First, we rely on -mod=vendor being set. 74 | env GOFLAGS=-mod=vendor 75 | exec go-fuzz-build -func=FuzzDependOnVendor 76 | exists foo-fuzz.zip 77 | 78 | # Validate we can start fuzzing. 79 | # Note that 'timeout(1)' will error here, so we preface the invocation with '!'. 80 | # For travis on Windows, we install 'timeout(1)' as part of our travis setup steps. 81 | # To test this locally on Windows, you might need to change 'timeout' to '\cygwin64\bin\timeout' or similar. 82 | ! exec timeout 5 go-fuzz -procs=1 -func=FuzzUsingVendor 83 | stderr 'workers: \d+, corpus: ' 84 | 85 | # Ask go-fuzz-build to build again, but now we do not specify -mod=vendor, 86 | # which results in a compilation failure because the non-vendored code 87 | # deliberately does not compile (as a way to validate we are using the vendored code above). 88 | env GOFLAGS= 89 | rm foo-fuzz.zip 90 | ! exec go-fuzz-build 91 | ! exists foo-fuzz.zip 92 | 93 | # Define two modules. 94 | # example.com/foo has a fuzz function, and depends on example.com/bar. 95 | # foo has vendored bar and (in theory) made a code change inside foo's vendor 96 | # directory. The bar code inside foo's vendor compiles, but the original 97 | # bar code does not compile. 98 | # The go.mod files set 'go 1.13' to avoid triggering the Go 1.14 auto 99 | # detection of a vendor directory. 100 | 101 | -- foo/fuzz.go -- 102 | package foo 103 | 104 | import "example.com/bar" 105 | 106 | func FuzzUsingVendor(data []byte) int { 107 | bar.Bar() 108 | return 0 109 | } 110 | 111 | -- foo/go.mod -- 112 | module example.com/foo 113 | 114 | go 1.13 115 | 116 | require ( 117 | example.com/bar v0.0.0 118 | ) 119 | 120 | replace example.com/bar => ../bar 121 | 122 | -- bar/bar.go_BAD -- 123 | package bar 124 | 125 | // This will not compile. 126 | func BarWillNotCompile() string { 127 | 128 | } 129 | 130 | -- bar/bar.go -- 131 | package bar 132 | 133 | // This compiles. 134 | func Bar() string { 135 | return "hello from bar" 136 | } 137 | 138 | -- bar/go.mod -- 139 | module example.com/bar 140 | 141 | go 1.13 142 | --------------------------------------------------------------------------------