github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/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 TestTransitivelyEnabledAutoCalls(t *testing.T) {
   107  	t.Parallel()
   108  	target, err := GetTarget("linux", "amd64")
   109  	if err != nil {
   110  		t.Fatal(err)
   111  	}
   112  	calls := make(map[*Syscall]bool)
   113  	for _, c := range target.Syscalls {
   114  		if c.Attrs.Automatic || c.Attrs.AutomaticHelper {
   115  			calls[c] = true
   116  		}
   117  	}
   118  	_, disabled := target.TransitivelyEnabledCalls(calls)
   119  	for c, reason := range disabled {
   120  		t.Errorf("disabled call %v: %v", c.Name, reason)
   121  	}
   122  }
   123  
   124  func TestGetInputResources(t *testing.T) {
   125  	expectedRequiredResources := map[string]bool{
   126  		"required_res1": false,
   127  		"required_res2": false,
   128  	}
   129  
   130  	t.Parallel()
   131  	target, err := GetTarget("test", "64")
   132  	if err != nil {
   133  		t.Fatal(err)
   134  	}
   135  
   136  	resources := target.getInputResources(target.SyscallMap["test$optional_res"])
   137  	for _, resource := range resources {
   138  		if _, ok := expectedRequiredResources[resource.Name]; ok {
   139  			expectedRequiredResources[resource.Name] = true
   140  		} else {
   141  			t.Fatalf(" unexpected %v", resource.Name)
   142  		}
   143  	}
   144  	for expectedRes, found := range expectedRequiredResources {
   145  		if !found {
   146  			t.Fatalf(" missing %v", expectedRes)
   147  		}
   148  	}
   149  }
   150  
   151  func TestClockGettime(t *testing.T) {
   152  	t.Parallel()
   153  	target, err := GetTarget("linux", "amd64")
   154  	if err != nil {
   155  		t.Fatal(err)
   156  	}
   157  	calls := make(map[*Syscall]bool)
   158  	for _, c := range target.Syscalls {
   159  		calls[c] = true
   160  	}
   161  	// Removal of clock_gettime should disable all calls that accept timespec/timeval.
   162  	delete(calls, target.SyscallMap["clock_gettime"])
   163  	trans, disabled := target.TransitivelyEnabledCalls(calls)
   164  	if len(trans)+10 > len(calls) || len(trans)+len(disabled) != len(calls) || len(trans) == 0 {
   165  		t.Fatalf("clock_gettime did not disable enough calls: before %v, after %v, disabled %v",
   166  			len(calls), len(trans), len(disabled))
   167  	}
   168  }
   169  
   170  func TestCreateResourceRotation(t *testing.T) {
   171  	target, rs, _ := initTest(t)
   172  	allCalls := make(map[*Syscall]bool)
   173  	for _, call := range target.Syscalls {
   174  		allCalls[call] = true
   175  	}
   176  	rotator := MakeRotator(target, allCalls, rand.New(rs))
   177  	testCreateResource(t, target, rotator.Select(), rs)
   178  }
   179  
   180  func TestCreateResourceHalf(t *testing.T) {
   181  	target, rs, _ := initTest(t)
   182  	r := rand.New(rs)
   183  	var halfCalls map[*Syscall]bool
   184  	for len(halfCalls) == 0 {
   185  		halfCalls = make(map[*Syscall]bool)
   186  		for _, call := range target.Syscalls {
   187  			if r.Intn(10) == 0 {
   188  				halfCalls[call] = true
   189  			}
   190  		}
   191  		halfCalls, _ = target.TransitivelyEnabledCalls(halfCalls)
   192  	}
   193  	testCreateResource(t, target, halfCalls, rs)
   194  }
   195  
   196  func testCreateResource(t *testing.T, target *Target, calls map[*Syscall]bool, rs rand.Source) {
   197  	r := newRand(target, rs)
   198  	r.inGenerateResource = true
   199  	ct := target.BuildChoiceTable(nil, calls)
   200  	for call := range calls {
   201  		if call.Attrs.Disabled {
   202  			continue
   203  		}
   204  		t.Logf("testing call %v", call.Name)
   205  		ForeachCallType(call, func(typ Type, ctx *TypeCtx) {
   206  			if res, ok := typ.(*ResourceType); ok && ctx.Dir != DirOut {
   207  				s := newState(target, ct, nil)
   208  				arg, calls := r.createResource(s, res, DirIn)
   209  				if arg == nil && !ctx.Optional {
   210  					t.Fatalf("failed to create resource %v", res.Name())
   211  				}
   212  				if arg != nil && len(calls) == 0 {
   213  					t.Fatalf("created resource %v, but got no calls", res.Name())
   214  				}
   215  			}
   216  		})
   217  	}
   218  }
   219  
   220  func TestPreferPreciseResources(t *testing.T) {
   221  	target, rs, _ := initRandomTargetTest(t, "test", "64")
   222  	r := newRand(target, rs)
   223  	counts := map[string]int{}
   224  	for i := 0; i < 2000; i++ {
   225  		s := newState(target, target.DefaultChoiceTable(), nil)
   226  		calls := r.generateParticularCall(s,
   227  			target.SyscallMap["test$consume_subtype_of_common"])
   228  		for _, call := range calls {
   229  			if call.Meta.Name == "test$consume_subtype_of_common" {
   230  				continue
   231  			}
   232  			counts[call.Meta.Name]++
   233  		}
   234  	}
   235  	assert.Greater(t, counts["test$produce_common"], 70)
   236  	assert.Greater(t, counts["test$also_produce_common"], 70)
   237  	assert.Greater(t, counts["test$produce_subtype_of_common"], 1000)
   238  }