github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/pkg/report/linux_test.go (about)

     1  // Copyright 2015 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 report
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"os"
    10  	"path/filepath"
    11  	"reflect"
    12  	"runtime"
    13  	"strings"
    14  	"testing"
    15  
    16  	"github.com/google/syzkaller/pkg/mgrconfig"
    17  	"github.com/google/syzkaller/pkg/osutil"
    18  	"github.com/google/syzkaller/pkg/symbolizer"
    19  	"github.com/google/syzkaller/sys/targets"
    20  )
    21  
    22  func TestLinuxIgnores(t *testing.T) {
    23  	cfg := &mgrconfig.Config{
    24  		Derived: mgrconfig.Derived{
    25  			TargetOS:   targets.Linux,
    26  			TargetArch: targets.AMD64,
    27  		},
    28  	}
    29  	reporter, err := NewReporter(cfg)
    30  	if err != nil {
    31  		t.Fatal(err)
    32  	}
    33  	cfg.Ignores = []string{"BUG: bug3"}
    34  	reporter1, err := NewReporter(cfg)
    35  	if err != nil {
    36  		t.Fatal(err)
    37  	}
    38  	cfg.Ignores = []string{"BUG: bug3", "BUG: bug1"}
    39  	reporter2, err := NewReporter(cfg)
    40  	if err != nil {
    41  		t.Fatal(err)
    42  	}
    43  	cfg.Ignores = []string{"BUG: bug3", "BUG: bug1", "BUG: bug2"}
    44  	reporter3, err := NewReporter(cfg)
    45  	if err != nil {
    46  		t.Fatal(err)
    47  	}
    48  
    49  	const log = `
    50  [    0.000000] BUG: bug1
    51  [    0.000000] BUG: bug2
    52  	`
    53  	if !reporter.ContainsCrash([]byte(log)) {
    54  		t.Fatalf("no crash")
    55  	}
    56  	if rep := reporter.Parse([]byte(log)); rep.Title != "BUG: bug1" {
    57  		t.Fatalf("want `BUG: bug1`, found `%v`", rep.Title)
    58  	}
    59  
    60  	if !reporter1.ContainsCrash([]byte(log)) {
    61  		t.Fatalf("no crash")
    62  	}
    63  	if rep := reporter1.Parse([]byte(log)); rep.Title != "BUG: bug1" {
    64  		t.Fatalf("want `BUG: bug1`, found `%v`", rep.Title)
    65  	}
    66  
    67  	if !reporter2.ContainsCrash([]byte(log)) {
    68  		t.Fatalf("no crash")
    69  	}
    70  	if rep := reporter2.Parse([]byte(log)); rep.Title != "BUG: bug2" {
    71  		t.Fatalf("want `BUG: bug2`, found `%v`", rep.Title)
    72  	}
    73  
    74  	if reporter3.ContainsCrash([]byte(log)) {
    75  		t.Fatalf("found crash, should be ignored")
    76  	}
    77  	if rep := reporter3.Parse([]byte(log)); rep != nil {
    78  		t.Fatalf("found `%v`, should be ignored", rep.Title)
    79  	}
    80  }
    81  
    82  func TestLinuxSymbolizeLine(t *testing.T) {
    83  	tests := []struct {
    84  		line   string
    85  		result string
    86  	}{
    87  		// Normal symbolization.
    88  		{
    89  			"[ 2713.153531]  [<ffffffff82d1b1d9>] foo+0x101/0x185\n",
    90  			"[ 2713.153531]  [<ffffffff82d1b1d9>] foo+0x101/0x185 foo.c:555\n",
    91  		},
    92  		{
    93  			"RIP: 0010:[<ffffffff8188c0e6>]  [<ffffffff8188c0e6>]  foo+0x101/0x185\n",
    94  			"RIP: 0010:[<ffffffff8188c0e6>]  [<ffffffff8188c0e6>]  foo+0x101/0x185 foo.c:550\n",
    95  		},
    96  		// Strip "./" file prefix.
    97  		{
    98  			"[ 2713.153531]  [<ffffffff82d1b1d9>] foo+0x111/0x185\n",
    99  			"[ 2713.153531]  [<ffffffff82d1b1d9>] foo+0x111/0x185 foo.h:111\n",
   100  		},
   101  		// Needs symbolization, but symbolizer returns nothing.
   102  		{
   103  			"[ 2713.153531]  [<ffffffff82d1b1d9>] foo+0x121/0x185\n",
   104  			"[ 2713.153531]  [<ffffffff82d1b1d9>] foo+0x121/0x185\n",
   105  		},
   106  		// Needs symbolization, but symbolizer returns error.
   107  		{
   108  			"[ 2713.153531]  [<ffffffff82d1b1d9>] foo+0x131/0x185\n",
   109  			"[ 2713.153531]  [<ffffffff82d1b1d9>] foo+0x131/0x185\n",
   110  		},
   111  		// Needs symbolization, but symbol is missing.
   112  		{
   113  			"[ 2713.153531]  [<ffffffff82d1b1d9>] bar+0x131/0x185\n",
   114  			"[ 2713.153531]  [<ffffffff82d1b1d9>] bar+0x131/0x185\n",
   115  		},
   116  		// Bad offset.
   117  		{
   118  			"[ 2713.153531]  [<ffffffff82d1b1d9>] bar+0xffffffffffffffffffff/0x185\n",
   119  			"[ 2713.153531]  [<ffffffff82d1b1d9>] bar+0xffffffffffffffffffff/0x185\n",
   120  		},
   121  		// Should not be symbolized.
   122  		{
   123  			"WARNING: CPU: 2 PID: 2636 at ipc/shm.c:162 foo+0x101/0x185\n",
   124  			"WARNING: CPU: 2 PID: 2636 at ipc/shm.c:162 foo+0x101/0x185 foo.c:555\n",
   125  		},
   126  		// Tricky function name.
   127  		{
   128  			"    [<ffffffff84e5bea0>] do_ipv6_setsockopt.isra.7.part.3+0x101/0x2830 \n",
   129  			"    [<ffffffff84e5bea0>] do_ipv6_setsockopt.isra.7.part.3+0x101/0x2830 net.c:111 \n",
   130  		},
   131  		// Old KASAN frame format (with tab).
   132  		{
   133  			"[   50.419727] 	baz+0x101/0x200\n",
   134  			"[   50.419727] 	baz+0x101/0x200 baz.c:100\n",
   135  		},
   136  		// Inlined frames.
   137  		{
   138  			"    [<ffffffff84e5bea0>] foo+0x141/0x185\n",
   139  			"    [<ffffffff84e5bea0>] inlined1 net.c:111 [inline]\n" +
   140  				"    [<ffffffff84e5bea0>] inlined2 mm.c:222 [inline]\n" +
   141  				"    [<ffffffff84e5bea0>] foo+0x141/0x185 kasan.c:333\n",
   142  		},
   143  		// Several symbols with the same name.
   144  		{
   145  			"[<ffffffff82d1b1d9>] baz+0x101/0x200\n",
   146  			"[<ffffffff82d1b1d9>] baz+0x101/0x200 baz.c:100\n",
   147  		},
   148  	}
   149  	symbols := map[string][]symbolizer.Symbol{
   150  		"foo": {
   151  			{Addr: 0x1000000, Size: 0x190},
   152  		},
   153  		"do_ipv6_setsockopt.isra.7.part.3": {
   154  			{Addr: 0x2000000, Size: 0x2830},
   155  		},
   156  		"baz": {
   157  			{Addr: 0x3000000, Size: 0x100},
   158  			{Addr: 0x4000000, Size: 0x200},
   159  			{Addr: 0x5000000, Size: 0x300},
   160  		},
   161  	}
   162  	symb := func(bin string, pc uint64) ([]symbolizer.Frame, error) {
   163  		if bin != "vmlinux" {
   164  			return nil, fmt.Errorf("unknown pc 0x%x", pc)
   165  		}
   166  		switch pc {
   167  		case 0x1000100:
   168  			return []symbolizer.Frame{
   169  				{
   170  					File: "/linux/foo.c",
   171  					Line: 555,
   172  				},
   173  			}, nil
   174  		case 0x1000101:
   175  			return []symbolizer.Frame{
   176  				{
   177  					File: "/linux/foo.c",
   178  					Line: 550,
   179  				},
   180  			}, nil
   181  		case 0x1000110:
   182  			return []symbolizer.Frame{
   183  				{
   184  					File: "/linux/./foo.h",
   185  					Line: 111,
   186  				},
   187  			}, nil
   188  		case 0x1000120:
   189  			return nil, nil
   190  		case 0x1000130:
   191  			return nil, fmt.Errorf("unknown pc 0x%x", pc)
   192  		case 0x2000100:
   193  			return []symbolizer.Frame{
   194  				{
   195  					File: "/linux/net.c",
   196  					Line: 111,
   197  				},
   198  			}, nil
   199  		case 0x1000140:
   200  			return []symbolizer.Frame{
   201  				{
   202  					Func:   "inlined1",
   203  					File:   "/linux/net.c",
   204  					Line:   111,
   205  					Inline: true,
   206  				},
   207  				{
   208  					Func:   "inlined2",
   209  					File:   "/linux/mm.c",
   210  					Line:   222,
   211  					Inline: true,
   212  				},
   213  				{
   214  					Func:   "noninlined3",
   215  					File:   "/linux/kasan.c",
   216  					Line:   333,
   217  					Inline: false,
   218  				},
   219  			}, nil
   220  		case 0x4000100:
   221  			return []symbolizer.Frame{
   222  				{
   223  					File: "/linux/baz.c",
   224  					Line: 100,
   225  				},
   226  			}, nil
   227  		default:
   228  			return nil, fmt.Errorf("unknown pc 0x%x", pc)
   229  		}
   230  	}
   231  	for i, test := range tests {
   232  		t.Run(fmt.Sprint(i), func(t *testing.T) {
   233  			result := symbolizeLine(symb, symbols, "vmlinux", "/linux", []byte(test.line))
   234  			if test.result != string(result) {
   235  				t.Errorf("want %q\n\t     get %q", test.result, string(result))
   236  			}
   237  		})
   238  	}
   239  }
   240  
   241  func prepareLinuxReporter(t *testing.T, arch string) (*Reporter, *linux) {
   242  	cfg := &mgrconfig.Config{
   243  		Derived: mgrconfig.Derived{
   244  			TargetOS:   targets.Linux,
   245  			TargetArch: arch,
   246  			SysTarget:  targets.Get(targets.Linux, arch),
   247  		},
   248  	}
   249  	reporter, err := NewReporter(cfg)
   250  	if err != nil {
   251  		t.Errorf("failed to create a reporter instance for %#v: %v", arch, err)
   252  	}
   253  	return reporter, reporter.impl.(*linux)
   254  }
   255  
   256  func TestParseLinuxOpcodes(t *testing.T) {
   257  	type opcodeTest struct {
   258  		arch   string
   259  		input  string
   260  		output *parsedOpcodes
   261  	}
   262  
   263  	tests := []opcodeTest{
   264  		// LE tests.
   265  		{
   266  			arch:  targets.AMD64,
   267  			input: "31 c0 <e8> f5 bf f7 ff",
   268  			output: &parsedOpcodes{
   269  				rawBytes: []byte{0x31, 0xc0, 0xe8, 0xf5, 0xbf, 0xf7, 0xff},
   270  				offset:   2,
   271  			},
   272  		},
   273  		{
   274  			arch:  targets.AMD64,
   275  			input: "c031 <f5e8> f7bf fff7 00ff",
   276  			output: &parsedOpcodes{
   277  				rawBytes: []byte{0x31, 0xc0, 0xe8, 0xf5, 0xbf, 0xf7, 0xf7, 0xff, 0xff, 0x00},
   278  				offset:   2,
   279  			},
   280  		},
   281  		{
   282  			arch:  targets.AMD64,
   283  			input: "(33221100) 77665544",
   284  			output: &parsedOpcodes{
   285  				rawBytes: []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77},
   286  				offset:   0,
   287  			},
   288  		},
   289  		// BE tests.
   290  		{
   291  			arch:  targets.S390x,
   292  			input: "31 c0 <e8> f5 bf f7 ff",
   293  			output: &parsedOpcodes{
   294  				rawBytes: []byte{0x31, 0xc0, 0xe8, 0xf5, 0xbf, 0xf7, 0xff},
   295  				offset:   2,
   296  			},
   297  		},
   298  		{
   299  			arch:  targets.S390x,
   300  			input: "31c0 <e8f5> bff5 f7ff ff00",
   301  			output: &parsedOpcodes{
   302  				rawBytes: []byte{0x31, 0xc0, 0xe8, 0xf5, 0xbf, 0xf5, 0xf7, 0xff, 0xff, 0x00},
   303  				offset:   2,
   304  			},
   305  		},
   306  		{
   307  			arch:  targets.S390x,
   308  			input: "<00112233> 44556677",
   309  			output: &parsedOpcodes{
   310  				rawBytes: []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77},
   311  				offset:   0,
   312  			},
   313  		},
   314  		// ARM Thumb tests.
   315  		{
   316  			arch:  targets.ARM,
   317  			input: "0011 (2233) 4455",
   318  			output: &parsedOpcodes{
   319  				rawBytes:       []byte{0x11, 0x00, 0x33, 0x22, 0x55, 0x44},
   320  				decompileFlags: FlagForceArmThumbMode,
   321  				offset:         2,
   322  			},
   323  		},
   324  		{
   325  			arch:  targets.ARM,
   326  			input: "(33221100) 77665544",
   327  			output: &parsedOpcodes{
   328  				rawBytes: []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77},
   329  				offset:   0,
   330  			},
   331  		},
   332  		// Bad input tests.
   333  		{
   334  			arch:   targets.AMD64,
   335  			input:  "00 11 22 33",
   336  			output: nil,
   337  		},
   338  		{
   339  			arch:   targets.AMD64,
   340  			input:  "aa bb <cc> zz",
   341  			output: nil,
   342  		},
   343  		{
   344  			arch:   targets.AMD64,
   345  			input:  "<00> 11 22 <33>",
   346  			output: nil,
   347  		},
   348  		{
   349  			arch:   targets.AMD64,
   350  			input:  "aa  <bb>",
   351  			output: nil,
   352  		},
   353  		{
   354  			arch:   targets.AMD64,
   355  			input:  "001122 334455",
   356  			output: nil,
   357  		},
   358  		{
   359  			arch:   targets.AMD64,
   360  			input:  "0011223344556677",
   361  			output: nil,
   362  		},
   363  	}
   364  
   365  	for idx, test := range tests {
   366  		test := test // Capturing the value.
   367  		t.Run(fmt.Sprintf("%s/%v", test.arch, idx), func(t *testing.T) {
   368  			t.Parallel()
   369  			_, linuxReporter := prepareLinuxReporter(t, test.arch)
   370  			ret, err := linuxReporter.parseOpcodes(test.input)
   371  			if test.output == nil && err == nil {
   372  				t.Errorf("expected an error on input %#v", test)
   373  			} else if test.output != nil && err != nil {
   374  				t.Errorf("unexpected error %v on input %#v", err, test.input)
   375  			} else if test.output != nil && !reflect.DeepEqual(ret, *test.output) {
   376  				t.Errorf("expected: %#v, got: %#v", test.output, ret)
   377  			}
   378  		})
   379  	}
   380  }
   381  
   382  func TestDisassemblyInReports(t *testing.T) {
   383  	if runtime.GOOS != targets.Linux {
   384  		t.Skipf("the test is meant to be run only under Linux")
   385  	}
   386  
   387  	archPath := filepath.Join("testdata", "linux", "decompile")
   388  	subFolders, err := os.ReadDir(archPath)
   389  	if err != nil {
   390  		t.Fatalf("disassembly reports failed: %v", err)
   391  	}
   392  
   393  	for _, obj := range subFolders {
   394  		if !obj.IsDir() {
   395  			continue
   396  		}
   397  		reporter, linuxReporter := prepareLinuxReporter(t, obj.Name())
   398  		if linuxReporter.target.BrokenCompiler != "" {
   399  			t.Skip("skipping the test due to broken cross-compiler:\n" + linuxReporter.target.BrokenCompiler)
   400  		}
   401  
   402  		testPath := filepath.Join(archPath, obj.Name())
   403  		testFiles, err := os.ReadDir(testPath)
   404  		if err != nil {
   405  			t.Fatalf("failed to list tests for %v: %v", obj.Name(), err)
   406  		}
   407  
   408  		for _, file := range testFiles {
   409  			if !strings.HasSuffix(file.Name(), ".in") {
   410  				continue
   411  			}
   412  			filePath := filepath.Join(testPath, strings.TrimSuffix(file.Name(), ".in"))
   413  			t.Run(obj.Name()+"/"+file.Name(), func(t *testing.T) {
   414  				testDisassembly(t, reporter, linuxReporter, filePath)
   415  			})
   416  		}
   417  	}
   418  }
   419  
   420  func testDisassembly(t *testing.T, reporter *Reporter, linuxReporter *linux, testFilePrefix string) {
   421  	t.Parallel()
   422  
   423  	input, err := os.ReadFile(testFilePrefix + ".in")
   424  	if err != nil {
   425  		t.Fatalf("failed to read input file: %v", err)
   426  	}
   427  
   428  	report := reporter.Parse(input)
   429  	if report == nil {
   430  		t.Fatalf("no bug report was found")
   431  	}
   432  
   433  	result := linuxReporter.decompileOpcodes(input, report)
   434  	if *flagUpdate {
   435  		osutil.WriteFile(testFilePrefix+".out", result)
   436  	}
   437  
   438  	output, err := os.ReadFile(testFilePrefix + ".out")
   439  	if err != nil {
   440  		t.Fatalf("failed to read output file: %v", err)
   441  	}
   442  
   443  	if !bytes.Equal(output, result) {
   444  		t.Fatalf("expected:\n%s\ngot:\n%s", output, result)
   445  	}
   446  }