github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/prog/hints_test.go (about)

     1  // Copyright 2017 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  	"encoding/hex"
     8  	"fmt"
     9  	"math/rand"
    10  	"reflect"
    11  	"sort"
    12  	"strings"
    13  	"testing"
    14  
    15  	"github.com/google/go-cmp/cmp"
    16  	"github.com/google/syzkaller/pkg/image"
    17  	"github.com/stretchr/testify/assert"
    18  )
    19  
    20  type ConstArgTest struct {
    21  	name    string
    22  	in      uint64
    23  	size    uint64
    24  	bitsize uint64
    25  	comps   CompMap
    26  	res     []uint64
    27  }
    28  
    29  type DataArgTest struct {
    30  	name  string
    31  	in    string
    32  	comps CompMap
    33  	res   map[string]bool
    34  }
    35  
    36  // Tests checkConstArg(). Is not intended to check correctness of any mutations.
    37  // Mutation are checked in their own tests.
    38  func TestHintsCheckConstArg(t *testing.T) {
    39  	target := initTargetTest(t, "test", "64")
    40  	var tests = []ConstArgTest{
    41  		{
    42  			name:  "one-replacer-test",
    43  			in:    0xdeadbeef,
    44  			size:  4,
    45  			comps: CompMap{0xdeadbeef: compSet(0xdeadbeef, 0xcafebabe)},
    46  			res:   []uint64{0xcafebabe},
    47  		},
    48  		// Test for cases when there's multiple comparisons (op1, op2), (op1, op3), ...
    49  		// Checks that for every such operand a program is generated.
    50  		{
    51  			name:  "multiple-replacers-test",
    52  			in:    0xabcd,
    53  			size:  2,
    54  			comps: CompMap{0xabcd: compSet(0x32, 0x33)},
    55  			res:   []uint64{0x32, 0x33},
    56  		},
    57  		// Checks that special ints are not used.
    58  		{
    59  			name:  "special-ints-test",
    60  			in:    0xabcd,
    61  			size:  2,
    62  			comps: CompMap{0xabcd: compSet(0x1, 0x2, 0x42)},
    63  			res:   []uint64{0x42},
    64  		},
    65  
    66  		// The following tests check the size limits for each replacer and for the initial value
    67  		// of the argument. The checks are made for positive and negative values and also for bitfields.
    68  		{
    69  			name: "int8-invalid-positive-value",
    70  			in:   0x1234,
    71  			size: 1,
    72  			comps: CompMap{
    73  				// void test8(i8 el) {
    74  				//		i16 w = (i16) el
    75  				//		if (w == 0x88) {...}
    76  				//		i16 other = 0xfffe
    77  				//		if (w == other)
    78  				//  }; test8(i8(0x1234));
    79  				0x34: compSet(0x88, 0x1122, 0xfffffffffffffffe, 0xffffffffffffff0a),
    80  				// This following args should be iggnored.
    81  				0x1234:             compSet(0xa1),
    82  				0xffffffffffffff34: compSet(0xaa),
    83  			},
    84  			res: []uint64{0x88, 0xfe},
    85  		},
    86  		{
    87  			name: "int8-invalid-negative-value",
    88  			in:   0x12ab,
    89  			size: 1,
    90  			comps: CompMap{
    91  				0xab:               compSet(0xab, 0xac, 0xabcd),
    92  				0xffffffffffffffab: compSet(0x11, 0x22, 0xffffffffffffff34),
    93  			},
    94  			res: []uint64{0x11, 0x22, 0xac},
    95  		},
    96  		{
    97  			name:    "int16-valid-value-bitsize-12",
    98  			in:      0x3ab,
    99  			size:    2,
   100  			bitsize: 12,
   101  			comps: CompMap{
   102  				0x3ab:              compSet(0x11, 0x1234, 0xfffffffffffffffe),
   103  				0x13ab:             compSet(0xab, 0xffa),
   104  				0xffffffffffffffab: compSet(0xfffffffffffffff1),
   105  				0xfffffffffffff3ab: compSet(0xff1, 0x12),
   106  			},
   107  			res: []uint64{0x11, 0x3f1, 0xffe},
   108  		},
   109  		{
   110  			name:    "int16-invalid-value-bitsize-12",
   111  			in:      0x71ab,
   112  			size:    2,
   113  			bitsize: 12,
   114  			comps: CompMap{
   115  				0x1ab: compSet(0x11, 0x1234, 0xfffffffffffffffe),
   116  			},
   117  			res: []uint64{0x11, 0xffe},
   118  		},
   119  		{
   120  			name:    "int16-negative-valid-value-bitsize-12",
   121  			in:      0x8ab,
   122  			size:    2,
   123  			bitsize: 12,
   124  			comps: CompMap{
   125  				0x8ab:              compSet(0x11),
   126  				0xffffffffffffffab: compSet(0x12, 0xffffffffffffff0a),
   127  				0xfffffffffffff8ab: compSet(0x13, 0xffffffffffffff00),
   128  			},
   129  			res: []uint64{0x11, 0x13, 0x812, 0xf00},
   130  		},
   131  		{
   132  			name:    "int16-negative-invalid-value-bitsize-12",
   133  			in:      0x88ab,
   134  			size:    2,
   135  			bitsize: 12,
   136  			comps: CompMap{
   137  				0x8ab:              compSet(0x13),
   138  				0xfffffffffffff8ab: compSet(0x11, 0xffffffffffffff11),
   139  			},
   140  			res: []uint64{0x11, 0x13, 0xf11},
   141  		},
   142  		{
   143  			name: "int32-invalid-value",
   144  			in:   0xaabaddcafe,
   145  			size: 4,
   146  			comps: CompMap{0xbaddcafe: compSet(0xab, 0xabcd, 0xbaddcafe,
   147  				0xdeadbeef, 0xaabbccddeeff1122)},
   148  			res: []uint64{0xab, 0xabcd, 0xdeadbeef},
   149  		},
   150  		{
   151  			name:  "int64-valid-value",
   152  			in:    0xdeadc0debaddcafe,
   153  			size:  8,
   154  			comps: CompMap{0xdeadc0debaddcafe: compSet(0xab, 0xabcd, 0xdeadbeef, 0xdeadbeefdeadbeef)},
   155  			res:   []uint64{0xab, 0xabcd, 0xdeadbeef, 0xdeadbeefdeadbeef},
   156  		},
   157  	}
   158  	meta := target.SyscallMap["test$hint_int"]
   159  	structType := meta.Args[0].Type.(*PtrType).Elem.(*StructType)
   160  	types := make(map[string]Type)
   161  	for _, field := range structType.Fields {
   162  		types[field.Name] = field.Type
   163  	}
   164  	for _, test := range tests {
   165  		t.Run(fmt.Sprintf("%v", test.name), func(t *testing.T) {
   166  			var res []uint64
   167  			typ := types[fmt.Sprintf("int%v_%v", test.size, test.bitsize)]
   168  			constArg := MakeConstArg(typ, DirIn, test.in)
   169  			checkConstArg(constArg, nil, test.comps, func() bool {
   170  				res = append(res, constArg.Val)
   171  				return true
   172  			})
   173  			if !reflect.DeepEqual(res, test.res) {
   174  				t.Fatalf("\ngot : %v\nwant: %v", res, test.res)
   175  			}
   176  		})
   177  	}
   178  }
   179  
   180  // Tests checkDataArg(). Is not intended to check correctness of any mutations.
   181  // Mutation are checked in their own tests.
   182  func TestHintsCheckDataArg(t *testing.T) {
   183  	target := initTargetTest(t, "test", "64")
   184  	// All inputs are in Little-Endian.
   185  	var tests = []DataArgTest{
   186  		{
   187  			"one-replacer-test",
   188  			"\xef\xbe\xad\xde",
   189  			CompMap{
   190  				0xdeadbeef: compSet(0xcafebabe, 0xdeadbeef),
   191  				0xbeef:     compSet(0xbeef),
   192  				0xef:       compSet(0xef),
   193  			},
   194  			map[string]bool{
   195  				"\xbe\xba\xfe\xca": true,
   196  			},
   197  		},
   198  		// Test for cases when there's multiple comparisons (op1, op2), (op1, op3), ...
   199  		// Checks that for every such operand a program is generated.
   200  		{
   201  			"multiple-replacers-test",
   202  			"\xcd\xab\x42\x42",
   203  			CompMap{0xabcd: compSet(0x44, 0x45)},
   204  			map[string]bool{
   205  				"\x44\x00\x42\x42": true, "\x45\x00\x42\x42": true,
   206  			},
   207  		},
   208  		// Checks that special ints are not used.
   209  		{
   210  			"special-ints-test",
   211  			"\xcd\xab\x42\x42",
   212  			CompMap{0xabcd: compSet(0x1, 0x45)},
   213  			map[string]bool{
   214  				"\x45\x00\x42\x42": true,
   215  			},
   216  		},
   217  		// Checks that ints of various sizes are extracted.
   218  		{
   219  			"different-sizes-test",
   220  			"\xef\xcd\xab\x90\x78\x56\x34\x12",
   221  			CompMap{
   222  				0xef:               compSet(0x11),
   223  				0xcdef:             compSet(0x2222),
   224  				0x90abcdef:         compSet(0x33333333),
   225  				0x1234567890abcdef: compSet(0x4444444444444444),
   226  			},
   227  			map[string]bool{
   228  				"\x11\xcd\xab\x90\x78\x56\x34\x12": true,
   229  				"\x22\x22\xab\x90\x78\x56\x34\x12": true,
   230  				"\x33\x33\x33\x33\x78\x56\x34\x12": true,
   231  				"\x44\x44\x44\x44\x44\x44\x44\x44": true,
   232  			},
   233  		},
   234  		// Checks that values with different offsets are extracted.
   235  		{
   236  			"different-offsets-test",
   237  			"\xab\xab\xab\xab\xab\xab\xab\xab\xab",
   238  			CompMap{
   239  				0xab:               compSet(0x11),
   240  				0xabab:             compSet(0x2222),
   241  				0xabababab:         compSet(0x33333333),
   242  				0xabababababababab: compSet(0x4444444444444444),
   243  			},
   244  			map[string]bool{
   245  				"\x11\xab\xab\xab\xab\xab\xab\xab\xab": true,
   246  				"\xab\x11\xab\xab\xab\xab\xab\xab\xab": true,
   247  				"\xab\xab\x11\xab\xab\xab\xab\xab\xab": true,
   248  				"\xab\xab\xab\x11\xab\xab\xab\xab\xab": true,
   249  				"\xab\xab\xab\xab\x11\xab\xab\xab\xab": true,
   250  				"\xab\xab\xab\xab\xab\x11\xab\xab\xab": true,
   251  				"\xab\xab\xab\xab\xab\xab\x11\xab\xab": true,
   252  				"\xab\xab\xab\xab\xab\xab\xab\x11\xab": true,
   253  				"\xab\xab\xab\xab\xab\xab\xab\xab\x11": true,
   254  				"\x22\x22\xab\xab\xab\xab\xab\xab\xab": true,
   255  				"\xab\x22\x22\xab\xab\xab\xab\xab\xab": true,
   256  				"\xab\xab\x22\x22\xab\xab\xab\xab\xab": true,
   257  				"\xab\xab\xab\x22\x22\xab\xab\xab\xab": true,
   258  				"\xab\xab\xab\xab\x22\x22\xab\xab\xab": true,
   259  				"\xab\xab\xab\xab\xab\x22\x22\xab\xab": true,
   260  				"\xab\xab\xab\xab\xab\xab\x22\x22\xab": true,
   261  				"\xab\xab\xab\xab\xab\xab\xab\x22\x22": true,
   262  				"\x33\x33\x33\x33\xab\xab\xab\xab\xab": true,
   263  				"\xab\x33\x33\x33\x33\xab\xab\xab\xab": true,
   264  				"\xab\xab\x33\x33\x33\x33\xab\xab\xab": true,
   265  				"\xab\xab\xab\x33\x33\x33\x33\xab\xab": true,
   266  				"\xab\xab\xab\xab\x33\x33\x33\x33\xab": true,
   267  				"\xab\xab\xab\xab\xab\x33\x33\x33\x33": true,
   268  				"\x44\x44\x44\x44\x44\x44\x44\x44\xab": true,
   269  				"\xab\x44\x44\x44\x44\x44\x44\x44\x44": true,
   270  			},
   271  		},
   272  		{
   273  			"replace-in-the-middle-of-a-larger-blob",
   274  			"\xef\xcd\xab\x90\x78\x56\x34\x12",
   275  			CompMap{0xffffffffffff90ab: compSet(0xffffffffffffaabb)},
   276  			map[string]bool{
   277  				"\xef\xcd\xbb\xaa\x78\x56\x34\x12": true,
   278  			},
   279  		},
   280  		{
   281  
   282  			"big-endian-replace",
   283  			"\xef\xcd\xab\x90\x78\x56\x34\x12",
   284  			CompMap{
   285  				// 0xff07 is reversed special int.
   286  				0xefcd:             compSet(0xaabb, 0xff07),
   287  				0x3412:             compSet(0xaabb, 0xff07),
   288  				0x9078:             compSet(0xaabb, 0x11223344, 0xff07),
   289  				0x90785634:         compSet(0xaabbccdd, 0x11223344),
   290  				0xefcdab9078563412: compSet(0x1122334455667788),
   291  			},
   292  			map[string]bool{
   293  				"\xaa\xbb\xab\x90\x78\x56\x34\x12": true,
   294  				"\xef\xcd\xab\x90\x78\x56\xaa\xbb": true,
   295  				"\xef\xcd\xab\xaa\xbb\x56\x34\x12": true,
   296  				"\xef\xcd\xab\xaa\xbb\xcc\xdd\x12": true,
   297  				"\xef\xcd\xab\x11\x22\x33\x44\x12": true,
   298  				"\x11\x22\x33\x44\x55\x66\x77\x88": true,
   299  			},
   300  		},
   301  	}
   302  	meta := target.SyscallMap["test$hint_data"]
   303  	typ := meta.Args[0].Type.(*PtrType).Elem // array[int8]
   304  	for _, test := range tests {
   305  		t.Run(fmt.Sprintf("%v", test.name), func(t *testing.T) {
   306  			res := make(map[string]bool)
   307  			dataArg := MakeDataArg(typ, DirIn, []byte(test.in))
   308  			checkDataArg(dataArg, test.comps, func() bool {
   309  				res[string(dataArg.Data())] = true
   310  				return true
   311  			})
   312  			if !reflect.DeepEqual(res, test.res) {
   313  				s := "\ngot:  ["
   314  				for x := range res {
   315  					s += fmt.Sprintf("0x%x, ", x)
   316  				}
   317  				s += "]\nwant: ["
   318  				for x := range test.res {
   319  					s += fmt.Sprintf("0x%x, ", x)
   320  				}
   321  				s += "]\n"
   322  				t.Fatal(s)
   323  			}
   324  		})
   325  	}
   326  }
   327  
   328  func TestHintsCompressedImage(t *testing.T) {
   329  	target := initTargetTest(t, "test", "64")
   330  	type Test struct {
   331  		input  string
   332  		comps  CompMap
   333  		output []string
   334  	}
   335  	var tests = []Test{
   336  		{
   337  			"\x00\x11\x22\x33\x44\x55\x66\x77",
   338  			CompMap{
   339  				// 1/2-bytes must not be replaced.
   340  				0x00:   compSet(0xaa),
   341  				0x11:   compSet(0xaa),
   342  				0x1122: compSet(0xaabb),
   343  				0x4455: compSet(0xaabb),
   344  				// Aligned 4-byte values are replaced in both little/big endian.
   345  				0x00112233: compSet(0xaabbccdd),
   346  				0x33221100: compSet(0xaabbccdd),
   347  				0x44556677: compSet(0xaabbccdd),
   348  				0x77665544: compSet(0xaabbccdd),
   349  				// Same for 8-byte values.
   350  				0x0011223344556677: compSet(0xaabbccddeeff9988),
   351  				0x7766554433221100: compSet(0xaabbccddeeff9988),
   352  				// Unaligned 4-bytes are not replaced.
   353  				0x11223344: compSet(0xaabbccdd),
   354  				0x22334455: compSet(0xaabbccdd),
   355  			},
   356  			[]string{
   357  				// Mutants for 4-byte values.
   358  				"\xaa\xbb\xcc\xdd\x44\x55\x66\x77",
   359  				"\xdd\xcc\xbb\xaa\x44\x55\x66\x77",
   360  				"\x00\x11\x22\x33\xaa\xbb\xcc\xdd",
   361  				"\x00\x11\x22\x33\xdd\xcc\xbb\xaa",
   362  				// Mutants for 8-byte values.
   363  				"\xaa\xbb\xcc\xdd\xee\xff\x99\x88",
   364  				"\x88\x99\xff\xee\xdd\xcc\xbb\xaa",
   365  			},
   366  		},
   367  		{
   368  			"\x00\x11\x22\x33\x44\x55\x66\x77",
   369  			CompMap{
   370  				// Special values are used as replacers.
   371  				0x00112233: compSet(0, 0xffffffff),
   372  			},
   373  			[]string{
   374  				// Mutants for 4-byte values.
   375  				"\x00\x00\x00\x00\x44\x55\x66\x77",
   376  				"\xff\xff\xff\xff\x44\x55\x66\x77",
   377  			},
   378  		},
   379  		{
   380  			// All 0s and 0xff must not be replaced.
   381  			"\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00",
   382  			CompMap{
   383  				0:                  compSet(0xaabbccdd),
   384  				0xffffffffffffffff: compSet(0xaabbccddaabbccdd),
   385  			},
   386  			nil,
   387  		},
   388  	}
   389  	typ := target.SyscallMap["serialize3"].Args[0].Type.(*PtrType).Elem.(*BufferType)
   390  	if typ.Kind != BufferCompressed {
   391  		panic("wrong arg type")
   392  	}
   393  	for i, test := range tests {
   394  		t.Run(fmt.Sprintf("%v", i), func(t *testing.T) {
   395  			var res []string
   396  			arg := MakeDataArg(typ, DirIn, image.Compress([]byte(test.input)))
   397  			generateHints(test.comps, arg, nil, func() bool {
   398  				res = append(res, string(arg.Data()))
   399  				return true
   400  			})
   401  			for i, compressed := range res {
   402  				data, dtor := image.MustDecompress([]byte(compressed))
   403  				res[i] = string(data)
   404  				dtor()
   405  			}
   406  			sort.Strings(res)
   407  			sort.Strings(test.output)
   408  			if diff := cmp.Diff(test.output, res); diff != "" {
   409  				t.Fatalf("got wrong mutants: %v", diff)
   410  			}
   411  			data, dtor := image.MustDecompress(arg.Data())
   412  			defer dtor()
   413  			if diff := cmp.Diff(test.input, string(data)); diff != "" {
   414  				t.Fatalf("argument got changed afterwards: %v", diff)
   415  			}
   416  		})
   417  	}
   418  }
   419  
   420  func TestHintsShrinkExpand(t *testing.T) {
   421  	t.Parallel()
   422  	// Naming conventions:
   423  	// b  - byte  variable (i8 or u8)
   424  	// w  - word  variable (i16 or u16)
   425  	// dw - dword variable (i32 or u32)
   426  	// qw - qword variable (i64 or u64)
   427  	// -----------------------------------------------------------------
   428  	// Shrink tests:
   429  	var tests = []ConstArgTest{
   430  		{
   431  			// Models the following code:
   432  			// void f(u16 w) {
   433  			//		u8 b = (u8) w;
   434  			//		if (b == 0xab) {...}
   435  			//		if (w == 0xcdcd) {...}
   436  			//  }; f(0x1234);
   437  			name: "shrink-16-test",
   438  			in:   0x1234,
   439  			comps: CompMap{
   440  				0x34:   compSet(0xab),
   441  				0x1234: compSet(0xcdcd),
   442  			},
   443  			res: []uint64{0x12ab, 0xcdcd},
   444  		},
   445  		{
   446  			// Models the following code:
   447  			// void f(u32 dw) {
   448  			//		u8 b = (u8) dw
   449  			//		i16 w = (i16) dw
   450  			//		if (b == 0xab) {...}
   451  			//		if (w == 0xcdcd) {...}
   452  			//		if (dw == 0xefefefef) {...}
   453  			//  }; f(0x12345678);
   454  			name: "shrink-32-test",
   455  			in:   0x12345678,
   456  			comps: CompMap{
   457  				0x78:       compSet(0xab),
   458  				0x5678:     compSet(0xcdcd),
   459  				0x12345678: compSet(0xefefefef),
   460  			},
   461  			res: []uint64{0x123456ab, 0x1234cdcd, 0xefefefef},
   462  		},
   463  		{
   464  			// Models the following code:
   465  			// void f(u64 qw) {
   466  			//		u8 b = (u8) qw
   467  			//		u16 w = (u16) qw
   468  			//		u32 dw = (u32) qw
   469  			//		if (b == 0xab) {...}
   470  			//		if (w == 0xcdcd) {...}
   471  			//		if (dw == 0xefefefef) {...}
   472  			//		if (qw == 0x0101010101010101) {...}
   473  			//  }; f(0x1234567890abcdef);
   474  			name: "shrink-64-test",
   475  			in:   0x1234567890abcdef,
   476  			comps: CompMap{
   477  				0xef:               compSet(0xab, 0xef),
   478  				0xcdef:             compSet(0xcdcd),
   479  				0x90abcdef:         compSet(0xefefefef),
   480  				0x1234567890abcdef: compSet(0x0101010101010101),
   481  			},
   482  			res: []uint64{
   483  				0x0101010101010101,
   484  				0x1234567890abcdab,
   485  				0x1234567890abcdcd,
   486  				0x12345678efefefef,
   487  			},
   488  		},
   489  		{
   490  			// Models the following code:
   491  			// void f(i16 w) {
   492  			//		i8 b = (i8) w;
   493  			//		i16 other = 0xabab;
   494  			//		if (b == other) {...}
   495  			//  }; f(0x1234);
   496  			// In such code the comparison will never be true, so we don't
   497  			// generate a hint for it.
   498  			name:  "shrink-with-a-wider-replacer-test1",
   499  			in:    0x1234,
   500  			comps: CompMap{0x34: compSet(0x1bab)},
   501  			res:   nil,
   502  		},
   503  		{
   504  			// Models the following code:
   505  			// void f(i16 w) {
   506  			//		i8 b = (i8) w;
   507  			//		i16 other = 0xfffd;
   508  			//		if (b == other) {...}
   509  			//  }; f(0x1234);
   510  			// In such code b will be sign extended to 0xff34 and, if we replace
   511  			// the lower byte, then the if statement will be true.
   512  			// Note that executor sign extends all the comparison operands to
   513  			// int64, so we model this accordingly.
   514  			name:  "shrink-with-a-wider-replacer-test2",
   515  			in:    0x1234,
   516  			comps: CompMap{0x34: compSet(0xfffffffffffffffd)},
   517  			res:   []uint64{0x12fd},
   518  		},
   519  		// -----------------------------------------------------------------
   520  		// Extend tests:
   521  		// Note that executor sign extends all the comparison operands to int64,
   522  		// so we model this accordingly.
   523  		{
   524  			// Models the following code:
   525  			// void f(i8 b) {
   526  			//		i64 qw = (i64) b;
   527  			//		if (qw == -2) {...};
   528  			// }; f(-1);
   529  			name:  "extend-8-test",
   530  			in:    0xff,
   531  			comps: CompMap{0xffffffffffffffff: compSet(0xfffffffffffffffe)},
   532  			res:   []uint64{0xfe},
   533  		},
   534  		{
   535  			// Models the following code:
   536  			// void f(i16 w) {
   537  			//		i64 qw = (i64) w;
   538  			//		if (qw == -2) {...};
   539  			// }; f(-1);
   540  			name:  "extend-16-test",
   541  			in:    0xffff,
   542  			comps: CompMap{0xffffffffffffffff: compSet(0xfffffffffffffffe)},
   543  			res:   []uint64{0xfffe},
   544  		},
   545  		{
   546  			// Models the following code:
   547  			// void f(i32 dw) {
   548  			//		i64 qw = (i32) dw;
   549  			//		if (qw == -2) {...};
   550  			// }; f(-1);
   551  			name:  "extend-32-test",
   552  			in:    0xffffffff,
   553  			comps: CompMap{0xffffffffffffffff: compSet(0xfffffffffffffffe)},
   554  			res:   []uint64{0xfffffffe},
   555  		},
   556  		{
   557  			// Models the following code:
   558  			// void f(i8 b) {
   559  			//		i16 w = (i16) b;
   560  			//		if (w == (i16) 0xfeff) {...};
   561  			// }; f(-1);
   562  			// There's no value for b that will make the comparison true,
   563  			// so we don't generate hints.
   564  			name:  "extend-with-a-wider-replacer-test",
   565  			in:    0xff,
   566  			comps: CompMap{0xffffffffffffffff: compSet(0xfffffffffffffeff)},
   567  			res:   nil,
   568  		},
   569  	}
   570  	for _, test := range tests {
   571  		t.Run(fmt.Sprintf("%v", test.name), func(t *testing.T) {
   572  			res := shrinkExpand(test.in, test.comps, 64, false)
   573  			if !reflect.DeepEqual(res, test.res) {
   574  				t.Fatalf("\ngot : %v\nwant: %v", res, test.res)
   575  			}
   576  		})
   577  	}
   578  }
   579  
   580  func TestHintsCall(t *testing.T) {
   581  	target := initTargetTest(t, "test", "64")
   582  	type Test struct {
   583  		in    string
   584  		comps CompMap
   585  		out   []string
   586  	}
   587  	tests := []Test{
   588  		{
   589  			in:    `ioctl$1(0x0, 0x111, 0x0)`,
   590  			comps: CompMap{0x111: compSet(0x0, 0x111, 0x222, 0x333, 0x444, 0x666)},
   591  			out: []string{
   592  				`ioctl$1(0x0, 0x666, 0x0)`,
   593  			},
   594  		},
   595  		{
   596  			// For the generic syscall mutations should not be restricted by related calls.
   597  			// But we won't have 0x1000 and 0x10000 because they are special ints.
   598  			in: `socket$generic(0x1, 0x2, 0x3)`,
   599  			comps: CompMap{
   600  				0x1: compSet(0x111, 0x333),
   601  				0x2: compSet(0x1000, 0x1100, 0x1200),
   602  				0x3: compSet(0x10000, 0x10100, 0x10200),
   603  			},
   604  			out: []string{
   605  				`socket$generic(0x333, 0x2, 0x3)`,
   606  			},
   607  		},
   608  		{
   609  			in: `socket$inet6(0x111, 0x222, 0x333)`,
   610  			comps: CompMap{
   611  				0x111: compSet(0x211),
   612  				0x222: compSet(0x1100, 0x1200, 0x1300),
   613  				0x333: compSet(0x10000, 0x10100, 0x10200, 0x10300),
   614  			},
   615  			out: []string{
   616  				`socket$inet6(0x111, 0x222, 0x10100)`,
   617  				`socket$inet6(0x111, 0x222, 0x10200)`,
   618  				`socket$inet6(0x111, 0x222, 0x10300)`,
   619  			},
   620  		},
   621  		{
   622  			in: `socket$netlink(0x111, 0x222, 0x333)`,
   623  			comps: CompMap{
   624  				0x111: compSet(0x211),
   625  				0x222: compSet(0x1100, 0x1200, 0x1300),
   626  				0x333: compSet(0x10000, 0x10100, 0x10200, 0x10300),
   627  			},
   628  			out: []string{
   629  				`socket$netlink(0x111, 0x1100, 0x333)`,
   630  				`socket$netlink(0x111, 0x1200, 0x333)`,
   631  				`socket$netlink(0x111, 0x1300, 0x333)`,
   632  			},
   633  		},
   634  	}
   635  	for i, test := range tests {
   636  		t.Run(fmt.Sprint(i), func(t *testing.T) {
   637  			p, err := target.Deserialize([]byte(test.in), Strict)
   638  			if err != nil {
   639  				t.Fatal(err)
   640  			}
   641  			var got []string
   642  			p.MutateWithHints(0, test.comps, func(newP *Prog) bool {
   643  				got = append(got, strings.TrimSpace(string(newP.Serialize())))
   644  				return true
   645  			})
   646  			assert.ElementsMatch(t, test.out, got)
   647  		})
   648  	}
   649  }
   650  
   651  func TestHintsRandom(t *testing.T) {
   652  	target, rs, iters := initTest(t)
   653  	ct := target.DefaultChoiceTable()
   654  	iters /= 10 // the test takes long
   655  	r := newRand(target, rs)
   656  	for i := 0; i < iters; i++ {
   657  		p := target.Generate(rs, 5, ct)
   658  		for j, c := range p.Calls {
   659  			vals := extractValues(c)
   660  			for k := 0; k < 5; k++ {
   661  				vals[r.randInt64()] = true
   662  			}
   663  			// In the test mode, MutateWithHints is essentially quadratic over the number of values
   664  			// since we run full prog validation on each run.
   665  			// To avoid consuming too much time, let's just skip all calls that are too big.
   666  			const valsCutOff = 10000
   667  			if len(vals) > valsCutOff {
   668  				t.Logf("iter %d: skipping call %d - too big", i, j)
   669  				continue
   670  			}
   671  			comps := make(CompMap)
   672  			for v := range vals {
   673  				comps.Add(1, v, r.randInt64(), true)
   674  			}
   675  			p.MutateWithHints(j, comps, func(p1 *Prog) bool { return true })
   676  		}
   677  	}
   678  }
   679  
   680  func extractValues(c *Call) map[uint64]bool {
   681  	vals := make(map[uint64]bool)
   682  	ForeachArg(c, func(arg Arg, _ *ArgCtx) {
   683  		if arg.Dir() == DirOut {
   684  			return
   685  		}
   686  		switch a := arg.(type) {
   687  		case *ConstArg:
   688  			vals[a.Val] = true
   689  		case *DataArg:
   690  			data := a.Data()
   691  			for i := range data {
   692  				vals[uint64(data[i])] = true
   693  				if i < len(data)-1 {
   694  					v := uint64(data[i]) | uint64(data[i+1])<<8
   695  					vals[v] = true
   696  				}
   697  				if i < len(data)-3 {
   698  					v := uint64(data[i]) | uint64(data[i+1])<<8 |
   699  						uint64(data[i+2])<<16 | uint64(data[i+3])<<24
   700  					vals[v] = true
   701  				}
   702  				if i < len(data)-7 {
   703  					v := uint64(data[i]) | uint64(data[i+1])<<8 |
   704  						uint64(data[i+2])<<16 | uint64(data[i+3])<<24 |
   705  						uint64(data[i+4])<<32 | uint64(data[i+5])<<40 |
   706  						uint64(data[i+6])<<48 | uint64(data[i+7])<<56
   707  					vals[v] = true
   708  				}
   709  			}
   710  		}
   711  	})
   712  	delete(vals, 0) // replacing 0 can yield too many condidates
   713  	return vals
   714  }
   715  
   716  func TestHintsData(t *testing.T) {
   717  	target := initTargetTest(t, "test", "64")
   718  	type Test struct {
   719  		in    string
   720  		comps CompMap
   721  		out   []string
   722  	}
   723  	tests := []Test{
   724  		{
   725  			in:    "0809101112131415",
   726  			comps: CompMap{0x12111009: compSet(0x42)},
   727  			out:   []string{"0842000000131415"},
   728  		},
   729  	}
   730  	for _, test := range tests {
   731  		p, err := target.Deserialize([]byte(fmt.Sprintf("test$hint_data(&AUTO=\"%v\")", test.in)), Strict)
   732  		if err != nil {
   733  			t.Fatal(err)
   734  		}
   735  		var got []string
   736  		p.MutateWithHints(0, test.comps, func(newP *Prog) bool {
   737  			got = append(got, hex.EncodeToString(
   738  				newP.Calls[0].Args[0].(*PointerArg).Res.(*DataArg).Data()))
   739  			return true
   740  		})
   741  		sort.Strings(test.out)
   742  		sort.Strings(got)
   743  		if !reflect.DeepEqual(got, test.out) {
   744  			t.Fatalf("comps: %v\ninput: %v\ngot : %+v\nwant: %+v",
   745  				test.comps, test.in, got, test.out)
   746  		}
   747  	}
   748  }
   749  
   750  func TestInplaceIntersect(t *testing.T) {
   751  	m1 := CompMap{
   752  		0xdead: compSet(0x1, 0x2),
   753  		0xbeef: compSet(0x3, 0x4),
   754  		0xffff: compSet(0x5),
   755  	}
   756  	m2 := CompMap{
   757  		0xdead: compSet(0x2),
   758  		0xbeef: compSet(0x3, 0x6),
   759  		0xeeee: compSet(0x6),
   760  	}
   761  	m1.InplaceIntersect(m2)
   762  	assert.Equal(t, CompMap{
   763  		0xdead: compSet(0x2),
   764  		0xbeef: compSet(0x3),
   765  	}, m1)
   766  }
   767  
   768  func BenchmarkHints(b *testing.B) {
   769  	target, cleanup := initBench(b)
   770  	defer cleanup()
   771  	rs := rand.NewSource(0)
   772  	r := newRand(target, rs)
   773  	ct := target.DefaultChoiceTable()
   774  	p := target.Generate(rs, 30, ct)
   775  	comps := make([]CompMap, len(p.Calls))
   776  	for i, c := range p.Calls {
   777  		vals := extractValues(c)
   778  		for j := 0; j < 5; j++ {
   779  			vals[r.randInt64()] = true
   780  		}
   781  		comps[i] = make(CompMap)
   782  		for v := range vals {
   783  			comps[i].Add(1, v, r.randInt64(), true)
   784  		}
   785  	}
   786  	b.RunParallel(func(pb *testing.PB) {
   787  		for pb.Next() {
   788  			for i := range p.Calls {
   789  				p.MutateWithHints(i, comps[i], func(p1 *Prog) bool { return true })
   790  			}
   791  		}
   792  	})
   793  }
   794  
   795  func TestHintsLimiter(t *testing.T) {
   796  	var limiter HintsLimiter
   797  
   798  	// Base case.
   799  	comps := make(CompMap)
   800  	comps.Add(1000, 1000, 1100, true)
   801  	for i := uint64(0); i < 9; i++ {
   802  		comps.Add(2000, 2000+i, 2100+i, true)
   803  	}
   804  	for i := uint64(0); i < 10; i++ {
   805  		comps.Add(3000, 3000+i, 3100+i, true)
   806  	}
   807  	for i := uint64(0); i < 11; i++ {
   808  		comps.Add(4000, 4000+i, 4100+i, true)
   809  	}
   810  	for i := uint64(0); i < 20; i++ {
   811  		comps.Add(5000, 5000+i, 5100+i, true)
   812  	}
   813  	assert.Equal(t, perPCCount(comps), map[uint64]int{
   814  		1000: 1,
   815  		2000: 9,
   816  		3000: 10,
   817  		4000: 11,
   818  		5000: 20,
   819  	})
   820  	limiter.Limit(comps)
   821  	assert.Equal(t, perPCCount(comps), map[uint64]int{
   822  		1000: 1,
   823  		2000: 9,
   824  		3000: 10,
   825  		4000: 10,
   826  		5000: 10,
   827  	})
   828  
   829  	// Test that counts are accumulated in the limiter.
   830  	comps = make(CompMap)
   831  	for i := uint64(0); i < 3; i++ {
   832  		comps.Add(1000, 1000+i, 1100+i, true)
   833  	}
   834  	for i := uint64(0); i < 3; i++ {
   835  		comps.Add(2000, 2000+i, 2100+i, true)
   836  	}
   837  	for i := uint64(0); i < 3; i++ {
   838  		comps.Add(3000, 3000+i, 3100+i, true)
   839  	}
   840  	assert.Equal(t, perPCCount(comps), map[uint64]int{
   841  		1000: 3,
   842  		2000: 3,
   843  		3000: 3,
   844  	})
   845  	limiter.Limit(comps)
   846  	assert.Equal(t, perPCCount(comps), map[uint64]int{
   847  		1000: 3,
   848  		2000: 1,
   849  	})
   850  }
   851  
   852  func perPCCount(comps CompMap) map[uint64]int {
   853  	res := make(map[uint64]int)
   854  	for _, ops2 := range comps {
   855  		for _, pcs := range ops2 {
   856  			for pc := range pcs {
   857  				res[pc]++
   858  			}
   859  		}
   860  	}
   861  	return res
   862  }
   863  
   864  func compSet(vals ...uint64) map[uint64]map[uint64]bool {
   865  	m := make(map[uint64]map[uint64]bool)
   866  	for _, v := range vals {
   867  		if m[v] == nil {
   868  			m[v] = make(map[uint64]bool)
   869  		}
   870  		m[v][1] = true
   871  	}
   872  	return m
   873  }