2018-11-17 19:37:04 +00:00
|
|
|
|
// Copyright 2018 Mikhail Klementev. All rights reserved.
|
|
|
|
|
// Use of this source code is governed by a AGPLv3 license
|
|
|
|
|
// (or later) that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
2019-08-13 21:54:59 +00:00
|
|
|
|
"database/sql"
|
2018-11-17 19:37:04 +00:00
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
2019-08-14 17:36:36 +00:00
|
|
|
|
"io"
|
2018-11-17 19:37:04 +00:00
|
|
|
|
"io/ioutil"
|
|
|
|
|
"log"
|
|
|
|
|
"math/rand"
|
|
|
|
|
"os"
|
|
|
|
|
"os/exec"
|
2019-08-16 04:43:29 +00:00
|
|
|
|
"os/user"
|
2018-11-17 19:37:04 +00:00
|
|
|
|
"strings"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"github.com/otiai10/copy"
|
|
|
|
|
"github.com/remeh/sizedwaitgroup"
|
2020-05-17 15:40:34 +00:00
|
|
|
|
"gopkg.in/logrusorgru/aurora.v2"
|
2018-11-17 19:37:04 +00:00
|
|
|
|
|
2019-02-02 21:24:29 +00:00
|
|
|
|
"code.dumpstack.io/tools/out-of-tree/config"
|
|
|
|
|
"code.dumpstack.io/tools/out-of-tree/qemu"
|
2018-11-17 19:37:04 +00:00
|
|
|
|
)
|
|
|
|
|
|
2019-08-30 00:33:43 +00:00
|
|
|
|
type runstate struct {
|
|
|
|
|
Overall, Success float64
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
state runstate
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func successRate(state runstate) float64 {
|
|
|
|
|
return state.Success / state.Overall
|
|
|
|
|
}
|
2018-12-07 03:14:10 +00:00
|
|
|
|
|
2019-08-17 09:05:06 +00:00
|
|
|
|
const pathDevNull = "/dev/null"
|
2019-08-14 17:36:36 +00:00
|
|
|
|
|
2018-12-08 02:53:29 +00:00
|
|
|
|
func dockerRun(timeout time.Duration, container, workdir, command string) (
|
|
|
|
|
output string, err error) {
|
|
|
|
|
|
|
|
|
|
cmd := exec.Command("docker", "run", "-v", workdir+":/work",
|
|
|
|
|
container, "bash", "-c", "cd /work && "+command)
|
|
|
|
|
|
|
|
|
|
timer := time.AfterFunc(timeout, func() {
|
|
|
|
|
cmd.Process.Kill()
|
|
|
|
|
})
|
|
|
|
|
defer timer.Stop()
|
|
|
|
|
|
|
|
|
|
raw, err := cmd.CombinedOutput()
|
|
|
|
|
if err != nil {
|
2019-08-12 22:58:34 +00:00
|
|
|
|
e := fmt.Sprintf("error `%v` for cmd `%v` with output `%v`",
|
|
|
|
|
err, command, string(raw))
|
|
|
|
|
err = errors.New(e)
|
2018-12-08 02:53:29 +00:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
output = string(raw)
|
|
|
|
|
return
|
2018-11-17 19:37:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-17 20:18:50 +00:00
|
|
|
|
func build(tmp string, ka config.Artifact, ki config.KernelInfo,
|
2018-11-17 19:37:04 +00:00
|
|
|
|
dockerTimeout time.Duration) (outPath, output string, err error) {
|
|
|
|
|
|
|
|
|
|
target := fmt.Sprintf("%d_%s", rand.Int(), ki.KernelRelease)
|
|
|
|
|
|
|
|
|
|
tmpSourcePath := tmp + "/source"
|
|
|
|
|
|
|
|
|
|
err = copy.Copy(ka.SourcePath, tmpSourcePath)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
outPath = tmpSourcePath + "/" + target
|
2018-11-17 20:18:50 +00:00
|
|
|
|
if ka.Type == config.KernelModule {
|
2018-11-17 19:37:04 +00:00
|
|
|
|
outPath += ".ko"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
kernel := "/lib/modules/" + ki.KernelRelease + "/build"
|
2019-08-16 05:05:26 +00:00
|
|
|
|
if ki.KernelSource != "" {
|
|
|
|
|
kernel = ki.KernelSource
|
|
|
|
|
}
|
2018-11-17 19:37:04 +00:00
|
|
|
|
|
2019-08-16 05:05:26 +00:00
|
|
|
|
if ki.ContainerName != "" {
|
|
|
|
|
output, err = dockerRun(dockerTimeout, ki.ContainerName,
|
|
|
|
|
tmpSourcePath, "make KERNEL="+kernel+" TARGET="+target+
|
|
|
|
|
" && chmod -R 777 /work")
|
|
|
|
|
} else {
|
|
|
|
|
command := "make KERNEL=" + kernel + " TARGET=" + target
|
|
|
|
|
cmd := exec.Command("bash", "-c", "cd "+tmpSourcePath+" && "+command)
|
|
|
|
|
timer := time.AfterFunc(dockerTimeout, func() {
|
|
|
|
|
cmd.Process.Kill()
|
|
|
|
|
})
|
|
|
|
|
defer timer.Stop()
|
|
|
|
|
|
|
|
|
|
var raw []byte
|
|
|
|
|
raw, err = cmd.CombinedOutput()
|
|
|
|
|
if err != nil {
|
|
|
|
|
e := fmt.Sprintf("error `%v` for cmd `%v` with output `%v`",
|
|
|
|
|
err, command, string(raw))
|
|
|
|
|
err = errors.New(e)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
output = string(raw)
|
|
|
|
|
}
|
2018-11-17 19:37:04 +00:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-17 09:05:06 +00:00
|
|
|
|
func testKernelModule(q *qemu.System, ka config.Artifact,
|
2018-11-17 20:18:50 +00:00
|
|
|
|
test string) (output string, err error) {
|
|
|
|
|
|
2018-11-17 19:37:04 +00:00
|
|
|
|
output, err = q.Command("root", test)
|
|
|
|
|
// TODO generic checks for WARNING's and so on
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-17 09:05:06 +00:00
|
|
|
|
func testKernelExploit(q *qemu.System, ka config.Artifact,
|
2018-11-17 20:18:50 +00:00
|
|
|
|
test, exploit string) (output string, err error) {
|
|
|
|
|
|
2018-11-17 19:37:04 +00:00
|
|
|
|
output, err = q.Command("user", "chmod +x "+exploit)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
randFilePath := fmt.Sprintf("/root/%d", rand.Int())
|
|
|
|
|
|
|
|
|
|
cmd := fmt.Sprintf("%s %s %s", test, exploit, randFilePath)
|
|
|
|
|
output, err = q.Command("user", cmd)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err = q.Command("root", "stat "+randFilePath)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-10 02:41:45 +00:00
|
|
|
|
func genOkFail(name string, ok bool) (aurv aurora.Value) {
|
2019-08-30 00:33:43 +00:00
|
|
|
|
state.Overall += 1
|
2018-11-17 19:37:04 +00:00
|
|
|
|
if ok {
|
2019-08-30 00:33:43 +00:00
|
|
|
|
state.Success += 1
|
2018-11-17 19:37:04 +00:00
|
|
|
|
s := " " + name + " SUCCESS "
|
2018-12-10 02:41:45 +00:00
|
|
|
|
aurv = aurora.BgGreen(aurora.Black(s))
|
2018-11-17 19:37:04 +00:00
|
|
|
|
} else {
|
|
|
|
|
s := " " + name + " FAILURE "
|
2020-05-17 15:40:34 +00:00
|
|
|
|
aurv = aurora.BgRed(aurora.White(aurora.Bold(s)))
|
2018-11-17 19:37:04 +00:00
|
|
|
|
}
|
2018-12-10 02:41:45 +00:00
|
|
|
|
return
|
2018-11-17 19:37:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-08-13 21:54:59 +00:00
|
|
|
|
type phasesResult struct {
|
2019-08-14 17:36:36 +00:00
|
|
|
|
BuildArtifact string
|
2019-08-13 21:54:59 +00:00
|
|
|
|
Build, Run, Test struct {
|
|
|
|
|
Output string
|
|
|
|
|
Ok bool
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-14 17:36:36 +00:00
|
|
|
|
func copyFile(sourcePath, destinationPath string) (err error) {
|
|
|
|
|
sourceFile, err := os.Open(sourcePath)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
defer sourceFile.Close()
|
|
|
|
|
|
|
|
|
|
destinationFile, err := os.Create(destinationPath)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if _, err := io.Copy(destinationFile, sourceFile); err != nil {
|
|
|
|
|
destinationFile.Close()
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return destinationFile.Close()
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-17 09:05:06 +00:00
|
|
|
|
func dumpResult(q *qemu.System, ka config.Artifact, ki config.KernelInfo,
|
2019-08-16 18:30:46 +00:00
|
|
|
|
res *phasesResult, dist, tag, binary string, db *sql.DB) {
|
2018-11-17 20:18:50 +00:00
|
|
|
|
|
2019-08-13 23:33:52 +00:00
|
|
|
|
// TODO merge (problem is it's not 100% same) with log.go:logLogEntry
|
|
|
|
|
|
2018-11-17 19:37:04 +00:00
|
|
|
|
distroInfo := fmt.Sprintf("%s-%s {%s}", ki.DistroType,
|
|
|
|
|
ki.DistroRelease, ki.KernelRelease)
|
|
|
|
|
|
|
|
|
|
colored := ""
|
2018-11-17 20:18:50 +00:00
|
|
|
|
if ka.Type == config.KernelExploit {
|
2018-11-17 19:37:04 +00:00
|
|
|
|
colored = aurora.Sprintf("[*] %40s: %s %s", distroInfo,
|
2019-08-13 21:54:59 +00:00
|
|
|
|
genOkFail("BUILD", res.Build.Ok),
|
|
|
|
|
genOkFail("LPE", res.Test.Ok))
|
2018-11-17 19:37:04 +00:00
|
|
|
|
} else {
|
|
|
|
|
colored = aurora.Sprintf("[*] %40s: %s %s %s", distroInfo,
|
2019-08-13 21:54:59 +00:00
|
|
|
|
genOkFail("BUILD", res.Build.Ok),
|
|
|
|
|
genOkFail("INSMOD", res.Run.Ok),
|
|
|
|
|
genOkFail("TEST", res.Test.Ok))
|
2018-11-17 19:37:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
additional := ""
|
|
|
|
|
if q.KernelPanic {
|
|
|
|
|
additional = "(panic)"
|
|
|
|
|
} else if q.KilledByTimeout {
|
|
|
|
|
additional = "(timeout)"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if additional != "" {
|
|
|
|
|
fmt.Println(colored, additional)
|
|
|
|
|
} else {
|
|
|
|
|
fmt.Println(colored)
|
|
|
|
|
}
|
2019-08-13 21:54:59 +00:00
|
|
|
|
|
2019-08-16 18:30:46 +00:00
|
|
|
|
err := addToLog(db, q, ka, ki, res, tag)
|
2019-08-13 21:54:59 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
log.Println("[db] addToLog (", ka, ") error:", err)
|
|
|
|
|
}
|
2019-08-14 17:36:36 +00:00
|
|
|
|
|
2019-08-17 09:05:06 +00:00
|
|
|
|
if binary == "" && dist != pathDevNull {
|
2019-08-14 17:36:36 +00:00
|
|
|
|
err = os.MkdirAll(dist, os.ModePerm)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Println("os.MkdirAll (", ka, ") error:", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
path := fmt.Sprintf("%s/%s-%s-%s", dist, ki.DistroType,
|
|
|
|
|
ki.DistroRelease, ki.KernelRelease)
|
|
|
|
|
if ka.Type != config.KernelExploit {
|
|
|
|
|
path += ".ko"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = copyFile(res.BuildArtifact, path)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Println("copyFile (", ka, ") error:", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-11-17 19:37:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-08-17 10:00:01 +00:00
|
|
|
|
func copyArtifactAndTest(q *qemu.System, ka config.Artifact,
|
|
|
|
|
res *phasesResult, remoteTest string) (err error) {
|
|
|
|
|
|
|
|
|
|
switch ka.Type {
|
|
|
|
|
case config.KernelModule:
|
|
|
|
|
res.Run.Output, err = q.CopyAndInsmod(res.BuildArtifact)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Println(res.Run.Output, err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
res.Run.Ok = true
|
|
|
|
|
|
|
|
|
|
res.Test.Output, err = testKernelModule(q, ka, remoteTest)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Println(res.Test.Output, err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
res.Test.Ok = true
|
|
|
|
|
case config.KernelExploit:
|
|
|
|
|
remoteExploit := fmt.Sprintf("/tmp/exploit_%d", rand.Int())
|
|
|
|
|
err = q.CopyFile("user", res.BuildArtifact, remoteExploit)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res.Test.Output, err = testKernelExploit(q, ka, remoteTest,
|
|
|
|
|
remoteExploit)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Println(res.Test.Output)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
res.Run.Ok = true // does not really used
|
|
|
|
|
res.Test.Ok = true
|
|
|
|
|
default:
|
|
|
|
|
log.Println("Unsupported artifact type")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func copyTest(q *qemu.System, testPath string, ka config.Artifact) (
|
|
|
|
|
remoteTest string, err error) {
|
|
|
|
|
|
|
|
|
|
remoteTest = fmt.Sprintf("/tmp/test_%d", rand.Int())
|
|
|
|
|
err = q.CopyFile("user", testPath, remoteTest)
|
|
|
|
|
if err != nil {
|
|
|
|
|
if ka.Type == config.KernelExploit {
|
|
|
|
|
q.Command("user",
|
|
|
|
|
"echo -e '#!/bin/sh\necho touch $2 | $1' "+
|
|
|
|
|
"> "+remoteTest+
|
|
|
|
|
" && chmod +x "+remoteTest)
|
|
|
|
|
} else {
|
|
|
|
|
q.Command("user", "echo '#!/bin/sh' "+
|
|
|
|
|
"> "+remoteTest+" && chmod +x "+remoteTest)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err = q.Command("root", "chmod +x "+remoteTest)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-17 20:18:50 +00:00
|
|
|
|
func whatever(swg *sizedwaitgroup.SizedWaitGroup, ka config.Artifact,
|
|
|
|
|
ki config.KernelInfo, binaryPath, testPath string,
|
2019-08-16 18:30:46 +00:00
|
|
|
|
qemuTimeout, dockerTimeout time.Duration, dist, tag string,
|
2019-11-14 15:38:16 +00:00
|
|
|
|
db *sql.DB, verbose bool) {
|
2018-11-17 19:37:04 +00:00
|
|
|
|
|
|
|
|
|
defer swg.Done()
|
|
|
|
|
|
|
|
|
|
kernel := qemu.Kernel{KernelPath: ki.KernelPath, InitrdPath: ki.InitrdPath}
|
2019-08-17 09:05:06 +00:00
|
|
|
|
q, err := qemu.NewSystem(qemu.X86x64, kernel, ki.RootFS)
|
2018-11-17 19:37:04 +00:00
|
|
|
|
if err != nil {
|
2018-12-01 14:30:28 +00:00
|
|
|
|
log.Println("Qemu creation error:", err)
|
2018-11-17 19:37:04 +00:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
q.Timeout = qemuTimeout
|
|
|
|
|
|
2019-08-19 18:34:13 +00:00
|
|
|
|
if ka.Qemu.Timeout.Duration != 0 {
|
|
|
|
|
q.Timeout = ka.Qemu.Timeout.Duration
|
|
|
|
|
}
|
2019-08-19 19:03:59 +00:00
|
|
|
|
if ka.Qemu.Cpus != 0 {
|
|
|
|
|
q.Cpus = ka.Qemu.Cpus
|
2019-08-19 18:34:13 +00:00
|
|
|
|
}
|
|
|
|
|
if ka.Qemu.Memory != 0 {
|
|
|
|
|
q.Memory = ka.Qemu.Memory
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-14 17:32:57 +00:00
|
|
|
|
if ka.Docker.Timeout.Duration != 0 {
|
|
|
|
|
dockerTimeout = ka.Docker.Timeout.Duration
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-19 19:03:59 +00:00
|
|
|
|
q.SetKASLR(!ka.Mitigations.DisableKaslr)
|
|
|
|
|
q.SetSMEP(!ka.Mitigations.DisableSmep)
|
|
|
|
|
q.SetSMAP(!ka.Mitigations.DisableSmap)
|
2019-11-14 15:37:34 +00:00
|
|
|
|
q.SetKPTI(!ka.Mitigations.DisableKpti)
|
2019-08-19 18:34:13 +00:00
|
|
|
|
|
2018-11-17 19:37:04 +00:00
|
|
|
|
err = q.Start()
|
|
|
|
|
if err != nil {
|
2018-12-01 14:30:28 +00:00
|
|
|
|
log.Println("Qemu start error:", err)
|
2018-11-17 19:37:04 +00:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
defer q.Stop()
|
|
|
|
|
|
2019-11-14 15:38:16 +00:00
|
|
|
|
if verbose {
|
|
|
|
|
go func() {
|
|
|
|
|
for !q.Died {
|
|
|
|
|
time.Sleep(time.Minute)
|
|
|
|
|
log.Println(ka.Name, ki.DistroType,
|
|
|
|
|
ki.DistroRelease, ki.KernelRelease,
|
|
|
|
|
"still alive")
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-16 04:43:29 +00:00
|
|
|
|
usr, err := user.Current()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
tmpdir := usr.HomeDir + "/.out-of-tree/tmp"
|
|
|
|
|
os.MkdirAll(tmpdir, os.ModePerm)
|
|
|
|
|
|
|
|
|
|
tmp, err := ioutil.TempDir(tmpdir, "out-of-tree_")
|
2018-11-17 19:37:04 +00:00
|
|
|
|
if err != nil {
|
2018-12-01 14:30:28 +00:00
|
|
|
|
log.Println("Temporary directory creation error:", err)
|
2018-11-17 19:37:04 +00:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
defer os.RemoveAll(tmp)
|
|
|
|
|
|
2019-08-13 21:54:59 +00:00
|
|
|
|
result := phasesResult{}
|
2019-08-16 18:30:46 +00:00
|
|
|
|
defer dumpResult(q, ka, ki, &result, dist, tag, binaryPath, db)
|
2018-11-17 19:37:04 +00:00
|
|
|
|
|
|
|
|
|
if binaryPath == "" {
|
2019-08-14 17:36:36 +00:00
|
|
|
|
result.BuildArtifact, result.Build.Output, err = build(tmp, ka,
|
|
|
|
|
ki, dockerTimeout)
|
2018-11-17 19:37:04 +00:00
|
|
|
|
if err != nil {
|
2019-08-16 04:43:29 +00:00
|
|
|
|
log.Println(err)
|
2018-11-17 19:37:04 +00:00
|
|
|
|
return
|
|
|
|
|
}
|
2019-08-13 21:54:59 +00:00
|
|
|
|
result.Build.Ok = true
|
2018-11-17 19:37:04 +00:00
|
|
|
|
} else {
|
2019-08-14 17:36:36 +00:00
|
|
|
|
result.BuildArtifact = binaryPath
|
2019-08-13 21:54:59 +00:00
|
|
|
|
result.Build.Ok = true
|
2018-11-17 19:37:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if testPath == "" {
|
2019-08-14 17:36:36 +00:00
|
|
|
|
testPath = result.BuildArtifact + "_test"
|
2019-08-18 15:04:24 +00:00
|
|
|
|
if !exists(testPath) {
|
|
|
|
|
testPath = tmp + "/" + "test.sh"
|
|
|
|
|
}
|
2018-11-17 19:37:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-08-17 10:00:01 +00:00
|
|
|
|
remoteTest, err := copyTest(q, testPath, ka)
|
2018-11-17 19:37:04 +00:00
|
|
|
|
if err != nil {
|
2019-08-17 10:00:01 +00:00
|
|
|
|
return
|
2018-11-17 19:37:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-06-14 20:14:59 +00:00
|
|
|
|
err = preloadModules(q, ka, ki, dockerTimeout)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Println(err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-17 10:00:01 +00:00
|
|
|
|
copyArtifactAndTest(q, ka, &result, remoteTest)
|
2018-11-17 19:37:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-08-12 23:21:38 +00:00
|
|
|
|
func shuffleKernels(a []config.KernelInfo) []config.KernelInfo {
|
|
|
|
|
// Fisher–Yates shuffle
|
|
|
|
|
for i := len(a) - 1; i > 0; i-- {
|
|
|
|
|
j := rand.Intn(i + 1)
|
|
|
|
|
a[i], a[j] = a[j], a[i]
|
|
|
|
|
}
|
|
|
|
|
return a
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-17 20:18:50 +00:00
|
|
|
|
func performCI(ka config.Artifact, kcfg config.KernelConfig, binaryPath,
|
2019-08-30 00:34:14 +00:00
|
|
|
|
testPath string, stop time.Time,
|
|
|
|
|
qemuTimeout, dockerTimeout time.Duration,
|
2019-08-16 18:50:34 +00:00
|
|
|
|
max, runs int64, dist, tag string, threads int,
|
2019-11-14 15:38:16 +00:00
|
|
|
|
db *sql.DB, verbose bool) (err error) {
|
2018-11-17 19:37:04 +00:00
|
|
|
|
|
|
|
|
|
found := false
|
|
|
|
|
|
2019-08-16 07:02:51 +00:00
|
|
|
|
swg := sizedwaitgroup.New(threads)
|
2019-08-12 23:21:38 +00:00
|
|
|
|
for _, kernel := range shuffleKernels(kcfg.Kernels) {
|
|
|
|
|
if max <= 0 {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-17 19:37:04 +00:00
|
|
|
|
var supported bool
|
|
|
|
|
supported, err = ka.Supported(kernel)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if supported {
|
|
|
|
|
found = true
|
2019-08-17 09:35:36 +00:00
|
|
|
|
max--
|
2019-08-16 18:50:34 +00:00
|
|
|
|
for i := int64(0); i < runs; i++ {
|
2019-08-30 00:34:14 +00:00
|
|
|
|
if !stop.IsZero() && time.Now().After(stop) {
|
|
|
|
|
break
|
|
|
|
|
}
|
2019-08-16 18:50:34 +00:00
|
|
|
|
swg.Add()
|
|
|
|
|
go whatever(&swg, ka, kernel, binaryPath,
|
|
|
|
|
testPath, qemuTimeout, dockerTimeout,
|
2019-11-14 15:38:16 +00:00
|
|
|
|
dist, tag, db, verbose)
|
2019-08-16 18:50:34 +00:00
|
|
|
|
}
|
2018-11-17 19:37:04 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
swg.Wait()
|
|
|
|
|
|
|
|
|
|
if !found {
|
|
|
|
|
err = errors.New("No supported kernels found")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func exists(path string) bool {
|
|
|
|
|
if _, err := os.Stat(path); err != nil {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-25 14:43:58 +00:00
|
|
|
|
func kernelMask(kernel string) (km config.KernelMask, err error) {
|
|
|
|
|
parts := strings.Split(kernel, ":")
|
|
|
|
|
if len(parts) != 2 {
|
|
|
|
|
err = errors.New("Kernel is not 'distroType:regex'")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dt, err := config.NewDistroType(parts[0])
|
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
km = config.KernelMask{DistroType: dt, ReleaseMask: parts[1]}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-10 03:00:26 +00:00
|
|
|
|
func genAllKernels() (sk []config.KernelMask, err error) {
|
|
|
|
|
for _, dType := range config.DistroTypeStrings {
|
|
|
|
|
var dt config.DistroType
|
|
|
|
|
dt, err = config.NewDistroType(dType)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sk = append(sk, config.KernelMask{
|
|
|
|
|
DistroType: dt,
|
|
|
|
|
ReleaseMask: ".*",
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-30 00:34:14 +00:00
|
|
|
|
// TODO: Now too many parameters, move all of them to some structure
|
2018-11-17 20:18:50 +00:00
|
|
|
|
func pewHandler(kcfg config.KernelConfig,
|
|
|
|
|
workPath, ovrrdKrnl, binary, test string, guess bool,
|
2019-08-30 00:34:14 +00:00
|
|
|
|
stop time.Time, qemuTimeout, dockerTimeout time.Duration,
|
2019-08-16 18:50:34 +00:00
|
|
|
|
max, runs int64, dist, tag string, threads int,
|
2019-11-14 15:38:16 +00:00
|
|
|
|
db *sql.DB, verbose bool) (err error) {
|
2018-11-17 19:37:04 +00:00
|
|
|
|
|
2018-11-17 20:18:50 +00:00
|
|
|
|
ka, err := config.ReadArtifactConfig(workPath + "/.out-of-tree.toml")
|
2018-11-17 19:37:04 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ka.SourcePath == "" {
|
|
|
|
|
ka.SourcePath = workPath
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ovrrdKrnl != "" {
|
2018-11-25 14:43:58 +00:00
|
|
|
|
var km config.KernelMask
|
|
|
|
|
km, err = kernelMask(ovrrdKrnl)
|
2018-11-17 19:37:04 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-17 20:18:50 +00:00
|
|
|
|
ka.SupportedKernels = []config.KernelMask{km}
|
2018-11-17 19:37:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if guess {
|
2018-12-10 03:00:26 +00:00
|
|
|
|
ka.SupportedKernels, err = genAllKernels()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
2018-11-17 19:37:04 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-30 00:34:14 +00:00
|
|
|
|
err = performCI(ka, kcfg, binary, test,
|
|
|
|
|
stop, qemuTimeout, dockerTimeout,
|
2019-11-14 15:38:16 +00:00
|
|
|
|
max, runs, dist, tag, threads, db, verbose)
|
2018-11-17 19:37:04 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|