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

     1  // Copyright 2021 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  	"testing"
     9  
    10  	"github.com/stretchr/testify/assert"
    11  )
    12  
    13  func TestAssignRandomAsync(t *testing.T) {
    14  	tests := []struct {
    15  		os    string
    16  		arch  string
    17  		orig  string
    18  		check func(*Prog) bool
    19  	}{
    20  		{
    21  			"linux", "amd64",
    22  			`r0 = openat(0xffffffffffffff9c, &AUTO='./file1\x00', 0x42, 0x1ff)
    23  write(r0, &AUTO="01010101", 0x4)
    24  read(r0, &AUTO=""/4, 0x4)
    25  close(r0)
    26  `,
    27  			func(p *Prog) bool {
    28  				return !p.Calls[0].Props.Async
    29  			},
    30  		},
    31  		{
    32  			"linux", "amd64",
    33  			`r0 = openat(0xffffffffffffff9c, &AUTO='./file1\x00', 0x42, 0x1ff)
    34  nanosleep(&AUTO={0x0,0x4C4B40}, &AUTO={0,0})
    35  write(r0, &AUTO="01010101", 0x4)
    36  read(r0, &AUTO=""/4, 0x4)
    37  close(r0)
    38  `,
    39  			func(p *Prog) bool {
    40  				return !p.Calls[0].Props.Async || !p.Calls[1].Props.Async
    41  			},
    42  		},
    43  		{
    44  			"linux", "amd64",
    45  			`r0 = openat(0xffffffffffffff9c, &AUTO='./file1\x00', 0x42, 0x1ff)
    46  r1 = dup(r0)
    47  r2 = dup(r1)
    48  r3 = dup(r2)
    49  r4 = dup(r3)
    50  `,
    51  			func(p *Prog) bool {
    52  				for _, call := range p.Calls[0 : len(p.Calls)-1] {
    53  					if call.Props.Async {
    54  						return false
    55  					}
    56  				}
    57  				return true
    58  			},
    59  		},
    60  	}
    61  	_, rs, iters := initTest(t)
    62  	r := rand.New(rs)
    63  	anyAsync := false
    64  	for _, test := range tests {
    65  		target, err := GetTarget(test.os, test.arch)
    66  		if err != nil {
    67  			t.Fatal(err)
    68  		}
    69  		p, err := target.Deserialize([]byte(test.orig), Strict)
    70  		if err != nil {
    71  			t.Fatal(err)
    72  		}
    73  		for i := 0; i < iters; i++ {
    74  			collided := AssignRandomAsync(p, r)
    75  			if !test.check(collided) {
    76  				t.Fatalf("bad async assignment:\n%s", collided.Serialize())
    77  			}
    78  			for _, call := range collided.Calls {
    79  				anyAsync = anyAsync || call.Props.Async
    80  			}
    81  		}
    82  	}
    83  	if !anyAsync {
    84  		t.Fatalf("not a single async was assigned")
    85  	}
    86  }
    87  
    88  func TestDoubleExecCollide(t *testing.T) {
    89  	tests := []struct {
    90  		os         string
    91  		arch       string
    92  		orig       string
    93  		duplicated string
    94  		shouldFail bool
    95  	}{
    96  		{
    97  			"linux", "amd64",
    98  			`r0 = openat(0xffffffffffffff9c, &AUTO='./file1\x00', 0x42, 0x1ff)
    99  r1 = dup(r0)
   100  r2 = dup(r1)
   101  r3 = dup(r2)
   102  r4 = dup(r2)
   103  r5 = dup(r3)
   104  `,
   105  			`r0 = openat(0xffffffffffffff9c, &(0x7f0000000040)='./file1\x00', 0x42, 0x1ff)
   106  r1 = dup(r0)
   107  r2 = dup(r1)
   108  r3 = dup(r2)
   109  dup(r2)
   110  dup(r3)
   111  openat(0xffffffffffffff9c, &(0x7f0000000040)='./file1\x00', 0x42, 0x1ff)
   112  dup(r0)
   113  dup(r1)
   114  dup(r2)
   115  dup(r2)
   116  dup(r3)
   117  `,
   118  			false,
   119  		},
   120  	}
   121  	_, rs, iters := initTest(t)
   122  	r := rand.New(rs)
   123  	for _, test := range tests {
   124  		target, err := GetTarget(test.os, test.arch)
   125  		if err != nil {
   126  			t.Fatal(err)
   127  		}
   128  		p, err := target.Deserialize([]byte(test.orig), Strict)
   129  		if err != nil {
   130  			t.Fatal(err)
   131  		}
   132  		for i := 0; i < iters; i++ {
   133  			collided, err := DoubleExecCollide(p, r)
   134  			if test.shouldFail && err == nil {
   135  				t.Fatalf("expected to fail, but it hasn't")
   136  			} else if !test.shouldFail && err != nil {
   137  				t.Fatalf("unexpected error: %s", err)
   138  			}
   139  			if test.duplicated != "" {
   140  				woProps := collided.Clone()
   141  				for _, c := range woProps.Calls {
   142  					c.Props = CallProps{}
   143  				}
   144  				serialized := string(woProps.Serialize())
   145  				if serialized != test.duplicated {
   146  					t.Fatalf("expected:%s\ngot:%s", test.duplicated, serialized)
   147  				}
   148  			}
   149  			// TODO: also test the `async` assignment.
   150  		}
   151  	}
   152  }
   153  
   154  func TestDupCallCollide(t *testing.T) {
   155  	tests := []struct {
   156  		os   string
   157  		arch string
   158  		orig string
   159  		rets []string
   160  	}{
   161  		{
   162  			"linux", "amd64",
   163  			`r0 = openat(0xffffffffffffff9c, &AUTO='./file1\x00', 0x42, 0x1ff)
   164  r1 = dup(r0)
   165  r2 = dup(r1)
   166  dup(r2)
   167  `,
   168  			[]string{
   169  				`r0 = openat(0xffffffffffffff9c, &(0x7f0000000040)='./file1\x00', 0x42, 0x1ff)
   170  dup(r0) (async)
   171  r1 = dup(r0)
   172  r2 = dup(r1)
   173  dup(r2)
   174  `,
   175  				`r0 = openat(0xffffffffffffff9c, &(0x7f0000000040)='./file1\x00', 0x42, 0x1ff)
   176  r1 = dup(r0)
   177  r2 = dup(r1)
   178  dup(r2) (async)
   179  dup(r2)
   180  `,
   181  			},
   182  		},
   183  	}
   184  	_, rs, iters := initTest(t)
   185  	if iters > 100 {
   186  		// Let's save resources -- we don't need that many for these small tests.
   187  		iters = 100
   188  	}
   189  	r := rand.New(rs)
   190  	for _, test := range tests {
   191  		target, err := GetTarget(test.os, test.arch)
   192  		if err != nil {
   193  			t.Fatal(err)
   194  		}
   195  		p, err := target.Deserialize([]byte(test.orig), Strict)
   196  		if err != nil {
   197  			t.Fatal(err)
   198  		}
   199  		detected := map[string]struct{}{}
   200  		for i := 0; i < iters; i++ {
   201  			collided, err := DupCallCollide(p, r)
   202  			assert.NoError(t, err)
   203  			detected[string(collided.Serialize())] = struct{}{}
   204  		}
   205  		for _, variant := range test.rets {
   206  			_, exists := detected[variant]
   207  			assert.True(t, exists)
   208  		}
   209  	}
   210  }