Skip to content

Commit

Permalink
syz-cluster: add a boot test
Browse files Browse the repository at this point in the history
Run a smoke test on the base kernel build and report back the results.
  • Loading branch information
a-nogikh committed Jan 21, 2025
1 parent ed2815a commit 41b2b2c
Show file tree
Hide file tree
Showing 8 changed files with 254 additions and 13 deletions.
4 changes: 4 additions & 0 deletions syz-cluster/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ build-build-step-dev:
eval $$(minikube docker-env);\
docker build -t build-step-local -f ./workflow/build-step/Dockerfile ../

build-boot-step-dev:
eval $$(minikube docker-env);\
docker build -t boot-step-local -f ./workflow/boot-step/Dockerfile ../

build-go-tests-dev:
eval $$(minikube docker-env);\
docker build -t go-tests-local -f Dockerfile.go-tests ../
Expand Down
9 changes: 2 additions & 7 deletions syz-cluster/dashboard/templates/series.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

{{define "content"}}
<div class="container">
<h2>Patch Series</h2>
Expand Down Expand Up @@ -82,18 +81,14 @@ <h3>Session {{.CreatedAt.Format "2006-01-02"}}</h3>
<tr>
<td colspan="4">
<table class="table mb-0">
<thead>
<tr>
<th scope="col">Title</th>
</tr>
</thead>
<tbody>
{{range . .Findings}}
{{range .Findings}}
<tr>
<td>{{.Title}}</td>
</tr>
{{end}}
</tbody>
</table>
</td>
</tr>
{{end}}
Expand Down
16 changes: 13 additions & 3 deletions syz-cluster/overlays/dev/workflow-roles.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,19 @@ metadata:
namespace: default
name: argo-workflow-role
rules:
- apiGroups: ["argoproj.io"]
resources: ["workflows"]
verbs: ["list", "create", "delete"]
- apiGroups:
- argoproj.io
resources:
- workflowartifactgctasks
- workflows
verbs:
- get
- list
- watch
- create
- update
- patch
- delete

---

Expand Down
32 changes: 29 additions & 3 deletions syz-cluster/pkg/workflow/template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ spec:
parameters:
- name: session-id
value: "some-session-id"
# TODO: there seems to be no way to pass env variables into the GC workflow.
# Set ARGO_ARTIFACT_GC_ENABLED=0 for the local setup?
# artifactGC:
# strategy: OnWorkflowCompletion
templates:
- name: main
# Don't schedule new steps if any of the previous steps failed.
Expand Down Expand Up @@ -53,7 +57,7 @@ spec:
failed: true
- - name: break-if-succeeded
template: exit-workflow
when: "{{steps['run-process-fuzz'].status}} == Succeeded"
when: "{{=steps['run-process-fuzz'].status == Succeeded}}"
- name: process-fuzz
inputs:
parameters:
Expand All @@ -64,7 +68,7 @@ spec:
arguments:
parameters:
- name: data
value: "{{= jsonpath(inputs.parameters.element, '$.base')}}"
value: "{{=jsonpath(inputs.parameters.element, '$.base')}}"
- - name: base-build
templateRef:
name: build-step-template
Expand Down Expand Up @@ -100,7 +104,29 @@ spec:
from: "{{steps.save-patched-req.outputs.artifacts.request}}"
- - name: continue-if-patched-build-failed
template: exit-workflow
when: "{{=jsonpath(steps['patched-build'].outputs.parameters.result, '$.success')}} == false"
when: "{{=jsonpath(steps['patched-build'].outputs.parameters.result, '$.success') == false}}"
- - name: boot-test-base
templateRef:
name: boot-step-template
template: boot-step
arguments:
artifacts:
- name: kernel
from: "{{steps.base-build.outputs.artifacts.kernel}}"
parameters:
- name: config
value: "{{=jsonpath(inputs.parameters.element, '$.config')}}"
- name: base-build-id
value: "{{=jsonpath(steps['base-build'].outputs.parameters.result, '$.build_id')}}"
- name: test-name
value: "Boot test: Base"
- - name: break-if-base-boot-failed
template: exit-workflow
arguments:
parameters:
- name: code
value: 0
when: "{{=jsonpath(steps['boot-test-base'].outputs.parameters.result, '$.success') == false}}"
- name: extract-request
inputs:
parameters:
Expand Down
45 changes: 45 additions & 0 deletions syz-cluster/workflow/boot-step/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# syntax=docker.io/docker/dockerfile:1.7-labs
# Copyright 2025 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.

# Build syzkaller.
FROM gcr.io/syzkaller/env as syzkaller-builder
WORKDIR /build
# First query the modules to facilitate caching.
COPY go.mod go.sum ./
RUN go mod download
COPY --exclude=vendor --exclude=syz-cluster . .
RUN make TARGETARCH=amd64

FROM golang:1.23-alpine AS boot-step-builder
WORKDIR /build

