Skip to content

Commit deb7287

Browse files
FlorentRevesta-nogikh
authored andcommitted
prog: annotate image assets with fsck logs
Syscall attributes are extended with a fsck command field which lets file system mount definitions specify a fsck-like command to run. This is required because all file systems have a custom fsck command invokation style. When uploading a compressed image asset to the dashboard, syz-manager also runs the fsck command and logs its output over the dashapi. The dashboard logs these fsck logs into the database. This has been requested by fs maintainer Ted Tso who would like to quickly understand whether a filesystem is corrupted or not before looking at a reproducer in more details. Ultimately, this could be used as an early triage sign to determine whether a bug is obviously critical.
1 parent 07e46fb commit deb7287

File tree

21 files changed

+263
-37
lines changed

21 files changed

+263
-37
lines changed

dashboard/app/api.go

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -491,7 +491,7 @@ func uploadBuild(c context.Context, now time.Time, ns string, req *dashapi.Build
491491
*Build, bool, error) {
492492
newAssets := []Asset{}
493493
for i, toAdd := range req.Assets {
494-
newAsset, err := parseIncomingAsset(c, toAdd)
494+
newAsset, err := parseIncomingAsset(c, toAdd, ns)
495495
if err != nil {
496496
return nil, false, fmt.Errorf("failed to parse asset #%d: %w", i, err)
497497
}
@@ -783,7 +783,8 @@ func apiReportCrash(c context.Context, ns string, r *http.Request, payload []byt
783783

784784
// nolint: gocyclo
785785
func reportCrash(c context.Context, build *Build, req *dashapi.Crash) (*Bug, error) {
786-
assets, err := parseCrashAssets(c, req)
786+
ns := build.Namespace
787+
assets, err := parseCrashAssets(c, req, ns)
787788
if err != nil {
788789
return nil, err
789790
}
@@ -798,7 +799,6 @@ func reportCrash(c context.Context, build *Build, req *dashapi.Crash) (*Bug, err
798799
}
799800
req.Maintainers = email.MergeEmailLists(req.Maintainers)
800801

801-
ns := build.Namespace
802802
bug, err := findBugForCrash(c, ns, req.AltTitles)
803803
if err != nil {
804804
return nil, fmt.Errorf("failed to find bug for the crash: %w", err)
@@ -895,10 +895,10 @@ func reportCrash(c context.Context, build *Build, req *dashapi.Crash) (*Bug, err
895895
return bug, nil
896896
}
897897

898-
func parseCrashAssets(c context.Context, req *dashapi.Crash) ([]Asset, error) {
898+
func parseCrashAssets(c context.Context, req *dashapi.Crash, ns string) ([]Asset, error) {
899899
assets := []Asset{}
900900
for i, toAdd := range req.Assets {
901-
newAsset, err := parseIncomingAsset(c, toAdd)
901+
newAsset, err := parseIncomingAsset(c, toAdd, ns)
902902
if err != nil {
903903
return nil, fmt.Errorf("failed to parse asset #%d: %w", i, err)
904904
}
@@ -1309,7 +1309,7 @@ func apiAddBuildAssets(c context.Context, ns string, r *http.Request, payload []
13091309
}
13101310
assets := []Asset{}
13111311
for i, toAdd := range req.Assets {
1312-
asset, err := parseIncomingAsset(c, toAdd)
1312+
asset, err := parseIncomingAsset(c, toAdd, ns)
13131313
if err != nil {
13141314
return nil, fmt.Errorf("failed to parse asset #%d: %w", i, err)
13151315
}
@@ -1322,7 +1322,7 @@ func apiAddBuildAssets(c context.Context, ns string, r *http.Request, payload []
13221322
return nil, nil
13231323
}
13241324

1325-
func parseIncomingAsset(c context.Context, newAsset dashapi.NewAsset) (Asset, error) {
1325+
func parseIncomingAsset(c context.Context, newAsset dashapi.NewAsset, ns string) (Asset, error) {
13261326
typeInfo := asset.GetTypeDescription(newAsset.Type)
13271327
if typeInfo == nil {
13281328
return Asset{}, fmt.Errorf("unknown asset type")
@@ -1331,10 +1331,19 @@ func parseIncomingAsset(c context.Context, newAsset dashapi.NewAsset) (Asset, er
13311331
if err != nil {
13321332
return Asset{}, fmt.Errorf("invalid URL: %w", err)
13331333
}
1334+
fsckLog := int64(0)
1335+
if len(newAsset.FsckLog) > 0 {
1336+
fsckLog, err = putText(c, ns, textFsckLog, newAsset.FsckLog)
1337+
if err != nil {
1338+
return Asset{}, err
1339+
}
1340+
}
13341341
return Asset{
13351342
Type: newAsset.Type,
13361343
DownloadURL: newAsset.DownloadURL,
13371344
CreateDate: timeNow(c),
1345+
FsckLog: fsckLog,
1346+
FsIsClean: newAsset.FsIsClean,
13381347
}, nil
13391348
}
13401349

dashboard/app/asset_storage.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,7 @@ func queryLatestManagerAssets(c context.Context, ns string, assetType dashapi.As
496496
return ret, nil
497497
}
498498

499-
func createAssetList(build *Build, crash *Crash, forReport bool) []dashapi.Asset {
499+
func createAssetList(c context.Context, build *Build, crash *Crash, forReport bool) []dashapi.Asset {
500500
var crashAssets []Asset
501501
if crash != nil {
502502
crashAssets = crash.Assets
@@ -507,11 +507,16 @@ func createAssetList(build *Build, crash *Crash, forReport bool) []dashapi.Asset
507507
if typeDescr == nil || forReport && typeDescr.NoReporting {
508508
continue
509509
}
510-
assetList = append(assetList, dashapi.Asset{
510+
newAsset := dashapi.Asset{
511511
Title: typeDescr.GetTitle(targets.Get(build.OS, build.Arch)),
512512
DownloadURL: reportAsset.DownloadURL,
513513
Type: reportAsset.Type,
514-
})
514+
}
515+
if reportAsset.FsckLog != 0 {
516+
newAsset.FsckLogURL = externalLink(c, textFsckLog, reportAsset.FsckLog)
517+
newAsset.FsIsClean = reportAsset.FsIsClean
518+
}
519+
assetList = append(assetList, newAsset)
515520
}
516521
sort.SliceStable(assetList, func(i, j int) bool {
517522
return asset.GetTypeDescription(assetList[i].Type).ReportingPrio <

dashboard/app/entities_datastore.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ type Asset struct {
5959
Type dashapi.AssetType
6060
DownloadURL string
6161
CreateDate time.Time
62+
FsckLog int64 // references to fsck logstext entity - 0 if fsck wasn't run
63+
FsIsClean bool // undefined value if FsckLog is 0
6264
}
6365

6466
type Build struct {
@@ -666,6 +668,7 @@ const (
666668
textLog = "Log"
667669
textError = "Error"
668670
textReproLog = "ReproLog"
671+
textFsckLog = "FsckLog"
669672
)
670673

671674
const (

dashboard/app/main.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,8 @@ type uiCrash struct {
405405
type uiAsset struct {
406406
Title string
407407
DownloadURL string
408+
FsckLogURL string
409+
FsIsClean bool
408410
}
409411

410412
type uiCrashTable struct {
@@ -2047,12 +2049,14 @@ func linkifyReport(report []byte, repo, commit string) template.HTML {
20472049

20482050
var sourceFileRe = regexp.MustCompile("( |\t|\n)([a-zA-Z0-9/_.-]+\\.(?:h|c|cc|cpp|s|S|go|rs)):([0-9]+)( |!|\\)|\t|\n)")
20492051

2050-
func makeUIAssets(build *Build, crash *Crash, forReport bool) []*uiAsset {
2052+
func makeUIAssets(c context.Context, build *Build, crash *Crash, forReport bool) []*uiAsset {
20512053
var uiAssets []*uiAsset
2052-
for _, asset := range createAssetList(build, crash, forReport) {
2054+
for _, asset := range createAssetList(c, build, crash, forReport) {
20532055
uiAssets = append(uiAssets, &uiAsset{
20542056
Title: asset.Title,
20552057
DownloadURL: asset.DownloadURL,
2058+
FsckLogURL: asset.FsckLogURL,
2059+
FsIsClean: asset.FsIsClean,
20562060
})
20572061
}
20582062
return uiAssets
@@ -2072,7 +2076,7 @@ func makeUICrash(c context.Context, crash *Crash, build *Build) *uiCrash {
20722076
ReproLogLink: textLink(textReproLog, crash.ReproLog),
20732077
ReproIsRevoked: crash.ReproIsRevoked,
20742078
MachineInfoLink: textLink(textMachineInfo, crash.MachineInfo),
2075-
Assets: makeUIAssets(build, crash, true),
2079+
Assets: makeUIAssets(c, build, crash, true),
20762080
}
20772081
if build != nil {
20782082
ui.uiBuild = makeUIBuild(c, build, true)
@@ -2094,7 +2098,7 @@ func makeUIBuild(c context.Context, build *Build, forReport bool) *uiBuild {
20942098
KernelCommitTitle: build.KernelCommitTitle,
20952099
KernelCommitDate: build.KernelCommitDate,
20962100
KernelConfigLink: textLink(textKernelConfig, build.KernelConfig),
2097-
Assets: makeUIAssets(build, nil, forReport),
2101+
Assets: makeUIAssets(c, build, nil, forReport),
20982102
}
20992103
}
21002104

dashboard/app/reporting.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -549,7 +549,7 @@ func crashBugReport(c context.Context, bug *Bug, crash *Crash, crashKey *db.Key,
549549
if !bugReporting.Reported.IsZero() {
550550
typ = dashapi.ReportRepro
551551
}
552-
assetList := createAssetList(build, crash, true)
552+
assetList := createAssetList(c, build, crash, true)
553553
kernelRepo := kernelRepoInfo(c, build)
554554
rep := &dashapi.BugReport{
555555
Type: typ,

dashboard/app/templates/templates.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,7 @@ <h1><a href="/{{$.Namespace}}">syzbot</a></h1>
460460
<td class="repro{{if $b.ReproIsRevoked}} stale_repro{{end}}">{{if $b.ReproCLink}}<a href="{{$b.ReproCLink}}">C</a>{{end}}</td>
461461
<td class="repro">{{if $b.MachineInfoLink}}<a href="{{$b.MachineInfoLink}}">info</a>{{end}}</td>
462462
<td class="assets">{{range $i, $asset := .Assets}}
463-
<span class="no-break">[<a href="{{$asset.DownloadURL}}">{{$asset.Title}}</a>]</span>
463+
<span class="no-break">[<a href="{{$asset.DownloadURL}}">{{$asset.Title}}</a>{{if $asset.FsckLogURL}} (<a href="{{$asset.FsckLogURL}}">{{if $asset.FsIsClean}}clean{{else}}corrupt{{end}} fs</a>){{end}}]</span>
464464
{{end}}</td>
465465
<td class="manager">{{$b.Manager}}</td>
466466
<td class="manager">{{$b.Title}}</td>

dashboard/dashapi/dashapi.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,8 @@ type Asset struct {
497497
Title string
498498
DownloadURL string
499499
Type AssetType
500+
FsckLogURL string
501+
FsIsClean bool
500502
}
501503

502504
type AssetType string
@@ -802,6 +804,8 @@ func (dash *Dashboard) UploadManagerStats(req *ManagerStatsReq) error {
802804
type NewAsset struct {
803805
DownloadURL string
804806
Type AssetType
807+
FsckLog []byte
808+
FsIsClean bool
805809
}
806810

807811
type AddBuildAssetsReq struct {

docs/syscall_descriptions_syntax.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ rest of the type-options are type-specific:
6767
value range start, how many values per process, underlying type
6868
"compressed_image": zlib-compressed disk image
6969
syscalls accepting compressed images must be marked with `no_generate`
70-
and `no_minimize` call attributes.
70+
and `no_minimize` call attributes. if the content of the decompressed image
71+
can be checked by a `fsck`-like command, use the `fsck` syscall attribute
7172
"text": machine code of the specified type, type-options:
7273
text type (x86_real, x86_16, x86_32, x86_64, arm64)
7374
"void": type with static size 0
@@ -101,6 +102,8 @@ Call attributes are:
101102
"breaks_returns": ignore return values of all subsequent calls in the program in fallback feedback (can't be trusted).
102103
"no_generate": do not try to generate this syscall, i.e. use only seed descriptions to produce it.
103104
"no_minimize": do not modify instances of this syscall when trying to minimize a crashing program.
105+
"fsck": the content of the compressed buffer argument for this syscall is a file system and the
106+
string argument is a fsck-like command that will be called to verify the filesystem
104107
"remote_cover": wait longer to collect remote coverage for this call.
105108
```
106109

pkg/compiler/testdata/all.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,10 @@ struct$fmt0 {
336336

337337
flags_with_one_value = 0
338338

339+
# Syscall attributes.
340+
341+
fsck_test() (fsck["fsck.test -n"])
342+
339343
# Compressed images.
340344

341345
struct_compressed {

pkg/compiler/testdata/errors.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,3 +507,5 @@ conditional_fields_union2 [
507507
u2 int32 ### either no fields have conditions or all except the last
508508
u3 int32
509509
]
510+
511+
invalid_string_attr() (invalid["string"]) ### unknown syscall invalid_string_attr attribute invalid

0 commit comments

Comments
 (0)