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