Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 16 additions & 7 deletions dashboard/app/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,7 @@ func uploadBuild(c context.Context, now time.Time, ns string, req *dashapi.Build
*Build, bool, error) {
newAssets := []Asset{}
for i, toAdd := range req.Assets {
newAsset, err := parseIncomingAsset(c, toAdd)
newAsset, err := parseIncomingAsset(c, toAdd, ns)
if err != nil {
return nil, false, fmt.Errorf("failed to parse asset #%d: %w", i, err)
}
Expand Down Expand Up @@ -783,7 +783,8 @@ func apiReportCrash(c context.Context, ns string, r *http.Request, payload []byt

// nolint: gocyclo
func reportCrash(c context.Context, build *Build, req *dashapi.Crash) (*Bug, error) {
assets, err := parseCrashAssets(c, req)
ns := build.Namespace
assets, err := parseCrashAssets(c, req, ns)
if err != nil {
return nil, err
}
Expand All @@ -798,7 +799,6 @@ func reportCrash(c context.Context, build *Build, req *dashapi.Crash) (*Bug, err
}
req.Maintainers = email.MergeEmailLists(req.Maintainers)

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

func parseCrashAssets(c context.Context, req *dashapi.Crash) ([]Asset, error) {
func parseCrashAssets(c context.Context, req *dashapi.Crash, ns string) ([]Asset, error) {
assets := []Asset{}
for i, toAdd := range req.Assets {
newAsset, err := parseIncomingAsset(c, toAdd)
newAsset, err := parseIncomingAsset(c, toAdd, ns)
if err != nil {
return nil, fmt.Errorf("failed to parse asset #%d: %w", i, err)
}
Expand Down Expand Up @@ -1309,7 +1309,7 @@ func apiAddBuildAssets(c context.Context, ns string, r *http.Request, payload []
}
assets := []Asset{}
for i, toAdd := range req.Assets {
asset, err := parseIncomingAsset(c, toAdd)
asset, err := parseIncomingAsset(c, toAdd, ns)
if err != nil {
return nil, fmt.Errorf("failed to parse asset #%d: %w", i, err)
}
Expand All @@ -1322,7 +1322,7 @@ func apiAddBuildAssets(c context.Context, ns string, r *http.Request, payload []
return nil, nil
}

func parseIncomingAsset(c context.Context, newAsset dashapi.NewAsset) (Asset, error) {
func parseIncomingAsset(c context.Context, newAsset dashapi.NewAsset, ns string) (Asset, error) {
typeInfo := asset.GetTypeDescription(newAsset.Type)
if typeInfo == nil {
return Asset{}, fmt.Errorf("unknown asset type")
Expand All @@ -1331,10 +1331,19 @@ func parseIncomingAsset(c context.Context, newAsset dashapi.NewAsset) (Asset, er
if err != nil {
return Asset{}, fmt.Errorf("invalid URL: %w", err)
}
fsckLog := int64(0)
if len(newAsset.FsckLog) > 0 {
fsckLog, err = putText(c, ns, textFsckLog, newAsset.FsckLog)
if err != nil {
return Asset{}, err
}
}
return Asset{
Type: newAsset.Type,
DownloadURL: newAsset.DownloadURL,
CreateDate: timeNow(c),
FsckLog: fsckLog,
FsIsClean: newAsset.FsIsClean,
}, nil
}

Expand Down
11 changes: 8 additions & 3 deletions dashboard/app/asset_storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ func queryLatestManagerAssets(c context.Context, ns string, assetType dashapi.As
return ret, nil
}

func createAssetList(build *Build, crash *Crash, forReport bool) []dashapi.Asset {
func createAssetList(c context.Context, build *Build, crash *Crash, forReport bool) []dashapi.Asset {
var crashAssets []Asset
if crash != nil {
crashAssets = crash.Assets
Expand All @@ -507,11 +507,16 @@ func createAssetList(build *Build, crash *Crash, forReport bool) []dashapi.Asset
if typeDescr == nil || forReport && typeDescr.NoReporting {
continue
}
assetList = append(assetList, dashapi.Asset{
newAsset := dashapi.Asset{
Title: typeDescr.GetTitle(targets.Get(build.OS, build.Arch)),
DownloadURL: reportAsset.DownloadURL,
Type: reportAsset.Type,
})
}
if reportAsset.FsckLog != 0 {
newAsset.FsckLogURL = externalLink(c, textFsckLog, reportAsset.FsckLog)
newAsset.FsIsClean = reportAsset.FsIsClean
}
assetList = append(assetList, newAsset)
}
sort.SliceStable(assetList, func(i, j int) bool {
return asset.GetTypeDescription(assetList[i].Type).ReportingPrio <
Expand Down
3 changes: 3 additions & 0 deletions dashboard/app/entities_datastore.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ type Asset struct {
Type dashapi.AssetType
DownloadURL string
CreateDate time.Time
FsckLog int64 // references to fsck logstext entity - 0 if fsck wasn't run
FsIsClean bool // undefined value if FsckLog is 0
}

type Build struct {
Expand Down Expand Up @@ -666,6 +668,7 @@ const (
textLog = "Log"
textError = "Error"
textReproLog = "ReproLog"
textFsckLog = "FsckLog"
)

const (
Expand Down
12 changes: 8 additions & 4 deletions dashboard/app/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,8 @@ type uiCrash struct {
type uiAsset struct {
Title string
DownloadURL string
FsckLogURL string
FsIsClean bool
}

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

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

func makeUIAssets(build *Build, crash *Crash, forReport bool) []*uiAsset {
func makeUIAssets(c context.Context, build *Build, crash *Crash, forReport bool) []*uiAsset {
var uiAssets []*uiAsset
for _, asset := range createAssetList(build, crash, forReport) {
for _, asset := range createAssetList(c, build, crash, forReport) {
uiAssets = append(uiAssets, &uiAsset{
Title: asset.Title,
DownloadURL: asset.DownloadURL,
FsckLogURL: asset.FsckLogURL,
FsIsClean: asset.FsIsClean,
})
}
return uiAssets
Expand All @@ -2072,7 +2076,7 @@ func makeUICrash(c context.Context, crash *Crash, build *Build) *uiCrash {
ReproLogLink: textLink(textReproLog, crash.ReproLog),
ReproIsRevoked: crash.ReproIsRevoked,
MachineInfoLink: textLink(textMachineInfo, crash.MachineInfo),
Assets: makeUIAssets(build, crash, true),
Assets: makeUIAssets(c, build, crash, true),
}
if build != nil {
ui.uiBuild = makeUIBuild(c, build, true)
Expand All @@ -2094,7 +2098,7 @@ func makeUIBuild(c context.Context, build *Build, forReport bool) *uiBuild {
KernelCommitTitle: build.KernelCommitTitle,
KernelCommitDate: build.KernelCommitDate,
KernelConfigLink: textLink(textKernelConfig, build.KernelConfig),
Assets: makeUIAssets(build, nil, forReport),
Assets: makeUIAssets(c, build, nil, forReport),
}
}

Expand Down
2 changes: 1 addition & 1 deletion dashboard/app/reporting.go
Original file line number Diff line number Diff line change
Expand Up @@ -549,7 +549,7 @@ func crashBugReport(c context.Context, bug *Bug, crash *Crash, crashKey *db.Key,
if !bugReporting.Reported.IsZero() {
typ = dashapi.ReportRepro
}
assetList := createAssetList(build, crash, true)
assetList := createAssetList(c, build, crash, true)
kernelRepo := kernelRepoInfo(c, build)
rep := &dashapi.BugReport{
Type: typ,
Expand Down
2 changes: 1 addition & 1 deletion dashboard/app/templates/templates.html
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,7 @@ <h1><a href="/{{$.Namespace}}">syzbot</a></h1>
<td class="repro{{if $b.ReproIsRevoked}} stale_repro{{end}}">{{if $b.ReproCLink}}<a href="{{$b.ReproCLink}}">C</a>{{end}}</td>
<td class="repro">{{if $b.MachineInfoLink}}<a href="{{$b.MachineInfoLink}}">info</a>{{end}}</td>
<td class="assets">{{range $i, $asset := .Assets}}
<span class="no-break">[<a href="{{$asset.DownloadURL}}">{{$asset.Title}}</a>]</span>
<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>
{{end}}</td>
<td class="manager">{{$b.Manager}}</td>
<td class="manager">{{$b.Title}}</td>
Expand Down
4 changes: 4 additions & 0 deletions dashboard/dashapi/dashapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,8 @@ type Asset struct {
Title string
DownloadURL string
Type AssetType
FsckLogURL string
FsIsClean bool
}

type AssetType string
Expand Down Expand Up @@ -802,6 +804,8 @@ func (dash *Dashboard) UploadManagerStats(req *ManagerStatsReq) error {
type NewAsset struct {
DownloadURL string
Type AssetType
FsckLog []byte
FsIsClean bool
}

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

Expand Down
3 changes: 3 additions & 0 deletions pkg/compiler/attrs.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const (
// This will facilitate const expressions in e.g. size[] or align[].
intAttr
exprAttr
stringAttr
)

type attrDesc struct {
Expand Down Expand Up @@ -80,6 +81,8 @@ func initCallAttrs() {
case reflect.Bool:
case reflect.Uint64:
desc.Type = intAttr
case reflect.String:
desc.Type = stringAttr
default:
panic("unsupported syscall attribute type")
}
Expand Down
6 changes: 3 additions & 3 deletions pkg/compiler/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ func (comp *compiler) checkStructFields(n *ast.Struct, typ, name string) {
prevFieldHadIf := false
for fieldIdx, f := range n.Fields {
if n.IsUnion {
_, exprs := comp.parseAttrs(unionFieldAttrs, f, f.Attrs)
_, exprs, _ := comp.parseAttrs(unionFieldAttrs, f, f.Attrs)
if fieldIdx > 0 && fieldIdx+1 < len(n.Fields) &&
prevFieldHadIf && exprs[attrIf] == nil {
comp.error(f.Pos, "either no fields have conditions or all except the last")
Expand All @@ -220,7 +220,7 @@ func (comp *compiler) checkStructFields(n *ast.Struct, typ, name string) {
}
continue
}
attrs, _ := comp.parseAttrs(structFieldAttrs, f, f.Attrs)
attrs, _, _ := comp.parseAttrs(structFieldAttrs, f, f.Attrs)
dirCount := attrs[attrIn] + attrs[attrOut] + attrs[attrInOut]
if dirCount != 0 {
hasDirections = true
Expand Down Expand Up @@ -1458,7 +1458,7 @@ func (comp *compiler) checkVarlen(n *ast.Struct) {
}
}
for i, f := range n.Fields {
_, exprs := comp.parseAttrs(structOrUnionFieldAttrs(n), f, f.Attrs)
_, exprs, _ := comp.parseAttrs(structOrUnionFieldAttrs(n), f, f.Attrs)
if !n.IsUnion && i == len(n.Fields)-1 {
break
}
Expand Down
37 changes: 27 additions & 10 deletions pkg/compiler/compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,53 +210,57 @@ func (comp *compiler) structIsVarlen(name string) bool {

func (comp *compiler) parseIntAttrs(descs map[string]*attrDesc, parent ast.Node,
attrs []*ast.Type) map[*attrDesc]uint64 {
intAttrs, _ := comp.parseAttrs(descs, parent, attrs)
intAttrs, _, _ := comp.parseAttrs(descs, parent, attrs)
return intAttrs
}

func (comp *compiler) parseAttrs(descs map[string]*attrDesc, parent ast.Node, attrs []*ast.Type) (
map[*attrDesc]uint64, map[*attrDesc]prog.Expression) {
map[*attrDesc]uint64, map[*attrDesc]prog.Expression, map[*attrDesc]string) {
_, parentType, parentName := parent.Info()
resInt := make(map[*attrDesc]uint64)
resExpr := make(map[*attrDesc]prog.Expression)
resString := make(map[*attrDesc]string)
for _, attr := range attrs {
if unexpected, _, ok := checkTypeKind(attr, kindIdent); !ok {
comp.error(attr.Pos, "unexpected %v, expect attribute", unexpected)
return resInt, resExpr
return resInt, resExpr, resString
}
if len(attr.Colon) != 0 {
comp.error(attr.Colon[0].Pos, "unexpected ':'")
return resInt, resExpr
return resInt, resExpr, resString
}
desc := descs[attr.Ident]
if desc == nil {
comp.error(attr.Pos, "unknown %v %v attribute %v", parentType, parentName, attr.Ident)
return resInt, resExpr
return resInt, resExpr, resString
}
_, dupInt := resInt[desc]
_, dupExpr := resExpr[desc]
if dupInt || dupExpr {
_, dupString := resString[desc]
if dupInt || dupExpr || dupString {
comp.error(attr.Pos, "duplicate %v %v attribute %v", parentType, parentName, attr.Ident)
return resInt, resExpr
return resInt, resExpr, resString
}

switch desc.Type {
case flagAttr:
resInt[desc] = 1
if len(attr.Args) != 0 {
comp.error(attr.Pos, "%v attribute has args", attr.Ident)
return nil, nil
return nil, nil, nil
}
case intAttr:
resInt[desc] = comp.parseAttrIntArg(attr)
case exprAttr:
resExpr[desc] = comp.parseAttrExprArg(attr)
case stringAttr:
resString[desc] = comp.parseAttrStringArg(attr)
default:
comp.error(attr.Pos, "attribute %v has unknown type", attr.Ident)
return nil, nil
return nil, nil, nil
}
}
return resInt, resExpr
return resInt, resExpr, resString
}

func (comp *compiler) parseAttrExprArg(attr *ast.Type) prog.Expression {
Expand Down Expand Up @@ -289,6 +293,19 @@ func (comp *compiler) parseAttrIntArg(attr *ast.Type) uint64 {
return sz.Value
}

func (comp *compiler) parseAttrStringArg(attr *ast.Type) string {
if len(attr.Args) != 1 {
comp.error(attr.Pos, "%v attribute is expected to have 1 argument", attr.Ident)
return ""
}
arg := attr.Args[0]
if !arg.HasString {
comp.error(attr.Pos, "%v argument must be a string", attr.Ident)
return ""
}
return arg.String
}

func (comp *compiler) getTypeDesc(t *ast.Type) *typeDesc {
if desc := builtinTypes[t.Ident]; desc != nil {
return desc
Expand Down
15 changes: 12 additions & 3 deletions pkg/compiler/gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,8 @@ func (comp *compiler) genSyscall(n *ast.Call, argSizes []uint64) *prog.Syscall {
ret = comp.genType(n.Ret, comp.ptrSize)
}
var attrs prog.SyscallAttrs
descAttrs := comp.parseIntAttrs(callAttrs, n, n.Attrs)
for desc, val := range descAttrs {
intAttrs, _, stringAttrs := comp.parseAttrs(callAttrs, n, n.Attrs)
for desc, val := range intAttrs {
fld := reflect.ValueOf(&attrs).Elem().FieldByName(desc.Name)
switch desc.Type {
case intAttr:
Expand All @@ -144,6 +144,15 @@ func (comp *compiler) genSyscall(n *ast.Call, argSizes []uint64) *prog.Syscall {
panic(fmt.Sprintf("unexpected attrDesc type: %q", desc.Type))
}
}
for desc, val := range stringAttrs {
fld := reflect.ValueOf(&attrs).Elem().FieldByName(desc.Name)
switch desc.Type {
case stringAttr:
fld.SetString(val)
default:
panic(fmt.Sprintf("unexpected attrDesc type: %q", desc.Type))
}
}
fields, _ := comp.genFieldArray(n.Args, argSizes)
return &prog.Syscall{
Name: n.Name.Name,
Expand Down Expand Up @@ -513,7 +522,7 @@ func (comp *compiler) genFieldDir(attrs map[*attrDesc]uint64) (prog.Dir, bool) {
}

func (comp *compiler) genField(f *ast.Field, argSize uint64, overlayDir prog.Dir) prog.Field {
intAttrs, exprAttrs := comp.parseAttrs(structFieldAttrs, f, f.Attrs)
intAttrs, exprAttrs, _ := comp.parseAttrs(structFieldAttrs, f, f.Attrs)
dir, hasDir := overlayDir, true
if overlayDir == prog.DirInOut {
dir, hasDir = comp.genFieldDir(intAttrs)
Expand Down
Loading
Loading