aboutsummaryrefslogtreecommitdiffstats
path: root/dashboard/app/kcidb.go
blob: 25ec2342469d44b347c2b049f8d9274d16f255d7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
// Copyright 2020 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 (
	"context"
	"fmt"
	"net/http"
	"time"

	"github.com/google/syzkaller/pkg/kcidb"
	"google.golang.org/appengine/v2"
	db "google.golang.org/appengine/v2/datastore"
	"google.golang.org/appengine/v2/log"
)

func initKcidb() {
	http.HandleFunc("/cron/kcidb_poll", handleKcidbPoll)
}

func handleKcidbPoll(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	for ns, cfg := range getConfig(c).Namespaces {
		if cfg.Kcidb == nil {
			continue
		}
		if err := handleKcidbNamespce(c, ns, cfg.Kcidb); err != nil {
			log.Errorf(c, "kcidb: %v failed: %v", ns, err)
		}
	}
}

func handleKcidbNamespce(c context.Context, ns string, cfg *KcidbConfig) error {
	client, err := kcidb.NewClient(c, cfg.Origin, cfg.RestURI, cfg.Token)
	if err != nil {
		return err
	}
	defer client.Close()

	filter := func(query *db.Query) *db.Query {
		return query.Filter("Namespace=", ns).
			Filter("Status=", BugStatusOpen)
	}
	reported := 0
	return foreachBug(c, filter, func(bug *Bug, bugKey *db.Key) error {
		if reported >= 30 {
			return nil
		}
		ok, err := publishKcidbBug(c, client, bug, bugKey)
		if err != nil {
			return err
		}
		if ok {
			reported++
		}
		return nil
	})
}

func publishKcidbBug(c context.Context, client *kcidb.Client, bug *Bug, bugKey *db.Key) (bool, error) {
	if bug.KcidbStatus != 0 ||
		bug.sanitizeAccess(c, AccessPublic) > AccessPublic ||
		bug.Reporting[len(bug.Reporting)-1].Reported.IsZero() ||
		bug.Status != BugStatusOpen && timeSince(c, bug.LastTime) > 7*24*time.Hour {
		return false, nil
	}
	rep, err := loadBugReport(c, bug)
	if err != nil {
		return false, err
	}
	// publish == false happens only for syzkaller build/test errors.
	// But if this ever happens for a kernel bug, then we also don't want to publish such bugs
	// with missing critical info.
	publish := rep.KernelCommit != "" && len(rep.KernelConfig) != 0

	if publish {
		if err := client.Publish(rep); err != nil {
			return false, err
		}
	}
	tx := func(c context.Context) error {
		bug := new(Bug)
		if err := db.Get(c, bugKey, bug); err != nil {
			return err
		}
		bug.KcidbStatus = 1
		if !publish {
			bug.KcidbStatus = 2
		}
		if _, err := db.Put(c, bugKey, bug); err != nil {
			return fmt.Errorf("failed to put bug: %w", err)
		}
		return nil
	}
	if err := runInTransaction(c, tx, nil); err != nil {
		return false, err
	}
	log.Infof(c, "published bug to kcidb: %v:%v '%v'", bug.Namespace, bugKey.StringID(), bug.displayTitle())
	return publish, nil
}