# Copy the code and the dependencies.
COPY go.mod go.sum ./
RUN go mod download
COPY pkg/ pkg/
# TODO: get rid of the prog/ dependency?
COPY prog/ prog/
COPY vm/ vm/
COPY executor/ executor/
COPY dashboard/dashapi/ dashboard/dashapi/
# Copying from the builder to take the `make descriptions` result.
COPY --from=syzkaller-builder /build/sys/ sys/
COPY syz-cluster/workflow/boot-step/*.go syz-cluster/workflow/boot-step/
COPY syz-cluster/pkg/ syz-cluster/pkg/

RUN go build -o /build/boot-step-bin /build/syz-cluster/workflow/boot-step

FROM debian:bookworm

RUN apt-get update && \
apt-get install -y qemu-system openssh-client

# pkg/osutil uses syzkaller user for sandboxing.
RUN useradd --create-home syzkaller

COPY --from=syzkaller-builder /build/bin/ /syzkaller/bin/
COPY --from=boot-step-builder /build/boot-step-bin /bin/boot-step
COPY syz-cluster/workflow/configs/ /configs/

ENTRYPOINT ["/bin/boot-tracker"]
98 changes: 98 additions & 0 deletions syz-cluster/workflow/boot-step/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Copyright 2025 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"
"flag"
"fmt"
"log"
"path/filepath"

"github.com/google/syzkaller/pkg/instance"
"github.com/google/syzkaller/pkg/mgrconfig"
"github.com/google/syzkaller/pkg/osutil"
"github.com/google/syzkaller/syz-cluster/pkg/api"
"github.com/google/syzkaller/syz-cluster/pkg/app"
)

var (
flagConfig = flag.String("config", "", "syzkaller config")
flagSession = flag.String("session", "", "session ID")
flagTestName = flag.String("test_name", "", "test name")
flagBaseBuild = flag.String("base_build", "", "base build ID")
flagPatchedBuild = flag.String("patched_build", "", "patched build ID")
flagOutput = flag.String("output", "", "where to store the result")
)

func main() {
flag.Parse()
if *flagConfig == "" || *flagSession == "" || *flagTestName == "" {
app.Fatalf("--config, --session and --test_name must be set")
}

ctx := context.Background()
client := app.DefaultClient()

testResult := &api.TestResult{
SessionID: *flagSession,
TestName: *flagTestName,
BaseBuildID: *flagBaseBuild,
PatchedBuildID: *flagPatchedBuild,
Result: api.TestRunning,
}
// Report that we've begun the test -- it will let us report the findings.
err := client.UploadTestResult(ctx, testResult)
if err != nil {
app.Fatalf("failed to upload test result: %v", err)
}

bootedFine, err := runTest(ctx, client)
if err != nil {
app.Fatalf("failed to run the boot test: %v", err)
}
if bootedFine {
testResult.Result = api.TestPassed
} else {
testResult.Result = api.TestFailed
}

// Report the test results.
err = client.UploadTestResult(ctx, testResult)
if err != nil {
app.Fatalf("failed to upload test result: %v", err)
}
if *flagOutput != "" {
osutil.WriteJSON(*flagOutput, &api.BootResult{
Success: bootedFine,
})
}
}

func runTest(ctx context.Context, client *api.Client) (bool, error) {
cfg, err := mgrconfig.LoadFile(filepath.Join("/configs", *flagConfig, "base.cfg"))
if err != nil {
return false, err
}
cfg.Workdir = "/tmp/test-workdir"
rep, err := instance.RunSmokeTest(cfg)
if err != nil {
return false, err
}
if rep != nil {
log.Printf("uploading the finding %q", rep.Title)
findingErr := client.UploadFinding(ctx, &api.Finding{
SessionID: *flagSession,
TestName: *flagTestName,
Title: rep.Title,
Report: rep.Report,
Log: rep.Output,
})
if findingErr != nil {
return false, fmt.Errorf("failed to report the finding: %w", findingErr)
}
return false, nil
}
return true, nil
}
60 changes: 60 additions & 0 deletions syz-cluster/workflow/boot-step/workflow-template.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Copyright 2025 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.

apiVersion: argoproj.io/v1alpha1
kind: WorkflowTemplate
metadata:
name: boot-step-template
spec:
templates:
- name: boot-step
inputs:
parameters:
- name: config
value: ""
- name: base-build-id
value: ""
- name: patched-build-id
value: ""
- name: test-name
value: ""
artifacts:
- name: kernel
path: /kernel
container:
image: boot-step-local
imagePullPolicy: IfNotPresent
command: ["/bin/boot-step"]
args: [
"--config", "{{inputs.parameters.config}}",
"--output", "/output/result.json",
"--session", "{{workflow.parameters.session-id}}",
"--test_name", "{{inputs.parameters.test-name}}",
"--base_build", "{{inputs.parameters.base-build-id}}",
"--patched_build", "{{inputs.parameters.patched-build-id}}"
]
volumeMounts:
- name: workdir
mountPath: /workdir
- name: output
mountPath: /output
- name: dev-kvm
mountPath: /dev/kvm
# Needed for /dev/kvm.
# TODO: there's a "device plugin" mechanism in k8s that can share it more safely.
securityContext:
privileged: true
volumes:
- name: workdir
emptyDir: {}
- name: output
emptyDir: {}
- name: dev-kvm
hostPath:
path: /dev/kvm
type: CharDevice
outputs:
parameters:
- name: result
valueFrom:
path: /output/result.json
3 changes: 3 additions & 0 deletions syz-cluster/workflow/build-step/workflow-template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,6 @@ spec:
- name: result
valueFrom:
path: /output/result.json
artifacts:
- name: kernel
path: /output

0 comments on commit 41b2b2c

Please sign in to comment.