github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/tools/syz-repro/repro.go (about)

     1  // Copyright 2015 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  	"context"
     8  	"flag"
     9  	"fmt"
    10  	"os"
    11  	"path/filepath"
    12  
    13  	"github.com/google/syzkaller/pkg/csource"
    14  	"github.com/google/syzkaller/pkg/flatrpc"
    15  	"github.com/google/syzkaller/pkg/log"
    16  	"github.com/google/syzkaller/pkg/mgrconfig"
    17  	"github.com/google/syzkaller/pkg/osutil"
    18  	"github.com/google/syzkaller/pkg/report"
    19  	"github.com/google/syzkaller/pkg/repro"
    20  	"github.com/google/syzkaller/vm"
    21  )
    22  
    23  var (
    24  	flagConfig = flag.String("config", "", "manager configuration file (manager.cfg)")
    25  	flagCount  = flag.Int("count", 0, "number of VMs to use (overrides config count param)")
    26  	flagDebug  = flag.Bool("debug", false, "print debug output")
    27  	flagOutput = flag.String("output", filepath.Join(".", "repro.txt"), "output syz repro file (repro.txt)")
    28  	flagCRepro = flag.String("crepro", filepath.Join(".", "repro.c"), "output c file (repro.c)")
    29  	flagTitle  = flag.String("title", "", "where to save the title of the reproduced bug")
    30  	flagStrace = flag.String("strace", "", "output strace log (strace_bin must be set)")
    31  )
    32  
    33  func main() {
    34  	os.Args = append(append([]string{}, os.Args[0], "-vv=10"), os.Args[1:]...)
    35  	flag.Parse()
    36  	if len(flag.Args()) != 1 || *flagConfig == "" {
    37  		log.Fatalf("usage: syz-repro -config=manager.cfg execution.log")
    38  	}
    39  	cfg, err := mgrconfig.LoadFile(*flagConfig)
    40  	if err != nil {
    41  		log.Fatalf("%v: %v", *flagConfig, err)
    42  	}
    43  	logFile := flag.Args()[0]
    44  	data, err := os.ReadFile(logFile)
    45  	if err != nil {
    46  		log.Fatalf("failed to open log file %v: %v", logFile, err)
    47  	}
    48  	vmPool, err := vm.Create(cfg, *flagDebug)
    49  	if err != nil {
    50  		log.Fatalf("%v", err)
    51  	}
    52  	defer vmPool.Close()
    53  	reporter, err := report.NewReporter(cfg)
    54  	if err != nil {
    55  		log.Fatalf("%v", err)
    56  	}
    57  	osutil.HandleInterrupts(vm.Shutdown)
    58  
    59  	count := vmPool.Count()
    60  	if *flagCount > 0 {
    61  		count = *flagCount
    62  	}
    63  	pool := vm.NewDispatcher(vmPool, nil)
    64  	pool.ReserveForRun(count)
    65  
    66  	ctx, done := context.WithCancel(context.Background())
    67  	go func() {
    68  		defer done()
    69  
    70  		res, stats, err := repro.Run(ctx, data, repro.Environment{
    71  			Config:   cfg,
    72  			Features: flatrpc.AllFeatures,
    73  			Reporter: reporter,
    74  			Pool:     pool,
    75  		})
    76  		if err != nil {
    77  			log.Logf(0, "reproduction failed: %v", err)
    78  		}
    79  		if stats != nil {
    80  			fmt.Printf("extracting prog: %v\n", stats.ExtractProgTime)
    81  			fmt.Printf("minimizing prog: %v\n", stats.MinimizeProgTime)
    82  			fmt.Printf("simplifying prog options: %v\n", stats.SimplifyProgTime)
    83  			fmt.Printf("extracting C: %v\n", stats.ExtractCTime)
    84  			fmt.Printf("simplifying C: %v\n", stats.SimplifyCTime)
    85  		}
    86  		if res == nil {
    87  			return
    88  		}
    89  
    90  		fmt.Printf("opts: %+v crepro: %v\n\n", res.Opts, res.CRepro)
    91  		progSerialized := res.Prog.Serialize()
    92  		fmt.Printf("%s\n", progSerialized)
    93  		if err = osutil.WriteFile(*flagOutput, progSerialized); err == nil {
    94  			fmt.Printf("program saved to %s\n", *flagOutput)
    95  		} else {
    96  			log.Logf(0, "failed to write prog to file: %v", err)
    97  		}
    98  
    99  		if res.Report != nil && *flagTitle != "" {
   100  			recordTitle(res, *flagTitle)
   101  		}
   102  		if res.CRepro {
   103  			recordCRepro(res, *flagCRepro)
   104  		}
   105  		if *flagStrace != "" {
   106  			result := repro.RunStrace(res, cfg, reporter, pool)
   107  			recordStraceResult(result, *flagStrace)
   108  		}
   109  	}()
   110  	pool.Loop(ctx)
   111  }
   112  
   113  func recordTitle(res *repro.Result, fileName string) {
   114  	if err := osutil.WriteFile(fileName, []byte(res.Report.Title)); err == nil {
   115  		fmt.Printf("bug title saved to %s\n", *flagTitle)
   116  	} else {
   117  		log.Logf(0, "failed to write bug title to file: %v", err)
   118  	}
   119  }
   120  
   121  func recordCRepro(res *repro.Result, fileName string) {
   122  	src, err := csource.Write(res.Prog, res.Opts)
   123  	if err != nil {
   124  		log.Fatalf("failed to generate C repro: %v", err)
   125  	}
   126  	if formatted, err := csource.Format(src); err == nil {
   127  		src = formatted
   128  	}
   129  	fmt.Printf("%s\n", src)
   130  
   131  	if err := osutil.WriteFile(fileName, src); err == nil {
   132  		fmt.Printf("C file saved to %s\n", *flagCRepro)
   133  	} else {
   134  		log.Logf(0, "failed to write C repro to file: %v", err)
   135  	}
   136  }
   137  
   138  func recordStraceResult(result *repro.StraceResult, fileName string) {
   139  	if result.Error != nil {
   140  		log.Logf(0, "failed to run strace: %v", result.Error)
   141  		return
   142  	}
   143  	if result.Report != nil {
   144  		log.Logf(0, "under strace repro crashed with title: %s", result.Report.Title)
   145  	} else {
   146  		log.Logf(0, "repro didn't crash under strace")
   147  	}
   148  	if err := osutil.WriteFile(fileName, result.Output); err == nil {
   149  		fmt.Printf("C file saved to %s\n", *flagStrace)
   150  	} else {
   151  		log.Logf(0, "failed to write strace output to file: %v", err)
   152  	}
   153  }