From c5b6fcddca80153daba328334bc48caa3a546ab5 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Thu, 29 Jun 2017 16:01:03 +0200 Subject: syz-hub: split client name and manager name This allows to reduce number of hub clients by grouping managers and creating one client per such group. --- Makefile | 5 ++- docs/connecting_several_managers.md | 28 ---------------- docs/hub.md | 33 +++++++++++++++++++ pkg/rpctype/rpctype.go | 30 ++++++++++++----- syz-hub/hub.go | 64 +++++++++++++++++++------------------ syz-manager/manager.go | 21 ++++++------ syz-manager/mgrconfig/mgrconfig.go | 16 ++++------ 7 files changed, 111 insertions(+), 86 deletions(-) delete mode 100644 docs/connecting_several_managers.md create mode 100644 docs/hub.md diff --git a/Makefile b/Makefile index 9ff948f78..9b719780b 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ ifeq ($(NOSTATIC), 0) STATIC_FLAG=-static endif -.PHONY: all format tidy clean manager fuzzer executor execprog ci mutate prog2c stress extract generate repro db bin/syz-extract bin/syz-sysgen android +.PHONY: all format tidy clean manager fuzzer executor execprog ci hub mutate prog2c stress extract generate repro db bin/syz-extract bin/syz-sysgen android all: go install ./syz-manager ./syz-fuzzer @@ -39,6 +39,9 @@ execprog: ci: go build $(GOFLAGS) -o ./bin/syz-ci github.com/google/syzkaller/syz-ci +hub: + go build $(GOFLAGS) -o ./bin/syz-hub github.com/google/syzkaller/syz-hub + repro: go build $(GOFLAGS) -o ./bin/syz-repro github.com/google/syzkaller/tools/syz-repro diff --git a/docs/connecting_several_managers.md b/docs/connecting_several_managers.md deleted file mode 100644 index 9925bbfb6..000000000 --- a/docs/connecting_several_managers.md +++ /dev/null @@ -1,28 +0,0 @@ -# How to connect several managers via Hub - -`syz-hub` program can be used to connect several `syz-manager`'s together and allow them to exchange programs. - -Build `syz-hub` with `go install github.com/google/syzkaller/syz-hub`. Then create a config file along the lines of: - -``` -{ - "http": ":80", - "rpc": ":55555", - "workdir": "/syzkaller/workdir", - "managers": [ - {"name": "manager1", "key": "6sCFsJVfyFQVhWVKJpKhHcHxpCH0gAxL"}, - {"name": "manager2", "key": "FZFSjthHHf8nKm2cqqAcAYKM5a3XM4Ao"}, - {"name": "manager3", "key": "fTrIBQCmkEq8NsvQXZiOUyop6uWLBuzf"} - ] -} -``` - -And start it with `$GOPATH/syz-hub -config hub.cfg`. Then add the following additional parameters to `syz-manager` config files of each manager: - -``` - "name": "manager1", - "hub_addr": "1.2.3.4:55555", - "hub_key": "6sCFsJVfyFQVhWVKJpKhHcHxpCH0gAxL", -``` - -And start managers. Once they triage local corpus, they will connect to the hub and start exchanging inputs. Both hub and manager web pages will show how many inputs they send/receive from the hub. diff --git a/docs/hub.md b/docs/hub.md new file mode 100644 index 000000000..7b7cb3f8c --- /dev/null +++ b/docs/hub.md @@ -0,0 +1,33 @@ +# Connecting several managers via Hub + +`syz-hub` program can be used to connect several `syz-manager`'s together and +allow them to exchange programs. + +Build `syz-hub` with `make hub`. Then create a config file along the lines of: + +``` +{ + "http": ":80", + "rpc": ":55555", + "workdir": "/syzkaller/workdir", + "clients": [ + {"name": "manager1", "key": "6sCFsJVfyFQVhWVKJpKhHcHxpCH0gAxL"}, + {"name": "manager2", "key": "FZFSjthHHf8nKm2cqqAcAYKM5a3XM4Ao"}, + {"name": "manager3", "key": "fTrIBQCmkEq8NsvQXZiOUyop6uWLBuzf"} + ] +} +``` + +And start it with `bin/syz-hub -config hub.cfg`. Then add the following +additional parameters to `syz-manager` config files of each manager: + +``` + "name": "manager1", + "hub_client": "manager1", + "hub_addr": "1.2.3.4:55555", + "hub_key": "6sCFsJVfyFQVhWVKJpKhHcHxpCH0gAxL", +``` + +And start managers. Once they triage local corpus, they will connect to the hub +and start exchanging inputs. Both hub and manager web pages will show how many +inputs they send/receive from the hub. diff --git a/pkg/rpctype/rpctype.go b/pkg/rpctype/rpctype.go index 8b251b717..ab3f2243a 100644 --- a/pkg/rpctype/rpctype.go +++ b/pkg/rpctype/rpctype.go @@ -58,21 +58,35 @@ type PollRes struct { } type HubConnectArgs struct { - Name string + // Client/Key are used for authentication. + Client string Key string - Fresh bool - Calls []string + // Manager name, must start with Client. + Manager string + // Manager has started with an empty corpus and requests whole hub corpus. + Fresh bool + // Set of system call names supported by this manager. + // Used to filter out programs with unsupported calls. + Calls []string + // Current manager corpus. Corpus [][]byte } type HubSyncArgs struct { - Name string - Key string - Add [][]byte - Del []string + // see HubConnectArgs. + Client string + Key string + Manager string + // Programs added to corpus since last sync or connect. + Add [][]byte + // Hashed of programs removed from corpus since last sync or connect. + Del []string } type HubSyncRes struct { + // Set of programs from other managers. Inputs [][]byte - More int + // Number of remaining pending programs, + // if >0 manager should do sync again. + More int } diff --git a/syz-hub/hub.go b/syz-hub/hub.go index 410660f9f..6e44c51cd 100644 --- a/syz-hub/hub.go +++ b/syz-hub/hub.go @@ -4,12 +4,12 @@ package main import ( - "encoding/json" "flag" "fmt" - "io/ioutil" + "strings" "sync" + "github.com/google/syzkaller/pkg/config" . "github.com/google/syzkaller/pkg/log" . "github.com/google/syzkaller/pkg/rpctype" "github.com/google/syzkaller/syz-hub/state" @@ -17,15 +17,13 @@ import ( var ( flagConfig = flag.String("config", "", "config file") - - cfg *Config ) type Config struct { - Http string - Rpc string - Workdir string - Managers []struct { + Http string + Rpc string + Workdir string + Clients []struct { Name string Key string } @@ -39,7 +37,10 @@ type Hub struct { func main() { flag.Parse() - cfg = readConfig(*flagConfig) + cfg := new(Config) + if err := config.LoadFile(*flagConfig, cfg); err != nil { + Fatal(err) + } EnableLogCaching(1000, 1<<20) st, err := state.Make(cfg.Workdir) @@ -50,7 +51,7 @@ func main() { st: st, keys: make(map[string]string), } - for _, mgr := range cfg.Managers { + for _, mgr := range cfg.Clients { hub.keys[mgr.Name] = mgr.Key } @@ -65,15 +66,16 @@ func main() { } func (hub *Hub) Connect(a *HubConnectArgs, r *int) error { - if key, ok := hub.keys[a.Name]; !ok || key != a.Key { - Logf(0, "connect from unauthorized manager %v", a.Name) - return fmt.Errorf("unauthorized manager") + name, err := hub.auth(a.Client, a.Key, a.Manager) + if err != nil { + return err } hub.mu.Lock() defer hub.mu.Unlock() - Logf(0, "connect from %v: fresh=%v calls=%v corpus=%v", a.Name, a.Fresh, len(a.Calls), len(a.Corpus)) - if err := hub.st.Connect(a.Name, a.Fresh, a.Calls, a.Corpus); err != nil { + Logf(0, "connect from %v: fresh=%v calls=%v corpus=%v", + name, a.Fresh, len(a.Calls), len(a.Corpus)) + if err := hub.st.Connect(name, a.Fresh, a.Calls, a.Corpus); err != nil { Logf(0, "connect error: %v", err) return err } @@ -81,35 +83,35 @@ func (hub *Hub) Connect(a *HubConnectArgs, r *int) error { } func (hub *Hub) Sync(a *HubSyncArgs, r *HubSyncRes) error { - if key, ok := hub.keys[a.Name]; !ok || key != a.Key { - Logf(0, "sync from unauthorized manager %v", a.Name) - return fmt.Errorf("unauthorized manager") + name, err := hub.auth(a.Client, a.Key, a.Manager) + if err != nil { + return err } hub.mu.Lock() defer hub.mu.Unlock() - inputs, more, err := hub.st.Sync(a.Name, a.Add, a.Del) + inputs, more, err := hub.st.Sync(name, a.Add, a.Del) if err != nil { Logf(0, "sync error: %v", err) return err } r.Inputs = inputs r.More = more - Logf(0, "sync from %v: add=%v del=%v new=%v pending=%v", a.Name, len(a.Add), len(a.Del), len(inputs), more) + Logf(0, "sync from %v: add=%v del=%v new=%v pending=%v", + name, len(a.Add), len(a.Del), len(inputs), more) return nil } -func readConfig(filename string) *Config { - if filename == "" { - Fatalf("supply config in -config flag") +func (hub *Hub) auth(client, key, manager string) (string, error) { + if key, ok := hub.keys[client]; !ok || key != key { + Logf(0, "connect from unauthorized client %v", client) + return "", fmt.Errorf("unauthorized manager") } - data, err := ioutil.ReadFile(filename) - if err != nil { - Fatalf("failed to read config file: %v", err) - } - cfg := new(Config) - if err := json.Unmarshal(data, cfg); err != nil { - Fatalf("failed to parse config file: %v", err) + if manager == "" { + manager = client + } else if !strings.HasPrefix(manager, client) { + Logf(0, "manager %v does not have client prefix %v", manager, client) + return "", fmt.Errorf("unauthorized manager") } - return cfg + return manager, nil } diff --git a/syz-manager/manager.go b/syz-manager/manager.go index 0b4ae3dc2..68e4a4348 100644 --- a/syz-manager/manager.go +++ b/syz-manager/manager.go @@ -280,7 +280,7 @@ func RunManager(cfg *mgrconfig.Config, syscalls map[int]bool) { }() } - if mgr.cfg.Hub_Addr != "" { + if mgr.cfg.Hub_Client != "" { go func() { for { time.Sleep(time.Minute) @@ -858,7 +858,7 @@ func (mgr *Manager) Poll(a *PollArgs, r *PollRes) error { if len(mgr.candidates) == 0 { mgr.candidates = nil if mgr.phase == phaseInit { - if mgr.cfg.Hub_Addr != "" { + if mgr.cfg.Hub_Client != "" { mgr.phase = phaseTriagedCorpus } else { mgr.phase = phaseTriagedHub @@ -891,10 +891,11 @@ func (mgr *Manager) hubSync() { mgr.minimizeCorpus() if mgr.hub == nil { a := &HubConnectArgs{ - Name: mgr.cfg.Name, - Key: mgr.cfg.Hub_Key, - Fresh: mgr.fresh, - Calls: mgr.enabledCalls, + Client: mgr.cfg.Hub_Client, + Key: mgr.cfg.Hub_Key, + Manager: mgr.cfg.Name, + Fresh: mgr.fresh, + Calls: mgr.enabledCalls, } hubCorpus := make(map[hash.Sig]bool) for _, inp := range mgr.corpus { @@ -924,8 +925,9 @@ func (mgr *Manager) hubSync() { } a := &HubSyncArgs{ - Name: mgr.cfg.Name, - Key: mgr.cfg.Hub_Key, + Client: mgr.cfg.Hub_Client, + Key: mgr.cfg.Hub_Key, + Manager: mgr.cfg.Name, } corpus := make(map[hash.Sig]bool) for _, inp := range mgr.corpus { @@ -971,7 +973,8 @@ func (mgr *Manager) hubSync() { mgr.stats["hub del"] += uint64(len(a.Del)) mgr.stats["hub drop"] += uint64(dropped) mgr.stats["hub new"] += uint64(len(r.Inputs) - dropped) - Logf(0, "hub sync: add %v, del %v, drop %v, new %v, more %v", len(a.Add), len(a.Del), dropped, len(r.Inputs)-dropped, r.More) + Logf(0, "hub sync: add %v, del %v, drop %v, new %v, more %v", + len(a.Add), len(a.Del), dropped, len(r.Inputs)-dropped, r.More) if len(r.Inputs)+r.More == 0 { break } diff --git a/syz-manager/mgrconfig/mgrconfig.go b/syz-manager/mgrconfig/mgrconfig.go index d6e2b4a2e..7a7aad74f 100644 --- a/syz-manager/mgrconfig/mgrconfig.go +++ b/syz-manager/mgrconfig/mgrconfig.go @@ -26,8 +26,9 @@ type Config struct { Image string // linux image for VMs Sshkey string // root ssh key for the image (may be empty for some VM types) - Hub_Addr string - Hub_Key string + Hub_Client string + Hub_Addr string + Hub_Key string Dashboard_Addr string Dashboard_Key string @@ -126,14 +127,11 @@ func load(data []byte, filename string) (*Config, map[int]bool, error) { return nil, nil, err } - if (cfg.Hub_Addr != "" || cfg.Dashboard_Addr != "") && cfg.Name == "" { - return nil, nil, fmt.Errorf("hub_addr//dashboard_addr is set, but name is empty") + if cfg.Hub_Client != "" && (cfg.Name == "" || cfg.Hub_Addr == "" || cfg.Hub_Key == "") { + return nil, nil, fmt.Errorf("hub_client is set, but name/hub_addr/hub_key is empty") } - if cfg.Hub_Addr != "" && cfg.Hub_Key == "" { - return nil, nil, fmt.Errorf("hub_addr is set, but hub_key is empty") - } - if cfg.Dashboard_Addr != "" && cfg.Dashboard_Key == "" { - return nil, nil, fmt.Errorf("dashboard_addr is set, but dashboard_key is empty") + if cfg.Dashboard_Addr != "" && (cfg.Name == "" || cfg.Dashboard_Key == "") { + return nil, nil, fmt.Errorf("dashboard_addr is set, but name/dashboard_key is empty") } return cfg, syscalls, nil -- cgit mrf-deployment