// Copyright 2017 syzkaller project authors. All rights reserved. // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. package main import ( "sync" "github.com/google/syzkaller/pkg/ipc" "github.com/google/syzkaller/prog" ) // WorkQueue holds global non-fuzzing work items (see the Work* structs below). // WorkQueue also does prioritization among work items, for example, we want // to triage and send to manager new inputs before we smash programs // in order to not permanently lose interesting programs in case of VM crash. type WorkQueue struct { mu sync.RWMutex triageCandidate []*WorkTriage candidate []*WorkCandidate triage []*WorkTriage smash []*WorkSmash procs int needCandidates chan struct{} } type ProgTypes int const ( ProgCandidate ProgTypes = 1 << iota ProgMinimized ProgSmashed ProgNormal ProgTypes = 0 ) // WorkTriage are programs for which we noticed potential new coverage during // first execution. But we are not sure yet if the coverage is real or not. // During triage we understand if these programs in fact give new coverage, // and if yes, minimize them and add to corpus. type WorkTriage struct { p *prog.Prog call int info ipc.CallInfo flags ProgTypes } // WorkCandidate are programs from hub. // We don't know yet if they are useful for this fuzzer or not. // A proc handles them the same way as locally generated/mutated programs. type WorkCandidate struct { p *prog.Prog flags ProgTypes } // WorkSmash are programs just added to corpus. // During smashing these programs receive a one-time special attention // (emit faults, collect comparison hints, etc). type WorkSmash struct { p *prog.Prog call int } func newWorkQueue(procs int, needCandidates chan struct{}) *WorkQueue { return &WorkQueue{ procs: procs, needCandidates: needCandidates, } } func (wq *WorkQueue) enqueue(item interface{}) { wq.mu.Lock() defer wq.mu.Unlock() switch item := item.(type) { case *WorkTriage: if item.flags&ProgCandidate != 0 { wq.triageCandidate = append(wq.triageCandidate, item) } else { wq.triage = append(wq.triage, item) } case *WorkCandidate: wq.candidate = append(wq.candidate, item) case *WorkSmash: wq.smash = append(wq.smash, item) default: panic("unknown work type") } } func (wq *WorkQueue) dequeue() (item interface{}) { wq.mu.RLock() if len(wq.triageCandidate)+len(wq.candidate)+len(wq.triage)+len(wq.smash) == 0 { wq.mu.RUnlock() return nil } wq.mu.RUnlock() wq.mu.Lock() wantCandidates := false if len(wq.triageCandidate) != 0 { last := len(wq.triageCandidate) - 1 item = wq.triageCandidate[last] wq.triageCandidate = wq.triageCandidate[:last] } else if len(wq.candidate) != 0 { last := len(wq.candidate) - 1 item = wq.candidate[last] wq.candidate = wq.candidate[:last] wantCandidates = len(wq.candidate) < wq.procs } else if len(wq.triage) != 0 { last := len(wq.triage) - 1 item = wq.triage[last] wq.triage = wq.triage[:last] } else if len(wq.smash) != 0 { last := len(wq.smash) - 1 item = wq.smash[last] wq.smash = wq.smash[:last] } wq.mu.Unlock() if wantCandidates { select { case wq.needCandidates <- struct{}{}: default: } } return item } func (wq *WorkQueue) wantCandidates() bool { wq.mu.RLock() defer wq.mu.RUnlock() return len(wq.candidate) < wq.procs }