github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/pkg/manager/diff_store.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 manager 5 6 import ( 7 "bytes" 8 "fmt" 9 "path/filepath" 10 "sort" 11 "sync" 12 "text/tabwriter" 13 "time" 14 15 "github.com/google/syzkaller/pkg/log" 16 "github.com/google/syzkaller/pkg/osutil" 17 ) 18 19 type DiffBug struct { 20 Title string 21 Base DiffBugInfo 22 Patched DiffBugInfo 23 } 24 25 func (bug DiffBug) PatchedOnly() bool { 26 return bug.Base.NotCrashed && bug.Patched.Crashes > 0 27 } 28 29 func (bug DiffBug) AffectsBoth() bool { 30 return bug.Base.Crashes > 0 && bug.Patched.Crashes > 0 31 } 32 33 type DiffBugInfo struct { 34 Crashes int // Count of detected crashes. 35 NotCrashed bool // If were proven not to crash by running a repro. 36 37 // File paths. 38 Report string 39 Repro string 40 ReproLog string 41 CrashLog string 42 } 43 44 // DiffFuzzerStore provides the functionality of a database of the patch fuzzing. 45 type DiffFuzzerStore struct { 46 BasePath string 47 48 mu sync.Mutex 49 bugs map[string]*DiffBug 50 } 51 52 func (s *DiffFuzzerStore) BaseCrashed(title string, report []byte) { 53 s.patch(title, func(obj *DiffBug) { 54 obj.Base.Crashes++ 55 if len(report) > 0 { 56 obj.Base.Report = s.saveFile(title, "base_report", report) 57 } 58 }) 59 } 60 61 func (s *DiffFuzzerStore) EverCrashedBase(title string) bool { 62 s.mu.Lock() 63 defer s.mu.Unlock() 64 obj := s.bugs[title] 65 return obj != nil && obj.Base.Crashes > 0 66 } 67 68 func (s *DiffFuzzerStore) BaseNotCrashed(title string) { 69 s.patch(title, func(obj *DiffBug) { 70 if obj.Base.Crashes == 0 { 71 obj.Base.NotCrashed = true 72 } 73 }) 74 } 75 76 func (s *DiffFuzzerStore) PatchedCrashed(title string, report, log []byte) { 77 s.patch(title, func(obj *DiffBug) { 78 obj.Patched.Crashes++ 79 if len(report) > 0 { 80 obj.Patched.Report = s.saveFile(title, "patched_report", report) 81 } 82 if len(log) > 0 && obj.Patched.CrashLog == "" { 83 obj.Patched.CrashLog = s.saveFile(title, "patched_crash_log", log) 84 } 85 }) 86 } 87 88 func (s *DiffFuzzerStore) SaveRepro(result *ReproResult) { 89 title := result.Crash.Report.Title 90 if result.Repro != nil { 91 // If there's a repro, save under the new title. 92 title = result.Repro.Report.Title 93 } 94 95 now := time.Now().Unix() 96 crashLog := fmt.Sprintf("%v.crash.log", now) 97 s.saveFile(title, crashLog, result.Crash.Output) 98 log.Logf(0, "%q: saved crash log into %s", title, crashLog) 99 100 s.patch(title, func(obj *DiffBug) { 101 if result.Repro != nil { 102 obj.Patched.Repro = s.saveFile(title, reproFileName, result.Repro.Prog.Serialize()) 103 } 104 if result.Stats != nil { 105 reproLog := fmt.Sprintf("%v.repro.log", now) 106 obj.Patched.ReproLog = s.saveFile(title, reproLog, result.Stats.FullLog()) 107 log.Logf(0, "%q: saved repro log into %s", title, reproLog) 108 } 109 }) 110 } 111 112 func (s *DiffFuzzerStore) List() []DiffBug { 113 s.mu.Lock() 114 defer s.mu.Unlock() 115 var list []DiffBug 116 for _, obj := range s.bugs { 117 list = append(list, *obj) 118 } 119 return list 120 } 121 122 func (s *DiffFuzzerStore) PlainTextDump() []byte { 123 list := s.List() 124 sort.Slice(list, func(i, j int) bool { 125 // Put patched-only on top, otherwise sort by the title. 126 first, second := list[i].PatchedOnly(), list[j].PatchedOnly() 127 if first != second { 128 return first 129 } 130 return list[i].Title < list[j].Title 131 }) 132 var buf bytes.Buffer 133 w := tabwriter.NewWriter(&buf, 0, 0, 1, ' ', 0) 134 fmt.Fprintln(w, "Title\tOn-Base\tOn-Patched") 135 136 printInfo := func(info *DiffBugInfo) { 137 if info.Crashes > 0 { 138 fmt.Fprintf(w, "%d crashes", info.Crashes) 139 } 140 if info.Repro != "" { 141 fmt.Fprintf(w, "[reproduced]") 142 } 143 } 144 145 for _, item := range list { 146 fmt.Fprintf(w, "%s\t", item.Title) 147 printInfo(&item.Base) 148 fmt.Fprintf(w, "\t") 149 printInfo(&item.Patched) 150 fmt.Fprintf(w, "\n") 151 } 152 w.Flush() 153 return buf.Bytes() 154 } 155 156 func (s *DiffFuzzerStore) saveFile(title, name string, data []byte) string { 157 hash := crashHash(title) 158 path := filepath.Join(s.BasePath, "crashes", hash) 159 osutil.MkdirAll(path) 160 osutil.WriteFile(filepath.Join(path, name), data) 161 return filepath.Join("crashes", hash, name) 162 } 163 164 func (s *DiffFuzzerStore) patch(title string, cb func(*DiffBug)) { 165 s.mu.Lock() 166 defer s.mu.Unlock() 167 if s.bugs == nil { 168 s.bugs = map[string]*DiffBug{} 169 } 170 obj, ok := s.bugs[title] 171 if !ok { 172 obj = &DiffBug{Title: title} 173 s.bugs[title] = obj 174 } 175 cb(obj) 176 }