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

     1  // Copyright 2016 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  	"bytes"
     8  	"encoding/binary"
     9  	"fmt"
    10  	"reflect"
    11  	"testing"
    12  
    13  	"github.com/bsm/histogram/v3"
    14  )
    15  
    16  func TestSerializeForExecRandom(t *testing.T) {
    17  	target, rs, iters := initTest(t)
    18  	ct := target.DefaultChoiceTable()
    19  	execSizes := histogram.New(1000)
    20  	textSizes := histogram.New(1000)
    21  	totalSize := 0
    22  	sizes := make(map[string]int)
    23  	for i := 0; i < iters; i++ {
    24  		p := target.Generate(rs, 10, ct)
    25  		buf, err := p.SerializeForExec()
    26  		if err != nil {
    27  			t.Fatalf("failed to serialize: %v", err)
    28  		}
    29  		got, err := target.DeserializeExec(buf, sizes)
    30  		if err != nil {
    31  			t.Fatal(err)
    32  		}
    33  		if n, err := ExecCallCount(buf); err != nil {
    34  			t.Fatal(err)
    35  		} else if n != len(got.Calls) {
    36  			t.Fatalf("mismatching number of calls: %v/%v", n, len(got.Calls))
    37  		}
    38  		totalSize += len(buf)
    39  		execSizes.Add(float64(len(buf)))
    40  		textSizes.Add(float64(len(p.Serialize())))
    41  	}
    42  	t.Logf("exec sizes: 10%%:%v 50%%:%v 90%%:%v",
    43  		int(execSizes.Quantile(0.1)), int(execSizes.Quantile(0.5)), int(execSizes.Quantile(0.9)))
    44  	t.Logf("text sizes: 10%%:%v 50%%:%v 90%%:%v",
    45  		int(textSizes.Quantile(0.1)), int(textSizes.Quantile(0.5)), int(textSizes.Quantile(0.9)))
    46  	for what, size := range sizes {
    47  		t.Logf("%-24v: %5.2f%%", what, float64(size)/float64(totalSize)*100)
    48  	}
    49  }
    50  
    51  // nolint: funlen
    52  func TestSerializeForExec(t *testing.T) {
    53  	target := initTargetTest(t, "test", "64")
    54  	var (
    55  		dataOffset = target.DataOffset
    56  		ptrSize    = target.PtrSize
    57  	)
    58  	callID := func(name string) uint64 {
    59  		c := target.SyscallMap[name]
    60  		if c == nil {
    61  			t.Fatalf("unknown syscall %v", name)
    62  		}
    63  		return uint64(c.ID)
    64  	}
    65  	tests := []struct {
    66  		prog       string
    67  		serialized []any
    68  		decoded    *ExecProg
    69  	}{
    70  		{
    71  			"test()",
    72  			[]any{
    73  				callID("test"), ExecNoCopyout, 0,
    74  				execInstrEOF,
    75  			},
    76  			&ExecProg{
    77  				Calls: []ExecCall{
    78  					{
    79  						Meta:  target.SyscallMap["test"],
    80  						Index: ExecNoCopyout,
    81  					},
    82  				},
    83  			},
    84  		},
    85  		{
    86  			"test$int(0x1, 0x2, 0x3, 0x4, 0x5)",
    87  			[]any{
    88  				callID("test$int"), ExecNoCopyout, 5,
    89  				execArgConst, 8, 1,
    90  				execArgConst, 1, 2,
    91  				execArgConst, 2, 3,
    92  				execArgConst, 4, 4,
    93  				execArgConst, 8, 5,
    94  				execInstrEOF,
    95  			},
    96  			nil,
    97  		},
    98  		{
    99  			"test$align0(&(0x7f0000000000)={0x1, 0x2, 0x3, 0x4, 0x5})",
   100  			[]any{
   101  				execInstrCopyin, 0, execArgConst, 2, 1,
   102  				execInstrCopyin, 4, execArgConst, 4, 2,
   103  				execInstrCopyin, 8, execArgConst, 1, 3,
   104  				execInstrCopyin, 10, execArgConst, 2, 4,
   105  				execInstrCopyin, 16, execArgConst, 8, 5,
   106  				callID("test$align0"), ExecNoCopyout, 1, execArgAddr64, 0,
   107  				execInstrEOF,
   108  			},
   109  			nil,
   110  		},
   111  		{
   112  			"test$align1(&(0x7f0000000000)={0x1, 0x2, 0x3, 0x4, 0x5})",
   113  			[]any{
   114  				execInstrCopyin, 0, execArgConst, 2, 1,
   115  				execInstrCopyin, 2, execArgConst, 4, 2,
   116  				execInstrCopyin, 6, execArgConst, 1, 3,
   117  				execInstrCopyin, 7, execArgConst, 2, 4,
   118  				execInstrCopyin, 9, execArgConst, 8, 5,
   119  				callID("test$align1"), ExecNoCopyout, 1, execArgAddr64, 0,
   120  				execInstrEOF,
   121  			},
   122  			nil,
   123  		},
   124  		{
   125  			"test$align2(&(0x7f0000000000)={0x42, {[0x43]}, {[0x44]}})",
   126  			[]any{
   127  				execInstrCopyin, 0, execArgConst, 1, 0x42,
   128  				execInstrCopyin, 1, execArgConst, 2, 0x43,
   129  				execInstrCopyin, 4, execArgConst, 2, 0x44,
   130  				callID("test$align2"), ExecNoCopyout, 1, execArgAddr64, 0,
   131  				execInstrEOF,
   132  			},
   133  			nil,
   134  		},
   135  		{
   136  			"test$align3(&(0x7f0000000000)={0x42, {0x43}, {0x44}})",
   137  			[]any{
   138  				execInstrCopyin, 0, execArgConst, 1, 0x42,
   139  				execInstrCopyin, 1, execArgConst, 1, 0x43,
   140  				execInstrCopyin, 4, execArgConst, 1, 0x44,
   141  				callID("test$align3"), ExecNoCopyout, 1, execArgAddr64, 0,
   142  				execInstrEOF,
   143  			},
   144  			nil,
   145  		},
   146  		{
   147  			"test$align4(&(0x7f0000000000)={{0x42, 0x43}, 0x44})",
   148  			[]any{
   149  				execInstrCopyin, 0, execArgConst, 1, 0x42,
   150  				execInstrCopyin, 1, execArgConst, 2, 0x43,
   151  				execInstrCopyin, 4, execArgConst, 1, 0x44,
   152  				callID("test$align4"), ExecNoCopyout, 1, execArgAddr64, 0,
   153  				execInstrEOF,
   154  			},
   155  			nil,
   156  		},
   157  		{
   158  			"test$align5(&(0x7f0000000000)={{0x42, []}, {0x43, [0x44, 0x45, 0x46]}, 0x47})",
   159  			[]any{
   160  				execInstrCopyin, 0, execArgConst, 8, 0x42,
   161  				execInstrCopyin, 8, execArgConst, 8, 0x43,
   162  				execInstrCopyin, 16, execArgConst, 2, 0x44,
   163  				execInstrCopyin, 18, execArgConst, 2, 0x45,
   164  				execInstrCopyin, 20, execArgConst, 2, 0x46,
   165  				execInstrCopyin, 22, execArgConst, 1, 0x47,
   166  				callID("test$align5"), ExecNoCopyout, 1, execArgAddr64, 0,
   167  				execInstrEOF,
   168  			},
   169  			nil,
   170  		},
   171  		{
   172  			"test$align6(&(0x7f0000000000)={0x42, [0x43]})",
   173  			[]any{
   174  				execInstrCopyin, 0, execArgConst, 1, 0x42,
   175  				execInstrCopyin, 4, execArgConst, 4, 0x43,
   176  				callID("test$align6"), ExecNoCopyout, 1, execArgAddr64, 0,
   177  				execInstrEOF,
   178  			},
   179  			nil,
   180  		},
   181  		{
   182  			"test$union0(&(0x7f0000000000)={0x1, @f2=0x2})",
   183  			[]any{
   184  				execInstrCopyin, 0, execArgConst, 8, 1,
   185  				execInstrCopyin, 8, execArgConst, 1, 2,
   186  				callID("test$union0"), ExecNoCopyout, 1, execArgAddr64, 0,
   187  				execInstrEOF,
   188  			},
   189  			nil,
   190  		},
   191  		{
   192  			"test$union1(&(0x7f0000000000)={@f1=0x42, 0x43})",
   193  			[]any{
   194  				execInstrCopyin, 0, execArgConst, 4, 0x42,
   195  				execInstrCopyin, 8, execArgConst, 1, 0x43,
   196  				callID("test$union1"), ExecNoCopyout, 1, execArgAddr64, 0,
   197  				execInstrEOF,
   198  			},
   199  			nil,
   200  		},
   201  		{
   202  			"test$union2(&(0x7f0000000000)={@f1=0x42, 0x43})",
   203  			[]any{
   204  				execInstrCopyin, 0, execArgConst, 4, 0x42,
   205  				execInstrCopyin, 4, execArgConst, 1, 0x43,
   206  				callID("test$union2"), ExecNoCopyout, 1, execArgAddr64, 0,
   207  				execInstrEOF,
   208  			},
   209  			nil,
   210  		},
   211  		{
   212  			"test$array0(&(0x7f0000000000)={0x1, [@f0=0x2, @f1=0x3], 0x4})",
   213  			[]any{
   214  				execInstrCopyin, 0, execArgConst, 1, 1,
   215  				execInstrCopyin, 1, execArgConst, 2, 2,
   216  				execInstrCopyin, 3, execArgConst, 8, 3,
   217  				execInstrCopyin, 11, execArgConst, 8, 4,
   218  				callID("test$array0"), ExecNoCopyout, 1, execArgAddr64, 0,
   219  				execInstrEOF,
   220  			},
   221  			nil,
   222  		},
   223  		{
   224  			"test$array1(&(0x7f0000000000)={0x42, \"0102030405\"})",
   225  			[]any{
   226  				execInstrCopyin, 0, execArgConst, 1, 0x42,
   227  				execInstrCopyin, 1, execArgData, 5, []byte{0x01, 0x02, 0x03, 0x04, 0x05},
   228  				callID("test$array1"), ExecNoCopyout, 1, execArgAddr64, 0,
   229  				execInstrEOF,
   230  			},
   231  			nil,
   232  		},
   233  		{
   234  			"test$array2(&(0x7f0000000000)={0x42, \"aaaaaaaabbbbbbbbccccccccdddddddd\", 0x43})",
   235  			[]any{
   236  				execInstrCopyin, 0, execArgConst, 2, 0x42,
   237  				execInstrCopyin, 2, execArgData, 16, []byte{
   238  					0xaa, 0xaa, 0xaa, 0xaa,
   239  					0xbb, 0xbb, 0xbb, 0xbb,
   240  					0xcc, 0xcc, 0xcc, 0xcc,
   241  					0xdd, 0xdd, 0xdd, 0xdd,
   242  				},
   243  				execInstrCopyin, 18, execArgConst, 2, 0x43,
   244  				callID("test$array2"), ExecNoCopyout, 1, execArgAddr64, 0,
   245  				execInstrEOF,
   246  			},
   247  			nil,
   248  		},
   249  		{
   250  			"test$end0(&(0x7f0000000000)={0x42, 0x42, 0x42, 0x42})",
   251  			[]any{
   252  				execInstrCopyin, 0, execArgConst, 1, 0x42,
   253  				execInstrCopyin, 1, execArgConst, 2 | 1<<8, 0x42,
   254  				execInstrCopyin, 3, execArgConst, 4 | 1<<8, 0x42,
   255  				execInstrCopyin, 7, execArgConst, 8 | 1<<8, 0x42,
   256  				callID("test$end0"), ExecNoCopyout, 1, execArgAddr64, 0,
   257  				execInstrEOF,
   258  			},
   259  			nil,
   260  		},
   261  		{
   262  			"test$end1(&(0x7f0000000000)={0xe, 0x42, 0x1})",
   263  			[]any{
   264  				execInstrCopyin, 0, execArgConst, 2 | 1<<8, 0xe,
   265  				execInstrCopyin, 2, execArgConst, 4 | 1<<8, 0x42,
   266  				execInstrCopyin, 6, execArgConst, 8 | 1<<8, 0x1,
   267  				callID("test$end1"), ExecNoCopyout, 1, execArgAddr64, 0,
   268  				execInstrEOF,
   269  			},
   270  			nil,
   271  		},
   272  		{
   273  			"test$bf0(&(0x7f0000000000)={0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42})",
   274  			[]any{
   275  				execInstrCopyin, 0, execArgConst, 2 | 0<<16 | 10<<24, 0x42,
   276  				execInstrCopyin, 8, execArgConst, 8, 0x42,
   277  				execInstrCopyin, 16, execArgConst, 2 | 0<<16 | 5<<24, 0x42,
   278  				execInstrCopyin, 16, execArgConst, 2 | 5<<16 | 6<<24, 0x42,
   279  				execInstrCopyin, 16, execArgConst, 4 | 11<<16 | 15<<24, 0x42,
   280  				execInstrCopyin, 20, execArgConst, 2 | 0<<16 | 11<<24, 0x42,
   281  				execInstrCopyin, 22, execArgConst, 2 | 1<<8 | 0<<16 | 11<<24, 0x42,
   282  				execInstrCopyin, 24, execArgConst, 1, 0x42,
   283  				callID("test$bf0"), ExecNoCopyout, 1, execArgAddr64, 0,
   284  				execInstrEOF,
   285  			},
   286  			&ExecProg{
   287  				Calls: []ExecCall{
   288  					{
   289  						Meta:  target.SyscallMap["test$bf0"],
   290  						Index: ExecNoCopyout,
   291  						Args: []ExecArg{
   292  							ExecArgConst{
   293  								Size:  ptrSize,
   294  								Value: dataOffset,
   295  							},
   296  						},
   297  						Copyin: []ExecCopyin{
   298  							{
   299  								Addr: dataOffset + 0,
   300  								Arg: ExecArgConst{
   301  									Size:           2,
   302  									Value:          0x42,
   303  									BitfieldOffset: 0,
   304  									BitfieldLength: 10,
   305  								},
   306  							},
   307  							{
   308  								Addr: dataOffset + 8,
   309  								Arg: ExecArgConst{
   310  									Size:  8,
   311  									Value: 0x42,
   312  								},
   313  							},
   314  							{
   315  								Addr: dataOffset + 16,
   316  								Arg: ExecArgConst{
   317  									Size:           2,
   318  									Value:          0x42,
   319  									BitfieldOffset: 0,
   320  									BitfieldLength: 5,
   321  								},
   322  							},
   323  							{
   324  								Addr: dataOffset + 16,
   325  								Arg: ExecArgConst{
   326  									Size:           2,
   327  									Value:          0x42,
   328  									BitfieldOffset: 5,
   329  									BitfieldLength: 6,
   330  								},
   331  							},
   332  							{
   333  								Addr: dataOffset + 16,
   334  								Arg: ExecArgConst{
   335  									Size:           4,
   336  									Value:          0x42,
   337  									BitfieldOffset: 11,
   338  									BitfieldLength: 15,
   339  								},
   340  							},
   341  							{
   342  								Addr: dataOffset + 20,
   343  								Arg: ExecArgConst{
   344  									Size:           2,
   345  									Value:          0x42,
   346  									BitfieldOffset: 0,
   347  									BitfieldLength: 11,
   348  								},
   349  							},
   350  							{
   351  								Addr: dataOffset + 22,
   352  								Arg: ExecArgConst{
   353  									Size:           2,
   354  									Format:         FormatBigEndian,
   355  									Value:          0x42,
   356  									BitfieldOffset: 0,
   357  									BitfieldLength: 11,
   358  								},
   359  							},
   360  							{
   361  								Addr: dataOffset + 24,
   362  								Arg: ExecArgConst{
   363  									Size:  1,
   364  									Value: 0x42,
   365  								},
   366  							},
   367  						},
   368  					},
   369  				},
   370  			},
   371  		},
   372  		{
   373  			"test$bf1(&(0x7f0000000000)={{0x42, 0x42, 0x42}, 0x42})",
   374  			[]any{
   375  				execInstrCopyin, 0, execArgConst, 4 | 0<<16 | 10<<24, 0x42,
   376  				execInstrCopyin, 0, execArgConst, 4 | 10<<16 | 10<<24, 0x42,
   377  				execInstrCopyin, 0, execArgConst, 4 | 20<<16 | 10<<24, 0x42,
   378  				execInstrCopyin, 4, execArgConst, 1, 0x42,
   379  				callID("test$bf1"), ExecNoCopyout, 1, execArgAddr64, 0,
   380  				execInstrEOF,
   381  			},
   382  			nil,
   383  		},
   384  		{
   385  			"test$res1(0xffff)",
   386  			[]any{
   387  				callID("test$res1"), ExecNoCopyout, 1, execArgConst, 4, 0xffff,
   388  				execInstrEOF,
   389  			},
   390  			nil,
   391  		},
   392  		{
   393  			"test$opt3(0x0)",
   394  			[]any{
   395  				callID("test$opt3"), ExecNoCopyout, 1, execArgConst, 8 | 4<<32, 0x64,
   396  				execInstrEOF,
   397  			},
   398  			nil,
   399  		},
   400  		{
   401  			// Special value that translates to 0 for all procs.
   402  			"test$opt3(0xffffffffffffffff)",
   403  			[]any{
   404  				callID("test$opt3"), ExecNoCopyout, 1, execArgConst, 8, 0,
   405  				execInstrEOF,
   406  			},
   407  			nil,
   408  		},
   409  		{
   410  			// NULL pointer must be encoded os 0.
   411  			"test$opt1(0x0)",
   412  			[]any{
   413  				callID("test$opt1"), ExecNoCopyout, 1, execArgAddr64, -dataOffset,
   414  				execInstrEOF,
   415  			},
   416  			nil,
   417  		},
   418  		{
   419  			"test$align7(&(0x7f0000000000)={{0x1, 0x2, 0x3, 0x4, 0x5, 0x6}, 0x42})",
   420  			[]any{
   421  				execInstrCopyin, 0, execArgConst, 1 | 0<<16 | 1<<24, 0x1,
   422  				execInstrCopyin, 0, execArgConst, 1 | 1<<16 | 1<<24, 0x2,
   423  				execInstrCopyin, 0, execArgConst, 1 | 2<<16 | 1<<24, 0x3,
   424  				execInstrCopyin, 0, execArgConst, 2 | 3<<16 | 1<<24, 0x4,
   425  				execInstrCopyin, 0, execArgConst, 2 | 4<<16 | 1<<24, 0x5,
   426  				execInstrCopyin, 0, execArgConst, 2 | 5<<16 | 1<<24, 0x6,
   427  				execInstrCopyin, 8, execArgConst, 1, 0x42,
   428  				callID("test$align7"), ExecNoCopyout, 1, execArgAddr64, 0,
   429  				execInstrEOF,
   430  			},
   431  			nil,
   432  		},
   433  		{
   434  			"test$excessive_fields1(0x0)",
   435  			[]any{
   436  				callID("test$excessive_fields1"), ExecNoCopyout, 1, execArgAddr64, -dataOffset,
   437  				execInstrEOF,
   438  			},
   439  			nil,
   440  		},
   441  		{
   442  			"test$excessive_fields1(0xffffffffffffffff)",
   443  			[]any{
   444  				callID("test$excessive_fields1"), ExecNoCopyout, 1, execArgAddr64, 0xffffffffffffffff - dataOffset,
   445  				execInstrEOF,
   446  			},
   447  			nil,
   448  		},
   449  		{
   450  			"test$excessive_fields1(0xfffffffffffffffe)",
   451  			[]any{
   452  				callID("test$excessive_fields1"), ExecNoCopyout, 1, execArgAddr64, 0x9999999999999999 - dataOffset,
   453  				execInstrEOF,
   454  			},
   455  			nil,
   456  		},
   457  		{
   458  			"test$csum_ipv4_tcp(&(0x7f0000000000)={{0x0, 0x1, 0x2}, {{0x0}, \"ab\"}})",
   459  			[]any{
   460  				execInstrCopyin, 0, execArgConst, 2, 0x0,
   461  				execInstrCopyin, 2, execArgConst, 4 | 1<<8, 0x1,
   462  				execInstrCopyin, 6, execArgConst, 4 | 1<<8, 0x2,
   463  				execInstrCopyin, 10, execArgConst, 2, 0x0,
   464  				execInstrCopyin, 12, execArgData, 1, []byte{0xab},
   465  				execInstrCopyin, 10, execArgCsum, 2, ExecArgCsumInet, 5,
   466  				ExecArgCsumChunkData, 2, 4,
   467  				ExecArgCsumChunkData, 6, 4,
   468  				ExecArgCsumChunkConst, 0x0600, 2,
   469  				ExecArgCsumChunkConst, 0x0300, 2,
   470  				ExecArgCsumChunkData, 10, 3,
   471  				execInstrCopyin, 0, execArgCsum, 2, ExecArgCsumInet, 1,
   472  				ExecArgCsumChunkData, 0, 10,
   473  				callID("test$csum_ipv4_tcp"), ExecNoCopyout, 1, execArgAddr64, 0,
   474  				execInstrEOF,
   475  			},
   476  			&ExecProg{
   477  				Calls: []ExecCall{
   478  					{
   479  						Meta:  target.SyscallMap["test$csum_ipv4_tcp"],
   480  						Index: ExecNoCopyout,
   481  						Args: []ExecArg{
   482  							ExecArgConst{
   483  								Value: dataOffset,
   484  								Size:  8,
   485  							},
   486  						},
   487  						Copyin: []ExecCopyin{
   488  							{
   489  								Addr: dataOffset,
   490  								Arg: ExecArgConst{
   491  									Value: 0,
   492  									Size:  2,
   493  								},
   494  							},
   495  							{
   496  								Addr: dataOffset + 2,
   497  								Arg: ExecArgConst{
   498  									Value:  1,
   499  									Size:   4,
   500  									Format: FormatBigEndian,
   501  								},
   502  							},
   503  							{
   504  								Addr: dataOffset + 6,
   505  								Arg: ExecArgConst{
   506  									Value:  2,
   507  									Size:   4,
   508  									Format: FormatBigEndian,
   509  								},
   510  							},
   511  							{
   512  								Addr: dataOffset + 10,
   513  								Arg: ExecArgConst{
   514  									Value: 0,
   515  									Size:  2,
   516  								},
   517  							},
   518  							{
   519  								Addr: dataOffset + 12,
   520  								Arg: ExecArgData{
   521  									Data: []byte{0xab},
   522  								},
   523  							},
   524  							{
   525  								Addr: dataOffset + 10,
   526  								Arg: ExecArgCsum{
   527  									Size: 2,
   528  									Kind: ExecArgCsumInet,
   529  									Chunks: []ExecCsumChunk{
   530  										{
   531  											Kind:  ExecArgCsumChunkData,
   532  											Value: dataOffset + 2,
   533  											Size:  4,
   534  										},
   535  										{
   536  											Kind:  ExecArgCsumChunkData,
   537  											Value: dataOffset + 6,
   538  											Size:  4,
   539  										},
   540  										{
   541  											Kind:  ExecArgCsumChunkConst,
   542  											Value: 0x0600,
   543  											Size:  2,
   544  										},
   545  										{
   546  											Kind:  ExecArgCsumChunkConst,
   547  											Value: 0x0300,
   548  											Size:  2,
   549  										},
   550  										{
   551  											Kind:  ExecArgCsumChunkData,
   552  											Value: dataOffset + 10,
   553  											Size:  3,
   554  										},
   555  									},
   556  								},
   557  							},
   558  							{
   559  								Addr: dataOffset,
   560  								Arg: ExecArgCsum{
   561  									Size: 2,
   562  									Kind: ExecArgCsumInet,
   563  									Chunks: []ExecCsumChunk{
   564  										{
   565  											Kind:  ExecArgCsumChunkData,
   566  											Value: dataOffset,
   567  											Size:  10,
   568  										},
   569  									},
   570  								},
   571  							},
   572  						},
   573  					},
   574  				},
   575  			},
   576  		},
   577  		{
   578  			`test() (fail_nth: 3)
   579  test() (fail_nth: 4)
   580  test() (async, rerun: 10)
   581  `,
   582  			[]any{
   583  				execInstrSetProps, 3, 0, 0,
   584  				callID("test"), ExecNoCopyout, 0,
   585  				execInstrSetProps, 4, 0, 0,
   586  				callID("test"), ExecNoCopyout, 0,
   587  				execInstrSetProps, 0, 1, 10,
   588  				callID("test"), ExecNoCopyout, 0,
   589  				execInstrEOF,
   590  			},
   591  			&ExecProg{
   592  				Calls: []ExecCall{
   593  					{
   594  						Meta:  target.SyscallMap["test"],
   595  						Index: ExecNoCopyout,
   596  						Props: CallProps{3, false, 0},
   597  					},
   598  					{
   599  						Meta:  target.SyscallMap["test"],
   600  						Index: ExecNoCopyout,
   601  						Props: CallProps{4, false, 0},
   602  					},
   603  					{
   604  						Meta:  target.SyscallMap["test"],
   605  						Index: ExecNoCopyout,
   606  						Props: CallProps{0, true, 10},
   607  					},
   608  				},
   609  			},
   610  		},
   611  		{
   612  			`test$res3(&(0x7f0000000010)=<r0=>0x0)
   613  test$res1(r0)
   614  `,
   615  			[]any{
   616  				callID("test$res3"), ExecNoCopyout, 1, execArgAddr64, 0x10,
   617  				execInstrCopyout, 0, 0x10, 4,
   618  				callID("test$res1"), ExecNoCopyout, 1, execArgResult, 4, 0, 0, 0, 0xffff,
   619  				execInstrEOF,
   620  			},
   621  			&ExecProg{
   622  				Calls: []ExecCall{
   623  					{
   624  						Meta:  target.SyscallMap["test$res3"],
   625  						Index: ExecNoCopyout,
   626  						Args: []ExecArg{
   627  							ExecArgConst{
   628  								Value: dataOffset + 0x10,
   629  								Size:  8,
   630  							},
   631  						},
   632  						Copyout: []ExecCopyout{
   633  							{
   634  								Index: 0,
   635  								Addr:  dataOffset + 0x10,
   636  								Size:  4,
   637  							},
   638  						},
   639  					},
   640  					{
   641  						Meta:  target.SyscallMap["test$res1"],
   642  						Index: ExecNoCopyout,
   643  						Args: []ExecArg{
   644  							ExecArgResult{
   645  								Size:    4,
   646  								Index:   0,
   647  								Default: 0xffff,
   648  							},
   649  						},
   650  					},
   651  				},
   652  				Vars: []uint64{0xffff},
   653  			},
   654  		},
   655  	}
   656  
   657  	for i, test := range tests {
   658  		i, test := i, test
   659  		t.Run(fmt.Sprintf("%v:%v", i, test.prog), func(t *testing.T) {
   660  			p, err := target.Deserialize([]byte(test.prog), Strict)
   661  			if err != nil {
   662  				t.Fatalf("failed to deserialize prog %v: %v", i, err)
   663  			}
   664  			data, err := p.SerializeForExec()
   665  			if err != nil {
   666  				t.Fatalf("failed to serialize: %v", err)
   667  			}
   668  			want := binary.AppendVarint(nil, int64(len(p.Calls)))
   669  			for _, e := range test.serialized {
   670  				switch elem := e.(type) {
   671  				case uint64:
   672  					want = binary.AppendVarint(want, int64(elem))
   673  				case int:
   674  					want = binary.AppendVarint(want, int64(elem))
   675  				case []byte:
   676  					want = append(want, elem...)
   677  				default:
   678  					t.Fatalf("unexpected elem type %T %#v", e, e)
   679  				}
   680  			}
   681  			if !bytes.Equal(data, want) {
   682  				t.Logf("want: %v", test.serialized)
   683  				t.Logf("got:  %q", data)
   684  				t.Fatalf("mismatch")
   685  			}
   686  			decoded, err := target.DeserializeExec(data, nil)
   687  			if err != nil {
   688  				t.Fatal(err)
   689  			}
   690  			if test.decoded != nil && !reflect.DeepEqual(decoded, *test.decoded) {
   691  				t.Logf("want: %#v", *test.decoded)
   692  				t.Logf("got:  %#v", decoded)
   693  				t.Fatalf("decoded mismatch")
   694  			}
   695  		})
   696  	}
   697  }
   698  
   699  func TestSerializeForExecOverflow(t *testing.T) {
   700  	target := initTargetTest(t, "test", "64")
   701  	type Test struct {
   702  		name     string
   703  		overflow bool
   704  		gen      func(w *bytes.Buffer)
   705  	}
   706  	tests := []Test{
   707  		{
   708  			name:     "few-resources",
   709  			overflow: false,
   710  			gen: func(w *bytes.Buffer) {
   711  				for i := 0; i < execMaxCommands-10; i++ {
   712  					fmt.Fprintf(w, "r%v = test$res0()\ntest$res1(r%v)\n", i, i)
   713  				}
   714  			},
   715  		},
   716  		{
   717  			name:     "overflow-resources",
   718  			overflow: true,
   719  			gen: func(w *bytes.Buffer) {
   720  				for i := 0; i < execMaxCommands+1; i++ {
   721  					fmt.Fprintf(w, "r%v = test$res0()\ntest$res1(r%v)\n", i, i)
   722  				}
   723  			},
   724  		},
   725  		{
   726  			name:     "no-verflow-buffer",
   727  			overflow: false,
   728  			gen: func(w *bytes.Buffer) {
   729  				fmt.Fprintf(w, "r0 = test$res0()\n")
   730  				for i := 0; i < 58e3; i++ {
   731  					fmt.Fprintf(w, "test$res1(r0)\n")
   732  				}
   733  			},
   734  		},
   735  		{
   736  			name:     "overflow-buffer",
   737  			overflow: true,
   738  			gen: func(w *bytes.Buffer) {
   739  				fmt.Fprintf(w, "r0 = test$res0()\n")
   740  				for i := 0; i < 4e5; i++ {
   741  					fmt.Fprintf(w, "test$res1(r0)\n")
   742  				}
   743  			},
   744  		},
   745  	}
   746  	for _, test := range tests {
   747  		t.Run(test.name, func(t *testing.T) {
   748  			data := new(bytes.Buffer)
   749  			test.gen(data)
   750  			p, err := target.Deserialize(data.Bytes(), Strict)
   751  			if err != nil {
   752  				t.Fatal(err)
   753  			}
   754  			_, err = p.SerializeForExec()
   755  			if test.overflow && err == nil {
   756  				t.Fatalf("want overflow but got no error")
   757  			}
   758  			if !test.overflow && err != nil {
   759  				t.Fatalf("want no overflow but got %v", err)
   760  			}
   761  		})
   762  	}
   763  }