github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/tools/syz-db/syz-db.go (about) 1 // Copyright 2017 syzkaller 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 "fmt" 9 "os" 10 "path/filepath" 11 "runtime" 12 "strconv" 13 "strings" 14 "time" 15 16 "github.com/google/syzkaller/pkg/db" 17 "github.com/google/syzkaller/pkg/hash" 18 "github.com/google/syzkaller/pkg/osutil" 19 "github.com/google/syzkaller/pkg/tool" 20 "github.com/google/syzkaller/prog" 21 _ "github.com/google/syzkaller/sys" 22 ) 23 24 func main() { 25 var ( 26 flagVersion = flag.Uint64("version", 0, "database version") 27 flagOS = flag.String("os", "", "target OS") 28 flagArch = flag.String("arch", "", "target arch") 29 ) 30 flag.Parse() 31 args := flag.Args() 32 if len(args) == 0 { 33 usage() 34 } 35 if args[0] == "bench" { 36 if len(args) != 2 { 37 usage() 38 } 39 target, err := prog.GetTarget(*flagOS, *flagArch) 40 if err != nil { 41 tool.Failf("failed to find target: %v", err) 42 } 43 bench(target, args[1]) 44 return 45 } 46 var target *prog.Target 47 if *flagOS != "" || *flagArch != "" { 48 var err error 49 target, err = prog.GetTarget(*flagOS, *flagArch) 50 if err != nil { 51 tool.Failf("failed to find target: %v", err) 52 } 53 } 54 switch args[0] { 55 case "pack": 56 if len(args) != 3 { 57 usage() 58 } 59 pack(args[1], args[2], target, *flagVersion) 60 case "unpack": 61 if len(args) != 3 { 62 usage() 63 } 64 unpack(args[1], args[2]) 65 case "merge": 66 if len(args) < 3 { 67 usage() 68 } 69 merge(args[1], args[2:], target) 70 default: 71 usage() 72 } 73 } 74 75 func usage() { 76 fmt.Fprintf(os.Stderr, `usage: syz-db can be used to manipulate corpus 77 databases that are used by syz-managers. The following generic arguments are 78 offered: 79 -arch string 80 -os string 81 -version uint 82 -vv int 83 84 they can be used for: 85 packing a database: 86 syz-db pack dir corpus.db 87 unpacking a database. A file containing performed syscalls will be returned: 88 syz-db unpack corpus.db dir 89 merging databases. No additional file will be created: The first file will be replaced by the merged result: 90 syz-db merge dst-corpus.db add-corpus.db* add-prog* 91 running a deserialization benchmark: 92 syz-db bench corpus.db 93 `) 94 os.Exit(1) 95 } 96 97 func pack(dir, file string, target *prog.Target, version uint64) { 98 files, err := os.ReadDir(dir) 99 if err != nil { 100 tool.Failf("failed to read dir: %v", err) 101 } 102 var records []db.Record 103 for _, file := range files { 104 data, err := os.ReadFile(filepath.Join(dir, file.Name())) 105 if err != nil { 106 tool.Failf("failed to read file %v: %v", file.Name(), err) 107 } 108 var seq uint64 109 key := file.Name() 110 if parts := strings.Split(file.Name(), "-"); len(parts) == 2 { 111 var err error 112 if seq, err = strconv.ParseUint(parts[1], 10, 64); err == nil { 113 key = parts[0] 114 } 115 } 116 if sig := hash.String(data); key != sig { 117 if target != nil { 118 p, err := target.Deserialize(data, prog.NonStrict) 119 if err != nil { 120 tool.Failf("failed to deserialize %v: %v", file.Name(), err) 121 } 122 data = p.Serialize() 123 sig = hash.String(data) 124 } 125 fmt.Fprintf(os.Stderr, "fixing hash %v -> %v\n", key, sig) 126 key = sig 127 } 128 records = append(records, db.Record{ 129 Val: data, 130 Seq: seq, 131 }) 132 } 133 if err := db.Create(file, version, records); err != nil { 134 tool.Fail(err) 135 } 136 } 137 138 func unpack(file, dir string) { 139 db, err := db.Open(file, false) 140 if err != nil { 141 tool.Failf("failed to open database: %v", err) 142 } 143 osutil.MkdirAll(dir) 144 for key, rec := range db.Records { 145 fname := filepath.Join(dir, key) 146 if rec.Seq != 0 { 147 fname += fmt.Sprintf("-%v", rec.Seq) 148 } 149 if err := osutil.WriteFile(fname, rec.Val); err != nil { 150 tool.Failf("failed to output file: %v", err) 151 } 152 } 153 } 154 155 func merge(file string, adds []string, target *prog.Target) { 156 dstDB, err := db.Open(file, false) 157 if err != nil { 158 tool.Failf("failed to open database: %v", err) 159 } 160 for _, add := range adds { 161 if addDB, err := db.Open(add, false); err == nil { 162 for key, rec := range addDB.Records { 163 dstDB.Save(key, rec.Val, rec.Seq) 164 } 165 continue 166 } else if target == nil { 167 tool.Failf("failed to open db %v: %v", add, err) 168 } 169 data, err := os.ReadFile(add) 170 if err != nil { 171 tool.Fail(err) 172 } 173 if _, err := target.Deserialize(data, prog.NonStrict); err != nil { 174 tool.Failf("failed to deserialize %v: %v", add, err) 175 } 176 dstDB.Save(hash.String(data), data, 0) 177 } 178 if err := dstDB.Flush(); err != nil { 179 tool.Failf("failed to save db: %v", err) 180 } 181 } 182 183 func bench(target *prog.Target, file string) { 184 start := time.Now() 185 db, err := db.Open(file, false) 186 if err != nil { 187 tool.Failf("failed to open database: %v", err) 188 } 189 var corpus []*prog.Prog 190 for _, rec := range db.Records { 191 p, err := target.Deserialize(rec.Val, prog.NonStrict) 192 if err != nil { 193 tool.Failf("failed to deserialize: %v\n%s", err, rec.Val) 194 } 195 corpus = append(corpus, p) 196 } 197 runtime.GC() 198 var stats runtime.MemStats 199 runtime.ReadMemStats(&stats) 200 fmt.Printf("allocs %v MB (%v M), next GC %v MB, sys heap %v MB, live allocs %v MB (%v M), time %v\n", 201 stats.TotalAlloc>>20, 202 stats.Mallocs>>20, 203 stats.NextGC>>20, 204 stats.HeapSys>>20, 205 stats.Alloc>>20, 206 (stats.Mallocs-stats.Frees)>>20, 207 time.Since(start)) 208 sink = corpus 209 _ = sink 210 } 211 212 var sink interface{}