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 }