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

     1  // Copyright 2018 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  
    11  // nolint:gocyclo
    12  func TestMinimize(t *testing.T) {
    13  	// nolint: lll
    14  	tests := []struct {
    15  		os              string
    16  		arch            string
    17  		orig            string
    18  		callIndex       int
    19  		pred            func(*Prog, int) bool
    20  		result          string
    21  		resultCallIndex int
    22  	}{
    23  		// Predicate always returns false, so must get the same program.
    24  		{
    25  			"linux", "amd64",
    26  			"mmap(&(0x7f0000000000/0x1000)=nil, 0x1000, 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" +
    27  				"sched_yield()\n" +
    28  				"pipe2(&(0x7f0000000000), 0x0)\n",
    29  			2,
    30  			func(p *Prog, callIndex int) bool {
    31  				if len(p.Calls) == 0 {
    32  					t.Fatalf("got an empty program")
    33  				}
    34  				if p.Calls[len(p.Calls)-1].Meta.Name != "pipe2" {
    35  					t.Fatalf("last call is removed")
    36  				}
    37  				return false
    38  			},
    39  			"mmap(&(0x7f0000000000/0x1000)=nil, 0x1000, 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" +
    40  				"sched_yield()\n" +
    41  				"pipe2(&(0x7f0000000000), 0x0)\n",
    42  			2,
    43  		},
    44  		// Remove a call.
    45  		{
    46  			"linux", "amd64",
    47  			"mmap(&(0x7f0000000000/0x1000)=nil, 0x1000, 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" +
    48  				"sched_yield()\n" +
    49  				"pipe2(&(0x7f0000000000)={0xffffffffffffffff, 0xffffffffffffffff}, 0x0)\n",
    50  			2,
    51  			func(p *Prog, callIndex int) bool {
    52  				// Aim at removal of sched_yield.
    53  				return len(p.Calls) == 2 && p.Calls[0].Meta.Name == "mmap" && p.Calls[1].Meta.Name == "pipe2"
    54  			},
    55  			"mmap(&(0x7f0000000000/0x1000)=nil, 0x1000, 0x0, 0x10, 0xffffffffffffffff, 0x0)\n" +
    56  				"pipe2(0x0, 0x0)\n",
    57  			1,
    58  		},
    59  		// Remove two dependent calls.
    60  		{
    61  			"linux", "amd64",
    62  			"mmap(&(0x7f0000000000/0x1000)=nil, 0x1000, 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" +
    63  				"pipe2(&(0x7f0000000000)={0x0, 0x0}, 0x0)\n" +
    64  				"sched_yield()\n",
    65  			2,
    66  			func(p *Prog, callIndex int) bool {
    67  				// Aim at removal of pipe2 and then mmap.
    68  				if len(p.Calls) == 2 && p.Calls[0].Meta.Name == "mmap" && p.Calls[1].Meta.Name == "sched_yield" {
    69  					return true
    70  				}
    71  				if len(p.Calls) == 1 && p.Calls[0].Meta.Name == "sched_yield" {
    72  					return true
    73  				}
    74  				return false
    75  			},
    76  			"sched_yield()\n",
    77  			0,
    78  		},
    79  		// Remove a call and replace results.
    80  		{
    81  			"linux", "amd64",
    82  			"mmap(&(0x7f0000000000/0x1000)=nil, 0x1000, 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" +
    83  				"pipe2(&(0x7f0000000000)={<r0=>0x0, 0x0}, 0x0)\n" +
    84  				"write(r0, &(0x7f0000000000)=\"1155\", 0x2)\n" +
    85  				"sched_yield()\n",
    86  			3,
    87  			func(p *Prog, callIndex int) bool {
    88  				return p.String() == "mmap-write-sched_yield"
    89  			},
    90  			"mmap(&(0x7f0000000000/0x1000)=nil, 0x1000, 0x0, 0x10, 0xffffffffffffffff, 0x0)\n" +
    91  				"write(0xffffffffffffffff, 0x0, 0x0)\n" +
    92  				"sched_yield()\n",
    93  			2,
    94  		},
    95  		// Remove a call and replace results.
    96  		{
    97  			"linux", "amd64",
    98  			"mmap(&(0x7f0000000000/0x1000)=nil, 0x1000, 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" +
    99  				"r0=open(&(0x7f0000000000)=\"1155\", 0x0, 0x0)\n" +
   100  				"write(r0, &(0x7f0000000000)=\"1155\", 0x2)\n" +
   101  				"sched_yield()\n",
   102  			-1,
   103  			func(p *Prog, callIndex int) bool {
   104  				return p.String() == "mmap-write-sched_yield"
   105  			},
   106  			"mmap(&(0x7f0000000000/0x1000)=nil, 0x1000, 0x0, 0x10, 0xffffffffffffffff, 0x0)\n" +
   107  				"write(0xffffffffffffffff, 0x0, 0x0)\n" +
   108  				"sched_yield()\n",
   109  			-1,
   110  		},
   111  		// Minimize pointer.
   112  		{
   113  			"linux", "amd64",
   114  			"pipe2(&(0x7f0000001000)={0xffffffffffffffff, 0xffffffffffffffff}, 0x0)\n",
   115  			-1,
   116  			func(p *Prog, callIndex int) bool {
   117  				return len(p.Calls) == 1 && p.Calls[0].Meta.Name == "pipe2"
   118  			},
   119  			"pipe2(0x0, 0x0)\n",
   120  			-1,
   121  		},
   122  		// Minimize pointee.
   123  		{
   124  			"linux", "amd64",
   125  			"pipe2(&(0x7f0000001000)={0xffffffffffffffff, 0xffffffffffffffff}, 0x0)\n",
   126  			-1,
   127  			func(p *Prog, callIndex int) bool {
   128  				return len(p.Calls) == 1 && p.Calls[0].Meta.Name == "pipe2" && p.Calls[0].Args[0].(*PointerArg).Address != 0
   129  			},
   130  			"pipe2(&(0x7f0000001000), 0x0)\n",
   131  			-1,
   132  		},
   133  		// Make sure we don't hang when minimizing resources.
   134  		{
   135  			"test", "64",
   136  			"r0 = test$res0()\n" +
   137  				"test$res1(r0)\n",
   138  			-1,
   139  			func(p *Prog, callIndex int) bool {
   140  				return false
   141  			},
   142  			"r0 = test$res0()\n" +
   143  				"test$res1(r0)\n",
   144  			-1,
   145  		},
   146  		{
   147  			"test", "64",
   148  			"minimize$0(0x1, 0x1)\n",
   149  			-1,
   150  			func(p *Prog, callIndex int) bool { return len(p.Calls) == 1 },
   151  			"minimize$0(0x1, 0xffffffffffffffff)\n",
   152  			-1,
   153  		},
   154  		// Clear unneeded fault injection.
   155  		{
   156  			"linux", "amd64",
   157  			"pipe2(0x0, 0x0) (fail_nth: 5)\n",
   158  			-1,
   159  			func(p *Prog, callIndex int) bool {
   160  				return len(p.Calls) == 1 && p.Calls[0].Meta.Name == "pipe2"
   161  			},
   162  			"pipe2(0x0, 0x0)\n",
   163  			-1,
   164  		},
   165  		// Keep important fault injection.
   166  		{
   167  			"linux", "amd64",
   168  			"pipe2(0x0, 0x0) (fail_nth: 5)\n",
   169  			-1,
   170  			func(p *Prog, callIndex int) bool {
   171  				return len(p.Calls) == 1 && p.Calls[0].Meta.Name == "pipe2" && p.Calls[0].Props.FailNth == 5
   172  			},
   173  			"pipe2(0x0, 0x0) (fail_nth: 5)\n",
   174  			-1,
   175  		},
   176  		// Clear unneeded async flag.
   177  		{
   178  			"linux", "amd64",
   179  			"pipe2(0x0, 0x0) (async)\n",
   180  			-1,
   181  			func(p *Prog, callIndex int) bool {
   182  				return len(p.Calls) == 1 && p.Calls[0].Meta.Name == "pipe2"
   183  			},
   184  			"pipe2(0x0, 0x0)\n",
   185  			-1,
   186  		},
   187  		// Keep important async flag.
   188  		{
   189  			"linux", "amd64",
   190  			"pipe2(0x0, 0x0) (async)\n",
   191  			-1,
   192  			func(p *Prog, callIndex int) bool {
   193  				return len(p.Calls) == 1 && p.Calls[0].Meta.Name == "pipe2" && p.Calls[0].Props.Async
   194  			},
   195  			"pipe2(0x0, 0x0) (async)\n",
   196  			-1,
   197  		},
   198  		// Clear unneeded rerun.
   199  		{
   200  			"linux", "amd64",
   201  			"pipe2(0x0, 0x0) (rerun: 100)\n",
   202  			-1,
   203  			func(p *Prog, callIndex int) bool {
   204  				return len(p.Calls) == 1 && p.Calls[0].Meta.Name == "pipe2"
   205  			},
   206  			"pipe2(0x0, 0x0)\n",
   207  			-1,
   208  		},
   209  		// Keep important rerun.
   210  		{
   211  			"linux", "amd64",
   212  			"pipe2(0x0, 0x0) (rerun: 100)\n",
   213  			-1,
   214  			func(p *Prog, callIndex int) bool {
   215  				return len(p.Calls) == 1 && p.Calls[0].Meta.Name == "pipe2" && p.Calls[0].Props.Rerun >= 100
   216  			},
   217  			"pipe2(0x0, 0x0) (rerun: 100)\n",
   218  			-1,
   219  		},
   220  		// Undo target.SpecialFileLenghts mutation (reduce file name length).
   221  		{
   222  			"test", "64",
   223  			"mutate9(&(0x7f0000000000)='./file0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\\x00')\n",
   224  			0,
   225  			func(p *Prog, callIndex int) bool {
   226  				return p.Calls[0].Args[0].(*PointerArg).Res != nil
   227  			},
   228  			"mutate9(&(0x7f0000000000)='./file0\\x00')\n",
   229  			0,
   230  		},
   231  		// Ensure `no_minimize` calls are untouched.
   232  		{
   233  			"linux", "amd64",
   234  			"syz_mount_image$ext4(&(0x7f0000000000)='ext4\\x00', &(0x7f0000000100)='./file0\\x00', 0x0, &(0x7f0000010020), 0x1, 0x15, &(0x7f0000000200)=\"$eJwqrqzKTszJSS0CBAAA//8TyQPi\")\n",
   235  			0,
   236  			func(p *Prog, callIndex int) bool {
   237  				// Anything is allowed except removing a call.
   238  				return len(p.Calls) > 0
   239  			},
   240  			"syz_mount_image$ext4(&(0x7f0000000000)='ext4\\x00', &(0x7f0000000100)='./file0\\x00', 0x0, &(0x7f0000010020), 0x1, 0x15, &(0x7f0000000200)=\"$eJwqrqzKTszJSS0CBAAA//8TyQPi\")\n",
   241  			0,
   242  		},
   243  	}
   244  	t.Parallel()
   245  	for ti, test := range tests {
   246  		target, err := GetTarget(test.os, test.arch)
   247  		if err != nil {
   248  			t.Fatal(err)
   249  		}
   250  		p, err := target.Deserialize([]byte(test.orig), Strict)
   251  		if err != nil {
   252  			t.Fatalf("failed to deserialize original program #%v: %v", ti, err)
   253  		}
   254  		p1, ci := Minimize(p, test.callIndex, false, test.pred)
   255  		res := p1.Serialize()
   256  		if string(res) != test.result {
   257  			t.Fatalf("minimization produced wrong result #%v\norig:\n%v\nexpect:\n%v\ngot:\n%v",
   258  				ti, test.orig, test.result, string(res))
   259  		}
   260  		if ci != test.resultCallIndex {
   261  			t.Fatalf("minimization broke call index #%v: got %v, want %v",
   262  				ti, ci, test.resultCallIndex)
   263  		}
   264  	}
   265  }
   266  
   267  func TestMinimizeRandom(t *testing.T) {
   268  	target, rs, iters := initTest(t)
   269  	iters /= 10 // Long test.
   270  	ct := target.DefaultChoiceTable()
   271  	r := rand.New(rs)
   272  	for i := 0; i < iters; i++ {
   273  		for _, crash := range []bool{false, true} {
   274  			p := target.Generate(rs, 5, ct)
   275  			copyP := p.Clone()
   276  			minP, _ := Minimize(p, len(p.Calls)-1, crash, func(p1 *Prog, callIndex int) bool {
   277  				if r.Intn(2) == 0 {
   278  					return false
   279  				}
   280  				copyP = p1.Clone()
   281  				return true
   282  			})
   283  			got := string(minP.Serialize())
   284  			want := string(copyP.Serialize())
   285  			if got != want {
   286  				t.Fatalf("program:\n%s\ngot:\n%v\nwant:\n%s", string(p.Serialize()), got, want)
   287  			}
   288  		}
   289  	}
   290  }
   291  
   292  func TestMinimizeCallIndex(t *testing.T) {
   293  	target, rs, iters := initTest(t)
   294  	ct := target.DefaultChoiceTable()
   295  	r := rand.New(rs)
   296  	for i := 0; i < iters; i++ {
   297  		p := target.Generate(rs, 5, ct)
   298  		ci := r.Intn(len(p.Calls))
   299  		p1, ci1 := Minimize(p, ci, r.Intn(2) == 0, func(p1 *Prog, callIndex int) bool {
   300  			return r.Intn(2) == 0
   301  		})
   302  		if ci1 < 0 || ci1 >= len(p1.Calls) || p.Calls[ci].Meta.Name != p1.Calls[ci1].Meta.Name {
   303  			t.Fatalf("bad call index after minimization")
   304  		}
   305  	}
   306  }