diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2024-05-23 10:13:32 +0200 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2024-05-27 14:15:44 +0000 |
| commit | 6ef3e47010676e4e159edb346c219e8d30cabadc (patch) | |
| tree | bd57f83dae5605eebc00f05975b58eb0f7947df0 | |
| parent | 800bc2fcbad74e0447105d470f5eb0a0ef3503f3 (diff) | |
syz-fuzzer: repair leak checking
Notify fuzzer from the manager when corpus triage has finished
to start leak checking.
Fixes #4728
| -rw-r--r-- | pkg/flatrpc/flatrpc.fbs | 7 | ||||
| -rw-r--r-- | pkg/flatrpc/flatrpc.go | 83 | ||||
| -rw-r--r-- | pkg/flatrpc/flatrpc.h | 123 | ||||
| -rw-r--r-- | pkg/flatrpc/helpers.go | 1 | ||||
| -rw-r--r-- | syz-fuzzer/fuzzer.go | 29 | ||||
| -rw-r--r-- | syz-manager/manager.go | 3 | ||||
| -rw-r--r-- | syz-manager/rpc.go | 30 |
7 files changed, 248 insertions, 28 deletions
diff --git a/pkg/flatrpc/flatrpc.fbs b/pkg/flatrpc/flatrpc.fbs index cdddef965..5b211bb31 100644 --- a/pkg/flatrpc/flatrpc.fbs +++ b/pkg/flatrpc/flatrpc.fbs @@ -76,6 +76,7 @@ table FeatureInfoRaw { union HostMessagesRaw { ExecRequest :ExecRequestRaw, SignalUpdate :SignalUpdateRaw, + StartLeakChecks :StartLeakChecksRaw } table HostMessageRaw { @@ -160,6 +161,12 @@ table SignalUpdateRaw { drop_max :[uint64]; } +// Leak checking is very slow so we don't do it while triaging the corpus +// (otherwise it takes infinity). This message serves as a signal that +// the corpus was triaged and the fuzzer can start leak checking. +table StartLeakChecksRaw { +} + // Notification from the executor that it started executing the program 'id'. // We want this request to be as small and as fast as possible b/c we need it // to reach the host (or at least leave the VM) before the VM crashes diff --git a/pkg/flatrpc/flatrpc.go b/pkg/flatrpc/flatrpc.go index 2ef9b2595..89172feeb 100644 --- a/pkg/flatrpc/flatrpc.go +++ b/pkg/flatrpc/flatrpc.go @@ -89,21 +89,24 @@ func (v Feature) String() string { type HostMessagesRaw byte const ( - HostMessagesRawNONE HostMessagesRaw = 0 - HostMessagesRawExecRequest HostMessagesRaw = 1 - HostMessagesRawSignalUpdate HostMessagesRaw = 2 + HostMessagesRawNONE HostMessagesRaw = 0 + HostMessagesRawExecRequest HostMessagesRaw = 1 + HostMessagesRawSignalUpdate HostMessagesRaw = 2 + HostMessagesRawStartLeakChecks HostMessagesRaw = 3 ) var EnumNamesHostMessagesRaw = map[HostMessagesRaw]string{ - HostMessagesRawNONE: "NONE", - HostMessagesRawExecRequest: "ExecRequest", - HostMessagesRawSignalUpdate: "SignalUpdate", + HostMessagesRawNONE: "NONE", + HostMessagesRawExecRequest: "ExecRequest", + HostMessagesRawSignalUpdate: "SignalUpdate", + HostMessagesRawStartLeakChecks: "StartLeakChecks", } var EnumValuesHostMessagesRaw = map[string]HostMessagesRaw{ - "NONE": HostMessagesRawNONE, - "ExecRequest": HostMessagesRawExecRequest, - "SignalUpdate": HostMessagesRawSignalUpdate, + "NONE": HostMessagesRawNONE, + "ExecRequest": HostMessagesRawExecRequest, + "SignalUpdate": HostMessagesRawSignalUpdate, + "StartLeakChecks": HostMessagesRawStartLeakChecks, } func (v HostMessagesRaw) String() string { @@ -127,6 +130,8 @@ func (t *HostMessagesRawT) Pack(builder *flatbuffers.Builder) flatbuffers.UOffse return t.Value.(*ExecRequestRawT).Pack(builder) case HostMessagesRawSignalUpdate: return t.Value.(*SignalUpdateRawT).Pack(builder) + case HostMessagesRawStartLeakChecks: + return t.Value.(*StartLeakChecksRawT).Pack(builder) } return 0 } @@ -139,6 +144,9 @@ func (rcv HostMessagesRaw) UnPack(table flatbuffers.Table) *HostMessagesRawT { case HostMessagesRawSignalUpdate: x := SignalUpdateRaw{_tab: table} return &HostMessagesRawT{Type: HostMessagesRawSignalUpdate, Value: x.UnPack()} + case HostMessagesRawStartLeakChecks: + x := StartLeakChecksRaw{_tab: table} + return &HostMessagesRawT{Type: HostMessagesRawStartLeakChecks, Value: x.UnPack()} } return nil } @@ -2080,6 +2088,63 @@ func SignalUpdateRawEnd(builder *flatbuffers.Builder) flatbuffers.UOffsetT { return builder.EndObject() } +type StartLeakChecksRawT struct { +} + +func (t *StartLeakChecksRawT) Pack(builder *flatbuffers.Builder) flatbuffers.UOffsetT { + if t == nil { + return 0 + } + StartLeakChecksRawStart(builder) + return StartLeakChecksRawEnd(builder) +} + +func (rcv *StartLeakChecksRaw) UnPackTo(t *StartLeakChecksRawT) { +} + +func (rcv *StartLeakChecksRaw) UnPack() *StartLeakChecksRawT { + if rcv == nil { + return nil + } + t := &StartLeakChecksRawT{} + rcv.UnPackTo(t) + return t +} + +type StartLeakChecksRaw struct { + _tab flatbuffers.Table +} + +func GetRootAsStartLeakChecksRaw(buf []byte, offset flatbuffers.UOffsetT) *StartLeakChecksRaw { + n := flatbuffers.GetUOffsetT(buf[offset:]) + x := &StartLeakChecksRaw{} + x.Init(buf, n+offset) + return x +} + +func GetSizePrefixedRootAsStartLeakChecksRaw(buf []byte, offset flatbuffers.UOffsetT) *StartLeakChecksRaw { + n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:]) + x := &StartLeakChecksRaw{} + x.Init(buf, n+offset+flatbuffers.SizeUint32) + return x +} + +func (rcv *StartLeakChecksRaw) Init(buf []byte, i flatbuffers.UOffsetT) { + rcv._tab.Bytes = buf + rcv._tab.Pos = i +} + +func (rcv *StartLeakChecksRaw) Table() flatbuffers.Table { + return rcv._tab +} + +func StartLeakChecksRawStart(builder *flatbuffers.Builder) { + builder.StartObject(0) +} +func StartLeakChecksRawEnd(builder *flatbuffers.Builder) flatbuffers.UOffsetT { + return builder.EndObject() +} + type ExecutingMessageRawT struct { Id int64 `json:"id"` ProcId int32 `json:"proc_id"` diff --git a/pkg/flatrpc/flatrpc.h b/pkg/flatrpc/flatrpc.h index 649319e08..aa42c41d2 100644 --- a/pkg/flatrpc/flatrpc.h +++ b/pkg/flatrpc/flatrpc.h @@ -61,6 +61,10 @@ struct SignalUpdateRaw; struct SignalUpdateRawBuilder; struct SignalUpdateRawT; +struct StartLeakChecksRaw; +struct StartLeakChecksRawBuilder; +struct StartLeakChecksRawT; + struct ExecutingMessageRaw; struct ExecutingMessageRawBuilder; struct ExecutingMessageRawT; @@ -161,31 +165,34 @@ enum class HostMessagesRaw : uint8_t { NONE = 0, ExecRequest = 1, SignalUpdate = 2, + StartLeakChecks = 3, MIN = NONE, - MAX = SignalUpdate + MAX = StartLeakChecks }; -inline const HostMessagesRaw (&EnumValuesHostMessagesRaw())[3] { +inline const HostMessagesRaw (&EnumValuesHostMessagesRaw())[4] { static const HostMessagesRaw values[] = { HostMessagesRaw::NONE, HostMessagesRaw::ExecRequest, - HostMessagesRaw::SignalUpdate + HostMessagesRaw::SignalUpdate, + HostMessagesRaw::StartLeakChecks }; return values; } inline const char * const *EnumNamesHostMessagesRaw() { - static const char * const names[4] = { + static const char * const names[5] = { "NONE", "ExecRequest", "SignalUpdate", + "StartLeakChecks", nullptr }; return names; } inline const char *EnumNameHostMessagesRaw(HostMessagesRaw e) { - if (flatbuffers::IsOutRange(e, HostMessagesRaw::NONE, HostMessagesRaw::SignalUpdate)) return ""; + if (flatbuffers::IsOutRange(e, HostMessagesRaw::NONE, HostMessagesRaw::StartLeakChecks)) return ""; const size_t index = static_cast<size_t>(e); return EnumNamesHostMessagesRaw()[index]; } @@ -202,6 +209,10 @@ template<> struct HostMessagesRawTraits<rpc::SignalUpdateRaw> { static const HostMessagesRaw enum_value = HostMessagesRaw::SignalUpdate; }; +template<> struct HostMessagesRawTraits<rpc::StartLeakChecksRaw> { + static const HostMessagesRaw enum_value = HostMessagesRaw::StartLeakChecks; +}; + template<typename T> struct HostMessagesRawUnionTraits { static const HostMessagesRaw enum_value = HostMessagesRaw::NONE; }; @@ -214,6 +225,10 @@ template<> struct HostMessagesRawUnionTraits<rpc::SignalUpdateRawT> { static const HostMessagesRaw enum_value = HostMessagesRaw::SignalUpdate; }; +template<> struct HostMessagesRawUnionTraits<rpc::StartLeakChecksRawT> { + static const HostMessagesRaw enum_value = HostMessagesRaw::StartLeakChecks; +}; + struct HostMessagesRawUnion { HostMessagesRaw type; void *value; @@ -260,6 +275,14 @@ struct HostMessagesRawUnion { return type == HostMessagesRaw::SignalUpdate ? reinterpret_cast<const rpc::SignalUpdateRawT *>(value) : nullptr; } + rpc::StartLeakChecksRawT *AsStartLeakChecks() { + return type == HostMessagesRaw::StartLeakChecks ? + reinterpret_cast<rpc::StartLeakChecksRawT *>(value) : nullptr; + } + const rpc::StartLeakChecksRawT *AsStartLeakChecks() const { + return type == HostMessagesRaw::StartLeakChecks ? + reinterpret_cast<const rpc::StartLeakChecksRawT *>(value) : nullptr; + } }; bool VerifyHostMessagesRaw(flatbuffers::Verifier &verifier, const void *obj, HostMessagesRaw type); @@ -1322,6 +1345,9 @@ struct HostMessageRaw FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const rpc::SignalUpdateRaw *msg_as_SignalUpdate() const { return msg_type() == rpc::HostMessagesRaw::SignalUpdate ? static_cast<const rpc::SignalUpdateRaw *>(msg()) : nullptr; } + const rpc::StartLeakChecksRaw *msg_as_StartLeakChecks() const { + return msg_type() == rpc::HostMessagesRaw::StartLeakChecks ? static_cast<const rpc::StartLeakChecksRaw *>(msg()) : nullptr; + } bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyField<uint8_t>(verifier, VT_MSG_TYPE, 1) && @@ -1342,6 +1368,10 @@ template<> inline const rpc::SignalUpdateRaw *HostMessageRaw::msg_as<rpc::Signal return msg_as_SignalUpdate(); } +template<> inline const rpc::StartLeakChecksRaw *HostMessageRaw::msg_as<rpc::StartLeakChecksRaw>() const { + return msg_as_StartLeakChecks(); +} + struct HostMessageRawBuilder { typedef HostMessageRaw Table; flatbuffers::FlatBufferBuilder &fbb_; @@ -1676,6 +1706,45 @@ inline flatbuffers::Offset<SignalUpdateRaw> CreateSignalUpdateRawDirect( flatbuffers::Offset<SignalUpdateRaw> CreateSignalUpdateRaw(flatbuffers::FlatBufferBuilder &_fbb, const SignalUpdateRawT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); +struct StartLeakChecksRawT : public flatbuffers::NativeTable { + typedef StartLeakChecksRaw TableType; +}; + +struct StartLeakChecksRaw FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef StartLeakChecksRawT NativeTableType; + typedef StartLeakChecksRawBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + verifier.EndTable(); + } + StartLeakChecksRawT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(StartLeakChecksRawT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset<StartLeakChecksRaw> Pack(flatbuffers::FlatBufferBuilder &_fbb, const StartLeakChecksRawT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct StartLeakChecksRawBuilder { + typedef StartLeakChecksRaw Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit StartLeakChecksRawBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset<StartLeakChecksRaw> Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset<StartLeakChecksRaw>(end); + return o; + } +}; + +inline flatbuffers::Offset<StartLeakChecksRaw> CreateStartLeakChecksRaw( + flatbuffers::FlatBufferBuilder &_fbb) { + StartLeakChecksRawBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset<StartLeakChecksRaw> CreateStartLeakChecksRaw(flatbuffers::FlatBufferBuilder &_fbb, const StartLeakChecksRawT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); + struct ExecutingMessageRawT : public flatbuffers::NativeTable { typedef ExecutingMessageRaw TableType; int64_t id = 0; @@ -2494,6 +2563,29 @@ inline flatbuffers::Offset<SignalUpdateRaw> CreateSignalUpdateRaw(flatbuffers::F _drop_max); } +inline StartLeakChecksRawT *StartLeakChecksRaw::UnPack(const flatbuffers::resolver_function_t *_resolver) const { + auto _o = std::unique_ptr<StartLeakChecksRawT>(new StartLeakChecksRawT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void StartLeakChecksRaw::UnPackTo(StartLeakChecksRawT *_o, const flatbuffers::resolver_function_t *_resolver) const { + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset<StartLeakChecksRaw> StartLeakChecksRaw::Pack(flatbuffers::FlatBufferBuilder &_fbb, const StartLeakChecksRawT* _o, const flatbuffers::rehasher_function_t *_rehasher) { + return CreateStartLeakChecksRaw(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset<StartLeakChecksRaw> CreateStartLeakChecksRaw(flatbuffers::FlatBufferBuilder &_fbb, const StartLeakChecksRawT *_o, const flatbuffers::rehasher_function_t *_rehasher) { + (void)_rehasher; + (void)_o; + struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const StartLeakChecksRawT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va; + return rpc::CreateStartLeakChecksRaw( + _fbb); +} + inline ExecutingMessageRawT *ExecutingMessageRaw::UnPack(const flatbuffers::resolver_function_t *_resolver) const { auto _o = std::unique_ptr<ExecutingMessageRawT>(new ExecutingMessageRawT()); UnPackTo(_o.get(), _resolver); @@ -2681,6 +2773,10 @@ inline bool VerifyHostMessagesRaw(flatbuffers::Verifier &verifier, const void *o auto ptr = reinterpret_cast<const rpc::SignalUpdateRaw *>(obj); return verifier.VerifyTable(ptr); } + case HostMessagesRaw::StartLeakChecks: { + auto ptr = reinterpret_cast<const rpc::StartLeakChecksRaw *>(obj); + return verifier.VerifyTable(ptr); + } default: return true; } } @@ -2708,6 +2804,10 @@ inline void *HostMessagesRawUnion::UnPack(const void *obj, HostMessagesRaw type, auto ptr = reinterpret_cast<const rpc::SignalUpdateRaw *>(obj); return ptr->UnPack(resolver); } + case HostMessagesRaw::StartLeakChecks: { + auto ptr = reinterpret_cast<const rpc::StartLeakChecksRaw *>(obj); + return ptr->UnPack(resolver); + } default: return nullptr; } } @@ -2723,6 +2823,10 @@ inline flatbuffers::Offset<void> HostMessagesRawUnion::Pack(flatbuffers::FlatBuf auto ptr = reinterpret_cast<const rpc::SignalUpdateRawT *>(value); return CreateSignalUpdateRaw(_fbb, ptr, _rehasher).Union(); } + case HostMessagesRaw::StartLeakChecks: { + auto ptr = reinterpret_cast<const rpc::StartLeakChecksRawT *>(value); + return CreateStartLeakChecksRaw(_fbb, ptr, _rehasher).Union(); + } default: return 0; } } @@ -2737,6 +2841,10 @@ inline HostMessagesRawUnion::HostMessagesRawUnion(const HostMessagesRawUnion &u) value = new rpc::SignalUpdateRawT(*reinterpret_cast<rpc::SignalUpdateRawT *>(u.value)); break; } + case HostMessagesRaw::StartLeakChecks: { + value = new rpc::StartLeakChecksRawT(*reinterpret_cast<rpc::StartLeakChecksRawT *>(u.value)); + break; + } default: break; } @@ -2754,6 +2862,11 @@ inline void HostMessagesRawUnion::Reset() { delete ptr; break; } + case HostMessagesRaw::StartLeakChecks: { + auto ptr = reinterpret_cast<rpc::StartLeakChecksRawT *>(value); + delete ptr; + break; + } default: break; } value = nullptr; diff --git a/pkg/flatrpc/helpers.go b/pkg/flatrpc/helpers.go index 4ee5ad76b..22dc893fe 100644 --- a/pkg/flatrpc/helpers.go +++ b/pkg/flatrpc/helpers.go @@ -26,6 +26,7 @@ type ExecutorMessages = ExecutorMessagesRawT type ExecutorMessage = ExecutorMessageRawT type ExecRequest = ExecRequestRawT type SignalUpdate = SignalUpdateRawT +type StartLeakChecks = StartLeakChecksRawT type ExecutingMessage = ExecutingMessageRawT type CallInfo = CallInfoRawT type Comparison = ComparisonRawT diff --git a/syz-fuzzer/fuzzer.go b/syz-fuzzer/fuzzer.go index 78e918447..4f1920224 100644 --- a/syz-fuzzer/fuzzer.go +++ b/syz-fuzzer/fuzzer.go @@ -33,13 +33,12 @@ import ( ) type FuzzerTool struct { - conn *flatrpc.Conn - executor string - gate *ipc.Gate - // TODO: repair triagedCandidates logic, it's broken now. - triagedCandidates uint32 - timeouts targets.Timeouts - leakFrames []string + conn *flatrpc.Conn + executor string + gate *ipc.Gate + checkLeaks atomic.Int32 + timeouts targets.Timeouts + leakFrames []string requests chan *flatrpc.ExecRequest signalMu sync.RWMutex @@ -181,25 +180,25 @@ func main() { func (tool *FuzzerTool) leakGateCallback() { // Leak checking is very slow so we don't do it while triaging the corpus // (otherwise it takes infinity). When we have presumably triaged the corpus - // (triagedCandidates == 1), we run leak checking bug ignore the result - // to flush any previous leaks. After that (triagedCandidates == 2) + // (checkLeaks == 1), we run leak checking bug ignore the result + // to flush any previous leaks. After that (checkLeaks == 2) // we do actual leak checking and report leaks. - triagedCandidates := atomic.LoadUint32(&tool.triagedCandidates) - if triagedCandidates == 0 { + checkLeaks := tool.checkLeaks.Load() + if checkLeaks == 0 { return } args := append([]string{"leak"}, tool.leakFrames...) timeout := tool.timeouts.NoOutput * 9 / 10 output, err := osutil.RunCmd(timeout, "", tool.executor, args...) - if err != nil && triagedCandidates == 2 { + if err != nil && checkLeaks == 2 { // If we exit right away, dying executors will dump lots of garbage to console. os.Stdout.Write(output) fmt.Printf("BUG: leak checking failed\n") time.Sleep(time.Hour) os.Exit(1) } - if triagedCandidates == 1 { - atomic.StoreUint32(&tool.triagedCandidates, 2) + if checkLeaks == 1 { + tool.checkLeaks.Store(2) } } @@ -245,6 +244,8 @@ func (tool *FuzzerTool) handleConn() { tool.requests <- msg case *flatrpc.SignalUpdate: tool.handleSignalUpdate(msg) + case *flatrpc.StartLeakChecks: + tool.checkLeaks.Store(1) } } } diff --git a/syz-manager/manager.go b/syz-manager/manager.go index 0d984de2d..23f58d9d5 100644 --- a/syz-manager/manager.go +++ b/syz-manager/manager.go @@ -1508,6 +1508,9 @@ func (mgr *Manager) fuzzerLoop(fuzzer *fuzzer.Fuzzer) { if fuzzer.StatCandidates.Val() == 0 { mgr.mu.Lock() if mgr.phase == phaseLoadedCorpus { + if mgr.enabledFeatures&flatrpc.FeatureLeak != 0 { + mgr.serv.startLeakChecking() + } go mgr.fuzzerSignalRotation() if mgr.cfg.HubClient != "" { mgr.phase = phaseTriagedCorpus diff --git a/syz-manager/rpc.go b/syz-manager/rpc.go index fdbdc7bb4..e1edf15e9 100644 --- a/syz-manager/rpc.go +++ b/syz-manager/rpc.go @@ -48,6 +48,7 @@ type RPCServer struct { mu sync.Mutex runners map[string]*Runner execSource queue.Source + checkLeaks bool statNumFuzzing *stats.Val statExecs *stats.Val @@ -145,8 +146,16 @@ func (serv *RPCServer) handleConn(conn *flatrpc.Conn) { runner.conn = conn runner.machineInfo = machineInfo runner.canonicalizer = canonicalizer + checkLeaks := serv.checkLeaks serv.mu.Unlock() + if checkLeaks { + if err := runner.sendStartLeakChecks(); err != nil { + log.Logf(2, "%v", err) + return + } + } + err = serv.connectionLoop(runner) log.Logf(2, "runner %v: %v", name, err) @@ -567,6 +576,27 @@ func (runner *Runner) sendSignalUpdate(plus, minus []uint64) error { return flatrpc.Send(runner.conn, msg) } +func (serv *RPCServer) startLeakChecking() { + serv.mu.Lock() + defer serv.mu.Unlock() + serv.checkLeaks = true + for _, runner := range serv.runners { + if runner.conn != nil { + runner.sendStartLeakChecks() + } + } +} + +func (runner *Runner) sendStartLeakChecks() error { + msg := &flatrpc.HostMessage{ + Msg: &flatrpc.HostMessages{ + Type: flatrpc.HostMessagesRawStartLeakChecks, + Value: &flatrpc.StartLeakChecks{}, + }, + } + return flatrpc.Send(runner.conn, msg) +} + func (serv *RPCServer) updateCoverFilter(newCover []uint64) { if len(newCover) == 0 || serv.coverFilter == nil { return |
