github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/pkg/corpus/corpus.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 corpus 5 6 import ( 7 "context" 8 "sync" 9 10 "github.com/google/syzkaller/pkg/cover" 11 "github.com/google/syzkaller/pkg/hash" 12 "github.com/google/syzkaller/pkg/signal" 13 "github.com/google/syzkaller/pkg/stats" 14 "github.com/google/syzkaller/prog" 15 ) 16 17 // Corpus object represents a set of syzkaller-found programs that 18 // cover the kernel up to the currently reached frontiers. 19 type Corpus struct { 20 ctx context.Context 21 mu sync.RWMutex 22 progs map[string]*Item 23 signal signal.Signal // total signal of all items 24 cover cover.Cover // total coverage of all items 25 updates chan<- NewItemEvent 26 *ProgramsList 27 StatProgs *stats.Val 28 StatSignal *stats.Val 29 StatCover *stats.Val 30 } 31 32 func NewCorpus(ctx context.Context) *Corpus { 33 return NewMonitoredCorpus(ctx, nil) 34 } 35 36 func NewMonitoredCorpus(ctx context.Context, updates chan<- NewItemEvent) *Corpus { 37 corpus := &Corpus{ 38 ctx: ctx, 39 progs: make(map[string]*Item), 40 updates: updates, 41 ProgramsList: &ProgramsList{}, 42 } 43 corpus.StatProgs = stats.Create("corpus", "Number of test programs in the corpus", stats.Console, 44 stats.Link("/corpus"), stats.Graph("corpus"), stats.LenOf(&corpus.progs, &corpus.mu)) 45 corpus.StatSignal = stats.Create("signal", "Fuzzing signal in the corpus", 46 stats.LenOf(&corpus.signal, &corpus.mu)) 47 corpus.StatCover = stats.Create("coverage", "Source coverage in the corpus", stats.Console, 48 stats.Link("/cover"), stats.Prometheus("syz_corpus_cover"), stats.LenOf(&corpus.cover, &corpus.mu)) 49 return corpus 50 } 51 52 // It may happen that a single program is relevant because of several 53 // sysalls. In that case, there will be several ItemUpdate entities. 54 type ItemUpdate struct { 55 Call int 56 RawCover []uint32 57 } 58 59 // Item objects are to be treated as immutable, otherwise it's just 60 // too hard to synchonize accesses to them across the whole project. 61 // When Corpus updates one of its items, it saves a copy of it. 62 type Item struct { 63 Sig string 64 Call int 65 Prog *prog.Prog 66 ProgData []byte // to save some Serialize() calls 67 HasAny bool // whether the prog contains squashed arguments 68 Signal signal.Signal 69 Cover []uint32 70 Updates []ItemUpdate 71 } 72 73 func (item Item) StringCall() string { 74 return item.Prog.CallName(item.Call) 75 } 76 77 type NewInput struct { 78 Prog *prog.Prog 79 Call int 80 Signal signal.Signal 81 Cover []uint32 82 RawCover []uint32 83 } 84 85 type NewItemEvent struct { 86 Sig string 87 Exists bool 88 ProgData []byte 89 NewCover []uint32 90 } 91 92 func (corpus *Corpus) Save(inp NewInput) { 93 progData := inp.Prog.Serialize() 94 sig := hash.String(progData) 95 96 corpus.mu.Lock() 97 defer corpus.mu.Unlock() 98 99 update := ItemUpdate{ 100 Call: inp.Call, 101 RawCover: inp.RawCover, 102 } 103 exists := false 104 if old, ok := corpus.progs[sig]; ok { 105 exists = true 106 newSignal := old.Signal.Copy() 107 newSignal.Merge(inp.Signal) 108 var newCover cover.Cover 109 newCover.Merge(old.Cover) 110 newCover.Merge(inp.Cover) 111 newItem := &Item{ 112 Sig: sig, 113 Prog: old.Prog, 114 ProgData: progData, 115 Call: old.Call, 116 HasAny: old.HasAny, 117 Signal: newSignal, 118 Cover: newCover.Serialize(), 119 Updates: append([]ItemUpdate{}, old.Updates...), 120 } 121 const maxUpdates = 32 122 if len(newItem.Updates) < maxUpdates { 123 newItem.Updates = append(newItem.Updates, update) 124 } 125 corpus.progs[sig] = newItem 126 } else { 127 corpus.progs[sig] = &Item{ 128 Sig: sig, 129 Call: inp.Call, 130 Prog: inp.Prog, 131 ProgData: progData, 132 HasAny: inp.Prog.ContainsAny(), 133 Signal: inp.Signal, 134 Cover: inp.Cover, 135 Updates: []ItemUpdate{update}, 136 } 137 corpus.saveProgram(inp.Prog, inp.Signal) 138 } 139 corpus.signal.Merge(inp.Signal) 140 newCover := corpus.cover.MergeDiff(inp.Cover) 141 if corpus.updates != nil { 142 select { 143 case <-corpus.ctx.Done(): 144 case corpus.updates <- NewItemEvent{ 145 Sig: sig, 146 Exists: exists, 147 ProgData: progData, 148 NewCover: newCover, 149 }: 150 } 151 } 152 } 153 func (corpus *Corpus) Signal() signal.Signal { 154 corpus.mu.RLock() 155 defer corpus.mu.RUnlock() 156 return corpus.signal.Copy() 157 } 158 159 func (corpus *Corpus) Items() []*Item { 160 corpus.mu.RLock() 161 defer corpus.mu.RUnlock() 162 ret := make([]*Item, 0, len(corpus.progs)) 163 for _, item := range corpus.progs { 164 ret = append(ret, item) 165 } 166 return ret 167 } 168 169 func (corpus *Corpus) Item(sig string) *Item { 170 corpus.mu.RLock() 171 defer corpus.mu.RUnlock() 172 return corpus.progs[sig] 173 } 174 175 type CallCov struct { 176 Count int 177 Cover cover.Cover 178 } 179 180 func (corpus *Corpus) CallCover() map[string]*CallCov { 181 corpus.mu.RLock() 182 defer corpus.mu.RUnlock() 183 calls := make(map[string]*CallCov) 184 for _, inp := range corpus.progs { 185 call := inp.StringCall() 186 if calls[call] == nil { 187 calls[call] = new(CallCov) 188 } 189 cc := calls[call] 190 cc.Count++ 191 cc.Cover.Merge(inp.Cover) 192 } 193 return calls 194 }