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

     1  // Copyright 2023 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  	"fmt"
     9  	"strings"
    10  	"testing"
    11  
    12  	"github.com/stretchr/testify/assert"
    13  )
    14  
    15  func TestGenerateConditionalFields(t *testing.T) {
    16  	// Ensure that we reach different combinations of conditional fields.
    17  	target, rs, _ := initRandomTargetTest(t, "test", "64")
    18  	ct := target.DefaultChoiceTable()
    19  	r := newRand(target, rs)
    20  
    21  	combinations := [][]bool{
    22  		{false, false},
    23  		{false, false},
    24  	}
    25  	b2i := func(b bool) int {
    26  		if b {
    27  			return 1
    28  		}
    29  		return 0
    30  	}
    31  	for i := 0; i < 150; i++ {
    32  		p := genConditionalFieldProg(target, ct, r)
    33  		f1, f2 := parseConditionalStructCall(t, p.Calls[len(p.Calls)-1])
    34  		combinations[b2i(f1)][b2i(f2)] = true
    35  	}
    36  	for _, first := range []int{0, 1} {
    37  		for _, second := range []int{0, 1} {
    38  			if !combinations[first][second] {
    39  				t.Fatalf("did not generate a combination f1=%v f2=%v", first, second)
    40  			}
    41  		}
    42  	}
    43  }
    44  
    45  func TestConditionalResources(t *testing.T) {
    46  	// Let's stress test the code and rely on various internal checks.
    47  	target, rs, _ := initRandomTargetTest(t, "test", "64")
    48  	ct := target.BuildChoiceTable(nil, map[*Syscall]bool{
    49  		target.SyscallMap["test$create_cond_resource"]: true,
    50  		target.SyscallMap["test$use_cond_resource"]:    true,
    51  	})
    52  	iters := 500
    53  	if testing.Short() {
    54  		iters /= 10
    55  	}
    56  	for i := 0; i < iters; i++ {
    57  		p := target.Generate(rs, 10, ct)
    58  		p.Mutate(rs, 10, ct, nil, nil)
    59  	}
    60  }
    61  
    62  func TestMutateConditionalFields(t *testing.T) {
    63  	target, rs, _ := initRandomTargetTest(t, "test", "64")
    64  	ct := target.DefaultChoiceTable()
    65  	r := newRand(target, rs)
    66  	iters := 500
    67  	if testing.Short() {
    68  		iters /= 10
    69  	}
    70  	nonAny := 0
    71  	for i := 0; i < iters; i++ {
    72  		prog := genConditionalFieldProg(target, ct, r)
    73  		for j := 0; j < 5; j++ {
    74  			prog.Mutate(rs, 10, ct, nil, nil)
    75  			hasAny := bytes.Contains(prog.Serialize(), []byte("ANY="))
    76  			if hasAny {
    77  				// No sense to verify these.
    78  				break
    79  			}
    80  			nonAny++
    81  			validateConditionalProg(t, prog)
    82  		}
    83  	}
    84  	assert.Greater(t, nonAny, 10) // Just in case.
    85  }
    86  
    87  func TestEvaluateConditionalFields(t *testing.T) {
    88  	target := InitTargetTest(t, "test", "64")
    89  	tests := []struct {
    90  		good []string
    91  		bad  []string
    92  	}{
    93  		{
    94  			good: []string{
    95  				`test$conditional_struct(&AUTO={0x0, @void, @void})`,
    96  				`test$conditional_struct(&AUTO={0x4, @void, @value=0x123})`,
    97  				`test$conditional_struct(&AUTO={0x6, @value={AUTO}, @value=0x123})`,
    98  			},
    99  			bad: []string{
   100  				`test$conditional_struct(&AUTO={0x0, @void, @value=0x123})`,
   101  				`test$conditional_struct(&AUTO={0x0, @value={AUTO}, @value=0x123})`,
   102  			},
   103  		},
   104  		{
   105  			good: []string{
   106  				`test$parent_conditions(&AUTO={0x0, @without_flag1=0x123, {0x0, @void}})`,
   107  				`test$parent_conditions(&AUTO={0x2, @with_flag1=0x123, {0x0, @void}})`,
   108  				`test$parent_conditions(&AUTO={0x4, @without_flag1=0x123, {0x0, @value=0x0}})`,
   109  				`test$parent_conditions(&AUTO={0x6, @with_flag1=0x123, {0x0, @value=0x0}})`,
   110  				// The @without_flag1 option is still possible.
   111  				`test$parent_conditions(&AUTO={0x2, @without_flag1=0x123, {0x0, @void}})`,
   112  			},
   113  			bad: []string{
   114  				`test$parent_conditions(&AUTO={0x0, @with_flag1=0x123, {0x0, @void}})`,
   115  				`test$parent_conditions(&AUTO={0x4, @with_flag1=0x123, {0x0, @void}})`,
   116  				`test$parent_conditions(&AUTO={0x4, @with_flag1=0x123, {0x0, @value=0x0}})`,
   117  			},
   118  		},
   119  	}
   120  
   121  	for i, test := range tests {
   122  		t.Run(fmt.Sprintf("%d", i), func(tt *testing.T) {
   123  			for _, good := range test.good {
   124  				_, err := target.Deserialize([]byte(good), Strict)
   125  				assert.NoError(tt, err)
   126  			}
   127  			for _, bad := range test.bad {
   128  				_, err := target.Deserialize([]byte(bad), Strict)
   129  				assert.ErrorIs(tt, err, ErrViolatedConditions,
   130  					"prog: %s", bad)
   131  			}
   132  		})
   133  	}
   134  }
   135  
   136  func TestConditionalMinimize(t *testing.T) {
   137  	tests := []struct {
   138  		input  string
   139  		pred   func(*Prog, int) bool
   140  		output string
   141  	}{
   142  		{
   143  			input: `test$conditional_struct(&AUTO={0x6, @value={AUTO}, @value=0x123})`,
   144  			pred: func(p *Prog, _ int) bool {
   145  				return len(p.Calls) == 1 && p.Calls[0].Meta.Name == `test$conditional_struct`
   146  			},
   147  			output: `test$conditional_struct(0x0)`,
   148  		},
   149  		{
   150  			input: `test$conditional_struct(&(0x7f0000000040)={0x6, @value, @value=0x123})`,
   151  			pred: func(p *Prog, _ int) bool {
   152  				return bytes.Contains(p.Serialize(), []byte("0x123"))
   153  			},
   154  			// We don't drop individual bits from integers, so there's no chance
   155  			// to turn 0x6 into 0x4.
   156  			output: `test$conditional_struct(&(0x7f0000000040)={0x6, @value, @value=0x123})`,
   157  		},
   158  		{
   159  			input: `test$conditional_struct_minimize(&(0x7f0000000040)={0x1, @value=0xaa, 0x1, @value=0xbb})`,
   160  			pred: func(p *Prog, _ int) bool {
   161  				return bytes.Contains(p.Serialize(), []byte("0xaa"))
   162  			},
   163  			output: `test$conditional_struct_minimize(&(0x7f0000000040)={0x1, @value=0xaa})`,
   164  		},
   165  		{
   166  			input: `test$conditional_struct_minimize(&(0x7f0000000040)={0x1, @value=0xaa, 0x1, @value=0xbb})`,
   167  			pred: func(p *Prog, _ int) bool {
   168  				return bytes.Contains(p.Serialize(), []byte("0xbb"))
   169  			},
   170  			output: `test$conditional_struct_minimize(&(0x7f0000000040)={0x0, @void, 0x1, @value=0xbb})`,
   171  		},
   172  		{
   173  			input: `test$conditional_struct_minimize(&(0x7f0000000040)={0x1, @value=0xaa, 0x1, @value=0xbb})`,
   174  			pred: func(p *Prog, _ int) bool {
   175  				serialized := p.Serialize()
   176  				return bytes.Contains(serialized, []byte("0xaa")) &&
   177  					bytes.Contains(serialized, []byte("0xbb"))
   178  			},
   179  			output: `test$conditional_struct_minimize(&(0x7f0000000040)={0x1, @value=0xaa, 0x1, @value=0xbb})`,
   180  		},
   181  	}
   182  
   183  	for i, test := range tests {
   184  		t.Run(fmt.Sprintf("%d", i), func(tt *testing.T) {
   185  			target, err := GetTarget("test", "64")
   186  			assert.NoError(tt, err)
   187  			p, err := target.Deserialize([]byte(test.input), Strict)
   188  			assert.NoError(tt, err)
   189  			p1, _ := Minimize(p, 0, false, test.pred)
   190  			res := p1.Serialize()
   191  			assert.Equal(tt, test.output, strings.TrimSpace(string(res)))
   192  		})
   193  	}
   194  }
   195  
   196  func genConditionalFieldProg(target *Target, ct *ChoiceTable, r *randGen) *Prog {
   197  	s := newState(target, ct, nil)
   198  	calls := r.generateParticularCall(s, target.SyscallMap["test$conditional_struct"])
   199  	return &Prog{
   200  		Target: target,
   201  		Calls:  calls,
   202  	}
   203  }
   204  
   205  const FLAG1 = 2
   206  const FLAG2 = 4
   207  
   208  func validateConditionalProg(t *testing.T, p *Prog) {
   209  	for _, call := range p.Calls {
   210  		if call.Meta.Name == "test$conditional_struct" {
   211  			parseConditionalStructCall(t, call)
   212  		}
   213  	}
   214  }
   215  
   216  // Validates a test$conditional_struct call.
   217  func parseConditionalStructCall(t *testing.T, c *Call) (bool, bool) {
   218  	if c.Meta.Name != "test$conditional_struct" {
   219  		t.Fatalf("generated wrong call %v", c.Meta.Name)
   220  	}
   221  	if len(c.Args) != 1 {
   222  		t.Fatalf("generated wrong number of args %v", len(c.Args))
   223  	}
   224  	va, ok := c.Args[0].(*PointerArg)
   225  	if !ok {
   226  		t.Fatalf("expected PointerArg: %v", c.Args[0])
   227  	}
   228  	if va.Res == nil {
   229  		// Cannot validate.
   230  		return false, false
   231  	}
   232  	ga, ok := va.Res.(*GroupArg)
   233  	if !ok {
   234  		t.Fatalf("expected GroupArg: %v", va.Res)
   235  	}
   236  	if len(ga.Inner) != 3 {
   237  		t.Fatalf("wrong number of struct args %v", len(ga.Inner))
   238  	}
   239  	mask := ga.Inner[0].(*ConstArg).Val
   240  	f1 := ga.Inner[1].(*UnionArg).Index == 0
   241  	f2 := ga.Inner[2].(*UnionArg).Index == 0
   242  	assert.Equal(t, mask&FLAG1 != 0, f1, "flag1 must only be set if mask&FLAG1")
   243  	assert.Equal(t, mask&FLAG2 != 0, f2, "flag2 must only be set if mask&FLAG2")
   244  	return f1, f2
   245  }
   246  
   247  func TestConditionalUnionFields(t *testing.T) {
   248  	// Ensure that we reach different combinations of conditional fields.
   249  	target, rs, _ := initRandomTargetTest(t, "test", "64")
   250  	ct := target.DefaultChoiceTable()
   251  	r := newRand(target, rs)
   252  
   253  	var zeroU1, zeroU2 int
   254  	var nonzeroU2 int
   255  	for i := 0; i < 100; i++ {
   256  		s := newState(target, ct, nil)
   257  		p := &Prog{
   258  			Target: target,
   259  			Calls:  r.generateParticularCall(s, target.SyscallMap["test$conditional_union"]),
   260  		}
   261  		if len(p.Calls) > 1 {
   262  			continue
   263  		}
   264  		text := string(p.SerializeVerbose())
   265  		if strings.Contains(text, "{0x0,") {
   266  			if strings.Contains(text, "@u1") {
   267  				zeroU1++
   268  			} else if strings.Contains(text, "@u2") {
   269  				zeroU2++
   270  			}
   271  		} else {
   272  			assert.NotContains(t, text, "@u1")
   273  			nonzeroU2++
   274  		}
   275  	}
   276  	assert.Greater(t, zeroU1, 0)
   277  	assert.Greater(t, zeroU2, 0)
   278  	assert.Greater(t, nonzeroU2, 0)
   279  }