github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/prog/resources_test.go (about)

     1  // Copyright 2015 syzkaller project authors. All rights reserved.
     2  // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
     3  
     4  package prog
     5  
     6  import (
     7  	"math/rand"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/google/syzkaller/pkg/testutil"
    12  	"github.com/stretchr/testify/assert"
    13  )
    14  
    15  func TestResourceCtors(t *testing.T) {
    16  	if testing.Short() && testutil.RaceEnabled {
    17  		t.Skip("too slow")
    18  	}
    19  	testEachTarget(t, func(t *testing.T, target *Target) {
    20  		for _, res := range target.Resources {
    21  			if len(target.calcResourceCtors(res, true)) == 0 && !strings.HasPrefix(res.Name, "ANY") &&
    22  				res.Name != "disabled_resource" {
    23  				t.Errorf("resource %v can't be created", res.Name)
    24  			}
    25  		}
    26  	})
    27  }
    28  
    29  func TestTransitivelyEnabledCalls(t *testing.T) {
    30  	testEachTarget(t, func(t *testing.T, target *Target) {
    31  		calls := make(map[*Syscall]bool)
    32  		for _, c := range target.Syscalls {
    33  			if c.Attrs.Disabled {
    34  				continue
    35  			}
    36  			calls[c] = true
    37  		}
    38  		enabled, disabled := target.TransitivelyEnabledCalls(calls)
    39  		for c, ok := range enabled {
    40  			if !ok {
    41  				t.Fatalf("syscalls %v is false in enabled map", c.Name)
    42  			}
    43  		}
    44  		if target.OS == "test" {
    45  			for c := range enabled {
    46  				if c.CallName == "unsupported" {
    47  					t.Errorf("call %v is not disabled", c.Name)
    48  				}
    49  			}
    50  			for c, reason := range disabled {
    51  				if c.CallName != "unsupported" {
    52  					t.Errorf("call %v is disabled: %v", c.Name, reason)
    53  				}
    54  			}
    55  		} else {
    56  			if len(enabled) != len(calls) {
    57  				t.Errorf("some calls are disabled: %v/%v", len(enabled), len(calls))
    58  			}
    59  			for c, reason := range disabled {
    60  				t.Errorf("disabled %v: %v", c.Name, reason)
    61  			}
    62  		}
    63  	})
    64  }
    65  
    66  func TestTransitivelyEnabledCallsLinux(t *testing.T) {
    67  	t.Parallel()
    68  	target, err := GetTarget("linux", "amd64")
    69  	if err != nil {
    70  		t.Fatal(err)
    71  	}
    72  	calls := make(map[*Syscall]bool)
    73  	for _, c := range target.Syscalls {
    74  		if c.Attrs.Disabled {
    75  			continue
    76  		}
    77  		calls[c] = true
    78  	}
    79  	delete(calls, target.SyscallMap["epoll_create"])
    80  	if trans, disabled := target.TransitivelyEnabledCalls(calls); len(disabled) != 0 || len(trans) != len(calls) {
    81  		t.Fatalf("still must be able to create epoll fd with epoll_create1")
    82  	}
    83  	delete(calls, target.SyscallMap["epoll_create1"])
    84  	trans, disabled := target.TransitivelyEnabledCalls(calls)
    85  	if len(calls)-8 != len(trans) ||
    86  		trans[target.SyscallMap["epoll_ctl$EPOLL_CTL_ADD"]] ||
    87  		trans[target.SyscallMap["epoll_ctl$EPOLL_CTL_MOD"]] ||
    88  		trans[target.SyscallMap["epoll_ctl$EPOLL_CTL_DEL"]] ||
    89  		trans[target.SyscallMap["epoll_wait"]] ||
    90  		trans[target.SyscallMap["epoll_pwait"]] ||
    91  		trans[target.SyscallMap["epoll_pwait2"]] ||
    92  		trans[target.SyscallMap["kcmp$KCMP_EPOLL_TFD"]] ||
    93  		trans[target.SyscallMap["syz_io_uring_submit$IORING_OP_EPOLL_CTL"]] {
    94  		t.Fatalf("epoll fd is not disabled")
    95  	}
    96  	if len(disabled) != 8 {
    97  		t.Fatalf("disabled %v syscalls, want 8", len(disabled))
    98  	}
    99  	for c, reason := range disabled {
   100  		if !strings.Contains(reason, "fd_epoll [epoll_create epoll_create1]") {
   101  			t.Fatalf("%v: wrong disable reason: %v", c.Name, reason)
   102  		}
   103  	}
   104  }
   105  
   106  func TestGetInputResources(t *testing.T) {
   107  	expectedRequiredResources := map[string]bool{
   108  		"required_res1": false,
   109  		"required_res2": false,
   110  	}
   111  
   112  	t.Parallel()
   113  	target, err := GetTarget("test", "64")
   114  	if err != nil {
   115  		t.Fatal(err)
   116  	}
   117  
   118  	resources := target.getInputResources(target.SyscallMap["test$optional_res"])
   119  	for _, resource := range resources {
   120  		if _, ok := expectedRequiredResources[resource.Name]; ok {
   121  			expectedRequiredResources[resource.Name] = true
   122  		} else {
   123  			t.Fatalf(" unexpected %v", resource.Name)
   124  		}
   125  	}
   126  	for expectedRes, found := range expectedRequiredResources {
   127  		if !found {
   128  			t.Fatalf(" missing %v", expectedRes)
   129  		}
   130  	}
   131  }
   132  
   133  func TestClockGettime(t *testing.T) {
   134  	t.Parallel()
   135  	target, err := GetTarget("linux", "amd64")
   136  	if err != nil {
   137  		t.Fatal(err)
   138  	}
   139  	calls := make(map[*Syscall]bool)
   140  	for _, c := range target.Syscalls {
   141  		calls[c] = true
   142  	}
   143  	// Removal of clock_gettime should disable all calls that accept timespec/timeval.
   144  	delete(calls, target.SyscallMap["clock_gettime"])
   145  	trans, disabled := target.TransitivelyEnabledCalls(calls)
   146  	if len(trans)+10 > len(calls) || len(trans)+len(disabled) != len(calls) || len(trans) == 0 {
   147  		t.Fatalf("clock_gettime did not disable enough calls: before %v, after %v, disabled %v",
   148  			len(calls), len(trans), len(disabled))
   149  	}
   150  }
   151  
   152  func TestCreateResourceRotation(t *testing.T) {
   153  	target, rs, _ := initTest(t)
   154  	allCalls := make(map[*Syscall]bool)
   155  	for _, call := range target.Syscalls {
   156  		allCalls[call] = true
   157  	}
   158  	rotator := MakeRotator(target, allCalls, rand.New(rs))
   159  	testCreateResource(t, target, rotator.Select(), rs)
   160  }
   161  
   162  func TestCreateResourceHalf(t *testing.T) {
   163  	target, rs, _ := initTest(t)
   164  	r := rand.New(rs)
   165  	var halfCalls map[*Syscall]bool
   166  	for len(halfCalls) == 0 {
   167  		halfCalls = make(map[*Syscall]bool)
   168  		for _, call := range target.Syscalls {
   169  			if r.Intn(10) == 0 {
   170  				halfCalls[call] = true
   171  			}
   172  		}
   173  		halfCalls, _ = target.TransitivelyEnabledCalls(halfCalls)
   174  	}
   175  	testCreateResource(t, target, halfCalls, rs)
   176  }
   177  
   178  func testCreateResource(t *testing.T, target *Target, calls map[*Syscall]bool, rs rand.Source) {
   179  	r := newRand(target, rs)
   180  	r.inGenerateResource = true
   181  	ct := target.BuildChoiceTable(nil, calls)
   182  	for call := range calls {
   183  		if call.Attrs.Disabled {
   184  			continue
   185  		}
   186  		t.Logf("testing call %v", call.Name)
   187  		ForeachCallType(call, func(typ Type, ctx *TypeCtx) {
   188  			if res, ok := typ.(*ResourceType); ok && ctx.Dir != DirOut {
   189  				s := newState(target, ct, nil)
   190  				arg, calls := r.createResource(s, res, DirIn)
   191  				if arg == nil && !ctx.Optional {
   192  					t.Fatalf("failed to create resource %v", res.Name())
   193  				}
   194  				if arg != nil && len(calls) == 0 {
   195  					t.Fatalf("created resource %v, but got no calls", res.Name())
   196  				}
   197  			}
   198  		})
   199  	}
   200  }
   201  
   202  func TestPreferPreciseResources(t *testing.T) {
   203  	target, rs, _ := initRandomTargetTest(t, "test", "64")
   204  	r := newRand(target, rs)
   205  	counts := map[string]int{}
   206  	for i := 0; i < 2000; i++ {
   207  		s := newState(target, target.DefaultChoiceTable(), nil)
   208  		calls := r.generateParticularCall(s,
   209  			target.SyscallMap["test$consume_subtype_of_common"])
   210  		for _, call := range calls {
   211  			if call.Meta.Name == "test$consume_subtype_of_common" {
   212  				continue
   213  			}
   214  			counts[call.Meta.Name]++
   215  		}
   216  	}
   217  	assert.Greater(t, counts["test$produce_common"], 70)
   218  	assert.Greater(t, counts["test$also_produce_common"], 70)
   219  	assert.Greater(t, counts["test$produce_subtype_of_common"], 1000)
   220  }