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