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

     1  // Copyright 2024 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  	"bytes"
     8  	"context"
     9  	"errors"
    10  	"fmt"
    11  	"time"
    12  
    13  	flatbuffers "github.com/google/flatbuffers/go"
    14  	"github.com/google/syzkaller/pkg/flatrpc"
    15  	"github.com/google/syzkaller/pkg/fuzzer/queue"
    16  	"github.com/google/syzkaller/pkg/log"
    17  	"github.com/google/syzkaller/pkg/manager"
    18  	"github.com/google/syzkaller/vm"
    19  	"github.com/google/syzkaller/vm/dispatcher"
    20  )
    21  
    22  func (mgr *Manager) snapshotInstance(ctx context.Context, inst *vm.Instance, updInfo dispatcher.UpdateInfo) {
    23  	mgr.servStats.StatNumFuzzing.Add(1)
    24  	defer mgr.servStats.StatNumFuzzing.Add(-1)
    25  
    26  	updInfo(func(info *dispatcher.Info) {
    27  		info.Status = "snapshot fuzzing"
    28  	})
    29  
    30  	err := mgr.snapshotLoop(ctx, inst)
    31  	if err != nil {
    32  		log.Error(err)
    33  	}
    34  }
    35  
    36  func (mgr *Manager) snapshotLoop(ctx context.Context, inst *vm.Instance) error {
    37  	executor, err := inst.Copy(mgr.cfg.ExecutorBin)
    38  	if err != nil {
    39  		return err
    40  	}
    41  	// All network connections (including ssh) will break once we start restoring snapshots.
    42  	// So we start a background process and log to /dev/kmsg.
    43  	cmd := fmt.Sprintf("nohup %v exec snapshot 1>/dev/null 2>/dev/kmsg </dev/null &", executor)
    44  	ctxTimeout, cancel := context.WithTimeout(ctx, time.Hour)
    45  	defer cancel()
    46  	if _, _, err := inst.Run(ctxTimeout, mgr.reporter, cmd); err != nil {
    47  		return err
    48  	}
    49  
    50  	builder := flatbuffers.NewBuilder(0)
    51  	var envFlags flatrpc.ExecEnv
    52  	for first := true; ctx.Err() == nil; first = false {
    53  		mgr.servStats.StatExecs.Add(1)
    54  		req := mgr.snapshotSource.Next(inst.Index())
    55  		if first {
    56  			envFlags = req.ExecOpts.EnvFlags
    57  			if err := mgr.snapshotSetup(inst, builder, envFlags); err != nil {
    58  				req.Done(&queue.Result{Status: queue.Crashed})
    59  				return err
    60  			}
    61  		}
    62  		if envFlags != req.ExecOpts.EnvFlags {
    63  			panic(fmt.Sprintf("request env flags has changed: 0x%x -> 0x%x",
    64  				envFlags, req.ExecOpts.EnvFlags))
    65  		}
    66  
    67  		res, output, err := mgr.snapshotRun(inst, builder, req)
    68  		if err != nil {
    69  			req.Done(&queue.Result{Status: queue.Crashed})
    70  			return err
    71  		}
    72  
    73  		if mgr.reporter.ContainsCrash(output) {
    74  			res.Status = queue.Crashed
    75  			rep := mgr.reporter.Parse(output)
    76  			buf := new(bytes.Buffer)
    77  			fmt.Fprintf(buf, "program:\n%s\n", req.Prog.Serialize())
    78  			buf.Write(rep.Output)
    79  			rep.Output = buf.Bytes()
    80  			mgr.crashes <- &manager.Crash{Report: rep}
    81  		}
    82  
    83  		req.Done(res)
    84  	}
    85  	return nil
    86  }
    87  
    88  func (mgr *Manager) snapshotSetup(inst *vm.Instance, builder *flatbuffers.Builder, env flatrpc.ExecEnv) error {
    89  	msg := flatrpc.SnapshotHandshakeT{
    90  		CoverEdges:       mgr.cfg.Experimental.CoverEdges,
    91  		Kernel64Bit:      mgr.cfg.SysTarget.PtrSize == 8,
    92  		Slowdown:         int32(mgr.cfg.Timeouts.Slowdown),
    93  		SyscallTimeoutMs: int32(mgr.cfg.Timeouts.Syscall / time.Millisecond),
    94  		ProgramTimeoutMs: int32(mgr.cfg.Timeouts.Program / time.Millisecond),
    95  		Features:         mgr.enabledFeatures,
    96  		EnvFlags:         env,
    97  		SandboxArg:       mgr.cfg.SandboxArg,
    98  	}
    99  	builder.Reset()
   100  	builder.Finish(msg.Pack(builder))
   101  	return inst.SetupSnapshot(builder.FinishedBytes())
   102  }
   103  
   104  func (mgr *Manager) snapshotRun(inst *vm.Instance, builder *flatbuffers.Builder, req *queue.Request) (
   105  	*queue.Result, []byte, error) {
   106  	progData, err := req.Prog.SerializeForExec()
   107  	if err != nil {
   108  		queue.StatExecBufferTooSmall.Add(1)
   109  		return &queue.Result{
   110  			Status: queue.ExecFailure,
   111  			Err:    fmt.Errorf("program serialization failed: %w", err),
   112  		}, nil, nil
   113  	}
   114  	msg := flatrpc.SnapshotRequestT{
   115  		ExecFlags: req.ExecOpts.ExecFlags,
   116  		NumCalls:  int32(len(req.Prog.Calls)),
   117  		ProgData:  progData,
   118  	}
   119  	for _, call := range req.ReturnAllSignal {
   120  		if call < 0 {
   121  			msg.AllExtraSignal = true
   122  		} else {
   123  			msg.AllCallSignal |= 1 << call
   124  		}
   125  	}
   126  	builder.Reset()
   127  	builder.Finish(msg.Pack(builder))
   128  
   129  	start := time.Now()
   130  	resData, output, err := inst.RunSnapshot(builder.FinishedBytes())
   131  	if err != nil {
   132  		return nil, nil, err
   133  	}
   134  	elapsed := time.Since(start)
   135  
   136  	res := parseExecResult(resData)
   137  	if res.Info != nil {
   138  		res.Info.Elapsed = uint64(elapsed)
   139  		for len(res.Info.Calls) < len(req.Prog.Calls) {
   140  			res.Info.Calls = append(res.Info.Calls, &flatrpc.CallInfo{
   141  				Error: 999,
   142  			})
   143  		}
   144  		res.Info.Calls = res.Info.Calls[:len(req.Prog.Calls)]
   145  		if len(res.Info.ExtraRaw) != 0 {
   146  			res.Info.Extra = res.Info.ExtraRaw[0]
   147  			for _, info := range res.Info.ExtraRaw[1:] {
   148  				res.Info.Extra.Cover = append(res.Info.Extra.Cover, info.Cover...)
   149  				res.Info.Extra.Signal = append(res.Info.Extra.Signal, info.Signal...)
   150  			}
   151  			res.Info.ExtraRaw = nil
   152  		}
   153  	}
   154  
   155  	ret := &queue.Result{
   156  		Status: queue.Success,
   157  		Info:   res.Info,
   158  	}
   159  	if res.Error != "" {
   160  		ret.Status = queue.ExecFailure
   161  		ret.Err = errors.New(res.Error)
   162  	}
   163  	if req.ReturnOutput {
   164  		ret.Output = output
   165  	}
   166  	return ret, output, nil
   167  }
   168  
   169  func parseExecResult(data []byte) *flatrpc.ExecResult {
   170  	if len(data) < flatbuffers.SizeUint32 {
   171  		return &flatrpc.ExecResult{
   172  			Error: "the buffer is too small",
   173  		}
   174  	}
   175  	raw, err := flatrpc.Parse[*flatrpc.ExecutorMessageRaw](data[flatbuffers.SizeUint32:])
   176  	if err != nil {
   177  		// Don't consider result parsing error as an infrastructure error,
   178  		// it's just the test program corrupted memory.
   179  		return &flatrpc.ExecResult{
   180  			Error: err.Error(),
   181  		}
   182  	}
   183  	res, ok := raw.Msg.Value.(*flatrpc.ExecResult)
   184  	if !ok {
   185  		return &flatrpc.ExecResult{
   186  			Error: "result is not ExecResult",
   187  		}
   188  	}
   189  	return res
   190  }