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{}