blob: 222e93c006c8dbea1dde8a99626d062025475009 [file] [log] [blame]
// Copyright 2015 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 prog
import (
"math/rand"
"strings"
"testing"
"github.com/google/syzkaller/pkg/testutil"
)
func TestResourceCtors(t *testing.T) {
if testing.Short() && testutil.RaceEnabled {
t.Skip("too slow")
}
testEachTarget(t, func(t *testing.T, target *Target) {
for _, res := range target.Resources {
if len(target.calcResourceCtors(res, true)) == 0 && !strings.HasPrefix(res.Name, "ANY") {
t.Errorf("resource %v can't be created", res.Name)
}
}
})
}
func TestTransitivelyEnabledCalls(t *testing.T) {
testEachTarget(t, func(t *testing.T, target *Target) {
calls := make(map[*Syscall]bool)
for _, c := range target.Syscalls {
calls[c] = true
}
enabled, disabled := target.TransitivelyEnabledCalls(calls)
for c, ok := range enabled {
if !ok {
t.Fatalf("syscalls %v is false in enabled map", c.Name)
}
}
if target.OS == "test" {
for c := range enabled {
if c.CallName == "unsupported" {
t.Errorf("call %v is not disabled", c.Name)
}
}
for c, reason := range disabled {
if c.CallName != "unsupported" {
t.Errorf("call %v is disabled: %v", c.Name, reason)
}
}
} else {
if len(enabled) != len(target.Syscalls) {
t.Errorf("some calls are disabled: %v/%v", len(enabled), len(target.Syscalls))
}
if len(disabled) != 0 {
for c, reason := range disabled {
t.Errorf("disabled %v: %v", c.Name, reason)
}
}
}
})
}
func TestTransitivelyEnabledCallsLinux(t *testing.T) {
t.Parallel()
target, err := GetTarget("linux", "amd64")
if err != nil {
t.Fatal(err)
}
calls := make(map[*Syscall]bool)
for _, c := range target.Syscalls {
calls[c] = true
}
delete(calls, target.SyscallMap["epoll_create"])
if trans, disabled := target.TransitivelyEnabledCalls(calls); len(disabled) != 0 || len(trans) != len(calls) {
t.Fatalf("still must be able to create epoll fd with epoll_create1")
}
delete(calls, target.SyscallMap["epoll_create1"])
trans, disabled := target.TransitivelyEnabledCalls(calls)
if len(calls)-8 != len(trans) ||
trans[target.SyscallMap["epoll_ctl$EPOLL_CTL_ADD"]] ||
trans[target.SyscallMap["epoll_ctl$EPOLL_CTL_MOD"]] ||
trans[target.SyscallMap["epoll_ctl$EPOLL_CTL_DEL"]] ||
trans[target.SyscallMap["epoll_wait"]] ||
trans[target.SyscallMap["epoll_pwait"]] ||
trans[target.SyscallMap["epoll_pwait2"]] ||
trans[target.SyscallMap["kcmp$KCMP_EPOLL_TFD"]] ||
trans[target.SyscallMap["syz_io_uring_submit$IORING_OP_EPOLL_CTL"]] {
t.Fatalf("epoll fd is not disabled")
}
if len(disabled) != 8 {
t.Fatalf("disabled %v syscalls, want 8", len(disabled))
}
for c, reason := range disabled {
if !strings.Contains(reason, "no syscalls can create resource fd_epoll,"+
" enable some syscalls that can create it [epoll_create epoll_create1]") {
t.Fatalf("%v: wrong disable reason: %v", c.Name, reason)
}
}
}
func TestGetInputResources(t *testing.T) {
expectedRequiredResources := map[string]bool{
"required_res1": false,
"required_res2": false,
}
t.Parallel()
target, err := GetTarget("test", "64")
if err != nil {
t.Fatal(err)
}
resources := target.getInputResources(target.SyscallMap["test$optional_res"])
for _, resource := range resources {
if _, ok := expectedRequiredResources[resource.Name]; ok {
expectedRequiredResources[resource.Name] = true
} else {
t.Fatalf(" unexpected %v", resource.Name)
}
}
for expectedRes, found := range expectedRequiredResources {
if !found {
t.Fatalf(" missing %v", expectedRes)
}
}
}
func TestClockGettime(t *testing.T) {
t.Parallel()
target, err := GetTarget("linux", "amd64")
if err != nil {
t.Fatal(err)
}
calls := make(map[*Syscall]bool)
for _, c := range target.Syscalls {
calls[c] = true
}
// Removal of clock_gettime should disable all calls that accept timespec/timeval.
delete(calls, target.SyscallMap["clock_gettime"])
trans, disabled := target.TransitivelyEnabledCalls(calls)
if len(trans)+10 > len(calls) || len(trans)+len(disabled) != len(calls) || len(trans) == 0 {
t.Fatalf("clock_gettime did not disable enough calls: before %v, after %v, disabled %v",
len(calls), len(trans), len(disabled))
}
}
func TestCreateResourceRotation(t *testing.T) {
target, rs, _ := initTest(t)
allCalls := make(map[*Syscall]bool)
for _, call := range target.Syscalls {
allCalls[call] = true
}
rotator := MakeRotator(target, allCalls, rand.New(rs))
testCreateResource(t, target, rotator.Select(), rs)
}
func TestCreateResourceHalf(t *testing.T) {
target, rs, _ := initTest(t)
r := rand.New(rs)
var halfCalls map[*Syscall]bool
for len(halfCalls) == 0 {
halfCalls = make(map[*Syscall]bool)
for _, call := range target.Syscalls {
if r.Intn(10) == 0 {
halfCalls[call] = true
}
}
halfCalls, _ = target.TransitivelyEnabledCalls(halfCalls)
}
testCreateResource(t, target, halfCalls, rs)
}
func testCreateResource(t *testing.T, target *Target, calls map[*Syscall]bool, rs rand.Source) {
r := newRand(target, rs)
r.inGenerateResource = true
ct := target.BuildChoiceTable(nil, calls)
for call := range calls {
t.Logf("testing call %v", call.Name)
ForeachCallType(call, func(typ Type, ctx *TypeCtx) {
if res, ok := typ.(*ResourceType); ok && ctx.Dir != DirOut {
s := newState(target, ct, nil)
arg, calls := r.createResource(s, res, DirIn)
if arg == nil && !ctx.Optional {
t.Fatalf("failed to create resource %v", res.Name())
}
if arg != nil && len(calls) == 0 {
t.Fatalf("created resource %v, but got no calls", res.Name())
}
}
})
}
}